From f219ed5baf6803c7972e97efa87bcb424a72a836 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 24 Jul 2013 11:37:45 -0500 Subject: [PATCH] Add support for params based on active network. This commit modifies the code to use params based on the active network which paves the way for supporting the special rules and different genesis blocks used by the test networks. --- accept.go | 2 +- chain.go | 4 +-- difficulty.go | 15 ++++++----- internal_test.go | 4 +-- params.go | 68 +++++++++++++++++++++++++++++++++++++++++++++++ process.go | 4 +-- test_coverage.txt | 23 ++++++++-------- validate.go | 7 ++--- validate_test.go | 18 ++++++++++++- 9 files changed, 117 insertions(+), 28 deletions(-) create mode 100644 params.go diff --git a/accept.go b/accept.go index e52dad09e..94fa73c78 100644 --- a/accept.go +++ b/accept.go @@ -32,7 +32,7 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block) error { // calculated difficulty based on the previous block and difficulty // retarget rules. blockHeader := block.MsgBlock().Header - expectedDifficulty, err := b.calcNextRequiredDifficulty(prevNode) + expectedDifficulty, err := b.calcNextRequiredDifficulty(prevNode, block) if err != nil { return err } diff --git a/chain.go b/chain.go index 40e378abb..4113c9148 100644 --- a/chain.go +++ b/chain.go @@ -339,7 +339,7 @@ func (b *BlockChain) getPrevNodeFromNode(node *blockNode) (*blockNode, error) { } // Genesis block. - if node.hash.IsEqual(&btcwire.GenesisHash) { + if node.hash.IsEqual(b.netParams().genesisHash) { return nil, nil } @@ -393,7 +393,7 @@ func (b *BlockChain) isMajorityVersion(minVer uint32, startNode *blockNode, numR func (b *BlockChain) calcPastMedianTime(startNode *blockNode) (time.Time, error) { // Genesis block. if startNode == nil { - return btcwire.GenesisBlock.Header.Timestamp, nil + return b.netParams().genesisBlock.Header.Timestamp, nil } // Create a slice of the previous few block timestamps used to calculate diff --git a/difficulty.go b/difficulty.go index c30ad7ac9..c39182ec1 100644 --- a/difficulty.go +++ b/difficulty.go @@ -6,6 +6,7 @@ package btcchain import ( "fmt" + "github.com/conformal/btcutil" "github.com/conformal/btcwire" "math/big" "time" @@ -50,10 +51,6 @@ var ( // oneLsh256 is 1 shifted left 256 bits. It is defined here to avoid // the overhead of creating it multiple times. oneLsh256 = new(big.Int).Lsh(bigOne, 256) - - // powLimit is the highest proof of work value a bitcoin block can have. - // It is the value 2^224 - 1. - powLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 224), bigOne) ) // ShaHashToBig converts a btcwire.ShaHash into a big.Int that can be used to @@ -185,11 +182,14 @@ func calcWork(bits uint32) *big.Rat { // can have given starting difficulty bits and a duration. It is mainly used to // verify that claimed proof of work by a block is sane as compared to a // known good checkpoint. -func calcEasiestDifficulty(bits uint32, duration time.Duration) uint32 { +func (b *BlockChain) calcEasiestDifficulty(bits uint32, duration time.Duration) uint32 { // Convert types used in the calculations below. durationVal := int64(duration) adjustmentFactor := big.NewInt(retargetAdjustmentFactor) + // Choose the correct proof of work limit for the active network. + powLimit := b.netParams().powLimit + // TODO(davec): Testnet has special rules. // Since easier difficulty equates to higher numbers, the easiest @@ -212,7 +212,10 @@ func calcEasiestDifficulty(bits uint32, duration time.Duration) uint32 { // calcNextRequiredDifficulty calculates the required difficulty for the block // after the passed previous block node based on the difficulty retarget rules. -func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode) (uint32, error) { +func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, block *btcutil.Block) (uint32, error) { + // Choose the correct proof of work limit for the active network. + powLimit := b.netParams().powLimit + // Genesis block. if lastNode == nil { return BigToCompact(powLimit), nil diff --git a/internal_test.go b/internal_test.go index d82fdb033..0ad1f9d48 100644 --- a/internal_test.go +++ b/internal_test.go @@ -18,8 +18,8 @@ import ( // TstCheckBlockSanity makes the internal checkBlockSanity function available to // the test package. -func TstCheckBlockSanity(block *btcutil.Block) error { - return checkBlockSanity(block) +func (b *BlockChain) TstCheckBlockSanity(block *btcutil.Block) error { + return b.checkBlockSanity(block) } // TstSetCoinbaseMaturity makes the ability to set the coinbase maturity diff --git a/params.go b/params.go new file mode 100644 index 000000000..5307e5908 --- /dev/null +++ b/params.go @@ -0,0 +1,68 @@ +// Copyright (c) 2013 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcchain + +import ( + "github.com/conformal/btcwire" + "math/big" +) + +// params is used to group parameters for various networks such as the main +// network and test networks. +type params struct { + genesisBlock *btcwire.MsgBlock + genesisHash *btcwire.ShaHash + powLimit *big.Int +} + +// mainNetParams contains parameters specific to the main network +// (btcwire.MainNet). +var mainNetParams = params{ + genesisBlock: &btcwire.GenesisBlock, + genesisHash: &btcwire.GenesisHash, + + // powLimit is the highest proof of work value a bitcoin block can have. + // It is the value 2^224 - 1 for the main network. + powLimit: new(big.Int).Sub(new(big.Int).Lsh(bigOne, 224), bigOne), +} + +// regressionParams contains parameters specific to the regression test network +// (btcwire.TestNet). +var regressionParams = params{ + genesisBlock: &btcwire.TestNetGenesisBlock, + genesisHash: &btcwire.TestNetGenesisHash, + + // powLimit is the highest proof of work value a bitcoin block can have. + // It is the value 2^256 - 1 for the regression test network. + powLimit: new(big.Int).Sub(new(big.Int).Lsh(bigOne, 255), bigOne), +} + +// testNet3Params contains parameters specific to the test network (version 3) +// (btcwire.TestNet3). +var testNet3Params = params{ + genesisBlock: &btcwire.TestNet3GenesisBlock, + genesisHash: &btcwire.TestNet3GenesisHash, + + // powLimit is the highest proof of work value a bitcoin block can have. + // It is the value 2^224 - 1 for the test network (version 3). + powLimit: new(big.Int).Sub(new(big.Int).Lsh(bigOne, 224), bigOne), +} + +// netParams returns parameters specific to the passed bitcoin network. +func (b *BlockChain) netParams() *params { + switch b.btcnet { + case btcwire.TestNet: + return ®ressionParams + + case btcwire.TestNet3: + return &testNet3Params + + // Return main net by default. + case btcwire.MainNet: + fallthrough + default: + return &mainNetParams + } +} diff --git a/process.go b/process.go index 5b96a6082..876dc3300 100644 --- a/process.go +++ b/process.go @@ -95,7 +95,7 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block) error { } // Perform preliminary sanity checks on the block and its transactions. - err = checkBlockSanity(block) + err = b.checkBlockSanity(block) if err != nil { return err } @@ -129,7 +129,7 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block) error { // expected based on elapsed time since the last checkpoint and // maximum adjustment allowed by the retarget rules. duration := blockHeader.Timestamp.Sub(checkpointTime) - requiredTarget := CompactToBig(calcEasiestDifficulty( + requiredTarget := CompactToBig(b.calcEasiestDifficulty( checkpointHeader.Bits, duration)) currentTarget := CompactToBig(blockHeader.Bits) if currentTarget.Cmp(requiredTarget) > 0 { diff --git a/test_coverage.txt b/test_coverage.txt index d2f3d9bca..ac6ba4c34 100644 --- a/test_coverage.txt +++ b/test_coverage.txt @@ -2,21 +2,21 @@ github.com/conformal/btcchain/chain.go BlockChain.removeOrphanBlock 100.00% (12/12) github.com/conformal/btcchain/chain.go BlockChain.getOrphanRoot 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/merkle.go nextPowerOfTwo 100.00% (4/4) github.com/conformal/btcchain/chain.go newBlockNode 100.00% (4/4) github.com/conformal/btcchain/difficulty.go calcWork 100.00% (3/3) github.com/conformal/btcchain/process.go BlockChain.blockExists 100.00% (3/3) github.com/conformal/btcchain/chain.go New 100.00% (2/2) github.com/conformal/btcchain/checkpoints.go newShaHashFromStr 100.00% (2/2) -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/timesorter.go timeSorter.Less 100.00% (1/1) -github.com/conformal/btcchain/log.go init 100.00% (1/1) github.com/conformal/btcchain/timesorter.go timeSorter.Swap 100.00% (1/1) github.com/conformal/btcchain/checkpoints.go BlockChain.DisableCheckpoints 100.00% (1/1) +github.com/conformal/btcchain/timesorter.go timeSorter.Less 100.00% (1/1) github.com/conformal/btcchain/timesorter.go timeSorter.Len 100.00% (1/1) +github.com/conformal/btcchain/log.go init 100.00% (1/1) +github.com/conformal/btcchain/log.go DisableLog 100.00% (1/1) github.com/conformal/btcchain/merkle.go BuildMerkleTreeStore 94.12% (16/17) github.com/conformal/btcchain/chain.go BlockChain.getReorganizeNodes 92.86% (13/14) github.com/conformal/btcchain/process.go BlockChain.processOrphans 91.67% (11/12) @@ -37,30 +37,31 @@ github.com/conformal/btcchain/chain.go BlockChain.disconnectBlock 76.92% (10 github.com/conformal/btcchain/chain.go BlockChain.addOrphanBlock 75.00% (12/16) github.com/conformal/btcchain/difficulty.go CompactToBig 75.00% (9/12) github.com/conformal/btcchain/validate.go BlockChain.checkConnectBlock 68.52% (37/54) -github.com/conformal/btcchain/validate.go checkBlockSanity 66.67% (30/45) +github.com/conformal/btcchain/validate.go BlockChain.checkBlockSanity 66.67% (30/45) github.com/conformal/btcchain/validate.go isNullOutpoint 66.67% (2/3) github.com/conformal/btcchain/scriptval.go validateTxIn 64.71% (11/17) github.com/conformal/btcchain/validate.go checkTransactionInputs 63.64% (28/44) github.com/conformal/btcchain/validate.go checkTransactionSanity 62.16% (23/37) github.com/conformal/btcchain/txlookup.go connectTransactions 60.00% (9/15) github.com/conformal/btcchain/validate.go isBIP0030Node 60.00% (3/5) +github.com/conformal/btcchain/params.go BlockChain.netParams 60.00% (3/5) github.com/conformal/btcchain/process.go BlockChain.ProcessBlock 59.09% (26/44) +github.com/conformal/btcchain/validate.go BlockChain.checkProofOfWork 58.82% (10/17) github.com/conformal/btcchain/validate.go BlockChain.checkBIP0030 57.14% (8/14) -github.com/conformal/btcchain/validate.go checkProofOfWork 56.25% (9/16) github.com/conformal/btcchain/chain.go BlockChain.loadBlockNode 50.00% (11/22) -github.com/conformal/btcchain/notifications.go BlockChain.sendNotification 50.00% (2/4) github.com/conformal/btcchain/checkpoints.go BlockChain.LatestCheckpoint 50.00% (2/4) +github.com/conformal/btcchain/notifications.go BlockChain.sendNotification 50.00% (2/4) github.com/conformal/btcchain/accept.go BlockChain.maybeAcceptBlock 49.23% (32/65) github.com/conformal/btcchain/chain.go BlockChain.getPrevNodeFromNode 33.33% (4/12) github.com/conformal/btcchain/checkpoints.go BlockChain.verifyCheckpoint 33.33% (2/6) github.com/conformal/btcchain/validate.go isFinalizedTransaction 23.08% (3/13) github.com/conformal/btcchain/checkpoints.go BlockChain.findLatestKnownCheckpoint 18.18% (2/11) -github.com/conformal/btcchain/difficulty.go BlockChain.calcNextRequiredDifficulty 10.71% (3/28) +github.com/conformal/btcchain/difficulty.go BlockChain.calcNextRequiredDifficulty 13.79% (4/29) 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/difficulty.go BigToCompact 0.00% (0/16) github.com/conformal/btcchain/validate.go checkSerializedHeight 0.00% (0/12) -github.com/conformal/btcchain/difficulty.go calcEasiestDifficulty 0.00% (0/9) +github.com/conformal/btcchain/difficulty.go BlockChain.calcEasiestDifficulty 0.00% (0/10) github.com/conformal/btcchain/chain.go removeChildNode 0.00% (0/8) github.com/conformal/btcchain/log.go SetLogWriter 0.00% (0/7) github.com/conformal/btcchain/checkpoints.go isNonstandardTransaction 0.00% (0/5) @@ -70,8 +71,8 @@ github.com/conformal/btcchain/notifications.go NotificationType.String 0.00% github.com/conformal/btcchain/chain.go addChildrenWork 0.00% (0/3) github.com/conformal/btcchain/log.go UseLogger 0.00% (0/1) github.com/conformal/btcchain/chain.go BlockChain.DisableVerify 0.00% (0/1) -github.com/conformal/btcchain/log.go logClosure.String 0.00% (0/1) github.com/conformal/btcchain/process.go RuleError.Error 0.00% (0/1) github.com/conformal/btcchain/log.go newLogClosure 0.00% (0/1) -github.com/conformal/btcchain ------------------------------------- 59.23% (571/964) +github.com/conformal/btcchain/log.go logClosure.String 0.00% (0/1) +github.com/conformal/btcchain ------------------------------------- 59.26% (576/972) diff --git a/validate.go b/validate.go index f30afd967..d5936a006 100644 --- a/validate.go +++ b/validate.go @@ -267,7 +267,7 @@ func checkTransactionSanity(tx *btcwire.MsgTx) error { // checkProofOfWork ensures the block header bits which indicate the target // difficulty is in min/max range and that the block hash is less than the // target difficulty as claimed. -func checkProofOfWork(block *btcutil.Block) error { +func (b *BlockChain) checkProofOfWork(block *btcutil.Block) error { // The target difficulty must be larger than zero. header := block.MsgBlock().Header target := CompactToBig(header.Bits) @@ -278,6 +278,7 @@ func checkProofOfWork(block *btcutil.Block) error { } // The target difficulty must be less than the maximum allowed. + powLimit := b.netParams().powLimit if target.Cmp(powLimit) > 0 { str := fmt.Sprintf("block target difficulty of %064x is "+ "higher than max of %064x", target, powLimit) @@ -407,7 +408,7 @@ func countP2SHSigOps(msgTx *btcwire.MsgTx, isCoinBaseTx bool, txStore map[btcwir // checkBlockSanity performs some preliminary checks on a block to ensure it is // sane before continuing with block processing. These checks are context free. -func checkBlockSanity(block *btcutil.Block) error { +func (b *BlockChain) checkBlockSanity(block *btcutil.Block) error { // NOTE: bitcoind does size limits checking here, but the size limits // have already been checked by btcwire for incoming blocks. Also, // btcwire checks the size limits on send too, so there is no need @@ -416,7 +417,7 @@ func checkBlockSanity(block *btcutil.Block) error { // Ensure the proof of work bits in the block header is in min/max range // and the block hash is less than the target value described by the // bits. - err := checkProofOfWork(block) + err := b.checkProofOfWork(block) if err != nil { return err } diff --git a/validate_test.go b/validate_test.go index 508aa9c5d..1d84b8d3b 100644 --- a/validate_test.go +++ b/validate_test.go @@ -6,15 +6,31 @@ package btcchain_test import ( "github.com/conformal/btcchain" + "github.com/conformal/btcdb" "github.com/conformal/btcutil" "github.com/conformal/btcwire" + "os" "testing" "time" ) func TestCheckBlockSanity(t *testing.T) { + // Create a new test database. + dbName := "cbsanitytest.db" + _ = os.Remove(dbName) + db, err := btcdb.CreateDB("sqlite", dbName) + if err != nil { + t.Errorf("Error creating db: %v\n", err) + } + defer os.Remove(dbName) + defer db.Close() + + // Create a new BlockChain instance using the underlying database for + // the main bitcoin network and ignore notifications. + chain := btcchain.New(db, btcwire.MainNet, nil) + block := btcutil.NewBlock(&Block100000, btcwire.ProtocolVersion) - err := btcchain.TstCheckBlockSanity(block) + err = chain.TstCheckBlockSanity(block) if err != nil { t.Errorf("CheckBlockSanity: %v", err) }