// newBlockImporter returns a new importer for the provided file reader seeker // and database. func newBlockImporter(db database.DB, r io.ReadSeeker) (*blockImporter, error) { // Create the various indexes as needed. // // CAUTION: the txindex needs to be first in the indexes array because // the addrindex uses data from the txindex during catchup. If the // addrindex is run first, it may not have the transactions from the // current block indexed. var indexes []indexers.Indexer if cfg.TxIndex || cfg.AddrIndex { // Enable transaction index if address index is enabled since it // requires it. if !cfg.TxIndex { log.Infof("Transaction index enabled because it is " + "required by the address index") cfg.TxIndex = true } else { log.Info("Transaction index is enabled") } indexes = append(indexes, indexers.NewTxIndex(db)) } if cfg.AddrIndex { log.Info("Address index is enabled") indexes = append(indexes, indexers.NewAddrIndex(db, activeNetParams)) } if !cfg.NoExistsAddrIndex { log.Info("Exists address index is enabled") indexes = append(indexes, indexers.NewExistsAddrIndex(db, activeNetParams)) } // Create an index manager if any of the optional indexes are enabled. var indexManager blockchain.IndexManager if len(indexes) > 0 { indexManager = indexers.NewManager(db, indexes, activeNetParams) } chain, err := blockchain.New(&blockchain.Config{ DB: db, ChainParams: activeNetParams, TimeSource: blockchain.NewMedianTime(), IndexManager: indexManager, }) if err != nil { return nil, err } return &blockImporter{ db: db, r: r, processQueue: make(chan []byte, 2), doneChan: make(chan bool), errChan: make(chan error), quit: make(chan struct{}), chain: chain, lastLogTime: time.Now(), startTime: time.Now(), }, nil }
// newBlockImporter returns a new importer for the provided file reader seeker // and database. func newBlockImporter(db database.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: blockchain.New(db, nil, activeNetParams, nil), medianTime: blockchain.NewMedianTime(), lastLogTime: time.Now(), } }
// TODO Make more elaborate tests for difficulty. The difficulty algorithms // have already been tested to death in simnet/testnet/mainnet simulations, // but we should really have a unit test for them that includes tests for // edge cases. func TestDiff(t *testing.T) { db, err := database.CreateDB("memdb") if err != nil { t.Errorf("Failed to create database: %v\n", err) return } defer db.Close() var tmdb *stake.TicketDB genesisBlock := dcrutil.NewBlock(chaincfg.MainNetParams.GenesisBlock) _, err = db.InsertBlock(genesisBlock) if err != nil { t.Errorf("Failed to insert genesis block: %v\n", err) return } chain := blockchain.New(db, tmdb, &chaincfg.MainNetParams, nil, nil) //timeSource := blockchain.NewMedianTime() // Grab some blocks // Build fake blockchain // Calc new difficulty ts := time.Now() d, err := chain.CalcNextRequiredDifficulty(ts) if err != nil { t.Errorf("Failed to get difficulty: %v\n", err) return } if d != 486604799 { // This is hardcoded in genesis block but not exported anywhere. t.Error("Failed to get initial difficulty.") } sd, err := chain.CalcNextRequiredStakeDifficulty() if err != nil { t.Errorf("Failed to get stake difficulty: %v\n", err) return } if sd != chaincfg.MainNetParams.MinimumStakeDiff { t.Error("Incorrect initial stake difficulty.") } // Compare // Repeat for a few more }
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) } }
// 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 Decred 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 be deleting // and creating a new database like this, but it is done here so this is // a complete working example and does not leave temporary files laying // around. dbPath := filepath.Join(os.TempDir(), "exampleprocessblock") _ = os.RemoveAll(dbPath) db, err := database.Create("ffldb", dbPath, chaincfg.MainNetParams.Net) if err != nil { fmt.Printf("Failed to create database: %v\n", err) return } defer os.RemoveAll(dbPath) defer db.Close() // Create a new BlockChain instance using the underlying database for // the main bitcoin network. This example does not demonstrate some // of the other available configuration options such as specifying a // notification callback and signature cache. Also, the caller would // ordinarily keep a reference to the median time source and add time // values obtained from other peers on the network so the local time is // adjusted to be in agreement with other peers. chain, err := blockchain.New(&blockchain.Config{ DB: db, ChainParams: &chaincfg.MainNetParams, TimeSource: blockchain.NewMedianTime(), }) if err != nil { fmt.Printf("Failed to create chain instance: %v\n", err) return } // Process a block. For this example, we are going to intentionally // cause an error by trying to process the genesis block which already // exists. genesisBlock := dcrutil.NewBlock(chaincfg.MainNetParams.GenesisBlock) _, isOrphan, err := chain.ProcessBlock(genesisBlock, blockchain.BFNone) if err != nil { fmt.Printf("Failed to create chain instance: %v\n", err) return } fmt.Printf("Block accepted. Is it an orphan?: %v", isOrphan) // This output is dependent on the genesis block, and needs to be // updated if the mainnet genesis block is updated. // Output: // Failed to process block: already have block 267a53b5ee86c24a48ec37aee4f4e7c0c4004892b7259e695e9f5b321f1ab9d2 }
// 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 Decred 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 := database.CreateDB("memdb") if err != nil { fmt.Printf("Failed to create database: %v\n", err) return } defer db.Close() var tmdb *stake.TicketDB // 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 := dcrutil.NewBlock(chaincfg.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 without an initialized signature // verification cache, using the underlying database for the main // bitcoin network and ignore notifications. chain := blockchain.New(db, tmdb, &chaincfg.MainNetParams, nil, nil) // Create a new median time source that is required by the upcoming // call to ProcessBlock. Ordinarily this would also add time values // obtained from other peers on the network so the local time is // adjusted to be in agreement with other peers. timeSource := blockchain.NewMedianTime() // 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, timeSource, blockchain.BFNone) if err != nil { fmt.Printf("Failed to process block: %v\n", err) return } fmt.Printf("Block accepted. Is it an orphan?: %v", isOrphan) // This output is dependent on the genesis block, and needs to be // updated if the mainnet genesis block is updated. // Output: // Failed to process block: already have block 267a53b5ee86c24a48ec37aee4f4e7c0c4004892b7259e695e9f5b321f1ab9d2 }
// 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 chain // since there is no point in finding candidates before already existing // checkpoints. func findCandidates(db database.Db, latestHash *chainhash.Hash) ([]*chaincfg.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 := blockchain.New(db, nil, activeNetParams, nil, nil) 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 := int64(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 := 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 { checkpoint := chaincfg.Checkpoint{ Height: block.Height(), Hash: block.Sha(), } 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, params *chaincfg.Params) (*blockchain.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 database.Db tmdb := new(stake.TicketDB) var teardown func() if testDbType == "memdb" { ndb, err := database.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() { tmdb.Close() 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 := database.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") tmdb.Close() 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 := dcrutil.NewBlock(params.GenesisBlock) genesisBlock.SetHeight(int64(0)) _, err := db.InsertBlock(genesisBlock) if err != nil { teardown() err := fmt.Errorf("failed to insert genesis block: %v", err) return nil, nil, err } // Start the ticket database. tmdb.Initialize(params, db) tmdb.RescanTicketDB() chain := blockchain.New(db, tmdb, params, nil) return chain, teardown, 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, params *chaincfg.Params) (*blockchain.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 database.DB var teardown func() if testDbType == "memdb" { ndb, err := database.Create(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 := database.Create(testDbType, dbPath, blockDataNet) 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() os.RemoveAll(dbPath) os.RemoveAll(testDbRoot) } } // Create the main chain instance. chain, err := blockchain.New(&blockchain.Config{ DB: db, ChainParams: params, TimeSource: blockchain.NewMedianTime(), }) if err != nil { teardown() err := fmt.Errorf("failed to create chain instance: %v", err) return nil, nil, err } return chain, teardown, nil }