1 // Copyright (c) 2013-2016 The btcsuite developers
2 // Use of this source code is governed by an ISC
3 // license that can be found in the LICENSE file.
12 "github.com/btcsuite/btcd/blockchain"
13 "github.com/btcsuite/btcd/chaincfg"
14 "github.com/btcsuite/btcd/chaincfg/chainhash"
15 "github.com/btcsuite/btcd/database"
18 const blockDbNamePrefix = "blocks"
24 // loadBlockDB opens the block database and returns a handle to it.
25 func loadBlockDB() (database.DB, error) {
26 // The database name is based on the database type.
27 dbName := blockDbNamePrefix + "_" + cfg.DbType
28 dbPath := filepath.Join(cfg.DataDir, dbName)
29 fmt.Printf("Loading block database from '%s'\n", dbPath)
30 db, err := database.Open(cfg.DbType, dbPath, activeNetParams.Net)
37 // findCandidates searches the chain backwards for checkpoint candidates and
38 // returns a slice of found candidates, if any. It also stops searching for
39 // candidates at the last checkpoint that is already hard coded into btcchain
40 // since there is no point in finding candidates before already existing
42 func findCandidates(chain *blockchain.BlockChain, latestHash *chainhash.Hash) ([]*chaincfg.Checkpoint, error) {
43 // Start with the latest block of the main chain.
44 block, err := chain.BlockByHash(latestHash)
49 // Get the latest known checkpoint.
50 latestCheckpoint := chain.LatestCheckpoint()
51 if latestCheckpoint == nil {
52 // Set the latest checkpoint to the genesis block if there isn't
54 latestCheckpoint = &chaincfg.Checkpoint{
55 Hash: activeNetParams.GenesisHash,
60 // The latest known block must be at least the last known checkpoint
61 // plus required checkpoint confirmations.
62 checkpointConfirmations := int32(blockchain.CheckpointConfirmations)
63 requiredHeight := latestCheckpoint.Height + checkpointConfirmations
64 if block.Height() < requiredHeight {
65 return nil, fmt.Errorf("the block database is only at height "+
66 "%d which is less than the latest checkpoint height "+
67 "of %d plus required confirmations of %d",
68 block.Height(), latestCheckpoint.Height,
69 checkpointConfirmations)
72 // For the first checkpoint, the required height is any block after the
73 // genesis block, so long as the chain has at least the required number
74 // of confirmations (which is enforced above).
75 if len(activeNetParams.Checkpoints) == 0 {
79 // Indeterminate progress setup.
80 numBlocksToTest := block.Height() - requiredHeight
81 progressInterval := (numBlocksToTest / 100) + 1 // min 1
82 fmt.Print("Searching for candidates")
85 // Loop backwards through the chain to find checkpoint candidates.
86 candidates := make([]*chaincfg.Checkpoint, 0, cfg.NumCandidates)
88 for len(candidates) < cfg.NumCandidates && block.Height() > requiredHeight {
90 if numTested%progressInterval == 0 {
94 // Determine if this block is a checkpoint candidate.
95 isCandidate, err := chain.IsCheckpointCandidate(block)
100 // All checks passed, so this node seems like a reasonable
101 // checkpoint candidate.
103 checkpoint := chaincfg.Checkpoint{
104 Height: block.Height(),
107 candidates = append(candidates, &checkpoint)
110 prevHash := &block.MsgBlock().Header.PrevBlock
111 block, err = chain.BlockByHash(prevHash)
117 return candidates, nil
120 // showCandidate display a checkpoint candidate using and output format
121 // determined by the configuration parameters. The Go syntax output
122 // uses the format the btcchain code expects for checkpoints added to the list.
123 func showCandidate(candidateNum int, checkpoint *chaincfg.Checkpoint) {
125 fmt.Printf("Candidate %d -- {%d, newShaHashFromStr(\"%v\")},\n",
126 candidateNum, checkpoint.Height, checkpoint.Hash)
130 fmt.Printf("Candidate %d -- Height: %d, Hash: %v\n", candidateNum,
131 checkpoint.Height, checkpoint.Hash)
136 // Load configuration and parse command line.
137 tcfg, _, err := loadConfig()
143 // Load the block database.
144 db, err := loadBlockDB()
146 fmt.Fprintln(os.Stderr, "failed to load database:", err)
151 // Setup chain. Ignore notifications since they aren't needed for this
153 chain, err := blockchain.New(&blockchain.Config{
155 ChainParams: activeNetParams,
156 TimeSource: blockchain.NewMedianTime(),
159 fmt.Fprintf(os.Stderr, "failed to initialize chain: %v\n", err)
163 // Get the latest block hash and height from the database and report
165 best := chain.BestSnapshot()
166 fmt.Printf("Block database loaded with block height %d\n", best.Height)
168 // Find checkpoint candidates.
169 candidates, err := findCandidates(chain, &best.Hash)
171 fmt.Fprintln(os.Stderr, "Unable to identify candidates:", err)
176 if len(candidates) == 0 {
177 fmt.Println("No candidates found.")
181 // Show the candidates.
182 for i, checkpoint := range candidates {
183 showCandidate(i+1, checkpoint)