diff --git a/blockchain/bench_test.go b/blockchain/bench_test.go index a680feca..43d3152b 100644 --- a/blockchain/bench_test.go +++ b/blockchain/bench_test.go @@ -2,12 +2,11 @@ // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package blockchain_test +package blockchain import ( "testing" - "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcutil" ) @@ -17,7 +16,7 @@ func BenchmarkIsCoinBase(b *testing.B) { tx, _ := btcutil.NewBlock(&Block100000).Tx(1) b.ResetTimer() for i := 0; i < b.N; i++ { - blockchain.IsCoinBase(tx) + IsCoinBase(tx) } } @@ -27,6 +26,6 @@ func BenchmarkIsCoinBaseTx(b *testing.B) { tx := Block100000.Transactions[1] b.ResetTimer() for i := 0; i < b.N; i++ { - blockchain.IsCoinBaseTx(tx) + IsCoinBaseTx(tx) } } diff --git a/blockchain/chain_test.go b/blockchain/chain_test.go index fcdcaf5c..e5b554f1 100644 --- a/blockchain/chain_test.go +++ b/blockchain/chain_test.go @@ -2,13 +2,12 @@ // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package blockchain_test +package blockchain import ( "testing" "time" - "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" @@ -49,7 +48,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], BFNone) if err != nil { t.Errorf("ProcessBlock fail on block %v: %v\n", i, err) return @@ -63,7 +62,7 @@ func TestHaveBlock(t *testing.T) { // Insert an orphan block. _, isOrphan, err := chain.ProcessBlock(btcutil.NewBlock(&Block100000), - blockchain.BFNone) + BFNone) if err != nil { t.Errorf("Unable to process block: %v", err) return @@ -125,12 +124,15 @@ func TestCalcSequenceLock(t *testing.T) { blockVersion := int32(0x20000000 | (uint32(1) << csvBit)) // Generate enough synthetic blocks to activate CSV. - chain, node := blockchain.TstNewFakeChain(netParams) + chain := newFakeChain(netParams) + node := chain.bestNode blockTime := node.Header().Timestamp numBlocksToActivate := (netParams.MinerConfirmationWindow * 3) for i := uint32(0); i < numBlocksToActivate; i++ { blockTime = blockTime.Add(time.Second) - node = chain.TstNewFakeNode(node, blockVersion, 0, blockTime) + node = newFakeNode(node, blockVersion, 0, blockTime) + chain.index.AddNode(node) + chain.bestNode = node } // Create a utxo view with a fake utxo for the inputs used in the @@ -142,11 +144,9 @@ func TestCalcSequenceLock(t *testing.T) { Value: 10, }}, }) - utxoView := blockchain.NewUtxoViewpoint() + utxoView := NewUtxoViewpoint() utxoView.AddTxOuts(targetTx, int32(numBlocksToActivate)-4) - bestHeader := node.Header() - bestHash := bestHeader.BlockHash() - utxoView.SetBestHash(&bestHash) + utxoView.SetBestHash(&node.hash) // Create a utxo that spends the fake utxo created above for use in the // transactions created in the tests. It has an age of 4 blocks. Note @@ -190,9 +190,9 @@ func TestCalcSequenceLock(t *testing.T) { tests := []struct { tx *wire.MsgTx - view *blockchain.UtxoViewpoint + view *UtxoViewpoint mempool bool - want *blockchain.SequenceLock + want *SequenceLock }{ // A transaction of version one should disable sequence locks // as the new sequence number semantics only apply to @@ -202,11 +202,11 @@ func TestCalcSequenceLock(t *testing.T) { Version: 1, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, - Sequence: blockchain.LockTimeToSequence(false, 3), + Sequence: LockTimeToSequence(false, 3), }}, }, view: utxoView, - want: &blockchain.SequenceLock{ + want: &SequenceLock{ Seconds: -1, BlockHeight: -1, }, @@ -223,7 +223,7 @@ func TestCalcSequenceLock(t *testing.T) { }}, }, view: utxoView, - want: &blockchain.SequenceLock{ + want: &SequenceLock{ Seconds: -1, BlockHeight: -1, }, @@ -239,11 +239,11 @@ func TestCalcSequenceLock(t *testing.T) { Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, - Sequence: blockchain.LockTimeToSequence(true, 2), + Sequence: LockTimeToSequence(true, 2), }}, }, view: utxoView, - want: &blockchain.SequenceLock{ + want: &SequenceLock{ Seconds: medianTime - 1, BlockHeight: -1, }, @@ -257,11 +257,11 @@ func TestCalcSequenceLock(t *testing.T) { Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, - Sequence: blockchain.LockTimeToSequence(true, 1024), + Sequence: LockTimeToSequence(true, 1024), }}, }, view: utxoView, - want: &blockchain.SequenceLock{ + want: &SequenceLock{ Seconds: medianTime + 1023, BlockHeight: -1, }, @@ -277,18 +277,18 @@ func TestCalcSequenceLock(t *testing.T) { Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, - Sequence: blockchain.LockTimeToSequence(true, 2560), + Sequence: LockTimeToSequence(true, 2560), }, { PreviousOutPoint: utxo, - Sequence: blockchain.LockTimeToSequence(false, 4), + Sequence: LockTimeToSequence(false, 4), }, { PreviousOutPoint: utxo, - Sequence: blockchain.LockTimeToSequence(false, 5) | + Sequence: LockTimeToSequence(false, 5) | wire.SequenceLockTimeDisabled, }}, }, view: utxoView, - want: &blockchain.SequenceLock{ + want: &SequenceLock{ Seconds: medianTime + (5 << wire.SequenceLockTimeGranularity) - 1, BlockHeight: prevUtxoHeight + 3, }, @@ -302,11 +302,11 @@ func TestCalcSequenceLock(t *testing.T) { Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, - Sequence: blockchain.LockTimeToSequence(false, 3), + Sequence: LockTimeToSequence(false, 3), }}, }, view: utxoView, - want: &blockchain.SequenceLock{ + want: &SequenceLock{ Seconds: -1, BlockHeight: prevUtxoHeight + 2, }, @@ -319,14 +319,14 @@ func TestCalcSequenceLock(t *testing.T) { Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, - Sequence: blockchain.LockTimeToSequence(true, 5120), + Sequence: LockTimeToSequence(true, 5120), }, { PreviousOutPoint: utxo, - Sequence: blockchain.LockTimeToSequence(true, 2560), + Sequence: LockTimeToSequence(true, 2560), }}, }, view: utxoView, - want: &blockchain.SequenceLock{ + want: &SequenceLock{ Seconds: medianTime + (10 << wire.SequenceLockTimeGranularity) - 1, BlockHeight: -1, }, @@ -340,14 +340,14 @@ func TestCalcSequenceLock(t *testing.T) { Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, - Sequence: blockchain.LockTimeToSequence(false, 1), + Sequence: LockTimeToSequence(false, 1), }, { PreviousOutPoint: utxo, - Sequence: blockchain.LockTimeToSequence(false, 11), + Sequence: LockTimeToSequence(false, 11), }}, }, view: utxoView, - want: &blockchain.SequenceLock{ + want: &SequenceLock{ Seconds: -1, BlockHeight: prevUtxoHeight + 10, }, @@ -360,20 +360,20 @@ func TestCalcSequenceLock(t *testing.T) { Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, - Sequence: blockchain.LockTimeToSequence(true, 2560), + Sequence: LockTimeToSequence(true, 2560), }, { PreviousOutPoint: utxo, - Sequence: blockchain.LockTimeToSequence(true, 6656), + Sequence: LockTimeToSequence(true, 6656), }, { PreviousOutPoint: utxo, - Sequence: blockchain.LockTimeToSequence(false, 3), + Sequence: LockTimeToSequence(false, 3), }, { PreviousOutPoint: utxo, - Sequence: blockchain.LockTimeToSequence(false, 9), + Sequence: LockTimeToSequence(false, 9), }}, }, view: utxoView, - want: &blockchain.SequenceLock{ + want: &SequenceLock{ Seconds: medianTime + (13 << wire.SequenceLockTimeGranularity) - 1, BlockHeight: prevUtxoHeight + 8, }, @@ -389,12 +389,12 @@ func TestCalcSequenceLock(t *testing.T) { Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: unConfUtxo, - Sequence: blockchain.LockTimeToSequence(false, 2), + Sequence: LockTimeToSequence(false, 2), }}, }, view: utxoView, mempool: true, - want: &blockchain.SequenceLock{ + want: &SequenceLock{ Seconds: -1, BlockHeight: nextBlockHeight + 1, }, @@ -407,12 +407,12 @@ func TestCalcSequenceLock(t *testing.T) { Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: unConfUtxo, - Sequence: blockchain.LockTimeToSequence(true, 1024), + Sequence: LockTimeToSequence(true, 1024), }}, }, view: utxoView, mempool: true, - want: &blockchain.SequenceLock{ + want: &SequenceLock{ Seconds: nextMedianTime + 1023, BlockHeight: -1, }, diff --git a/blockchain/common_test.go b/blockchain/common_test.go index 21683478..24e0388c 100644 --- a/blockchain/common_test.go +++ b/blockchain/common_test.go @@ -1,8 +1,8 @@ -// Copyright (c) 2013-2016 The btcsuite developers +// Copyright (c) 2013-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package blockchain_test +package blockchain import ( "compress/bzip2" @@ -12,14 +12,15 @@ import ( "os" "path/filepath" "strings" + "time" - "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/database" _ "github.com/btcsuite/btcd/database/ffldb" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" ) const ( @@ -56,10 +57,68 @@ func isSupportedDbType(dbType string) bool { return false } +// loadBlocks reads files containing bitcoin block data (gzipped but otherwise +// in the format bitcoind writes) from disk and returns them as an array of +// btcutil.Block. This is largely borrowed from the test code in btcdb. +func loadBlocks(filename string) (blocks []*btcutil.Block, err error) { + filename = filepath.Join("testdata/", filename) + + var network = wire.MainNet + var dr io.Reader + var fi io.ReadCloser + + fi, err = os.Open(filename) + if err != nil { + return + } + + if strings.HasSuffix(filename, ".bz2") { + dr = bzip2.NewReader(fi) + } else { + dr = fi + } + defer fi.Close() + + var block *btcutil.Block + + err = nil + for height := int64(1); err == nil; height++ { + var rintbuf uint32 + err = binary.Read(dr, binary.LittleEndian, &rintbuf) + if err == io.EOF { + // hit end of file at expected offset: no warning + height-- + err = nil + break + } + if err != nil { + break + } + if rintbuf != uint32(network) { + break + } + err = binary.Read(dr, binary.LittleEndian, &rintbuf) + blocklen := rintbuf + + rbytes := make([]byte, blocklen) + + // read block + dr.Read(rbytes) + + block, err = btcutil.NewBlockFromBytes(rbytes) + if err != nil { + return + } + blocks = append(blocks, block) + } + + return +} + // chainSetup is used to create a new db and chain instance with the genesis // block already inserted. In addition to the new chain instance, it returns // a teardown function the caller should invoke when done testing to clean up. -func chainSetup(dbName string, params *chaincfg.Params) (*blockchain.BlockChain, func(), error) { +func chainSetup(dbName string, params *chaincfg.Params) (*BlockChain, func(), error) { if !isSupportedDbType(testDbType) { return nil, nil, fmt.Errorf("unsupported db type %v", testDbType) } @@ -113,11 +172,11 @@ func chainSetup(dbName string, params *chaincfg.Params) (*blockchain.BlockChain, paramsCopy := *params // Create the main chain instance. - chain, err := blockchain.New(&blockchain.Config{ + chain, err := New(&Config{ DB: db, ChainParams: ¶msCopy, Checkpoints: nil, - TimeSource: blockchain.NewMedianTime(), + TimeSource: NewMedianTime(), SigCache: txscript.NewSigCache(1000), }) if err != nil { @@ -129,7 +188,7 @@ func chainSetup(dbName string, params *chaincfg.Params) (*blockchain.BlockChain, } // loadUtxoView returns a utxo view loaded from a file. -func loadUtxoView(filename string) (*blockchain.UtxoViewpoint, error) { +func loadUtxoView(filename string) (*UtxoViewpoint, error) { // The utxostore file format is: // // @@ -151,7 +210,7 @@ func loadUtxoView(filename string) (*blockchain.UtxoViewpoint, error) { } defer fi.Close() - view := blockchain.NewUtxoViewpoint() + view := NewUtxoViewpoint() for { // Hash of the utxo entry. var hash chainhash.Hash @@ -179,7 +238,7 @@ func loadUtxoView(filename string) (*blockchain.UtxoViewpoint, error) { } // Deserialize it and add it to the view. - utxoEntry, err := blockchain.TstDeserializeUtxoEntry(serialized) + utxoEntry, err := deserializeUtxoEntry(serialized) if err != nil { return nil, err } @@ -188,3 +247,53 @@ func loadUtxoView(filename string) (*blockchain.UtxoViewpoint, error) { return view, nil } + +// TstSetCoinbaseMaturity makes the ability to set the coinbase maturity +// available when running tests. +func (b *BlockChain) TstSetCoinbaseMaturity(maturity uint16) { + b.chainParams.CoinbaseMaturity = maturity +} + +// newFakeChain returns a chain that is usable for syntetic tests. It is +// important to note that this chain has no database associated with it, so +// it is not usable with all functions and the tests must take care when making +// use of it. +func newFakeChain(params *chaincfg.Params) *BlockChain { + // Create a genesis block node and block index index populated with it + // for use when creating the fake chain below. + node := newBlockNode(¶ms.GenesisBlock.Header, 0) + node.inMainChain = true + index := newBlockIndex(nil, params) + index.AddNode(node) + + targetTimespan := int64(params.TargetTimespan / time.Second) + targetTimePerBlock := int64(params.TargetTimePerBlock / time.Second) + adjustmentFactor := params.RetargetAdjustmentFactor + return &BlockChain{ + chainParams: params, + timeSource: NewMedianTime(), + minRetargetTimespan: targetTimespan / adjustmentFactor, + maxRetargetTimespan: targetTimespan * adjustmentFactor, + blocksPerRetarget: int32(targetTimespan / targetTimePerBlock), + index: index, + warningCaches: newThresholdCaches(vbNumBits), + deploymentCaches: newThresholdCaches(chaincfg.DefinedDeployments), + bestNode: node, + } +} + +// newFakeNode creates a block node connected to the passed parent with the +// provided fields populated and fake values for the other fields. +func newFakeNode(parent *blockNode, blockVersion int32, bits uint32, timestamp time.Time) *blockNode { + // Make up a header and create a block node from it. + header := &wire.BlockHeader{ + Version: blockVersion, + PrevBlock: parent.hash, + Bits: bits, + Timestamp: timestamp, + } + node := newBlockNode(header, parent.height+1) + node.parent = parent + node.workSum.Add(parent.workSum, node.workSum) + return node +} diff --git a/blockchain/difficulty_test.go b/blockchain/difficulty_test.go index 58fa7f85..b42b7c73 100644 --- a/blockchain/difficulty_test.go +++ b/blockchain/difficulty_test.go @@ -1,16 +1,16 @@ -// Copyright (c) 2014 The btcsuite developers +// Copyright (c) 2014-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package blockchain_test +package blockchain import ( "math/big" "testing" - - "github.com/btcsuite/btcd/blockchain" ) +// TestBigToCompact ensures BigToCompact converts big integers to the expected +// compact representation. func TestBigToCompact(t *testing.T) { tests := []struct { in int64 @@ -22,7 +22,7 @@ func TestBigToCompact(t *testing.T) { for x, test := range tests { n := big.NewInt(test.in) - r := blockchain.BigToCompact(n) + r := BigToCompact(n) if r != test.out { t.Errorf("TestBigToCompact test #%d failed: got %d want %d\n", x, r, test.out) @@ -31,6 +31,8 @@ func TestBigToCompact(t *testing.T) { } } +// TestCompactToBig ensures CompactToBig converts numbers using the compact +// representation to the expected big intergers. func TestCompactToBig(t *testing.T) { tests := []struct { in uint32 @@ -40,7 +42,7 @@ func TestCompactToBig(t *testing.T) { } for x, test := range tests { - n := blockchain.CompactToBig(test.in) + n := CompactToBig(test.in) want := big.NewInt(test.out) if n.Cmp(want) != 0 { t.Errorf("TestCompactToBig test #%d failed: got %d want %d\n", @@ -50,6 +52,8 @@ func TestCompactToBig(t *testing.T) { } } +// TestCalcWork ensures CalcWork calculates the expected work value from values +// in compact representation. func TestCalcWork(t *testing.T) { tests := []struct { in uint32 @@ -61,7 +65,7 @@ func TestCalcWork(t *testing.T) { for x, test := range tests { bits := uint32(test.in) - r := blockchain.CalcWork(bits) + r := CalcWork(bits) if r.Int64() != test.out { t.Errorf("TestCalcWork test #%d failed: got %v want %d\n", x, r.Int64(), test.out) diff --git a/blockchain/error_test.go b/blockchain/error_test.go index d9724ebf..cf740de9 100644 --- a/blockchain/error_test.go +++ b/blockchain/error_test.go @@ -1,59 +1,57 @@ -// Copyright (c) 2014 The btcsuite developers +// Copyright (c) 2014-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package blockchain_test +package blockchain import ( "testing" - - "github.com/btcsuite/btcd/blockchain" ) // TestErrorCodeStringer tests the stringized output for the ErrorCode type. func TestErrorCodeStringer(t *testing.T) { tests := []struct { - in blockchain.ErrorCode + in ErrorCode want string }{ - {blockchain.ErrDuplicateBlock, "ErrDuplicateBlock"}, - {blockchain.ErrBlockTooBig, "ErrBlockTooBig"}, - {blockchain.ErrBlockVersionTooOld, "ErrBlockVersionTooOld"}, - {blockchain.ErrInvalidTime, "ErrInvalidTime"}, - {blockchain.ErrTimeTooOld, "ErrTimeTooOld"}, - {blockchain.ErrTimeTooNew, "ErrTimeTooNew"}, - {blockchain.ErrDifficultyTooLow, "ErrDifficultyTooLow"}, - {blockchain.ErrUnexpectedDifficulty, "ErrUnexpectedDifficulty"}, - {blockchain.ErrHighHash, "ErrHighHash"}, - {blockchain.ErrBadMerkleRoot, "ErrBadMerkleRoot"}, - {blockchain.ErrBadCheckpoint, "ErrBadCheckpoint"}, - {blockchain.ErrForkTooOld, "ErrForkTooOld"}, - {blockchain.ErrCheckpointTimeTooOld, "ErrCheckpointTimeTooOld"}, - {blockchain.ErrNoTransactions, "ErrNoTransactions"}, - {blockchain.ErrTooManyTransactions, "ErrTooManyTransactions"}, - {blockchain.ErrNoTxInputs, "ErrNoTxInputs"}, - {blockchain.ErrNoTxOutputs, "ErrNoTxOutputs"}, - {blockchain.ErrTxTooBig, "ErrTxTooBig"}, - {blockchain.ErrBadTxOutValue, "ErrBadTxOutValue"}, - {blockchain.ErrDuplicateTxInputs, "ErrDuplicateTxInputs"}, - {blockchain.ErrBadTxInput, "ErrBadTxInput"}, - {blockchain.ErrBadCheckpoint, "ErrBadCheckpoint"}, - {blockchain.ErrMissingTxOut, "ErrMissingTxOut"}, - {blockchain.ErrUnfinalizedTx, "ErrUnfinalizedTx"}, - {blockchain.ErrDuplicateTx, "ErrDuplicateTx"}, - {blockchain.ErrOverwriteTx, "ErrOverwriteTx"}, - {blockchain.ErrImmatureSpend, "ErrImmatureSpend"}, - {blockchain.ErrSpendTooHigh, "ErrSpendTooHigh"}, - {blockchain.ErrBadFees, "ErrBadFees"}, - {blockchain.ErrTooManySigOps, "ErrTooManySigOps"}, - {blockchain.ErrFirstTxNotCoinbase, "ErrFirstTxNotCoinbase"}, - {blockchain.ErrMultipleCoinbases, "ErrMultipleCoinbases"}, - {blockchain.ErrBadCoinbaseScriptLen, "ErrBadCoinbaseScriptLen"}, - {blockchain.ErrBadCoinbaseValue, "ErrBadCoinbaseValue"}, - {blockchain.ErrMissingCoinbaseHeight, "ErrMissingCoinbaseHeight"}, - {blockchain.ErrBadCoinbaseHeight, "ErrBadCoinbaseHeight"}, - {blockchain.ErrScriptMalformed, "ErrScriptMalformed"}, - {blockchain.ErrScriptValidation, "ErrScriptValidation"}, + {ErrDuplicateBlock, "ErrDuplicateBlock"}, + {ErrBlockTooBig, "ErrBlockTooBig"}, + {ErrBlockVersionTooOld, "ErrBlockVersionTooOld"}, + {ErrInvalidTime, "ErrInvalidTime"}, + {ErrTimeTooOld, "ErrTimeTooOld"}, + {ErrTimeTooNew, "ErrTimeTooNew"}, + {ErrDifficultyTooLow, "ErrDifficultyTooLow"}, + {ErrUnexpectedDifficulty, "ErrUnexpectedDifficulty"}, + {ErrHighHash, "ErrHighHash"}, + {ErrBadMerkleRoot, "ErrBadMerkleRoot"}, + {ErrBadCheckpoint, "ErrBadCheckpoint"}, + {ErrForkTooOld, "ErrForkTooOld"}, + {ErrCheckpointTimeTooOld, "ErrCheckpointTimeTooOld"}, + {ErrNoTransactions, "ErrNoTransactions"}, + {ErrTooManyTransactions, "ErrTooManyTransactions"}, + {ErrNoTxInputs, "ErrNoTxInputs"}, + {ErrNoTxOutputs, "ErrNoTxOutputs"}, + {ErrTxTooBig, "ErrTxTooBig"}, + {ErrBadTxOutValue, "ErrBadTxOutValue"}, + {ErrDuplicateTxInputs, "ErrDuplicateTxInputs"}, + {ErrBadTxInput, "ErrBadTxInput"}, + {ErrBadCheckpoint, "ErrBadCheckpoint"}, + {ErrMissingTxOut, "ErrMissingTxOut"}, + {ErrUnfinalizedTx, "ErrUnfinalizedTx"}, + {ErrDuplicateTx, "ErrDuplicateTx"}, + {ErrOverwriteTx, "ErrOverwriteTx"}, + {ErrImmatureSpend, "ErrImmatureSpend"}, + {ErrSpendTooHigh, "ErrSpendTooHigh"}, + {ErrBadFees, "ErrBadFees"}, + {ErrTooManySigOps, "ErrTooManySigOps"}, + {ErrFirstTxNotCoinbase, "ErrFirstTxNotCoinbase"}, + {ErrMultipleCoinbases, "ErrMultipleCoinbases"}, + {ErrBadCoinbaseScriptLen, "ErrBadCoinbaseScriptLen"}, + {ErrBadCoinbaseValue, "ErrBadCoinbaseValue"}, + {ErrMissingCoinbaseHeight, "ErrMissingCoinbaseHeight"}, + {ErrBadCoinbaseHeight, "ErrBadCoinbaseHeight"}, + {ErrScriptMalformed, "ErrScriptMalformed"}, + {ErrScriptValidation, "ErrScriptValidation"}, {0xffff, "Unknown ErrorCode (65535)"}, } @@ -71,15 +69,15 @@ func TestErrorCodeStringer(t *testing.T) { // TestRuleError tests the error output for the RuleError type. func TestRuleError(t *testing.T) { tests := []struct { - in blockchain.RuleError + in RuleError want string }{ { - blockchain.RuleError{Description: "duplicate block"}, + RuleError{Description: "duplicate block"}, "duplicate block", }, { - blockchain.RuleError{Description: "human-readable error"}, + RuleError{Description: "human-readable error"}, "human-readable error", }, } @@ -100,19 +98,19 @@ func TestDeploymentError(t *testing.T) { t.Parallel() tests := []struct { - in blockchain.DeploymentError + in DeploymentError want string }{ { - blockchain.DeploymentError(0), + DeploymentError(0), "deployment ID 0 does not exist", }, { - blockchain.DeploymentError(10), + DeploymentError(10), "deployment ID 10 does not exist", }, { - blockchain.DeploymentError(123), + DeploymentError(123), "deployment ID 123 does not exist", }, } diff --git a/blockchain/fullblocks_test.go b/blockchain/fullblocks_test.go index 52386a52..3ae0d0eb 100644 --- a/blockchain/fullblocks_test.go +++ b/blockchain/fullblocks_test.go @@ -1,5 +1,5 @@ // Copyright (c) 2016 The Decred developers -// Copyright (c) 2016 The btcsuite developers +// Copyright (c) 2016-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -7,16 +7,128 @@ package blockchain_test import ( "bytes" + "fmt" + "os" + "path/filepath" "testing" "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/blockchain/fullblocktests" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/database" + _ "github.com/btcsuite/btcd/database/ffldb" + "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) +const ( + // testDbType is the database backend type to use for the tests. + testDbType = "ffldb" + + // testDbRoot is the root directory used to create all test databases. + testDbRoot = "testdbs" + + // blockDataNet is the expected network in the test block data. + blockDataNet = wire.MainNet +) + +// filesExists returns whether or not the named file or directory exists. +func fileExists(name string) bool { + if _, err := os.Stat(name); err != nil { + if os.IsNotExist(err) { + return false + } + } + return true +} + +// isSupportedDbType returns whether or not the passed database type is +// currently supported. +func isSupportedDbType(dbType string) bool { + supportedDrivers := database.SupportedDrivers() + for _, driver := range supportedDrivers { + if dbType == driver { + return true + } + } + + return false +} + +// chainSetup is used to create a new db and chain instance with the genesis +// block already inserted. In addition to the new chain instance, it returns +// a teardown function the caller should invoke when done testing to clean up. +func chainSetup(dbName string, params *chaincfg.Params) (*blockchain.BlockChain, func(), error) { + if !isSupportedDbType(testDbType) { + return nil, nil, fmt.Errorf("unsupported db type %v", testDbType) + } + + // Handle memory database specially since it doesn't need the disk + // specific handling. + var db database.DB + var teardown func() + if testDbType == "memdb" { + ndb, err := database.Create(testDbType) + if err != nil { + return nil, nil, fmt.Errorf("error creating db: %v", err) + } + db = ndb + + // Setup a teardown function for cleaning up. This function is + // returned to the caller to be invoked when it is done testing. + teardown = func() { + db.Close() + } + } else { + // Create the root directory for test databases. + if !fileExists(testDbRoot) { + if err := os.MkdirAll(testDbRoot, 0700); err != nil { + err := fmt.Errorf("unable to create test db "+ + "root: %v", err) + return nil, nil, err + } + } + + // Create a new database to store the accepted blocks into. + dbPath := filepath.Join(testDbRoot, dbName) + _ = os.RemoveAll(dbPath) + ndb, err := database.Create(testDbType, dbPath, blockDataNet) + if err != nil { + return nil, nil, fmt.Errorf("error creating db: %v", err) + } + db = ndb + + // Setup a teardown function for cleaning up. This function is + // returned to the caller to be invoked when it is done testing. + teardown = func() { + db.Close() + os.RemoveAll(dbPath) + os.RemoveAll(testDbRoot) + } + } + + // Copy the chain params to ensure any modifications the tests do to + // the chain parameters do not affect the global instance. + paramsCopy := *params + + // Create the main chain instance. + chain, err := blockchain.New(&blockchain.Config{ + DB: db, + ChainParams: ¶msCopy, + Checkpoints: nil, + TimeSource: blockchain.NewMedianTime(), + SigCache: txscript.NewSigCache(1000), + }) + if err != nil { + teardown() + err := fmt.Errorf("failed to create chain instance: %v", err) + return nil, nil, err + } + return chain, teardown, nil +} + // TestFullBlocks ensures all tests generated by the fullblocktests package // have the expected result when processed via ProcessBlock. func TestFullBlocks(t *testing.T) { diff --git a/blockchain/internal_test.go b/blockchain/internal_test.go deleted file mode 100644 index 15886b83..00000000 --- a/blockchain/internal_test.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) 2013-2017 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -/* -This test file is part of the blockchain package rather than than the -blockchain_test package so it can bridge access to the internals to properly -test cases which are either not possible or can't reliably be tested via the -public interface. The functions are only exported while the tests are being -run. -*/ - -package blockchain - -import ( - "sort" - "time" - - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/wire" -) - -// TstSetCoinbaseMaturity makes the ability to set the coinbase maturity -// available to the test package. -func (b *BlockChain) TstSetCoinbaseMaturity(maturity uint16) { - b.chainParams.CoinbaseMaturity = maturity -} - -// TstTimeSorter makes the internal timeSorter type available to the test -// package. -func TstTimeSorter(times []int64) sort.Interface { - return timeSorter(times) -} - -// TstCheckSerializedHeight makes the internal checkSerializedHeight function -// available to the test package. -var TstCheckSerializedHeight = checkSerializedHeight - -// TstSetMaxMedianTimeEntries makes the ability to set the maximum number of -// median time entries available to the test package. -func TstSetMaxMedianTimeEntries(val int) { - maxMedianTimeEntries = val -} - -// TstCheckBlockScripts makes the internal checkBlockScripts function available -// to the test package. -var TstCheckBlockScripts = checkBlockScripts - -// TstDeserializeUtxoEntry makes the internal deserializeUtxoEntry function -// available to the test package. -var TstDeserializeUtxoEntry = deserializeUtxoEntry - -// TstNewFakeChain returns a chain that is usable for syntetic tests. It is -// important to note that this chain has no database associated with it, so -// it is not usable with all functions and the tests must take care when making -// use of it. -func TstNewFakeChain(params *chaincfg.Params) (*BlockChain, *blockNode) { - // Create a genesis block node and block index index populated with it - // for use when creating the fake chain below. - node := newBlockNode(¶ms.GenesisBlock.Header, 0) - node.inMainChain = true - index := newBlockIndex(nil, params) - index.AddNode(node) - - targetTimespan := int64(params.TargetTimespan / time.Second) - targetTimePerBlock := int64(params.TargetTimePerBlock / time.Second) - adjustmentFactor := params.RetargetAdjustmentFactor - return &BlockChain{ - chainParams: params, - timeSource: NewMedianTime(), - minRetargetTimespan: targetTimespan / adjustmentFactor, - maxRetargetTimespan: targetTimespan * adjustmentFactor, - blocksPerRetarget: int32(targetTimespan / targetTimePerBlock), - index: index, - warningCaches: newThresholdCaches(vbNumBits), - deploymentCaches: newThresholdCaches(chaincfg.DefinedDeployments), - bestNode: node, - }, node -} - -// TstNewFakeNode creates a block node connected to the passed parent with the -// provided fields populated and fake values for the other fields and adds it -// to the blockchain's index as well as makes it the best node. -func (b *BlockChain) TstNewFakeNode(parent *blockNode, blockVersion int32, bits uint32, timestamp time.Time) *blockNode { - // Make up a header and create a block node from it. - header := &wire.BlockHeader{ - Version: blockVersion, - PrevBlock: parent.hash, - Bits: bits, - Timestamp: timestamp, - } - node := newBlockNode(header, parent.height+1) - node.parent = parent - node.workSum.Add(parent.workSum, node.workSum) - - b.index.AddNode(node) - b.bestNode = node - return node -} diff --git a/blockchain/mediantime_test.go b/blockchain/mediantime_test.go index 44d3105a..f5d74a75 100644 --- a/blockchain/mediantime_test.go +++ b/blockchain/mediantime_test.go @@ -1,15 +1,13 @@ -// Copyright (c) 2013-2014 The btcsuite developers +// Copyright (c) 2013-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package blockchain_test +package blockchain import ( "strconv" "testing" "time" - - "github.com/btcsuite/btcd/blockchain" ) // TestMedianTime tests the medianTime implementation. @@ -55,11 +53,11 @@ func TestMedianTime(t *testing.T) { } // Modify the max number of allowed median time entries for these tests. - blockchain.TstSetMaxMedianTimeEntries(10) - defer blockchain.TstSetMaxMedianTimeEntries(200) + maxMedianTimeEntries = 10 + defer func() { maxMedianTimeEntries = 200 }() for i, test := range tests { - filter := blockchain.NewMedianTime() + filter := NewMedianTime() for j, offset := range test.in { id := strconv.Itoa(j) now := time.Unix(time.Now().Unix(), 0) diff --git a/blockchain/merkle_test.go b/blockchain/merkle_test.go index c1642b71..275ffef3 100644 --- a/blockchain/merkle_test.go +++ b/blockchain/merkle_test.go @@ -1,20 +1,19 @@ -// Copyright (c) 2013-2014 The btcsuite developers +// Copyright (c) 2013-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package blockchain_test +package blockchain import ( "testing" - "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcutil" ) // TestMerkle tests the BuildMerkleTreeStore API. func TestMerkle(t *testing.T) { block := btcutil.NewBlock(&Block100000) - merkles := blockchain.BuildMerkleTreeStore(block.Transactions(), false) + merkles := BuildMerkleTreeStore(block.Transactions(), false) calculatedMerkleRoot := merkles[len(merkles)-1] wantMerkle := &Block100000.Header.MerkleRoot if !wantMerkle.IsEqual(calculatedMerkleRoot) { diff --git a/blockchain/notifications_test.go b/blockchain/notifications_test.go index 672f61b8..fde58735 100644 --- a/blockchain/notifications_test.go +++ b/blockchain/notifications_test.go @@ -2,12 +2,11 @@ // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package blockchain_test +package blockchain import ( "testing" - "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/chaincfg" ) @@ -27,8 +26,8 @@ func TestNotifications(t *testing.T) { defer teardownFunc() notificationCount := 0 - callback := func(notification *blockchain.Notification) { - if notification.Type == blockchain.NTBlockAccepted { + callback := func(notification *Notification) { + if notification.Type == NTBlockAccepted { notificationCount++ } } @@ -40,7 +39,7 @@ func TestNotifications(t *testing.T) { chain.Subscribe(callback) } - _, _, err = chain.ProcessBlock(blocks[1], blockchain.BFNone) + _, _, err = chain.ProcessBlock(blocks[1], BFNone) if err != nil { t.Fatalf("ProcessBlock fail on block 1: %v\n", err) } diff --git a/blockchain/reorganization_test.go b/blockchain/reorganization_test.go deleted file mode 100644 index 4c1f21af..00000000 --- a/blockchain/reorganization_test.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) 2013-2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package blockchain_test - -import ( - "compress/bzip2" - "encoding/binary" - "io" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/btcsuite/btcd/blockchain" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" -) - -// TestReorganization loads a set of test blocks which force a chain -// reorganization to test the block chain handling code. -// The test blocks were originally from a post on the bitcoin talk forums: -// https://bitcointalk.org/index.php?topic=46370.msg577556#msg577556 -func TestReorganization(t *testing.T) { - // Intentionally load the side chain blocks out of order to ensure - // orphans are handled properly along with chain reorganization. - testFiles := []string{ - "blk_0_to_4.dat.bz2", - "blk_4A.dat.bz2", - "blk_5A.dat.bz2", - "blk_3A.dat.bz2", - } - - var blocks []*btcutil.Block - for _, file := range testFiles { - blockTmp, err := loadBlocks(file) - if err != nil { - t.Errorf("Error loading file: %v\n", err) - } - blocks = append(blocks, blockTmp...) - } - - t.Logf("Number of blocks: %v\n", len(blocks)) - - // Create a new database and chain instance to run tests against. - chain, teardownFunc, err := chainSetup("reorg", &chaincfg.MainNetParams) - if err != nil { - t.Errorf("Failed to setup chain instance: %v", err) - return - } - defer teardownFunc() - - // Since we're not dealing with the real block chain set the coinbase - // maturity to 1. - chain.TstSetCoinbaseMaturity(1) - - expectedOrphans := map[int]struct{}{5: {}, 6: {}} - for i := 1; i < len(blocks); i++ { - _, isOrphan, err := chain.ProcessBlock(blocks[i], blockchain.BFNone) - if err != nil { - t.Errorf("ProcessBlock fail on block %v: %v\n", i, err) - return - } - if _, ok := expectedOrphans[i]; !ok && isOrphan { - t.Errorf("ProcessBlock incorrectly returned block %v "+ - "is an orphan\n", i) - } - } -} - -// loadBlocks reads files containing bitcoin block data (gzipped but otherwise -// in the format bitcoind writes) from disk and returns them as an array of -// btcutil.Block. This is largely borrowed from the test code in btcdb. -func loadBlocks(filename string) (blocks []*btcutil.Block, err error) { - filename = filepath.Join("testdata/", filename) - - var network = wire.MainNet - var dr io.Reader - var fi io.ReadCloser - - fi, err = os.Open(filename) - if err != nil { - return - } - - if strings.HasSuffix(filename, ".bz2") { - dr = bzip2.NewReader(fi) - } else { - dr = fi - } - defer fi.Close() - - var block *btcutil.Block - - err = nil - for height := int64(1); err == nil; height++ { - var rintbuf uint32 - err = binary.Read(dr, binary.LittleEndian, &rintbuf) - if err == io.EOF { - // hit end of file at expected offset: no warning - height-- - err = nil - break - } - if err != nil { - break - } - if rintbuf != uint32(network) { - break - } - err = binary.Read(dr, binary.LittleEndian, &rintbuf) - blocklen := rintbuf - - rbytes := make([]byte, blocklen) - - // read block - dr.Read(rbytes) - - block, err = btcutil.NewBlockFromBytes(rbytes) - if err != nil { - return - } - blocks = append(blocks, block) - } - - return -} diff --git a/blockchain/scriptval_test.go b/blockchain/scriptval_test.go index 62a44b24..56450b7a 100644 --- a/blockchain/scriptval_test.go +++ b/blockchain/scriptval_test.go @@ -1,15 +1,14 @@ -// Copyright (c) 2013-2016 The btcsuite developers +// Copyright (c) 2013-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package blockchain_test +package blockchain import ( "fmt" "runtime" "testing" - "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/txscript" ) @@ -42,8 +41,7 @@ func TestCheckBlockScripts(t *testing.T) { } scriptFlags := txscript.ScriptBip16 - err = blockchain.TstCheckBlockScripts(blocks[0], view, scriptFlags, - nil, nil) + err = checkBlockScripts(blocks[0], view, scriptFlags, nil, nil) if err != nil { t.Errorf("Transaction script validation failed: %v\n", err) return diff --git a/blockchain/timesorter_test.go b/blockchain/timesorter_test.go index e1516557..68eb6932 100644 --- a/blockchain/timesorter_test.go +++ b/blockchain/timesorter_test.go @@ -2,14 +2,12 @@ // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package blockchain_test +package blockchain import ( "reflect" "sort" "testing" - - "github.com/btcsuite/btcd/blockchain" ) // TestTimeSorter tests the timeSorter implementation. @@ -39,7 +37,7 @@ func TestTimeSorter(t *testing.T) { for i, test := range tests { result := make([]int64, len(test.in)) copy(result, test.in) - sort.Sort(blockchain.TstTimeSorter(result)) + sort.Sort(timeSorter(result)) if !reflect.DeepEqual(result, test.want) { t.Errorf("timeSorter #%d got %v want %v", i, result, test.want) diff --git a/blockchain/validate_test.go b/blockchain/validate_test.go index 8ce3a14a..ede41514 100644 --- a/blockchain/validate_test.go +++ b/blockchain/validate_test.go @@ -1,8 +1,8 @@ -// Copyright (c) 2013-2016 The btcsuite developers +// Copyright (c) 2013-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package blockchain_test +package blockchain import ( "math" @@ -10,7 +10,6 @@ import ( "testing" "time" - "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" @@ -20,15 +19,15 @@ import ( // TestSequenceLocksActive tests the SequenceLockActive function to ensure it // works as expected in all possible combinations/scenarios. func TestSequenceLocksActive(t *testing.T) { - seqLock := func(h int32, s int64) *blockchain.SequenceLock { - return &blockchain.SequenceLock{ + seqLock := func(h int32, s int64) *SequenceLock { + return &SequenceLock{ Seconds: s, BlockHeight: h, } } tests := []struct { - seqLock *blockchain.SequenceLock + seqLock *SequenceLock blockHeight int32 mtp time.Time @@ -55,7 +54,7 @@ func TestSequenceLocksActive(t *testing.T) { t.Logf("Running %d sequence locks tests", len(tests)) for i, test := range tests { - got := blockchain.SequenceLockActive(test.seqLock, + got := SequenceLockActive(test.seqLock, test.blockHeight, test.mtp) if got != test.want { t.Fatalf("SequenceLockActive #%d got %v want %v", i, @@ -89,8 +88,8 @@ func TestCheckConnectBlock(t *testing.T) { func TestCheckBlockSanity(t *testing.T) { powLimit := chaincfg.MainNetParams.PowLimit block := btcutil.NewBlock(&Block100000) - timeSource := blockchain.NewMedianTime() - err := blockchain.CheckBlockSanity(block, powLimit, timeSource) + timeSource := NewMedianTime() + err := CheckBlockSanity(block, powLimit, timeSource) if err != nil { t.Errorf("CheckBlockSanity: %v", err) } @@ -99,7 +98,7 @@ func TestCheckBlockSanity(t *testing.T) { // second fails. timestamp := block.MsgBlock().Header.Timestamp block.MsgBlock().Header.Timestamp = timestamp.Add(time.Nanosecond) - err = blockchain.CheckBlockSanity(block, powLimit, timeSource) + err = CheckBlockSanity(block, powLimit, timeSource) if err == nil { t.Errorf("CheckBlockSanity: error is nil when it shouldn't be") } @@ -115,11 +114,11 @@ func TestCheckSerializedHeight(t *testing.T) { coinbaseTx.AddTxIn(wire.NewTxIn(coinbaseOutpoint, nil, nil)) // Expected rule errors. - missingHeightError := blockchain.RuleError{ - ErrorCode: blockchain.ErrMissingCoinbaseHeight, + missingHeightError := RuleError{ + ErrorCode: ErrMissingCoinbaseHeight, } - badHeightError := blockchain.RuleError{ - ErrorCode: blockchain.ErrBadCoinbaseHeight, + badHeightError := RuleError{ + ErrorCode: ErrBadCoinbaseHeight, } tests := []struct { @@ -151,15 +150,15 @@ func TestCheckSerializedHeight(t *testing.T) { msgTx.TxIn[0].SignatureScript = test.sigScript tx := btcutil.NewTx(msgTx) - err := blockchain.TstCheckSerializedHeight(tx, test.wantHeight) + err := checkSerializedHeight(tx, test.wantHeight) if reflect.TypeOf(err) != reflect.TypeOf(test.err) { t.Errorf("checkSerializedHeight #%d wrong error type "+ "got: %v <%T>, want: %T", i, err, err, test.err) continue } - if rerr, ok := err.(blockchain.RuleError); ok { - trerr := test.err.(blockchain.RuleError) + if rerr, ok := err.(RuleError); ok { + trerr := test.err.(RuleError) if rerr.ErrorCode != trerr.ErrorCode { t.Errorf("checkSerializedHeight #%d wrong "+ "error code got: %v, want: %v", i,