diff --git a/blockchain/chain.go b/blockchain/chain.go index 640de298..c9c35167 100644 --- a/blockchain/chain.go +++ b/blockchain/chain.go @@ -123,8 +123,8 @@ type BlockChain struct { // These fields are related to checkpoint handling. They are protected // by the chain lock. - nextCheckpoint *chaincfg.Checkpoint - checkpointBlock *btcutil.Block + nextCheckpoint *chaincfg.Checkpoint + checkpointNode *blockNode // The state is used as a fairly efficient way to cache information // about the current best chain state that is returned to callers when diff --git a/blockchain/checkpoints.go b/blockchain/checkpoints.go index 7ac9e5c9..12e023c8 100644 --- a/blockchain/checkpoints.go +++ b/blockchain/checkpoints.go @@ -80,87 +80,58 @@ func (b *BlockChain) verifyCheckpoint(height int32, hash *chainhash.Hash) bool { // findPreviousCheckpoint finds the most recent checkpoint that is already // available in the downloaded portion of the block chain and returns the -// associated block. It returns nil if a checkpoint can't be found (this should -// really only happen for blocks before the first checkpoint). +// associated block node. It returns nil if a checkpoint can't be found (this +// should really only happen for blocks before the first checkpoint). // // This function MUST be called with the chain lock held (for reads). -func (b *BlockChain) findPreviousCheckpoint() (*btcutil.Block, error) { +func (b *BlockChain) findPreviousCheckpoint() (*blockNode, error) { if !b.HasCheckpoints() { return nil, nil } - checkpoints := b.checkpoints - numCheckpoints := len(checkpoints) - if numCheckpoints == 0 { - // No checkpoints. - return nil, nil - } - // Perform the initial search to find and cache the latest known // checkpoint if the best chain is not known yet or we haven't already // previously searched. - if b.checkpointBlock == nil && b.nextCheckpoint == nil { + checkpoints := b.checkpoints + numCheckpoints := len(checkpoints) + if b.checkpointNode == nil && b.nextCheckpoint == nil { // Loop backwards through the available checkpoints to find one // that is already available. - checkpointIndex := -1 - err := b.db.View(func(dbTx database.Tx) error { - for i := numCheckpoints - 1; i >= 0; i-- { - if dbMainChainHasBlock(dbTx, checkpoints[i].Hash) { - checkpointIndex = i - break - } + for i := numCheckpoints - 1; i >= 0; i-- { + node := b.index.LookupNode(checkpoints[i].Hash) + if node == nil || !node.inMainChain { + continue } - return nil - }) - if err != nil { - return nil, err + + // Checkpoint found. Cache it for future lookups and + // set the next expected checkpoint accordingly. + b.checkpointNode = node + if i < numCheckpoints-1 { + b.nextCheckpoint = &checkpoints[i+1] + } + return b.checkpointNode, nil } // No known latest checkpoint. This will only happen on blocks // before the first known checkpoint. So, set the next expected // checkpoint to the first checkpoint and return the fact there // is no latest known checkpoint block. - if checkpointIndex == -1 { - b.nextCheckpoint = &checkpoints[0] - return nil, nil - } - - // Cache the latest known checkpoint block for future lookups. - checkpoint := checkpoints[checkpointIndex] - err = b.db.View(func(dbTx database.Tx) error { - block, err := dbFetchBlockByHash(dbTx, checkpoint.Hash) - if err != nil { - return err - } - b.checkpointBlock = block - - // Set the next expected checkpoint block accordingly. - b.nextCheckpoint = nil - if checkpointIndex < numCheckpoints-1 { - b.nextCheckpoint = &checkpoints[checkpointIndex+1] - } - - return nil - }) - if err != nil { - return nil, err - } - - return b.checkpointBlock, nil + b.nextCheckpoint = &checkpoints[0] + return nil, nil } // At this point we've already searched for the latest known checkpoint, // so when there is no next checkpoint, the current checkpoint lockin // will always be the latest known checkpoint. if b.nextCheckpoint == nil { - return b.checkpointBlock, nil + return b.checkpointNode, nil } // When there is a next checkpoint and the height of the current best // chain does not exceed it, the current checkpoint lockin is still // the latest known checkpoint. if b.bestNode.height < b.nextCheckpoint.Height { - return b.checkpointBlock, nil + return b.checkpointNode, nil } // We've reached or exceeded the next checkpoint height. Note that @@ -168,21 +139,17 @@ func (b *BlockChain) findPreviousCheckpoint() (*btcutil.Block, error) { // any blocks before the checkpoint, so we don't have to worry about the // checkpoint going away out from under us due to a chain reorganize. - // Cache the latest known checkpoint block for future lookups. Note - // that if this lookup fails something is very wrong since the chain - // has already passed the checkpoint which was verified as accurate - // before inserting it. - err := b.db.View(func(tx database.Tx) error { - block, err := dbFetchBlockByHash(tx, b.nextCheckpoint.Hash) - if err != nil { - return err - } - b.checkpointBlock = block - return nil - }) - if err != nil { - return nil, err + // Cache the latest known checkpoint for future lookups. Note that if + // this lookup fails something is very wrong since the chain has already + // passed the checkpoint which was verified as accurate before inserting + // it. + checkpointNode := b.index.LookupNode(b.nextCheckpoint.Hash) + if checkpointNode == nil { + return nil, AssertError(fmt.Sprintf("findPreviousCheckpoint "+ + "failed lookup of known good block node %s", + b.nextCheckpoint.Hash)) } + b.checkpointNode = checkpointNode // Set the next expected checkpoint. checkpointIndex := -1 @@ -197,7 +164,7 @@ func (b *BlockChain) findPreviousCheckpoint() (*btcutil.Block, error) { b.nextCheckpoint = &checkpoints[checkpointIndex+1] } - return b.checkpointBlock, nil + return b.checkpointNode, nil } // isNonstandardTransaction determines whether a transaction contains any diff --git a/blockchain/process.go b/blockchain/process.go index 82d4c403..fa438a8e 100644 --- a/blockchain/process.go +++ b/blockchain/process.go @@ -6,6 +6,7 @@ package blockchain import ( "fmt" + "time" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/database" @@ -182,14 +183,13 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bo // used to eat memory, and ensuring expected (versus claimed) proof of // work requirements since the previous checkpoint are met. blockHeader := &block.MsgBlock().Header - checkpointBlock, err := b.findPreviousCheckpoint() + checkpointNode, err := b.findPreviousCheckpoint() if err != nil { return false, false, err } - if checkpointBlock != nil { + if checkpointNode != nil { // Ensure the block timestamp is after the checkpoint timestamp. - checkpointHeader := &checkpointBlock.MsgBlock().Header - checkpointTime := checkpointHeader.Timestamp + checkpointTime := time.Unix(checkpointNode.timestamp, 0) if blockHeader.Timestamp.Before(checkpointTime) { str := fmt.Sprintf("block %v has timestamp %v before "+ "last checkpoint timestamp %v", blockHash, @@ -205,7 +205,7 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bo // maximum adjustment allowed by the retarget rules. duration := blockHeader.Timestamp.Sub(checkpointTime) requiredTarget := CompactToBig(b.calcEasiestDifficulty( - checkpointHeader.Bits, duration)) + checkpointNode.bits, duration)) currentTarget := CompactToBig(blockHeader.Bits) if currentTarget.Cmp(requiredTarget) > 0 { str := fmt.Sprintf("block target difficulty of %064x "+ diff --git a/blockchain/validate.go b/blockchain/validate.go index 01c31fee..2cfa4ca6 100644 --- a/blockchain/validate.go +++ b/blockchain/validate.go @@ -692,14 +692,14 @@ func (b *BlockChain) checkBlockHeaderContext(header *wire.BlockHeader, prevNode // chain before it. This prevents storage of new, otherwise valid, // blocks which build off of old blocks that are likely at a much easier // difficulty and therefore could be used to waste cache and disk space. - checkpointBlock, err := b.findPreviousCheckpoint() + checkpointNode, err := b.findPreviousCheckpoint() if err != nil { return err } - if checkpointBlock != nil && blockHeight < checkpointBlock.Height() { + if checkpointNode != nil && blockHeight < checkpointNode.height { str := fmt.Sprintf("block at height %d forks the main chain "+ "before the previous checkpoint at height %d", - blockHeight, checkpointBlock.Height()) + blockHeight, checkpointNode.height) return ruleError(ErrForkTooOld, str) }