github.com/dashpay/godash@v0.0.0-20160726055534-e038a21e0e3d/cmd/findcheckpoint/findcheckpoint.go (about) 1 // Copyright (c) 2013-2016 The btcsuite developers 2 // Copyright (c) 2016 The Dash developers 3 // Use of this source code is governed by an ISC 4 // license that can be found in the LICENSE file. 5 6 package main 7 8 import ( 9 "fmt" 10 "os" 11 "path/filepath" 12 13 "github.com/dashpay/godash/blockchain" 14 "github.com/dashpay/godash/chaincfg" 15 "github.com/dashpay/godash/database" 16 "github.com/dashpay/godash/wire" 17 ) 18 19 const blockDbNamePrefix = "blocks" 20 21 var ( 22 cfg *config 23 ) 24 25 // loadBlockDB opens the block database and returns a handle to it. 26 func loadBlockDB() (database.DB, error) { 27 // The database name is based on the database type. 28 dbName := blockDbNamePrefix + "_" + cfg.DbType 29 dbPath := filepath.Join(cfg.DataDir, dbName) 30 fmt.Printf("Loading block database from '%s'\n", dbPath) 31 db, err := database.Open(cfg.DbType, dbPath, activeNetParams.Net) 32 if err != nil { 33 return nil, err 34 } 35 return db, nil 36 } 37 38 // findCandidates searches the chain backwards for checkpoint candidates and 39 // returns a slice of found candidates, if any. It also stops searching for 40 // candidates at the last checkpoint that is already hard coded into btcchain 41 // since there is no point in finding candidates before already existing 42 // checkpoints. 43 func findCandidates(chain *blockchain.BlockChain, latestHash *wire.ShaHash) ([]*chaincfg.Checkpoint, error) { 44 // Start with the latest block of the main chain. 45 block, err := chain.BlockByHash(latestHash) 46 if err != nil { 47 return nil, err 48 } 49 50 // Get the latest known checkpoint. 51 latestCheckpoint := chain.LatestCheckpoint() 52 if latestCheckpoint == nil { 53 // Set the latest checkpoint to the genesis block if there isn't 54 // already one. 55 latestCheckpoint = &chaincfg.Checkpoint{ 56 Hash: activeNetParams.GenesisHash, 57 Height: 0, 58 } 59 } 60 61 // The latest known block must be at least the last known checkpoint 62 // plus required checkpoint confirmations. 63 checkpointConfirmations := int32(blockchain.CheckpointConfirmations) 64 requiredHeight := latestCheckpoint.Height + checkpointConfirmations 65 if block.Height() < requiredHeight { 66 return nil, fmt.Errorf("the block database is only at height "+ 67 "%d which is less than the latest checkpoint height "+ 68 "of %d plus required confirmations of %d", 69 block.Height(), latestCheckpoint.Height, 70 checkpointConfirmations) 71 } 72 73 // For the first checkpoint, the required height is any block after the 74 // genesis block, so long as the chain has at least the required number 75 // of confirmations (which is enforced above). 76 if len(activeNetParams.Checkpoints) == 0 { 77 requiredHeight = 1 78 } 79 80 // Indeterminate progress setup. 81 numBlocksToTest := block.Height() - requiredHeight 82 progressInterval := (numBlocksToTest / 100) + 1 // min 1 83 fmt.Print("Searching for candidates") 84 defer fmt.Println() 85 86 // Loop backwards through the chain to find checkpoint candidates. 87 candidates := make([]*chaincfg.Checkpoint, 0, cfg.NumCandidates) 88 numTested := int32(0) 89 for len(candidates) < cfg.NumCandidates && block.Height() > requiredHeight { 90 // Display progress. 91 if numTested%progressInterval == 0 { 92 fmt.Print(".") 93 } 94 95 // Determine if this block is a checkpoint candidate. 96 isCandidate, err := chain.IsCheckpointCandidate(block) 97 if err != nil { 98 return nil, err 99 } 100 101 // All checks passed, so this node seems like a reasonable 102 // checkpoint candidate. 103 if isCandidate { 104 checkpoint := chaincfg.Checkpoint{ 105 Height: block.Height(), 106 Hash: block.Sha(), 107 } 108 candidates = append(candidates, &checkpoint) 109 } 110 111 prevHash := &block.MsgBlock().Header.PrevBlock 112 block, err = chain.BlockByHash(prevHash) 113 if err != nil { 114 return nil, err 115 } 116 numTested++ 117 } 118 return candidates, nil 119 } 120 121 // showCandidate display a checkpoint candidate using and output format 122 // determined by the configuration parameters. The Go syntax output 123 // uses the format the btcchain code expects for checkpoints added to the list. 124 func showCandidate(candidateNum int, checkpoint *chaincfg.Checkpoint) { 125 if cfg.UseGoOutput { 126 fmt.Printf("Candidate %d -- {%d, newShaHashFromStr(\"%v\")},\n", 127 candidateNum, checkpoint.Height, checkpoint.Hash) 128 return 129 } 130 131 fmt.Printf("Candidate %d -- Height: %d, Hash: %v\n", candidateNum, 132 checkpoint.Height, checkpoint.Hash) 133 134 } 135 136 func main() { 137 // Load configuration and parse command line. 138 tcfg, _, err := loadConfig() 139 if err != nil { 140 return 141 } 142 cfg = tcfg 143 144 // Load the block database. 145 db, err := loadBlockDB() 146 if err != nil { 147 fmt.Fprintln(os.Stderr, "failed to load database:", err) 148 return 149 } 150 defer db.Close() 151 152 // Setup chain. Ignore notifications since they aren't needed for this 153 // util. 154 chain, err := blockchain.New(&blockchain.Config{ 155 DB: db, 156 ChainParams: activeNetParams, 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 }