diff --git a/accept.go b/accept.go index e52dad09..94fa73c7 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 40e378ab..4113c914 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 c30ad7ac..c39182ec 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 d82fdb03..0ad1f9d4 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 00000000..5307e590 --- /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 5b96a608..876dc330 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 d2f3d9bc..ac6ba4c3 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 f30afd96..d5936a00 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 508aa9c5..1d84b8d3 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) }