github.com/btcsuite/btcd@v0.24.0/cmd/findcheckpoint/findcheckpoint.go (about) 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. 4 5 package main 6 7 import ( 8 "fmt" 9 "os" 10 "path/filepath" 11 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" 16 ) 17 18 const blockDbNamePrefix = "blocks" 19 20 var ( 21 cfg *config 22 ) 23 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) 31 if err != nil { 32 return nil, err 33 } 34 return db, nil 35 } 36 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 41 // checkpoints. 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) 45 if err != nil { 46 return nil, err 47 } 48 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 53 // already one. 54 latestCheckpoint = &chaincfg.Checkpoint{ 55 Hash: activeNetParams.GenesisHash, 56 Height: 0, 57 } 58 } 59 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) 70 } 71 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 { 76 requiredHeight = 1 77 } 78 79 // Indeterminate progress setup. 80 numBlocksToTest := block.Height() - requiredHeight 81 progressInterval := (numBlocksToTest / 100) + 1 // min 1 82 fmt.Print("Searching for candidates") 83 defer fmt.Println() 84 85 // Loop backwards through the chain to find checkpoint candidates. 86 candidates := make([]*chaincfg.Checkpoint, 0, cfg.NumCandidates) 87 numTested := int32(0) 88 for len(candidates) < cfg.NumCandidates && block.Height() > requiredHeight { 89 // Display progress. 90 if numTested%progressInterval == 0 { 91 fmt.Print(".") 92 } 93 94 // Determine if this block is a checkpoint candidate. 95 isCandidate, err := chain.IsCheckpointCandidate(block) 96 if err != nil { 97 return nil, err 98 } 99 100 // All checks passed, so this node seems like a reasonable 101 // checkpoint candidate. 102 if isCandidate { 103 checkpoint := chaincfg.Checkpoint{ 104 Height: block.Height(), 105 Hash: block.Hash(), 106 } 107 candidates = append(candidates, &checkpoint) 108 } 109 110 prevHash := &block.MsgBlock().Header.PrevBlock 111 block, err = chain.BlockByHash(prevHash) 112 if err != nil { 113 return nil, err 114 } 115 numTested++ 116 } 117 return candidates, nil 118 } 119 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) { 124 if cfg.UseGoOutput { 125 fmt.Printf("Candidate %d -- {%d, newShaHashFromStr(\"%v\")},\n", 126 candidateNum, checkpoint.Height, checkpoint.Hash) 127 return 128 } 129 130 fmt.Printf("Candidate %d -- Height: %d, Hash: %v\n", candidateNum, 131 checkpoint.Height, checkpoint.Hash) 132 133 } 134 135 func main() { 136 // Load configuration and parse command line. 137 tcfg, _, err := loadConfig() 138 if err != nil { 139 return 140 } 141 cfg = tcfg 142 143 // Load the block database. 144 db, err := loadBlockDB() 145 if err != nil { 146 fmt.Fprintln(os.Stderr, "failed to load database:", err) 147 return 148 } 149 defer db.Close() 150 151 // Setup chain. Ignore notifications since they aren't needed for this 152 // util. 153 chain, err := blockchain.New(&blockchain.Config{ 154 DB: db, 155 ChainParams: activeNetParams, 156 TimeSource: blockchain.NewMedianTime(), 157 }) 158 if err != nil { 159 fmt.Fprintf(os.Stderr, "failed to initialize chain: %v\n", err) 160 return 161 } 162 163 // Get the latest block hash and height from the database and report 164 // status. 165 best := chain.BestSnapshot() 166 fmt.Printf("Block database loaded with block height %d\n", best.Height) 167 168 // Find checkpoint candidates. 169 candidates, err := findCandidates(chain, &best.Hash) 170 if err != nil { 171 fmt.Fprintln(os.Stderr, "Unable to identify candidates:", err) 172 return 173 } 174 175 // No candidates. 176 if len(candidates) == 0 { 177 fmt.Println("No candidates found.") 178 return 179 } 180 181 // Show the candidates. 182 for i, checkpoint := range candidates { 183 showCandidate(i+1, checkpoint) 184 } 185 }