bd4e64d1d4
This is mostly a backport of some of the same modifications made in Decred along with a few additional things cleaned up. In particular, this updates the code to make use of the new chainhash package. Also, since this required API changes anyways and the hash algorithm is no longer tied specifically to SHA, all other functions throughout the code base which had "Sha" in their name have been changed to Hash so they are not incorrectly implying the hash algorithm. The following is an overview of the changes: - Remove the wire.ShaHash type - Update all references to wire.ShaHash to the new chainhash.Hash type - Rename the following functions and update all references: - wire.BlockHeader.BlockSha -> BlockHash - wire.MsgBlock.BlockSha -> BlockHash - wire.MsgBlock.TxShas -> TxHashes - wire.MsgTx.TxSha -> TxHash - blockchain.ShaHashToBig -> HashToBig - peer.ShaFunc -> peer.HashFunc - Rename all variables that included sha in their name to include hash instead - Update for function name changes in other dependent packages such as btcutil - Update copyright dates on all modified files - Update glide.lock file to use the required version of btcutil
184 lines
5.2 KiB
Go
184 lines
5.2 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/btcsuite/btcd/blockchain"
|
|
"github.com/btcsuite/btcd/chaincfg"
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
"github.com/btcsuite/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 chain 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 into btcchain
|
|
// since there is no point in finding candidates before already existing
|
|
// checkpoints.
|
|
func findCandidates(chain *blockchain.BlockChain, latestHash *chainhash.Hash) ([]*chaincfg.Checkpoint, error) {
|
|
// Start with the latest block of the main chain.
|
|
block, err := chain.BlockByHash(latestHash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Get the latest known checkpoint.
|
|
latestCheckpoint := chain.LatestCheckpoint()
|
|
if latestCheckpoint == nil {
|
|
// Set the latest checkpoint to the genesis block if there isn't
|
|
// already one.
|
|
latestCheckpoint = &chaincfg.Checkpoint{
|
|
Hash: activeNetParams.GenesisHash,
|
|
Height: 0,
|
|
}
|
|
}
|
|
|
|
// The latest known block must be at least the last known checkpoint
|
|
// plus required checkpoint confirmations.
|
|
checkpointConfirmations := int32(blockchain.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 chain 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 chain to find checkpoint candidates.
|
|
candidates := make([]*chaincfg.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 := chain.IsCheckpointCandidate(block)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// All checks passed, so this node seems like a reasonable
|
|
// checkpoint candidate.
|
|
if isCandidate {
|
|
checkpoint := chaincfg.Checkpoint{
|
|
Height: block.Height(),
|
|
Hash: block.Hash(),
|
|
}
|
|
candidates = append(candidates, &checkpoint)
|
|
}
|
|
|
|
prevHash := &block.MsgBlock().Header.PrevBlock
|
|
block, err = chain.BlockByHash(prevHash)
|
|
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 *chaincfg.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.
|
|
chain, err := blockchain.New(&blockchain.Config{
|
|
DB: db,
|
|
ChainParams: activeNetParams,
|
|
})
|
|
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.
|
|
best := chain.BestSnapshot()
|
|
fmt.Printf("Block database loaded with block height %d\n", best.Height)
|
|
|
|
// Find checkpoint candidates.
|
|
candidates, err := findCandidates(chain, best.Hash)
|
|
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)
|
|
}
|
|
}
|