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  }