// newBlockManager returns a new bitcoin block manager. // Use Start to begin processing asynchronous block and inv updates. func newBlockManager(s *server) (*blockManager, error) { bm := blockManager{ server: s, blockPeer: make(map[btcwire.ShaHash]*peer), requestedTxns: make(map[btcwire.ShaHash]bool), requestedBlocks: make(map[btcwire.ShaHash]bool), lastBlockLogTime: time.Now(), msgChan: make(chan interface{}, cfg.MaxPeers*3), quit: make(chan bool), } bm.blockChain = btcchain.New(s.db, s.btcnet, bm.handleNotifyMsg) bm.blockChain.DisableCheckpoints(cfg.DisableCheckpoints) if cfg.DisableCheckpoints { log.Info("BMGR: Checkpoints are disabled") } log.Infof("BMGR: Generating initial block node index. This may " + "take a while...") err := bm.blockChain.GenerateInitialIndex() if err != nil { return nil, err } log.Infof("BMGR: Block index generation complete") return &bm, nil }
// newBlockImporter returns a new importer for the provided file reader seeker // and database. func newBlockImporter(db btcdb.Db, r io.ReadSeeker) *blockImporter { return &blockImporter{ db: db, r: r, processQueue: make(chan []byte, 2), doneChan: make(chan bool), errChan: make(chan error), quit: make(chan bool), chain: btcchain.New(db, activeNetwork, nil), } }
// newBlockImporter returns a new importer for the provided file reader seeker // and database. func newBlockImporter(db btcdb.Db, r io.ReadSeeker) *blockImporter { return &blockImporter{ db: db, r: r, processQueue: make(chan []byte, 2), doneChan: make(chan bool), errChan: make(chan error), quit: make(chan struct{}), chain: btcchain.New(db, activeNetParams, nil), lastLogTime: time.Now(), } }
// newBlockManager returns a new bitcoin block manager. // Use Start to begin processing asynchronous block and inv updates. func newBlockManager(s *server) (*blockManager, error) { newestHash, height, err := s.db.NewestSha() if err != nil { return nil, err } bm := blockManager{ server: s, blockPeer: make(map[btcwire.ShaHash]*peer), requestedTxns: make(map[btcwire.ShaHash]bool), requestedBlocks: make(map[btcwire.ShaHash]bool), lastBlockLogTime: time.Now(), msgChan: make(chan interface{}, cfg.MaxPeers*3), headerList: list.New(), quit: make(chan bool), } bm.blockChain = btcchain.New(s.db, s.btcnet, bm.handleNotifyMsg) bm.blockChain.DisableCheckpoints(cfg.DisableCheckpoints) if !cfg.DisableCheckpoints { // Initialize the next checkpoint based on the current height. bm.nextCheckpoint = bm.findNextHeaderCheckpoint(height) if bm.nextCheckpoint != nil { bm.resetHeaderState(newestHash, height) } } else { bmgrLog.Info("Checkpoints are disabled") } bmgrLog.Infof("Generating initial block node index. This may " + "take a while...") err = bm.blockChain.GenerateInitialIndex() if err != nil { return nil, err } bmgrLog.Infof("Block index generation complete") // Initialize the chain state now that the intial block node index has // been generated. bm.updateChainState(newestHash, height) return &bm, nil }
// This example demonstrates how to create a new chain instance and use // ProcessBlock to attempt to attempt add a block to the chain. As the package // overview documentation describes, this includes all of the Bitcoin consensus // rules. This example intentionally attempts to insert a duplicate genesis // block to illustrate how an invalid block is handled. func ExampleBlockChain_ProcessBlock() { // Create a new database to store the accepted blocks into. Typically // this would be opening an existing database and would not use memdb // which is a memory-only database backend, but we create a new db // here so this is a complete working example. db, err := btcdb.CreateDB("memdb") if err != nil { fmt.Printf("Failed to create database: %v\n", err) return } defer db.Close() // Insert the main network genesis block. This is part of the initial // database setup. Like above, this typically would not be needed when // opening an existing database. genesisBlock := btcutil.NewBlock(btcnet.MainNetParams.GenesisBlock) _, err = db.InsertBlock(genesisBlock) if err != nil { fmt.Printf("Failed to insert genesis block: %v\n", err) return } // Create a new BlockChain instance using the underlying database for // the main bitcoin network and ignore notifications. chain := btcchain.New(db, &btcnet.MainNetParams, nil) // Process a block. For this example, we are going to intentionally // cause an error by trying to process the genesis block which already // exists. isOrphan, err := chain.ProcessBlock(genesisBlock, btcchain.BFNone) if err != nil { fmt.Printf("Failed to process block: %v\n", err) return } fmt.Printf("Block accepted. Is it an orphan?: %v", isOrphan) // Output: // Failed to process block: already have block 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f }
// 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(db btcdb.Db, latestHash *btcwire.ShaHash) ([]*btcchain.Checkpoint, error) { // Start with the latest block of the main chain. block, err := db.FetchBlockBySha(latestHash) if err != nil { return nil, err } // Setup chain and get the latest checkpoint. Ignore notifications // since they aren't needed for this util. chain := btcchain.New(db, activeNetwork, nil) latestCheckpoint := chain.LatestCheckpoint() if latestCheckpoint == nil { return nil, fmt.Errorf("unable to retrieve latest checkpoint") } // The latest known block must be at least the last known checkpoint // plus required checkpoint confirmations. checkpointConfirmations := int64(btcchain.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) } // 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. var candidates []*btcchain.Checkpoint numTested := int64(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 { candidateHash, err := block.Sha() if err != nil { return nil, err } checkpoint := btcchain.Checkpoint{ Height: block.Height(), Hash: candidateHash, } candidates = append(candidates, &checkpoint) } prevHash := &block.MsgBlock().Header.PrevBlock block, err = db.FetchBlockBySha(prevHash) if err != nil { return nil, err } numTested++ } return candidates, nil }
// chainSetup is used to create a new db and chain instance with the genesis // block already inserted. In addition to the new chain instnce, it returns // a teardown function the caller should invoke when done testing to clean up. func chainSetup(dbName string) (*btcchain.BlockChain, func(), error) { if !isSupportedDbType(testDbType) { return nil, nil, fmt.Errorf("unsupported db type %v", testDbType) } // Handle memory database specially since it doesn't need the disk // specific handling. var db btcdb.Db var teardown func() if testDbType == "memdb" { ndb, err := btcdb.CreateDB(testDbType) if err != nil { return nil, nil, fmt.Errorf("error creating db: %v", err) } db = ndb // Setup a teardown function for cleaning up. This function is // returned to the caller to be invoked when it is done testing. teardown = func() { db.Close() } } else { // Create the root directory for test databases. if !fileExists(testDbRoot) { if err := os.MkdirAll(testDbRoot, 0700); err != nil { err := fmt.Errorf("unable to create test db "+ "root: %v", err) return nil, nil, err } } // Create a new database to store the accepted blocks into. dbPath := filepath.Join(testDbRoot, dbName) _ = os.RemoveAll(dbPath) ndb, err := btcdb.CreateDB(testDbType, dbPath) if err != nil { return nil, nil, fmt.Errorf("error creating db: %v", err) } db = ndb // Setup a teardown function for cleaning up. This function is // returned to the caller to be invoked when it is done testing. teardown = func() { dbVersionPath := filepath.Join(testDbRoot, dbName+".ver") db.Sync() db.Close() os.RemoveAll(dbPath) os.Remove(dbVersionPath) os.RemoveAll(testDbRoot) } } // Insert the main network genesis block. This is part of the initial // database setup. genesisBlock := btcutil.NewBlock(&btcwire.GenesisBlock) _, err := db.InsertBlock(genesisBlock) if err != nil { teardown() err := fmt.Errorf("failed to insert genesis block: %v", err) return nil, nil, err } chain := btcchain.New(db, btcwire.MainNet, nil) return chain, teardown, nil }