From 1676ecd7a6b3ccc3bc75e427b4d6a497e406a198 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Tue, 11 Mar 2014 13:18:27 -0500 Subject: [PATCH] Export CheckConnectBlock function. This commit makes a slight variant of the existing checkConnectBlock function available to external callers. The new exported version checks if the passed block will connect to the end of the current main chain. In order to support this, a few other small modifications have been made to the initial index generation and the existing checkConnectBlock since it previously made some assumptions about the state of genesis block which can no longer be assumed due accepting blocks from callers directly. Also, add a quick test to ensure the new function fails when checking if the genesis block will connect again when it's already inserted. --- chain.go | 8 ++++---- test_coverage.txt | 40 +++++++++++++++++++++------------------- validate.go | 35 +++++++++++++++++++++++++++++++---- validate_test.go | 24 ++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 27 deletions(-) diff --git a/chain.go b/chain.go index 4b51e4b0..9d4bb1f3 100644 --- a/chain.go +++ b/chain.go @@ -340,7 +340,7 @@ func (b *BlockChain) GenerateInitialIndex() error { // Calculate the starting height based on the minimum number of nodes // needed in memory. - startHeight := endHeight - (minMemoryNodes + 1) + startHeight := endHeight - minMemoryNodes if startHeight < 0 { startHeight = 0 } @@ -353,7 +353,7 @@ func (b *BlockChain) GenerateInitialIndex() error { // is going to be nuked eventually, the bug isn't being fixed in the // driver. In the mean time, work around the issue by calling // FetchBlockBySha multiple times with the appropriate indices as needed. - for start := startHeight; start < endHeight; { + for start := startHeight; start <= endHeight; { hashList, err := b.db.FetchHeightRange(start, endHeight+1) if err != nil { return err @@ -368,8 +368,8 @@ func (b *BlockChain) GenerateInitialIndex() error { // Loop forwards through each block loading the node into the // index for the block. for _, hash := range hashList { - // Make a copy of the hash to make sure there are no references - // into the list so it can be freed. + // Make a copy of the hash to make sure there are no + // references into the list so it can be freed. hashCopy := hash node, err := b.loadBlockNode(&hashCopy) if err != nil { diff --git a/test_coverage.txt b/test_coverage.txt index 87b56e96..ca3ea7e1 100644 --- a/test_coverage.txt +++ b/test_coverage.txt @@ -2,27 +2,28 @@ github.com/conformal/btcchain/validate.go checkSerializedHeight 100.00% (17/17) github.com/conformal/btcchain/chain.go BlockChain.removeOrphanBlock 100.00% (16/16) github.com/conformal/btcchain/txlookup.go disconnectTransactions 100.00% (13/13) -github.com/conformal/btcchain/validate.go countSigOps 100.00% (9/9) +github.com/conformal/btcchain/validate.go CountSigOps 100.00% (9/9) +github.com/conformal/btcchain/validate.go BlockChain.CheckConnectBlock 100.00% (7/7) github.com/conformal/btcchain/checkpoints.go init 100.00% (6/6) -github.com/conformal/btcchain/merkle.go hashMerkleBranches 100.00% (5/5) github.com/conformal/btcchain/difficulty.go ShaHashToBig 100.00% (5/5) +github.com/conformal/btcchain/merkle.go hashMerkleBranches 100.00% (5/5) github.com/conformal/btcchain/chain.go BlockChain.IsKnownOrphan 100.00% (5/5) github.com/conformal/btcchain/merkle.go nextPowerOfTwo 100.00% (4/4) github.com/conformal/btcchain/chain.go newBlockNode 100.00% (3/3) github.com/conformal/btcchain/process.go BlockChain.blockExists 100.00% (3/3) -github.com/conformal/btcchain/checkpoints.go newShaHashFromStr 100.00% (2/2) github.com/conformal/btcchain/chain.go New 100.00% (2/2) -github.com/conformal/btcchain/timesorter.go timeSorter.Swap 100.00% (1/1) -github.com/conformal/btcchain/validate.go BlockChain.calcBlockSubsidy 100.00% (1/1) +github.com/conformal/btcchain/checkpoints.go newShaHashFromStr 100.00% (2/2) +github.com/conformal/btcchain/timesorter.go timeSorter.Less 100.00% (1/1) github.com/conformal/btcchain/chain.go BlockChain.HaveBlock 100.00% (1/1) github.com/conformal/btcchain/log.go init 100.00% (1/1) -github.com/conformal/btcchain/timesorter.go timeSorter.Less 100.00% (1/1) +github.com/conformal/btcchain/timesorter.go timeSorter.Swap 100.00% (1/1) github.com/conformal/btcchain/timesorter.go timeSorter.Len 100.00% (1/1) -github.com/conformal/btcchain/scriptval.go newTxValidator 100.00% (1/1) github.com/conformal/btcchain/checkpoints.go BlockChain.DisableCheckpoints 100.00% (1/1) +github.com/conformal/btcchain/scriptval.go newTxValidator 100.00% (1/1) github.com/conformal/btcchain/scriptval.go txValidator.sendResult 100.00% (1/1) -github.com/conformal/btcchain/log.go DisableLog 100.00% (1/1) github.com/conformal/btcchain/params.go BlockChain.chainParams 100.00% (1/1) +github.com/conformal/btcchain/log.go DisableLog 100.00% (1/1) +github.com/conformal/btcchain/validate.go CalcBlockSubsidy 100.00% (1/1) github.com/conformal/btcchain/txlookup.go fetchTxStoreMain 95.65% (22/23) github.com/conformal/btcchain/merkle.go BuildMerkleTreeStore 93.33% (14/15) github.com/conformal/btcchain/chain.go BlockChain.getReorganizeNodes 92.86% (13/14) @@ -35,19 +36,21 @@ github.com/conformal/btcchain/chain.go BlockChain.reorganizeChain 85.29% (29 github.com/conformal/btcchain/process.go BlockChain.processOrphans 84.21% (16/19) github.com/conformal/btcchain/chain.go BlockChain.connectBlock 83.33% (10/12) github.com/conformal/btcchain/chain.go BlockChain.calcPastMedianTime 82.35% (14/17) +github.com/conformal/btcchain/validate.go BlockChain.checkBIP0030 82.35% (14/17) github.com/conformal/btcchain/chain.go BlockChain.isMajorityVersion 80.00% (8/10) github.com/conformal/btcchain/difficulty.go CalcWork 80.00% (4/5) github.com/conformal/btcchain/chain.go BlockChain.addOrphanBlock 77.78% (14/18) github.com/conformal/btcchain/chain.go BlockChain.getPrevNodeFromBlock 77.78% (7/9) +github.com/conformal/btcchain/chain.go BlockChain.GenerateInitialIndex 77.27% (17/22) github.com/conformal/btcchain/chain.go BlockChain.disconnectBlock 76.92% (10/13) github.com/conformal/btcchain/txlookup.go BlockChain.fetchInputTransactions 75.00% (18/24) github.com/conformal/btcchain/difficulty.go BigToCompact 75.00% (12/16) github.com/conformal/btcchain/difficulty.go CompactToBig 75.00% (9/12) -github.com/conformal/btcchain/validate.go BlockChain.checkConnectBlock 69.23% (36/52) +github.com/conformal/btcchain/validate.go isTransactionSpent 75.00% (3/4) +github.com/conformal/btcchain/validate.go BlockChain.checkConnectBlock 71.15% (37/52) github.com/conformal/btcchain/validate.go CheckBlockSanity 67.44% (29/43) github.com/conformal/btcchain/validate.go isNullOutpoint 66.67% (2/3) github.com/conformal/btcchain/validate.go CheckTransactionInputs 65.12% (28/43) -github.com/conformal/btcchain/validate.go BlockChain.checkBIP0030 64.71% (11/17) github.com/conformal/btcchain/txlookup.go connectTransactions 61.54% (8/13) github.com/conformal/btcchain/validate.go CheckTransactionSanity 61.11% (22/36) github.com/conformal/btcchain/validate.go isBIP0030Node 60.00% (3/5) @@ -63,12 +66,11 @@ github.com/conformal/btcchain/notifications.go BlockChain.sendNotification 50 github.com/conformal/btcchain/chain.go BlockChain.pruneBlockNodes 41.18% (7/17) github.com/conformal/btcchain/validate.go IsFinalizedTransaction 28.57% (4/14) github.com/conformal/btcchain/checkpoints.go BlockChain.verifyCheckpoint 22.22% (2/9) -github.com/conformal/btcchain/difficulty.go BlockChain.calcNextRequiredDifficulty 17.07% (7/41) +github.com/conformal/btcchain/difficulty.go BlockChain.calcNextRequiredDifficulty 17.50% (7/40) github.com/conformal/btcchain/checkpoints.go BlockChain.findPreviousCheckpoint 4.88% (2/41) github.com/conformal/btcchain/blocklocator.go BlockChain.BlockLocatorFromHash 0.00% (0/39) github.com/conformal/btcchain/checkpoints.go BlockChain.IsCheckpointCandidate 0.00% (0/32) -github.com/conformal/btcchain/validate.go countP2SHSigOps 0.00% (0/26) -github.com/conformal/btcchain/chain.go BlockChain.GenerateInitialIndex 0.00% (0/22) +github.com/conformal/btcchain/validate.go CountP2SHSigOps 0.00% (0/26) github.com/conformal/btcchain/difficulty.go BlockChain.calcEasiestDifficulty 0.00% (0/15) github.com/conformal/btcchain/difficulty.go BlockChain.findPrevTestNetDifficulty 0.00% (0/12) github.com/conformal/btcchain/chain.go BlockChain.removeBlockNode 0.00% (0/12) @@ -81,14 +83,14 @@ github.com/conformal/btcchain/blocklocator.go BlockChain.LatestBlockLocator 0 github.com/conformal/btcchain/txlookup.go BlockChain.FetchTransactionStore 0.00% (0/6) github.com/conformal/btcchain/checkpoints.go isNonstandardTransaction 0.00% (0/5) github.com/conformal/btcchain/checkpoints.go BlockChain.checkpointData 0.00% (0/4) -github.com/conformal/btcchain/validate.go isTransactionSpent 0.00% (0/4) -github.com/conformal/btcchain/chain.go addChildrenWork 0.00% (0/3) github.com/conformal/btcchain/notifications.go NotificationType.String 0.00% (0/3) +github.com/conformal/btcchain/chain.go addChildrenWork 0.00% (0/3) github.com/conformal/btcchain/checkpoints.go BlockChain.Checkpoints 0.00% (0/3) -github.com/conformal/btcchain/log.go logClosure.String 0.00% (0/1) -github.com/conformal/btcchain/log.go newLogClosure 0.00% (0/1) github.com/conformal/btcchain/chain.go BlockChain.DisableVerify 0.00% (0/1) -github.com/conformal/btcchain/log.go UseLogger 0.00% (0/1) github.com/conformal/btcchain/process.go RuleError.Error 0.00% (0/1) -github.com/conformal/btcchain ------------------------------------- 53.75% (666/1239) +github.com/conformal/btcchain/difficulty.go BlockChain.CalcNextRequiredDifficulty 0.00% (0/1) +github.com/conformal/btcchain/log.go logClosure.String 0.00% (0/1) +github.com/conformal/btcchain/log.go UseLogger 0.00% (0/1) +github.com/conformal/btcchain/log.go newLogClosure 0.00% (0/1) +github.com/conformal/btcchain ------------------------------------- 55.94% (697/1246) diff --git a/validate.go b/validate.go index e156a79a..71428570 100644 --- a/validate.go +++ b/validate.go @@ -711,6 +711,15 @@ func CheckTransactionInputs(tx *btcutil.Tx, txHeight int64, txStore TxStore) (in // checkConnectBlock performs several checks to confirm connecting the passed // block to the main chain (including whatever reorganization might be necessary // to get this node to the main chain) does not violate any rules. +// +// The CheckConnectBlock function makes use of this function to perform the +// bulk of its work. The only difference is this function accepts a node which +// may or may not require reorganization to connect it to the main chain whereas +// CheckConnectBlock creates a new node which specifically connects to the end +// of the current main chain and then calls this function with that node. +// +// See the comments for CheckConnectBlock for some examples of the type of +// checks performed by this function. func (b *BlockChain) checkConnectBlock(node *blockNode, block *btcutil.Block) error { // If the side chain blocks end up in the database, a call to // checkBlockSanity should be done here in case a previous version @@ -718,12 +727,9 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block *btcutil.Block) er // implementation only currently uses memory for the side chain blocks, // it isn't currently necessary. - // TODO(davec): Keep a flag if this has already been done to avoid - // multiple runs. - // The coinbase for the Genesis block is not spendable, so just return // now. - if node.hash.IsEqual(b.chainParams().GenesisHash) { + if node.hash.IsEqual(b.chainParams().GenesisHash) && b.bestChain == nil { return nil } @@ -864,3 +870,24 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block *btcutil.Block) er return nil } + +// CheckConnectBlock performs several checks to confirm connecting the passed +// block to the main chain does not violate any rules. An example of some of +// the checks performed are ensuring connecting the block would not cause any +// duplicate transaction hashes for old transactions that aren't already fully +// spent, double spends, exceeding the maximum allowed signature operations +// per block, invalid values in relation to the expected block subisidy, or +// fail transaction script validation. +// +// This function is NOT safe for concurrent access. +func (b *BlockChain) CheckConnectBlock(block *btcutil.Block) error { + prevNode := b.bestChain + blockSha, _ := block.Sha() + newNode := newBlockNode(&block.MsgBlock().Header, blockSha, block.Height()) + if prevNode != nil { + newNode.parent = prevNode + newNode.workSum.Add(prevNode.workSum, newNode.workSum) + } + + return b.checkConnectBlock(newNode, block) +} diff --git a/validate_test.go b/validate_test.go index 00bd4894..0157cedd 100644 --- a/validate_test.go +++ b/validate_test.go @@ -14,6 +14,30 @@ import ( "time" ) +// TestCheckConnectBlock tests the CheckConnectBlock function to ensure it +// fails +func TestCheckConnectBlock(t *testing.T) { + // Create a new database and chain instance to run tests against. + chain, teardownFunc, err := chainSetup("checkconnectblock") + if err != nil { + t.Errorf("Failed to setup chain instance: %v", err) + return + } + defer teardownFunc() + + err = chain.GenerateInitialIndex() + if err != nil { + t.Errorf("GenerateInitialIndex: %v", err) + } + + // The genesis block should fail to connect since it's already + // inserted. + err = chain.CheckConnectBlock(btcutil.NewBlock(&btcwire.GenesisBlock)) + if err == nil { + t.Errorf("CheckConnectBlock: Did not received expected error") + } +} + func TestCheckBlockSanity(t *testing.T) { powLimit := btcchain.ChainParams(btcwire.MainNet).PowLimit block := btcutil.NewBlock(&Block100000)