kaspad/cmd/findcheckpoint/findcheckpoint.go
stasatdaglabs 37cd482db3 [DEV-75] Update UTXO model to work with Diffs (#53)
* [DEV-75] Removed fetchEntryByHash, which was no longer used.

* [DEV-75] Removed a few functions in manager.go that weren't used by anything.

* [DEV-75] checkConnectBlock will soon not accept a utxoViewpoint, so removed the call to view.Tip() so that it could be deleted.

* [DEV-75] Got rid of tips in UtxoViewpoint.

* [DEV-75] Added the full UTXO set to the virtual block.

* [DEV-75] Implemented UTXO-wrangling when adding a new block.

* [DEV-75] Added isCoinbase to utxoEntry creation.

* [DEV-75] Added blockHeight to utxoEntry creation.

* Implemented fetching the fullUTXOSet from the database.

* [DEV-75] Simplified DAGState because almost all of the state in it was unnecessary. Also got rid of dbDAGState.

* [DEV-75] Made the process around adding a new block, UTXO-wise, much safer.

* [DEV-75] Implemented melding the virtual UTXO diff to the database.

* [DEV-75] Fixed utxoSet loading from the wrong bucket.

* [DEV-75] Began pruning utxoviewpoint.go. Replaced FetchUtxoEntry with a fullUTXOSet-based GetUTXOEntry.

* [DEV-75] Removed fetchUtxos.

* [DEV-75] Moved GetUTXOEntry into the virtual block.

* [DEV-75] Updated IndexManager to not use utxoViewpoints.

* [DEV-75] Fixed some bad login in restoreUTXO involving nodeDiffs.

* [DEV-75] Got rid of the UTXO spend journal, which wasn't used anywhere.

* [DEV-75] Moved some STXO-related validation logic out of connectToDAG and into checkConnectBlock.

* [DEV-75] Renamed UtxoXxx to UTXOXxx. Removed a bunch of methods that were no longer used by anything.

* [DEV-75] Another Utxo -> UTXO rename.

* [DEV-75] Removed IsModified from UTXOView, which was not used anywhere.

* [DEV-75] Renamed nodeDiff to provisionalNode. Added a bunch of comments.

* [DEV-75] Removed the test for genesis in pastUTXO, since it can never happen.

* [DEV-75] Wrote tests for errors in pastUTXO.

* [DEV-75] Wrote tests for errors in restoreUTXO.

* [DEV-75] Improved testErrorThroughPatching.

* [DEV-75] Wrote tests for errors in verifyAndBuildUTXO.

* [DEV-75] Used TipHashes instead of tips().hashes(), fixed comments in a few places, and brought back an error return that I erroneously removed.

* [DEV-75] Removed UTXO set logs.

* [DEV-75] Recreated add/remove/contains functions for utxoCollection.

* [DEV-75] Changed newUTXOEntry to use an object initializer.

* [DEV-75] Changed the UTXO bucket version to 1.

* Added a comment that clarifies that the index is not initialized before initDAGState is called.

* [DEV-75] Renamed GetVirtualBlock to VirtualBlock.

* [DEV-75] Removed superfluous variables.

* [DEV-75] Combined connectBlockToParents with updateParentsDiffs.

* [DEV-75] Removed another superfluous variable.

* [DEV-75] In pastUTXO, first fetch transactions from the database and only then add them.

* [DEV-75] Reworded the comment for commit().

* [DEV-75] Made all the connectUTXO subfunctions not BlockDAG methods.

* [DEV-75] Updated the comment for connectUTXO.

* [DEV-75] Updated the comment explaining why we're creating provisionalNodes.

* [DEV-75] Removed a couple of unnecessary calls to toProvisionalNode.

* [DEV-75] Renamed connectUTXO to applyUTXOChanges.

* [DEV-75] Replaced allProvisionalNodes with provisionalNodeSet, an object that holds provisionalNodes for this particular call to applyUTXOChanges.

* [DEV-75] Changed createProvisionalNode to accept a boolean "withParents" instead of relying on the caller to supply the node's parents or an empty set.

* [DEV-75] Made most applyUTXOChanges subfunctions be methods on provisionalNode.

* [DEV-75] Fixed a couple of bad comments.

* [DEV-75] Added descriptive error messages to callers of applyUTXOChanges.

* [DEV-75] Fixed weird English.

* [DEV-75] Replaced DAGState with a slice of DAG tip hashes.

* [DEV-75] In createProvisionalNode, changed withParents to withRelatives to avoid certain kinds of attacks.

* [DEV-75] Renamed createdProvisionalNode to newProvisionalNode.

* [DEV-75] Added precalculating the amount of transactions inside a new block's blue set.

* [DEV-75] Pruned unnecessary variable.
2018-08-30 16:00:03 +03:00

187 lines
5.3 KiB
Go

// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"os"
"path/filepath"
"github.com/daglabs/btcd/blockdag"
"github.com/daglabs/btcd/dagconfig"
"github.com/daglabs/btcd/dagconfig/daghash"
"github.com/daglabs/btcd/database"
)
const blockDbNamePrefix = "blocks"
var (
cfg *config
)
// loadBlockDB opens the block database and returns a handle to it.
func loadBlockDB() (database.DB, error) {
// The database name is based on the database type.
dbName := blockDbNamePrefix + "_" + cfg.DbType
dbPath := filepath.Join(cfg.DataDir, dbName)
fmt.Printf("Loading block database from '%s'\n", dbPath)
db, err := database.Open(cfg.DbType, dbPath, activeNetParams.Net)
if err != nil {
return nil, err
}
return db, nil
}
// findCandidates searches the DAG backwards for checkpoint candidates and
// returns a slice of found candidates, if any. It also stops searching for
// candidates at the last checkpoint that is already hard coded since there
// is no point in finding candidates before already existing checkpoints.
func findCandidates(dag *blockdag.BlockDAG, selectedTipHash *daghash.Hash) ([]*dagconfig.Checkpoint, error) {
// Start with the selected tip.
block, err := dag.BlockByHash(selectedTipHash)
if err != nil {
return nil, err
}
// Get the latest known checkpoint.
latestCheckpoint := dag.LatestCheckpoint()
if latestCheckpoint == nil {
// Set the latest checkpoint to the genesis block if there isn't
// already one.
latestCheckpoint = &dagconfig.Checkpoint{
Hash: activeNetParams.GenesisHash,
Height: 0,
}
}
// The latest known block must be at least the last known checkpoint
// plus required checkpoint confirmations.
checkpointConfirmations := int32(blockdag.CheckpointConfirmations)
requiredHeight := latestCheckpoint.Height + checkpointConfirmations
if block.Height() < requiredHeight {
return nil, fmt.Errorf("the block database is only at height "+
"%d which is less than the latest checkpoint height "+
"of %d plus required confirmations of %d",
block.Height(), latestCheckpoint.Height,
checkpointConfirmations)
}
// For the first checkpoint, the required height is any block after the
// genesis block, so long as the DAG has at least the required number
// of confirmations (which is enforced above).
if len(activeNetParams.Checkpoints) == 0 {
requiredHeight = 1
}
// Indeterminate progress setup.
numBlocksToTest := block.Height() - requiredHeight
progressInterval := (numBlocksToTest / 100) + 1 // min 1
fmt.Print("Searching for candidates")
defer fmt.Println()
// Loop backwards through the DAG to find checkpoint candidates.
candidates := make([]*dagconfig.Checkpoint, 0, cfg.NumCandidates)
numTested := int32(0)
for len(candidates) < cfg.NumCandidates && block.Height() > requiredHeight {
// Display progress.
if numTested%progressInterval == 0 {
fmt.Print(".")
}
// Determine if this block is a checkpoint candidate.
isCandidate, err := dag.IsCheckpointCandidate(block)
if err != nil {
return nil, err
}
// All checks passed, so this node seems like a reasonable
// checkpoint candidate.
if isCandidate {
checkpoint := dagconfig.Checkpoint{
Height: block.Height(),
Hash: block.Hash(),
}
candidates = append(candidates, &checkpoint)
}
prevBlockHashes := block.MsgBlock().Header.PrevBlocks
selectedBlockHash := &prevBlockHashes[0]
block, err = dag.BlockByHash(selectedBlockHash)
if err != nil {
return nil, err
}
numTested++
}
return candidates, nil
}
// showCandidate display a checkpoint candidate using and output format
// determined by the configuration parameters. The Go syntax output
// uses the format the btcchain code expects for checkpoints added to the list.
func showCandidate(candidateNum int, checkpoint *dagconfig.Checkpoint) {
if cfg.UseGoOutput {
fmt.Printf("Candidate %d -- {%d, newShaHashFromStr(\"%v\")},\n",
candidateNum, checkpoint.Height, checkpoint.Hash)
return
}
fmt.Printf("Candidate %d -- Height: %d, Hash: %v\n", candidateNum,
checkpoint.Height, checkpoint.Hash)
}
func main() {
// Load configuration and parse command line.
tcfg, _, err := loadConfig()
if err != nil {
return
}
cfg = tcfg
// Load the block database.
db, err := loadBlockDB()
if err != nil {
fmt.Fprintln(os.Stderr, "failed to load database:", err)
return
}
defer db.Close()
// Setup chain. Ignore notifications since they aren't needed for this
// util.
dag, err := blockdag.New(&blockdag.Config{
DB: db,
DAGParams: activeNetParams,
TimeSource: blockdag.NewMedianTime(),
})
if err != nil {
fmt.Fprintf(os.Stderr, "failed to initialize chain: %v\n", err)
return
}
// Get the latest block hash and height from the database and report
// status.
virtualBlock := dag.VirtualBlock()
fmt.Printf("Block database loaded with block height %d\n", virtualBlock.SelectedTipHeight())
// Find checkpoint candidates.
selectedTipHash := virtualBlock.SelectedTipHash()
candidates, err := findCandidates(dag, &selectedTipHash)
if err != nil {
fmt.Fprintln(os.Stderr, "Unable to identify candidates:", err)
return
}
// No candidates.
if len(candidates) == 0 {
fmt.Println("No candidates found.")
return
}
// Show the candidates.
for i, checkpoint := range candidates {
showCandidate(i+1, checkpoint)
}
}