diff --git a/accept.go b/accept.go index 645d7ea7d..10b6a2223 100644 --- a/accept.go +++ b/accept.go @@ -13,10 +13,12 @@ import ( // 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 fastAdd argument modifies the behavior of the function by avoiding the -// somewhat expensive operation: BIP34 validation, it also passes the argument -// down to connectBestChain() -func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, fastAdd bool) error { +// +// The flags modify the behavior of this function as follows: +// - BFFastAdd: The somewhat expensive BIP0034 validation is not performed. +func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) error { + fastAdd := flags&BFFastAdd == BFFastAdd + // Get a block node for the block previous to this one. Will be nil // if this is the genesis block. prevNode, err := b.getPrevNodeFromBlock(block) @@ -164,7 +166,7 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, fastAdd bool) error // 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, fastAdd) + err = b.connectBestChain(newNode, block, flags) if err != nil { return err } diff --git a/chain.go b/chain.go index f78506a98..b9eb97ad0 100644 --- a/chain.go +++ b/chain.go @@ -888,9 +888,13 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error // 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. -// The fastAdd argument avoids the call to checkConnectBlock which does -// several expensive transaction validation operations. -func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fastAdd bool) error { +// +// The flags modify the behavior of this function as follows: +// - BFFastAdd: Avoids the call to checkConnectBlock which does several +// expensive transaction validation operations. +func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, flags BehaviorFlags) error { + fastAdd := flags&BFFastAdd == BFFastAdd + // We haven't selected a best chain yet or we are extending the main // (best) chain with a new block. This is the most common case. if b.bestChain == nil || node.parent.hash.IsEqual(b.bestChain.hash) { @@ -957,6 +961,7 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fas "which forks the chain at height %d/block %v", node.hash, fork.height, fork.hash) } + return nil } diff --git a/chain_test.go b/chain_test.go index c99c0ba27..17b53de98 100644 --- a/chain_test.go +++ b/chain_test.go @@ -48,7 +48,7 @@ func TestHaveBlock(t *testing.T) { btcchain.TstSetCoinbaseMaturity(1) for i := 1; i < len(blocks); i++ { - isOrphan, err := chain.ProcessBlock(blocks[i], false) + isOrphan, err := chain.ProcessBlock(blocks[i], btcchain.BFNone) if err != nil { t.Errorf("ProcessBlock fail on block %v: %v\n", i, err) return @@ -61,7 +61,7 @@ func TestHaveBlock(t *testing.T) { } // Insert an orphan block. - isOrphan, err := chain.ProcessBlock(btcutil.NewBlock(&Block100000), false) + isOrphan, err := chain.ProcessBlock(btcutil.NewBlock(&Block100000), btcchain.BFNone) if err != nil { t.Errorf("Unable to process block: %v", err) return diff --git a/doc.go b/doc.go index 052db9e4f..24dbb6037 100644 --- a/doc.go +++ b/doc.go @@ -112,7 +112,7 @@ intentionally causes an error by attempting to process a duplicate block. // Process a block. For this example, we are going to intentionally // cause an error by trying to process the genesis block which already // exists. - _, err = chain.ProcessBlock(genesisBlock, false) + _, err = chain.ProcessBlock(genesisBlock, btcchain.BFNone) if err != nil { fmt.Printf("Failed to process block: %v\n", err) return diff --git a/process.go b/process.go index 5e18f0b08..bb5d452f1 100644 --- a/process.go +++ b/process.go @@ -10,6 +10,21 @@ import ( "github.com/conformal/btcwire" ) +// BehaviorFlags is a bitmask defining tweaks to the normal behavior when +// performing chain processing and consensus rules checks. +type BehaviorFlags uint32 + +const ( + // BFFastAdd may be set to indicate that several checks can be avoided + // for the block since it is already known to fit into the chain due to + // already proving it correct links into the chain up to a known + // checkpoint. This is primarily used for headers-first mode. + BFFastAdd BehaviorFlags = 1 << iota + + // BFNone is a convenience value to specifically indicate no flags. + BFNone BehaviorFlags = 0 +) + // blockExists determines whether a block with the given hash exists either in // the main chain or any side chains. func (b *BlockChain) blockExists(hash *btcwire.ShaHash) bool { @@ -26,7 +41,10 @@ func (b *BlockChain) blockExists(hash *btcwire.ShaHash) bool { // block hash (they are no longer orphans if true) and potentially accepts them. // It repeats the process for the newly accepted blocks (to detect further // orphans which may no longer be orphans) until there are no more. -func (b *BlockChain) processOrphans(hash *btcwire.ShaHash) error { +// +// The flags do not modify the behavior of this function directly, however they +// are needed to pass along to maybeAcceptBlock. +func (b *BlockChain) processOrphans(hash *btcwire.ShaHash, flags BehaviorFlags) error { // Start with processing at least the passed hash. Leave a little room // for additional orphan blocks that need to be processed without // needing to grow the array in the common case. @@ -63,7 +81,7 @@ func (b *BlockChain) processOrphans(hash *btcwire.ShaHash) error { i-- // Potentially accept the block into the block chain. - err := b.maybeAcceptBlock(orphan.block, false) + err := b.maybeAcceptBlock(orphan.block, flags) if err != nil { return err } @@ -85,7 +103,9 @@ func (b *BlockChain) processOrphans(hash *btcwire.ShaHash) error { // 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. -func (b *BlockChain) ProcessBlock(block *btcutil.Block, fastAdd bool) (bool, error) { +func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bool, error) { + fastAdd := flags&BFFastAdd == BFFastAdd + blockHash, err := block.Sha() if err != nil { return false, err @@ -154,7 +174,6 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, fastAdd bool) (bool, err // Handle orphan blocks. prevHash := &blockHeader.PrevBlock if !prevHash.IsEqual(zeroHash) && !b.blockExists(prevHash) { - // Add the orphan block to the orphan pool. log.Infof("Adding orphan block %v with parent %v", blockHash, prevHash) b.addOrphanBlock(block) @@ -164,7 +183,7 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, fastAdd bool) (bool, err // The block has passed all context independent checks and appears sane // enough to potentially accept it into the block chain. - err = b.maybeAcceptBlock(block, fastAdd) + err = b.maybeAcceptBlock(block, flags) if err != nil { return false, err } @@ -172,7 +191,7 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, fastAdd bool) (bool, err // Accept any orphan blocks that depend on this block (they are no // longer orphans) and repeat for those accepted blocks until there are // no more. - err = b.processOrphans(blockHash) + err = b.processOrphans(blockHash, flags) if err != nil { return false, err } diff --git a/reorganization_test.go b/reorganization_test.go index 7e7d400f4..2bca2ff2b 100644 --- a/reorganization_test.go +++ b/reorganization_test.go @@ -59,7 +59,7 @@ func TestReorganization(t *testing.T) { expectedOrphans := map[int]bool{5: true, 6: true} for i := 1; i < len(blocks); i++ { - isOrphan, err := chain.ProcessBlock(blocks[i], false) + isOrphan, err := chain.ProcessBlock(blocks[i], btcchain.BFNone) if err != nil { t.Errorf("ProcessBlock fail on block %v: %v\n", i, err) return