diff --git a/blockchain/accept.go b/blockchain/accept.go index fbb921ee..f1815b3e 100644 --- a/blockchain/accept.go +++ b/blockchain/accept.go @@ -6,10 +6,11 @@ package blockchain import "github.com/btcsuite/btcutil" -// maybeAcceptBlock potentially accepts a block into the memory block chain. -// It performs several validation checks which depend on its position within -// the block chain before adding it. The block is expected to have already gone -// through ProcessBlock before calling this function with it. +// maybeAcceptBlock potentially accepts a block into the block chain and, if +// accepted, returns whether or not it is on the main chain. It performs +// several validation checks which depend on its position within the block chain +// before adding it. The block is expected to have already gone through +// ProcessBlock before calling this function with it. // // The flags modify the behavior of this function as follows: // - BFDryRun: The memory chain index will not be pruned and no accept @@ -19,7 +20,7 @@ import "github.com/btcsuite/btcutil" // their documentation for how the flags modify their behavior. // // This function MUST be called with the chain state lock held (for writes). -func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) error { +func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) (bool, error) { dryRun := flags&BFDryRun == BFDryRun // Get a block node for the block previous to this one. Will be nil @@ -27,7 +28,7 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) prevNode, err := b.getPrevNodeFromBlock(block) if err != nil { log.Errorf("getPrevNodeFromBlock: %v", err) - return err + return false, err } // The height of this block is one more than the referenced previous @@ -42,7 +43,7 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) // position of the block within the block chain. err = b.checkBlockContext(block, prevNode, flags) if err != nil { - return err + return false, err } // Prune block nodes which are no longer needed before creating @@ -50,7 +51,7 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) if !dryRun { err = b.pruneBlockNodes() if err != nil { - return err + return false, err } } @@ -67,9 +68,9 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) // Connect the passed block to the chain while respecting proper chain // selection according to the chain with the most proof of work. This // also handles validation of the transaction scripts. - err = b.connectBestChain(newNode, block, flags) + isMainChain, err := b.connectBestChain(newNode, block, flags) if err != nil { - return err + return false, err } // Notify the caller that the new block was accepted into the block @@ -81,5 +82,5 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) b.chainLock.Lock() } - return nil + return isMainChain, nil } diff --git a/blockchain/chain.go b/blockchain/chain.go index dad7fd99..f838dd10 100644 --- a/blockchain/chain.go +++ b/blockchain/chain.go @@ -1205,7 +1205,9 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List, flags // proof of work. In the typical case, the new block simply extends the main // chain. However, it may also be extending (or creating) a side chain (fork) // which may or may not end up becoming the main chain depending on which fork -// cumulatively has the most proof of work. +// cumulatively has the most proof of work. It returns whether or not the block +// ended up on the main chain (either due to extending the main chain or causing +// a reorganization to become the main chain). // // The flags modify the behavior of this function as follows: // - BFFastAdd: Avoids several expensive transaction validation operations. @@ -1215,7 +1217,7 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List, flags // modifying the state are avoided. // // This function MUST be called with the chain state lock held (for writes). -func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, flags BehaviorFlags) error { +func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, flags BehaviorFlags) (bool, error) { fastAdd := flags&BFFastAdd == BFFastAdd dryRun := flags&BFDryRun == BFDryRun @@ -1231,13 +1233,13 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla if !fastAdd { err := b.checkConnectBlock(node, block, view, &stxos) if err != nil { - return err + return false, err } } // Don't connect the block if performing a dry run. if dryRun { - return nil + return true, nil } // In the fast add case the code to check the block connection @@ -1247,18 +1249,18 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla if fastAdd { err := view.fetchInputUtxos(b.db, block) if err != nil { - return err + return false, err } err = view.connectTransactions(block, &stxos) if err != nil { - return err + return false, err } } // Connect the block to the main chain. err := b.connectBlock(node, block, view, stxos) if err != nil { - return err + return false, err } // Connect the parent node to this node. @@ -1266,7 +1268,7 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla node.parent.children = append(node.parent.children, node) } - return nil + return true, nil } if fastAdd { log.Warnf("fastAdd set in the side chain case? %v\n", @@ -1305,7 +1307,7 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla if node.workSum.Cmp(b.bestNode.workSum) <= 0 { // Skip Logging info when the dry run flag is set. if dryRun { - return nil + return false, nil } // Find the fork point. @@ -1327,7 +1329,7 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla node.hash, fork.height, fork.hash) } - return nil + return false, nil } // We're extending (or creating) a side chain and the cumulative work @@ -1346,10 +1348,10 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla } err := b.reorganizeChain(detachNodes, attachNodes, flags) if err != nil { - return err + return false, err } - return nil + return true, nil } // IsCurrent returns whether or not the chain believes it is current. Several diff --git a/blockchain/chain_test.go b/blockchain/chain_test.go index e964d13f..8f5a2bd9 100644 --- a/blockchain/chain_test.go +++ b/blockchain/chain_test.go @@ -49,7 +49,7 @@ func TestHaveBlock(t *testing.T) { chain.TstSetCoinbaseMaturity(1) for i := 1; i < len(blocks); i++ { - isOrphan, err := chain.ProcessBlock(blocks[i], blockchain.BFNone) + _, isOrphan, err := chain.ProcessBlock(blocks[i], blockchain.BFNone) if err != nil { t.Errorf("ProcessBlock fail on block %v: %v\n", i, err) return @@ -62,7 +62,7 @@ func TestHaveBlock(t *testing.T) { } // Insert an orphan block. - isOrphan, err := chain.ProcessBlock(btcutil.NewBlock(&Block100000), + _, isOrphan, err := chain.ProcessBlock(btcutil.NewBlock(&Block100000), blockchain.BFNone) if err != nil { t.Errorf("Unable to process block: %v", err) diff --git a/blockchain/example_test.go b/blockchain/example_test.go index ef39b72c..c7163e0e 100644 --- a/blockchain/example_test.go +++ b/blockchain/example_test.go @@ -59,11 +59,13 @@ func ExampleBlockChain_ProcessBlock() { // cause an error by trying to process the genesis block which already // exists. genesisBlock := btcutil.NewBlock(chaincfg.MainNetParams.GenesisBlock) - isOrphan, err := chain.ProcessBlock(genesisBlock, blockchain.BFNone) + isMainChain, isOrphan, err := chain.ProcessBlock(genesisBlock, + blockchain.BFNone) if err != nil { fmt.Printf("Failed to process block: %v\n", err) return } + fmt.Printf("Block accepted. Is it on the main chain?: %v", isMainChain) fmt.Printf("Block accepted. Is it an orphan?: %v", isOrphan) // Output: diff --git a/blockchain/process.go b/blockchain/process.go index fbfe5208..143f555c 100644 --- a/blockchain/process.go +++ b/blockchain/process.go @@ -101,7 +101,7 @@ func (b *BlockChain) processOrphans(hash *chainhash.Hash, flags BehaviorFlags) e i-- // Potentially accept the block into the block chain. - err := b.maybeAcceptBlock(orphan.block, flags) + _, err := b.maybeAcceptBlock(orphan.block, flags) if err != nil { return err } @@ -120,12 +120,12 @@ func (b *BlockChain) processOrphans(hash *chainhash.Hash, flags BehaviorFlags) e // blocks, ensuring blocks follow all rules, orphan handling, and insertion into // the block chain along with best chain selection and reorganization. // -// It returns a bool which indicates whether or not the block is an orphan and -// any errors that occurred during processing. The returned bool is only valid -// when the error is nil. +// When no errors occurred during processing, the first return value indicates +// whether or not the block is on the main chain and the second indicates +// whether or not the block is an orphan. // // This function is safe for concurrent access. -func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bool, error) { +func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bool, bool, error) { b.chainLock.Lock() defer b.chainLock.Unlock() @@ -138,23 +138,23 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bo // The block must not already exist in the main chain or side chains. exists, err := b.blockExists(blockHash) if err != nil { - return false, err + return false, false, err } if exists { str := fmt.Sprintf("already have block %v", blockHash) - return false, ruleError(ErrDuplicateBlock, str) + return false, false, ruleError(ErrDuplicateBlock, str) } // The block must not already exist as an orphan. if _, exists := b.orphans[*blockHash]; exists { str := fmt.Sprintf("already have block (orphan) %v", blockHash) - return false, ruleError(ErrDuplicateBlock, str) + return false, false, ruleError(ErrDuplicateBlock, str) } // Perform preliminary sanity checks on the block and its transactions. err = checkBlockSanity(block, b.chainParams.PowLimit, b.timeSource, flags) if err != nil { - return false, err + return false, false, err } // Find the previous checkpoint and perform some additional checks based @@ -166,7 +166,7 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bo blockHeader := &block.MsgBlock().Header checkpointBlock, err := b.findPreviousCheckpoint() if err != nil { - return false, err + return false, false, err } if checkpointBlock != nil { // Ensure the block timestamp is after the checkpoint timestamp. @@ -176,7 +176,7 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bo str := fmt.Sprintf("block %v has timestamp %v before "+ "last checkpoint timestamp %v", blockHash, blockHeader.Timestamp, checkpointTime) - return false, ruleError(ErrCheckpointTimeTooOld, str) + return false, false, ruleError(ErrCheckpointTimeTooOld, str) } if !fastAdd { // Even though the checks prior to now have already ensured the @@ -193,34 +193,32 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bo str := fmt.Sprintf("block target difficulty of %064x "+ "is too low when compared to the previous "+ "checkpoint", currentTarget) - return false, ruleError(ErrDifficultyTooLow, str) + return false, false, ruleError(ErrDifficultyTooLow, str) } } } // Handle orphan blocks. prevHash := &blockHeader.PrevBlock - if !prevHash.IsEqual(zeroHash) { - prevHashExists, err := b.blockExists(prevHash) - if err != nil { - return false, err + prevHashExists, err := b.blockExists(prevHash) + if err != nil { + return false, false, err + } + if !prevHashExists { + if !dryRun { + log.Infof("Adding orphan block %v with parent %v", + blockHash, prevHash) + b.addOrphanBlock(block) } - if !prevHashExists { - if !dryRun { - log.Infof("Adding orphan block %v with parent %v", - blockHash, prevHash) - b.addOrphanBlock(block) - } - return true, nil - } + return false, true, nil } // The block has passed all context independent checks and appears sane // enough to potentially accept it into the block chain. - err = b.maybeAcceptBlock(block, flags) + isMainChain, err := b.maybeAcceptBlock(block, flags) if err != nil { - return false, err + return false, false, err } // Don't process any orphans or log when the dry run flag is set. @@ -230,11 +228,11 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bo // there are no more. err := b.processOrphans(blockHash, flags) if err != nil { - return false, err + return false, false, err } log.Debugf("Accepted block %v", blockHash) } - return false, nil + return isMainChain, false, nil } diff --git a/blockchain/reorganization_test.go b/blockchain/reorganization_test.go index ecd2ff6d..edd81a64 100644 --- a/blockchain/reorganization_test.go +++ b/blockchain/reorganization_test.go @@ -60,7 +60,7 @@ func TestReorganization(t *testing.T) { expectedOrphans := map[int]struct{}{5: {}, 6: {}} for i := 1; i < len(blocks); i++ { - isOrphan, err := chain.ProcessBlock(blocks[i], blockchain.BFNone) + _, isOrphan, err := chain.ProcessBlock(blocks[i], blockchain.BFNone) if err != nil { t.Errorf("ProcessBlock fail on block %v: %v\n", i, err) return diff --git a/blockmanager.go b/blockmanager.go index bdc60791..37967a62 100644 --- a/blockmanager.go +++ b/blockmanager.go @@ -566,7 +566,7 @@ func (b *blockManager) handleBlockMsg(bmsg *blockMsg) { // Process the block to include validation, best chain selection, orphan // handling, etc. - isOrphan, err := b.chain.ProcessBlock(bmsg.block, behaviorFlags) + _, isOrphan, err := b.chain.ProcessBlock(bmsg.block, behaviorFlags) if err != nil { // When the error is a rule error, it means the block was simply // rejected as opposed to something actually going wrong, so log @@ -1121,8 +1121,8 @@ out: msg.reply <- b.syncPeer case processBlockMsg: - isOrphan, err := b.chain.ProcessBlock(msg.block, - msg.flags) + _, isOrphan, err := b.chain.ProcessBlock( + msg.block, msg.flags) if err != nil { msg.reply <- processBlockResponse{ isOrphan: false, diff --git a/cmd/addblock/import.go b/cmd/addblock/import.go index 854d5b10..b7a30369 100644 --- a/cmd/addblock/import.go +++ b/cmd/addblock/import.go @@ -129,10 +129,15 @@ func (bi *blockImporter) processBlock(serializedBlock []byte) (bool, error) { // Ensure the blocks follows all of the chain rules and match up to the // known checkpoints. - isOrphan, err := bi.chain.ProcessBlock(block, blockchain.BFFastAdd) + isMainChain, isOrphan, err := bi.chain.ProcessBlock(block, + blockchain.BFFastAdd) if err != nil { return false, err } + if !isMainChain { + return false, fmt.Errorf("import file contains an block that "+ + "does not extend the main chain: %v", blockHash) + } if isOrphan { return false, fmt.Errorf("import file contains an orphan "+ "block: %v", blockHash)