From 6099ce56bd8fb17f83f2ee2522d78469641b67f2 Mon Sep 17 00:00:00 2001 From: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com> Date: Tue, 31 Dec 2019 16:16:10 +0200 Subject: [PATCH] [NOD-5] Remove TestFullBlocks and package fullblocktests (#564) --- blockdag/fullblocks_test.go | 317 ---- blockdag/fullblocktests/README.md | 29 - blockdag/fullblocktests/doc.go | 20 - blockdag/fullblocktests/generate.go | 2102 --------------------------- blockdag/fullblocktests/params.go | 140 -- 5 files changed, 2608 deletions(-) delete mode 100644 blockdag/fullblocks_test.go delete mode 100644 blockdag/fullblocktests/README.md delete mode 100644 blockdag/fullblocktests/doc.go delete mode 100644 blockdag/fullblocktests/generate.go delete mode 100644 blockdag/fullblocktests/params.go diff --git a/blockdag/fullblocks_test.go b/blockdag/fullblocks_test.go deleted file mode 100644 index f30d27cab..000000000 --- a/blockdag/fullblocks_test.go +++ /dev/null @@ -1,317 +0,0 @@ -// Copyright (c) 2016 The Decred 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. - -package blockdag_test - -import ( - "bytes" - "github.com/pkg/errors" - "os" - "path/filepath" - "testing" - - "github.com/kaspanet/kaspad/blockdag" - "github.com/kaspanet/kaspad/blockdag/fullblocktests" - "github.com/kaspanet/kaspad/dagconfig" - "github.com/kaspanet/kaspad/database" - _ "github.com/kaspanet/kaspad/database/ffldb" - "github.com/kaspanet/kaspad/txscript" - "github.com/kaspanet/kaspad/util" - "github.com/kaspanet/kaspad/util/daghash" - "github.com/kaspanet/kaspad/wire" -) - -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 -} - -// DAGSetup is used to create a new db and DAG instance with the genesis -// block already inserted. In addition to the new DAG instance, it returns -// a teardown function the caller should invoke when done testing to clean up. -func DAGSetup(dbName string, params *dagconfig.Params) (*blockdag.BlockDAG, func(), error) { - if !isSupportedDbType(testDbType) { - return nil, nil, errors.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, errors.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 := errors.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, errors.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 DAG params to ensure any modifications the tests do to - // the DAG parameters do not affect the global instance. - paramsCopy := *params - - // Create the DAG instance. - dag, err := blockdag.New(&blockdag.Config{ - DB: db, - DAGParams: ¶msCopy, - TimeSource: blockdag.NewMedianTime(), - SigCache: txscript.NewSigCache(1000), - }) - if err != nil { - teardown() - err := errors.Errorf("failed to create DAG instance: %v", err) - return nil, nil, err - } - return dag, teardown, nil -} - -// TestFullBlocks ensures all tests generated by the fullblocktests package -// have the expected result when processed via ProcessBlock. -func TestFullBlocks(t *testing.T) { - // TODO: (Stas) This test was disabled for until we have implemented Phantom - // Ticket: https://daglabs.atlassian.net/browse/DEV-60 - t.SkipNow() - - tests, err := fullblocktests.Generate(false) - if err != nil { - t.Fatalf("failed to generate tests: %v", err) - } - - // Create a new database and DAG instance to run tests against. - dag, teardownFunc, err := DAGSetup("fullblocktest", - &dagconfig.RegressionNetParams) - if err != nil { - t.Errorf("Failed to setup chain instance: %v", err) - return - } - defer teardownFunc() - - // testAcceptedBlock attempts to process the block in the provided test - // instance and ensures that it was accepted according to the flags - // specified in the test. - testAcceptedBlock := func(item fullblocktests.AcceptedBlock) { - blockHeight := item.Height - block := util.NewBlock(item.Block) - block.SetChainHeight(blockHeight) - t.Logf("Testing block %s (hash %s, height %d)", - item.Name, block.Hash(), blockHeight) - - isOrphan, delay, err := dag.ProcessBlock(block, - blockdag.BFNone) - if err != nil { - t.Fatalf("block %q (hash %s, height %d) should "+ - "have been accepted: %v", item.Name, - block.Hash(), blockHeight, err) - } - - if delay != item.Delay { - t.Fatalf("block %q (hash %s, height %d) unexpected "+ - "delay -- got %v, want %v", item.Name, - block.Hash(), blockHeight, delay, - item.Delay) - } - - if isOrphan != item.IsOrphan { - t.Fatalf("block %q (hash %s, height %d) unexpected "+ - "orphan flag -- got %v, want %v", item.Name, - block.Hash(), blockHeight, isOrphan, - item.IsOrphan) - } - } - - // testRejectedBlock attempts to process the block in the provided test - // instance and ensures that it was rejected with the reject code - // specified in the test. - testRejectedBlock := func(item fullblocktests.RejectedBlock) { - blockHeight := item.Height - block := util.NewBlock(item.Block) - block.SetChainHeight(blockHeight) - t.Logf("Testing block %s (hash %s, height %d)", - item.Name, block.Hash(), blockHeight) - - _, _, err := dag.ProcessBlock(block, blockdag.BFNone) - if err == nil { - t.Fatalf("block %q (hash %s, height %d) should not "+ - "have been accepted", item.Name, block.Hash(), - blockHeight) - } - - // Ensure the error code is of the expected type and the reject - // code matches the value specified in the test instance. - rerr, ok := err.(blockdag.RuleError) - if !ok { - t.Fatalf("block %q (hash %s, height %d) returned "+ - "unexpected error type -- got %T, want "+ - "blockchain.RuleError", item.Name, block.Hash(), - blockHeight, err) - } - if rerr.ErrorCode != item.RejectCode { - t.Fatalf("block %q (hash %s, height %d) does not have "+ - "expected reject code -- got %v, want %v", - item.Name, block.Hash(), blockHeight, - rerr.ErrorCode, item.RejectCode) - } - } - - // testRejectedNonCanonicalBlock attempts to decode the block in the - // provided test instance and ensures that it failed to decode with a - // message error. - testRejectedNonCanonicalBlock := func(item fullblocktests.RejectedNonCanonicalBlock) { - headerLen := len(item.RawBlock) - if headerLen > 80 { - headerLen = 80 - } - blockHash := daghash.DoubleHashH(item.RawBlock[0:headerLen]) - blockHeight := item.Height - t.Logf("Testing block %s (hash %s, height %d)", item.Name, - blockHash, blockHeight) - - // Ensure there is an error due to deserializing the block. - var msgBlock wire.MsgBlock - err := msgBlock.KaspaDecode(bytes.NewReader(item.RawBlock), 0) - if _, ok := err.(*wire.MessageError); !ok { - t.Fatalf("block %q (hash %s, height %d) should have "+ - "failed to decode", item.Name, blockHash, - blockHeight) - } - } - - // testOrphanOrRejectedBlock attempts to process the block in the - // provided test instance and ensures that it was either accepted as an - // orphan or rejected with a rule violation. - testOrphanOrRejectedBlock := func(item fullblocktests.OrphanOrRejectedBlock) { - blockHeight := item.Height - block := util.NewBlock(item.Block) - block.SetChainHeight(blockHeight) - t.Logf("Testing block %s (hash %s, height %d)", - item.Name, block.Hash(), blockHeight) - - isOrphan, delay, err := dag.ProcessBlock(block, blockdag.BFNone) - if err != nil { - // Ensure the error code is of the expected type. - if _, ok := err.(blockdag.RuleError); !ok { - t.Fatalf("block %q (hash %s, height %d) "+ - "returned unexpected error type -- "+ - "got %T, want blockdag.RuleError", - item.Name, block.Hash(), blockHeight, - err) - } - } - - if delay != 0 { - t.Fatalf("block %q (hash %s, height %d) "+ - "is too far in the future", - item.Name, block.Hash(), blockHeight) - } - - if !isOrphan { - t.Fatalf("block %q (hash %s, height %d) was accepted, "+ - "but is not considered an orphan", item.Name, - block.Hash(), blockHeight) - } - } - - // testExpectedTip ensures the current tip of the blockDAG is the - // block specified in the provided test instance. - testExpectedTip := func(item fullblocktests.ExpectedTip) { - blockHeight := item.Height - block := util.NewBlock(item.Block) - block.SetChainHeight(blockHeight) - t.Logf("Testing tip for block %s (hash %s, height %d)", - item.Name, block.Hash(), blockHeight) - - // Ensure hash and height match. - if dag.SelectedTipHash() != item.Block.BlockHash() || - dag.ChainHeight() != blockHeight { //TODO: (Ori) the use of dag.ChainHeight() and virtualBlock.HighestTipHash() is wrong, and was done only for compilation - - t.Fatalf("block %q (hash %s, height %d) should be "+ - "the current tip -- got (hash %s, height %d)", - item.Name, block.Hash(), blockHeight, dag.SelectedTipHash(), - dag.ChainHeight()) //TODO: (Ori) the use of dag.ChainHeight() and virtualBlock.HighestTipHash() is wrong, and was done only for compilation - } - } - - for testNum, test := range tests { - for itemNum, item := range test { - switch item := item.(type) { - case fullblocktests.AcceptedBlock: - testAcceptedBlock(item) - case fullblocktests.RejectedBlock: - testRejectedBlock(item) - case fullblocktests.RejectedNonCanonicalBlock: - testRejectedNonCanonicalBlock(item) - case fullblocktests.OrphanOrRejectedBlock: - testOrphanOrRejectedBlock(item) - case fullblocktests.ExpectedTip: - testExpectedTip(item) - default: - t.Fatalf("test #%d, item #%d is not one of "+ - "the supported test instance types -- "+ - "got type: %T", testNum, itemNum, item) - } - } - } -} diff --git a/blockdag/fullblocktests/README.md b/blockdag/fullblocktests/README.md deleted file mode 100644 index 4705ba4f6..000000000 --- a/blockdag/fullblocktests/README.md +++ /dev/null @@ -1,29 +0,0 @@ -fullblocktests -============== - -[![Build Status](http://img.shields.io/travis/btcsuite/btcd.svg)](https://travis-ci.org/btcsuite/btcd) -[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) -[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/kaspanet/kaspad/blockchain/fullblocktests) - -Package fullblocktests provides a set of full block tests to be used for testing -the consensus validation rules. The tests are intended to be flexible enough to -allow both unit-style tests directly against the blockchain code as well as -integration style tests over the peer-to-peer network. To achieve that goal, -each test contains additional information about the expected result, however -that information can be ignored when doing comparison tests between two -independent versions over the peer-to-peer network. - -This package has intentionally been designed so it can be used as a standalone -package for any projects needing to test their implementation against a full set -of blocks that exercise the consensus validation rules. - -## Installation and Updating - -```bash -$ go get -u github.com/kaspanet/kaspad/blockchain/fullblocktests -``` - -## License - -Package fullblocktests is licensed under the [copyfree](http://copyfree.org) ISC -License. diff --git a/blockdag/fullblocktests/doc.go b/blockdag/fullblocktests/doc.go deleted file mode 100644 index c072fefc2..000000000 --- a/blockdag/fullblocktests/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -/* -Package fullblocktests provides a set of block consensus validation tests. - -All of the generated test instances involve full blocks that are to be used for -testing the consensus validation rules. The tests are intended to be flexible -enough to allow both unit-style tests directly against the blockchain code as -well as integration style tests over the peer-to-peer network. To achieve that -goal, each test contains additional information about the expected result, -however that information can be ignored when doing comparison tests between two -independent versions over the peer-to-peer network. - -This package has intentionally been designed so it can be used as a standalone -package for any projects needing to test their implementation against a full set -of blocks that exercise the consensus validation rules. -*/ -package fullblocktests diff --git a/blockdag/fullblocktests/generate.go b/blockdag/fullblocktests/generate.go deleted file mode 100644 index 84f456de4..000000000 --- a/blockdag/fullblocktests/generate.go +++ /dev/null @@ -1,2102 +0,0 @@ -// Copyright (c) 2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -// The vast majority of the rules tested in this package were ported from the -// the original Java-based 'official' block acceptance tests at -// https://github.com/TheBlueMatt/test-scripts as well as some additional tests -// available in the Core python port of the same. - -package fullblocktests - -import ( - "bytes" - "encoding/binary" - "fmt" - "github.com/pkg/errors" - "math" - "runtime" - "time" - - "github.com/kaspanet/kaspad/blockdag" - "github.com/kaspanet/kaspad/dagconfig" - "github.com/kaspanet/kaspad/ecc" - "github.com/kaspanet/kaspad/txscript" - "github.com/kaspanet/kaspad/util" - "github.com/kaspanet/kaspad/util/daghash" - "github.com/kaspanet/kaspad/util/random" - "github.com/kaspanet/kaspad/wire" -) - -const ( - // Intentionally defined here rather than using constants from codebase - // to ensure consensus changes are detected. - maxBlockSigOps = 20000 - maxBlockSize = 1000000 - minCoinbaseScriptLen = 2 - maxCoinbaseScriptLen = 100 - medianTimeBlocks = 11 - maxScriptElementSize = 520 - - // numLargeReorgBlocks is the number of blocks to use in the large block - // reorg test (when enabled). This is the equivalent of 1 week's worth - // of blocks. - numLargeReorgBlocks = 1088 -) - -var ( - // opTrueScript is simply a public key script that contains the OP_TRUE - // opcode. It is defined here to reduce garbage creation. - opTrueScript = []byte{txscript.OpTrue} - - // lowFee is a single sompi and exists to make the test code more - // readable. - lowFee = util.Amount(1) -) - -// TestInstance is an interface that describes a specific test instance returned -// by the tests generated in this package. It should be type asserted to one -// of the concrete test instance types in order to test accordingly. -type TestInstance interface { - FullBlockTestInstance() -} - -// AcceptedBlock defines a test instance that expects a block to be accepted to -// the blockchain either by extending the main chain, on a side chain, or as an -// orphan. -type AcceptedBlock struct { - Name string - Block *wire.MsgBlock - Height uint64 - IsOrphan bool - Delay time.Duration -} - -// Ensure AcceptedBlock implements the TestInstance interface. -var _ TestInstance = AcceptedBlock{} - -// FullBlockTestInstance only exists to allow AcceptedBlock to be treated as a -// TestInstance. -// -// This implements the TestInstance interface. -func (b AcceptedBlock) FullBlockTestInstance() {} - -// RejectedBlock defines a test instance that expects a block to be rejected by -// the blockchain consensus rules. -type RejectedBlock struct { - Name string - Block *wire.MsgBlock - Height uint64 - RejectCode blockdag.ErrorCode -} - -// Ensure RejectedBlock implements the TestInstance interface. -var _ TestInstance = RejectedBlock{} - -// FullBlockTestInstance only exists to allow RejectedBlock to be treated as a -// TestInstance. -// -// This implements the TestInstance interface. -func (b RejectedBlock) FullBlockTestInstance() {} - -// OrphanOrRejectedBlock defines a test instance that expects a block to either -// be accepted as an orphan or rejected. This is useful since some -// implementations might optimize the immediate rejection of orphan blocks when -// their parent was previously rejected, while others might accept it as an -// orphan that eventually gets flushed (since the parent can never be accepted -// to ultimately link it). -type OrphanOrRejectedBlock struct { - Name string - Block *wire.MsgBlock - Height uint64 -} - -// Ensure ExpectedTip implements the TestInstance interface. -var _ TestInstance = OrphanOrRejectedBlock{} - -// FullBlockTestInstance only exists to allow OrphanOrRejectedBlock to be -// treated as a TestInstance. -// -// This implements the TestInstance interface. -func (b OrphanOrRejectedBlock) FullBlockTestInstance() {} - -// ExpectedTip defines a test instance that expects a block to be the current -// tip of the main chain. -type ExpectedTip struct { - Name string - Block *wire.MsgBlock - Height uint64 -} - -// Ensure ExpectedTip implements the TestInstance interface. -var _ TestInstance = ExpectedTip{} - -// FullBlockTestInstance only exists to allow ExpectedTip to be treated as a -// TestInstance. -// -// This implements the TestInstance interface. -func (b ExpectedTip) FullBlockTestInstance() {} - -// RejectedNonCanonicalBlock defines a test instance that expects a serialized -// block that is not canonical and therefore should be rejected. -type RejectedNonCanonicalBlock struct { - Name string - RawBlock []byte - Height uint64 -} - -// FullBlockTestInstance only exists to allow RejectedNonCanonicalBlock to be treated as -// a TestInstance. -// -// This implements the TestInstance interface. -func (b RejectedNonCanonicalBlock) FullBlockTestInstance() {} - -// spendableOut represents a transaction output that is spendable along with -// additional metadata such as the block its in and how much it pays. -type spendableOut struct { - prevOut wire.Outpoint - amount util.Amount -} - -// makeSpendableOutForTx returns a spendable output for the given transaction -// and transaction output index within the transaction. -func makeSpendableOutForTx(tx *wire.MsgTx, txOutIndex uint32) spendableOut { - return spendableOut{ - prevOut: wire.Outpoint{ - TxID: *tx.TxID(), - Index: txOutIndex, - }, - amount: util.Amount(tx.TxOut[txOutIndex].Value), - } -} - -// makeSpendableOut returns a spendable output for the given block, transaction -// index within the block, and transaction output index within the transaction. -func makeSpendableOut(block *wire.MsgBlock, txIndex, txOutIndex uint32) spendableOut { - return makeSpendableOutForTx(block.Transactions[txIndex], txOutIndex) -} - -// testGenerator houses state used to easy the process of generating test blocks -// that build from one another along with housing other useful things such as -// available spendable outputs used throughout the tests. -type testGenerator struct { - params *dagconfig.Params - tip *wire.MsgBlock - tipName string - tipHeight uint64 - blocks map[daghash.Hash]*wire.MsgBlock - blocksByName map[string]*wire.MsgBlock - blockHeights map[string]uint64 - - // Used for tracking spendable coinbase outputs. - spendableOuts []spendableOut - prevCollectedHash *daghash.Hash - - // Common key for any tests which require signed transactions. - privKey *ecc.PrivateKey - - powMaxBits uint32 -} - -// makeTestGenerator returns a test generator instance initialized with the -// genesis block as the tip. -func makeTestGenerator(params *dagconfig.Params) (testGenerator, error) { - privKey, _ := ecc.PrivKeyFromBytes(ecc.S256(), []byte{0x01}) - genesis := params.GenesisBlock - genesisHash := genesis.BlockHash() - return testGenerator{ - params: params, - blocks: map[daghash.Hash]*wire.MsgBlock{*genesisHash: genesis}, - blocksByName: map[string]*wire.MsgBlock{"genesis": genesis}, - blockHeights: map[string]uint64{"genesis": 0}, - tip: genesis, - tipName: "genesis", - tipHeight: 0, - privKey: privKey, - powMaxBits: util.BigToCompact(params.PowMax), - }, nil -} - -// payToScriptHashScript returns a standard pay-to-script-hash for the provided -// redeem script. -func payToScriptHashScript(redeemScript []byte) []byte { - redeemScriptHash := util.Hash160(redeemScript) - script, err := txscript.NewScriptBuilder(). - AddOp(txscript.OpHash160).AddData(redeemScriptHash). - AddOp(txscript.OpEqual).Script() - if err != nil { - panic(err) - } - return script -} - -// pushDataScript returns a script with the provided items individually pushed -// to the stack. -func pushDataScript(items ...[]byte) []byte { - builder := txscript.NewScriptBuilder() - for _, item := range items { - builder.AddData(item) - } - script, err := builder.Script() - if err != nil { - panic(err) - } - return script -} - -// standardCoinbaseScript returns a standard script suitable for use as the -// signature script of the coinbase transaction of a new block. -func standardCoinbaseScript(extraNonce uint64) ([]byte, error) { - return txscript.NewScriptBuilder(). - AddInt64(int64(extraNonce)).Script() -} - -// opReturnScript returns a provably-pruneable OP_RETURN script with the -// provided data. -func opReturnScript(data []byte) []byte { - builder := txscript.NewScriptBuilder() - script, err := builder.AddOp(txscript.OpReturn).AddData(data).Script() - if err != nil { - panic(err) - } - return script -} - -// uniqueOpReturnScript returns a standard provably-pruneable OP_RETURN script -// with a random uint64 encoded as the data. -func uniqueOpReturnScript() []byte { - rand, err := random.Uint64() - if err != nil { - panic(err) - } - - data := make([]byte, 8) - binary.LittleEndian.PutUint64(data[0:8], rand) - return opReturnScript(data) -} - -// createCoinbaseTx returns a coinbase transaction paying an appropriate -// subsidy based on the passed block height. -func (g *testGenerator) createCoinbaseTx(blueScore uint64) *wire.MsgTx { - extraNonce := uint64(0) - coinbaseScript, err := standardCoinbaseScript(extraNonce) - if err != nil { - panic(err) - } - - txIn := &wire.TxIn{ - // Coinbase transactions have no inputs, so previous outpoint is - // zero hash and max index. - PreviousOutpoint: *wire.NewOutpoint(&daghash.TxID{}, - wire.MaxPrevOutIndex), - Sequence: wire.MaxTxInSequenceNum, - SignatureScript: coinbaseScript, - } - txOut := &wire.TxOut{ - Value: blockdag.CalcBlockSubsidy(blueScore, g.params), - ScriptPubKey: opTrueScript, - } - return wire.NewNativeMsgTx(1, []*wire.TxIn{txIn}, []*wire.TxOut{txOut}) -} - -// calcHashMerkleRoot creates a merkle tree from the slice of transactions and -// returns the root of the tree. -func calcHashMerkleRoot(txns []*wire.MsgTx) *daghash.Hash { - if len(txns) == 0 { - return &daghash.Hash{} - } - - utilTxns := make([]*util.Tx, 0, len(txns)) - for _, tx := range txns { - utilTxns = append(utilTxns, util.NewTx(tx)) - } - merkleTree := blockdag.BuildHashMerkleTreeStore(utilTxns) - return merkleTree.Root() -} - -// solveBlock attempts to find a nonce which makes the passed block header hash -// to a value less than the target difficulty. When a successful solution is -// found true is returned and the nonce field of the passed header is updated -// with the solution. False is returned if no solution exists. -// -// NOTE: This function will never solve blocks with a nonce of 0. This is done -// so the 'nextBlock' function can properly detect when a nonce was modified by -// a munge function. -func solveBlock(header *wire.BlockHeader) bool { - // sbResult is used by the solver goroutines to send results. - type sbResult struct { - found bool - nonce uint64 - } - - // solver accepts a block header and a nonce range to test. It is - // intended to be run as a goroutine. - targetDifficulty := util.CompactToBig(header.Bits) - quit := make(chan bool) - results := make(chan sbResult) - solver := func(hdr wire.BlockHeader, startNonce, stopNonce uint64) { - // We need to modify the nonce field of the header, so make sure - // we work with a copy of the original header. - for i := startNonce; i >= startNonce && i <= stopNonce; i++ { - select { - case <-quit: - return - default: - hdr.Nonce = i - hash := hdr.BlockHash() - if daghash.HashToBig(hash).Cmp( - targetDifficulty) <= 0 { - - results <- sbResult{true, i} - return - } - } - } - results <- sbResult{false, 0} - } - - startNonce := uint64(1) - stopNonce := uint64(math.MaxUint64) - numCores := uint64(runtime.NumCPU()) - noncesPerCore := (stopNonce - startNonce) / numCores - for i := uint64(0); i < numCores; i++ { - rangeStart := startNonce + (noncesPerCore * i) - rangeStop := startNonce + (noncesPerCore * (i + 1)) - 1 - if i == numCores-1 { - rangeStop = stopNonce - } - go solver(*header, rangeStart, rangeStop) - } - for i := uint64(0); i < numCores; i++ { - result := <-results - if result.found { - close(quit) - header.Nonce = result.nonce - return true - } - } - - return false -} - -// additionalCoinbase returns a function that itself takes a block and -// modifies it by adding the provided amount to coinbase subsidy. -func additionalCoinbase(amount util.Amount) func(*wire.MsgBlock) { - return func(b *wire.MsgBlock) { - // Increase the first proof-of-work coinbase subsidy by the - // provided amount. - b.Transactions[0].TxOut[0].Value += uint64(amount) - } -} - -// additionalSpendFee returns a function that itself takes a block and modifies -// it by adding the provided fee to the spending transaction. -// -// NOTE: The coinbase value is NOT updated to reflect the additional fee. Use -// 'additionalCoinbase' for that purpose. -func additionalSpendFee(fee util.Amount) func(*wire.MsgBlock) { - return func(b *wire.MsgBlock) { - // Increase the fee of the spending transaction by reducing the - // amount paid. - if uint64(fee) > b.Transactions[1].TxOut[0].Value { - panic(fmt.Sprintf("additionalSpendFee: fee of %d "+ - "exceeds available spend transaction value", - fee)) - } - b.Transactions[1].TxOut[0].Value -= uint64(fee) - } -} - -// replaceSpendScript returns a function that itself takes a block and modifies -// it by replacing the public key script of the spending transaction. -func replaceSpendScript(scriptPubKey []byte) func(*wire.MsgBlock) { - return func(b *wire.MsgBlock) { - b.Transactions[1].TxOut[0].ScriptPubKey = scriptPubKey - } -} - -// replaceCoinbaseSigScript returns a function that itself takes a block and -// modifies it by replacing the signature key script of the coinbase. -func replaceCoinbaseSigScript(script []byte) func(*wire.MsgBlock) { - return func(b *wire.MsgBlock) { - b.Transactions[0].TxIn[0].SignatureScript = script - } -} - -// additionalTx returns a function that itself takes a block and modifies it by -// adding the the provided transaction. -func additionalTx(tx *wire.MsgTx) func(*wire.MsgBlock) { - return func(b *wire.MsgBlock) { - b.AddTransaction(tx) - } -} - -// createSpendTx creates a transaction that spends from the provided spendable -// output and includes an additional unique OP_RETURN output to ensure the -// transaction ends up with a unique hash. The script is a simple OP_TRUE -// script which avoids the need to track addresses and signature scripts in the -// tests. -func createSpendTx(spend *spendableOut, fee util.Amount) *wire.MsgTx { - txIn := &wire.TxIn{ - PreviousOutpoint: spend.prevOut, - Sequence: wire.MaxTxInSequenceNum, - SignatureScript: nil, - } - spendTx := wire.NewNativeMsgTx(1, []*wire.TxIn{txIn}, nil) - spendTx.AddTxOut(wire.NewTxOut(uint64(spend.amount-fee), - opTrueScript)) - spendTx.AddTxOut(wire.NewTxOut(0, uniqueOpReturnScript())) - - return spendTx -} - -// createSpendTxForTx creates a transaction that spends from the first output of -// the provided transaction and includes an additional unique OP_RETURN output -// to ensure the transaction ends up with a unique hash. The public key script -// is a simple OP_TRUE script which avoids the need to track addresses and -// signature scripts in the tests. The signature script is nil. -func createSpendTxForTx(tx *wire.MsgTx, fee util.Amount) *wire.MsgTx { - spend := makeSpendableOutForTx(tx, 0) - return createSpendTx(&spend, fee) -} - -// nextBlock builds a new block that extends the current tip associated with the -// generator and updates the generator's tip to the newly generated block. -// -// The block will include the following: -// - A coinbase that pays the required subsidy to an OP_TRUE script -// - When a spendable output is provided: -// - A transaction that spends from the provided output the following outputs: -// - One that pays the inputs amount minus 1 atom to an OP_TRUE script -// - One that contains an OP_RETURN output with a random uint64 in order to -// ensure the transaction has a unique hash -// -// Additionally, if one or more munge functions are specified, they will be -// invoked with the block prior to solving it. This provides callers with the -// opportunity to modify the block which is especially useful for testing. -// -// In order to simply the logic in the munge functions, the following rules are -// applied after all munge functions have been invoked: -// - The merkle root will be recalculated unless it was manually changed -// - The block will be solved unless the nonce was changed -func (g *testGenerator) nextBlock(blockName string, spend *spendableOut, mungers ...func(*wire.MsgBlock)) *wire.MsgBlock { - // Create coinbase transaction for the block using any additional - // subsidy if specified. - nextHeight := g.tipHeight + 1 - coinbaseTx := g.createCoinbaseTx(nextHeight) - txns := []*wire.MsgTx{coinbaseTx} - if spend != nil { - // Create the transaction with a fee of 1 atom for the - // miner and increase the coinbase subsidy accordingly. - fee := util.Amount(1) - coinbaseTx.TxOut[0].Value += uint64(fee) - - // Create a transaction that spends from the provided spendable - // output and includes an additional unique OP_RETURN output to - // ensure the transaction ends up with a unique hash, then add - // add it to the list of transactions to include in the block. - // The script is a simple OP_TRUE script in order to avoid the - // need to track addresses and signature scripts in the tests. - txns = append(txns, createSpendTx(spend, fee)) - } - - // Use a timestamp that is one second after the previous block unless - // this is the first block in which case the current time is used. - var ts time.Time - if nextHeight == 1 { - ts = time.Unix(time.Now().Unix(), 0) - } else { - ts = g.tip.Header.Timestamp.Add(time.Second) - } - - block := wire.MsgBlock{ - Header: wire.BlockHeader{ - Version: 1, - ParentHashes: []*daghash.Hash{g.tip.BlockHash()}, // TODO: (Stas) This is wrong. Modified only to satisfy compilation. - HashMerkleRoot: calcHashMerkleRoot(txns), - Bits: g.powMaxBits, - Timestamp: ts, - Nonce: 0, // To be solved. - }, - Transactions: txns, - } - - // Perform any block munging just before solving. Only recalculate the - // merkle root if it wasn't manually changed by a munge function. - curMerkleRoot := block.Header.HashMerkleRoot - curNonce := block.Header.Nonce - for _, f := range mungers { - f(&block) - } - if block.Header.HashMerkleRoot == curMerkleRoot { - block.Header.HashMerkleRoot = calcHashMerkleRoot(block.Transactions) - } - - // Only solve the block if the nonce wasn't manually changed by a munge - // function. - if block.Header.Nonce == curNonce && !solveBlock(&block.Header) { - panic(fmt.Sprintf("Unable to solve block at height %d", - nextHeight)) - } - - // Update generator state and return the block. - blockHash := block.BlockHash() - g.blocks[*blockHash] = &block - g.blocksByName[blockName] = &block - g.blockHeights[blockName] = nextHeight - g.tip = &block - g.tipName = blockName - g.tipHeight = nextHeight - return &block -} - -// updateBlockState manually updates the generator state to remove all internal -// map references to a block via its old hash and insert new ones for the new -// block hash. This is useful if the test code has to manually change a block -// after 'nextBlock' has returned. -func (g *testGenerator) updateBlockState(oldBlockName string, oldBlockHash *daghash.Hash, newBlockName string, newBlock *wire.MsgBlock) { - // Look up the height from the existing entries. - blockHeight := g.blockHeights[oldBlockName] - - // Remove existing entries. - delete(g.blocks, *oldBlockHash) - delete(g.blocksByName, oldBlockName) - delete(g.blockHeights, oldBlockName) - - // Add new entries. - newBlockHash := newBlock.BlockHash() - g.blocks[*newBlockHash] = newBlock - g.blocksByName[newBlockName] = newBlock - g.blockHeights[newBlockName] = blockHeight -} - -// setTip changes the tip of the instance to the block with the provided name. -// This is useful since the tip is used for things such as generating subsequent -// blocks. -func (g *testGenerator) setTip(blockName string) { - g.tip = g.blocksByName[blockName] - g.tipName = blockName - g.tipHeight = g.blockHeights[blockName] -} - -// oldestCoinbaseOuts removes the oldest coinbase output that was previously -// saved to the generator and returns the set as a slice. -func (g *testGenerator) oldestCoinbaseOut() spendableOut { - op := g.spendableOuts[0] - g.spendableOuts = g.spendableOuts[1:] - return op -} - -// saveTipCoinbaseOut adds the coinbase tx output in the current tip block to -// the list of spendable outputs. -func (g *testGenerator) saveTipCoinbaseOut() { - g.spendableOuts = append(g.spendableOuts, makeSpendableOut(g.tip, 0, 0)) - g.prevCollectedHash = g.tip.BlockHash() -} - -// saveSpendableCoinbaseOuts adds all coinbase outputs from the last block that -// had its coinbase tx output colleted to the current tip. This is useful to -// batch the collection of coinbase outputs once the tests reach a stable point -// so they don't have to manually add them for the right tests which will -// ultimately end up being the best chain. -func (g *testGenerator) saveSpendableCoinbaseOuts() { - // Ensure tip is reset to the current one when done. - curTipName := g.tipName - defer g.setTip(curTipName) - - // Loop through the ancestors of the current tip until the - // reaching the block that has already had the coinbase outputs - // collected. - var collectBlocks []*wire.MsgBlock - // TODO: (Evgeny) This is wrong. Modified only to satisfy compilation. - for b := g.tip; b != nil; b = g.blocks[*b.Header.ParentHashes[0]] { - if b.BlockHash() == g.prevCollectedHash { - break - } - collectBlocks = append(collectBlocks, b) - } - for i := range collectBlocks { - g.tip = collectBlocks[len(collectBlocks)-1-i] - g.saveTipCoinbaseOut() - } -} - -// nonCanonicalVarInt return a variable-length encoded integer that is encoded -// with 9 bytes even though it could be encoded with a minimal canonical -// encoding. -func nonCanonicalVarInt(val uint32) []byte { - var rv [9]byte - rv[0] = 0xff - binary.LittleEndian.PutUint64(rv[1:], uint64(val)) - return rv[:] -} - -// encodeNonCanonicalBlock serializes the block in a non-canonical way by -// encoding the number of transactions using a variable-length encoded integer -// with 9 bytes even though it should be encoded with a minimal canonical -// encoding. -func encodeNonCanonicalBlock(b *wire.MsgBlock) []byte { - var buf bytes.Buffer - b.Header.KaspaEncode(&buf, 0) - buf.Write(nonCanonicalVarInt(uint32(len(b.Transactions)))) - for _, tx := range b.Transactions { - tx.KaspaEncode(&buf, 0) - } - return buf.Bytes() -} - -// cloneBlock returns a deep copy of the provided block. -func cloneBlock(b *wire.MsgBlock) wire.MsgBlock { - var blockCopy wire.MsgBlock - blockCopy.Header = b.Header - for _, tx := range b.Transactions { - blockCopy.AddTransaction(tx.Copy()) - } - return blockCopy -} - -// repeatOpcode returns a byte slice with the provided opcode repeated the -// specified number of times. -func repeatOpcode(opcode uint8, numRepeats int) []byte { - return bytes.Repeat([]byte{opcode}, numRepeats) -} - -// assertScriptSigOpsCount panics if the provided script does not have the -// specified number of signature operations. -func assertScriptSigOpsCount(script []byte, expected int) { - numSigOps := txscript.GetSigOpCount(script) - if numSigOps != expected { - _, file, line, _ := runtime.Caller(1) - panic(fmt.Sprintf("assertion failed at %s:%d: generated number "+ - "of sigops for script is %d instead of expected %d", - file, line, numSigOps, expected)) - } -} - -// countBlockSigOps returns the number of legacy signature operations in the -// scripts in the passed block. -func countBlockSigOps(block *wire.MsgBlock) int { - totalSigOps := 0 - for _, tx := range block.Transactions { - for _, txIn := range tx.TxIn { - numSigOps := txscript.GetSigOpCount(txIn.SignatureScript) - totalSigOps += numSigOps - } - for _, txOut := range tx.TxOut { - numSigOps := txscript.GetSigOpCount(txOut.ScriptPubKey) - totalSigOps += numSigOps - } - } - - return totalSigOps -} - -// assertTipBlockSigOpsCount panics if the current tip block associated with the -// generator does not have the specified number of signature operations. -func (g *testGenerator) assertTipBlockSigOpsCount(expected int) { - numSigOps := countBlockSigOps(g.tip) - if numSigOps != expected { - panic(fmt.Sprintf("generated number of sigops for block %q "+ - "(height %d) is %d instead of expected %d", g.tipName, - g.tipHeight, numSigOps, expected)) - } -} - -// assertTipBlockSize panics if the if the current tip block associated with the -// generator does not have the specified size when serialized. -func (g *testGenerator) assertTipBlockSize(expected int) { - serializeSize := g.tip.SerializeSize() - if serializeSize != expected { - panic(fmt.Sprintf("block size of block %q (height %d) is %d "+ - "instead of expected %d", g.tipName, g.tipHeight, - serializeSize, expected)) - } -} - -// assertTipNonCanonicalBlockSize panics if the if the current tip block -// associated with the generator does not have the specified non-canonical size -// when serialized. -func (g *testGenerator) assertTipNonCanonicalBlockSize(expected int) { - serializeSize := len(encodeNonCanonicalBlock(g.tip)) - if serializeSize != expected { - panic(fmt.Sprintf("block size of block %q (height %d) is %d "+ - "instead of expected %d", g.tipName, g.tipHeight, - serializeSize, expected)) - } -} - -// assertTipBlockNumTxns panics if the number of transactions in the current tip -// block associated with the generator does not match the specified value. -func (g *testGenerator) assertTipBlockNumTxns(expected int) { - numTxns := len(g.tip.Transactions) - if numTxns != expected { - panic(fmt.Sprintf("number of txns in block %q (height %d) is "+ - "%d instead of expected %d", g.tipName, g.tipHeight, - numTxns, expected)) - } -} - -// assertTipBlockHash panics if the current tip block associated with the -// generator does not match the specified hash. -func (g *testGenerator) assertTipBlockHash(expected *daghash.Hash) { - hash := g.tip.BlockHash() - if !hash.IsEqual(expected) { - panic(fmt.Sprintf("block hash of block %q (height %d) is %s "+ - "instead of expected %s", g.tipName, g.tipHeight, hash, - expected)) - } -} - -// assertTipBlockMerkleRoot panics if the merkle root in header of the current -// tip block associated with the generator does not match the specified hash. -func (g *testGenerator) assertTipBlockMerkleRoot(expected *daghash.Hash) { - hash := g.tip.Header.HashMerkleRoot - if !hash.IsEqual(expected) { - panic(fmt.Sprintf("merkle root of block %q (height %d) is %s "+ - "instead of expected %s", g.tipName, g.tipHeight, hash, - expected)) - } -} - -// assertTipBlockTxOutOpReturn panics if the current tip block associated with -// the generator does not have an OP_RETURN script for the transaction output at -// the provided tx index and output index. -func (g *testGenerator) assertTipBlockTxOutOpReturn(txIndex, txOutIndex uint32) { - if txIndex >= uint32(len(g.tip.Transactions)) { - panic(fmt.Sprintf("Transaction index %d in block %q "+ - "(height %d) does not exist", txIndex, g.tipName, - g.tipHeight)) - } - - tx := g.tip.Transactions[txIndex] - if txOutIndex >= uint32(len(tx.TxOut)) { - panic(fmt.Sprintf("transaction index %d output %d in block %q "+ - "(height %d) does not exist", txIndex, txOutIndex, - g.tipName, g.tipHeight)) - } - - txOut := tx.TxOut[txOutIndex] - if txOut.ScriptPubKey[0] != txscript.OpReturn { - panic(fmt.Sprintf("transaction index %d output %d in block %q "+ - "(height %d) is not an OP_RETURN", txIndex, txOutIndex, - g.tipName, g.tipHeight)) - } -} - -// Generate returns a slice of tests that can be used to exercise the consensus -// validation rules. The tests are intended to be flexible enough to allow both -// unit-style tests directly against the blockchain code as well as integration -// style tests over the peer-to-peer network. To achieve that goal, each test -// contains additional information about the expected result, however that -// information can be ignored when doing comparison tests between two -// independent versions over the peer-to-peer network. -func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) { - // In order to simplify the generation code which really should never - // fail unless the test code itself is broken, panics are used - // internally. This deferred func ensures any panics don't escape the - // generator by replacing the named error return with the underlying - // panic error. - defer func() { - if r := recover(); r != nil { - tests = nil - - switch rt := r.(type) { - case string: - err = errors.New(rt) - case error: - err = rt - default: - err = errors.New("Unknown panic") - } - } - }() - - // Create a test generator instance initialized with the genesis block - // as the tip. - g, err := makeTestGenerator(&dagconfig.RegressionNetParams) - if err != nil { - return nil, err - } - - // Define some convenience helper functions to return an individual test - // instance that has the described characteristics. - // - // acceptBlock creates a test instance that expects the provided block - // to be accepted by the consensus rules. - // - // rejectBlock creates a test instance that expects the provided block - // to be rejected by the consensus rules. - // - // rejectNonCanonicalBlock creates a test instance that encodes the - // provided block using a non-canonical encoded as described by the - // encodeNonCanonicalBlock function and expected it to be rejected. - // - // orphanOrRejectBlock creates a test instance that expected the - // provided block to either by accepted as an orphan or rejected by the - // consensus rules. - // - // expectTipBlock creates a test instance that expects the provided - // block to be the current tip of the block chain. - acceptBlock := func(blockName string, block *wire.MsgBlock, isOrphan bool) TestInstance { - blockHeight := g.blockHeights[blockName] - return AcceptedBlock{blockName, block, blockHeight, isOrphan, 0} - } - rejectBlock := func(blockName string, block *wire.MsgBlock, code blockdag.ErrorCode) TestInstance { - blockHeight := g.blockHeights[blockName] - return RejectedBlock{blockName, block, blockHeight, code} - } - rejectNonCanonicalBlock := func(blockName string, block *wire.MsgBlock) TestInstance { - blockHeight := g.blockHeights[blockName] - encoded := encodeNonCanonicalBlock(block) - return RejectedNonCanonicalBlock{blockName, encoded, blockHeight} - } - orphanOrRejectBlock := func(blockName string, block *wire.MsgBlock) TestInstance { - blockHeight := g.blockHeights[blockName] - return OrphanOrRejectedBlock{blockName, block, blockHeight} - } - expectTipBlock := func(blockName string, block *wire.MsgBlock) TestInstance { - blockHeight := g.blockHeights[blockName] - return ExpectedTip{blockName, block, blockHeight} - } - - // Define some convenience helper functions to populate the tests slice - // with test instances that have the described characteristics. - // - // accepted creates and appends a single acceptBlock test instance for - // the current tip which expects the block to be accepted to the main - // chain. - // - // acceptedToSideChainWithExpectedTip creates an appends a two-instance - // test. The first instance is an acceptBlock test instance for the - // current tip which expects the block to be accepted to a side chain. - // The second instance is an expectBlockTip test instance for provided - // values. - // - // rejected creates and appends a single rejectBlock test instance for - // the current tip. - // - // rejectedNonCanonical creates and appends a single - // rejectNonCanonicalBlock test instance for the current tip. - // - // orphanedOrRejected creates and appends a single orphanOrRejectBlock - // test instance for the current tip. - accepted := func() { - tests = append(tests, []TestInstance{ - acceptBlock(g.tipName, g.tip, false), - }) - } - acceptedToSideChainWithExpectedTip := func(tipName string) { - tests = append(tests, []TestInstance{ - acceptBlock(g.tipName, g.tip, false), - expectTipBlock(tipName, g.blocksByName[tipName]), - }) - } - rejected := func(code blockdag.ErrorCode) { - tests = append(tests, []TestInstance{ - rejectBlock(g.tipName, g.tip, code), - }) - } - rejectedNonCanonical := func() { - tests = append(tests, []TestInstance{ - rejectNonCanonicalBlock(g.tipName, g.tip), - }) - } - orphanedOrRejected := func() { - tests = append(tests, []TestInstance{ - orphanOrRejectBlock(g.tipName, g.tip), - }) - } - - // --------------------------------------------------------------------- - // Generate enough blocks to have mature coinbase outputs to work with. - // - // genesis -> bm0 -> bm1 -> ... -> bm99 - // --------------------------------------------------------------------- - - coinbaseMaturity := g.params.BlockCoinbaseMaturity - var testInstances []TestInstance - for i := uint64(0); i < coinbaseMaturity; i++ { - blockName := fmt.Sprintf("bm%d", i) - g.nextBlock(blockName, nil) - g.saveTipCoinbaseOut() - testInstances = append(testInstances, acceptBlock(g.tipName, - g.tip, false)) - } - tests = append(tests, testInstances) - - // Collect spendable outputs. This simplifies the code below. - var outs []*spendableOut - for i := uint64(0); i < coinbaseMaturity; i++ { - op := g.oldestCoinbaseOut() - outs = append(outs, &op) - } - - // --------------------------------------------------------------------- - // Basic forking and reorg tests. - // --------------------------------------------------------------------- - - // --------------------------------------------------------------------- - // The comments below identify the structure of the chain being built. - // - // The values in parenthesis repesent which outputs are being spent. - // - // For example, b1(0) indicates the first collected spendable output - // which, due to the code above to create the correct number of blocks, - // is the first output that can be spent at the current block height due - // to the coinbase maturity requirement. - // --------------------------------------------------------------------- - - // Start by building a couple of blocks at current tip (value in parens - // is which output is spent): - // - // ... -> b1(0) -> b2(1) - g.nextBlock("b1", outs[0]) - accepted() - - g.nextBlock("b2", outs[1]) - accepted() - - // Create a fork from b1. There should not be a reorg since b2 was seen - // first. - // - // ... -> b1(0) -> b2(1) - // \-> b3(1) - g.setTip("b1") - g.nextBlock("b3", outs[1]) - b3Tx1Out := makeSpendableOut(g.tip, 1, 0) - acceptedToSideChainWithExpectedTip("b2") - - // Extend b3 fork to make the alternative chain longer and force reorg. - // - // ... -> b1(0) -> b2(1) - // \-> b3(1) -> b4(2) - g.nextBlock("b4", outs[2]) - accepted() - - // Extend b2 fork twice to make first chain longer and force reorg. - // - // ... -> b1(0) -> b2(1) -> b5(2) -> b6(3) - // \-> b3(1) -> b4(2) - g.setTip("b2") - g.nextBlock("b5", outs[2]) - acceptedToSideChainWithExpectedTip("b4") - - g.nextBlock("b6", outs[3]) - accepted() - - // --------------------------------------------------------------------- - // Double spend tests. - // --------------------------------------------------------------------- - - // Create a fork that double spends. - // - // ... -> b1(0) -> b2(1) -> b5(2) -> b6(3) - // \-> b7(2) -> b8(4) - // \-> b3(1) -> b4(2) - g.setTip("b5") - g.nextBlock("b7", outs[2]) - acceptedToSideChainWithExpectedTip("b6") - - g.nextBlock("b8", outs[4]) - rejected(blockdag.ErrMissingTxOut) - - // --------------------------------------------------------------------- - // Too much proof-of-work coinbase tests. - // --------------------------------------------------------------------- - - // --------------------------------------------------------------------- - // Checksig signature operation count tests. - // --------------------------------------------------------------------- - - // Add a block with max allowed signature operations using OP_CHECKSIG. - // - // ... -> b5(2) -> b12(3) -> b13(4) -> b15(5) - // \-> b3(1) -> b4(2) - g.setTip("b13") - manySigOps := repeatOpcode(txscript.OpCheckSig, maxBlockSigOps) - g.nextBlock("b15", outs[5], replaceSpendScript(manySigOps)) - g.assertTipBlockSigOpsCount(maxBlockSigOps) - accepted() - - // Attempt to add block with more than max allowed signature operations - // using OP_CHECKSIG. - // - // ... -> b5(2) -> b12(3) -> b13(4) -> b15(5) - // \ \-> b16(7) - // \-> b3(1) -> b4(2) - tooManySigOps := repeatOpcode(txscript.OpCheckSig, maxBlockSigOps+1) - g.nextBlock("b16", outs[6], replaceSpendScript(tooManySigOps)) - g.assertTipBlockSigOpsCount(maxBlockSigOps + 1) - rejected(blockdag.ErrTooManySigOps) - - // --------------------------------------------------------------------- - // Cross-fork spend tests. - // --------------------------------------------------------------------- - - // Create block that spends a tx created on a different fork. - // - // ... -> b5(2) -> b12(3) -> b13(4) -> b15(5) - // \ \-> b17(b3.tx[1]) - // \-> b3(1) -> b4(2) - g.setTip("b15") - g.nextBlock("b17", &b3Tx1Out) - rejected(blockdag.ErrMissingTxOut) - - // Create block that forks and spends a tx created on a third fork. - // - // ... -> b5(2) -> b12(3) -> b13(4) -> b15(5) - // | \-> b18(b3.tx[1]) -> b19(6) - // \-> b3(1) -> b4(2) - g.setTip("b13") - g.nextBlock("b18", &b3Tx1Out) - acceptedToSideChainWithExpectedTip("b15") - - g.nextBlock("b19", outs[6]) - rejected(blockdag.ErrMissingTxOut) - - // --------------------------------------------------------------------- - // Immature coinbase tests. - // --------------------------------------------------------------------- - - // Create block that spends immature coinbase. - // - // ... -> b13(4) -> b15(5) - // \-> b20(7) - g.setTip("b15") - g.nextBlock("b20", outs[7]) - rejected(blockdag.ErrImmatureSpend) - - // Create block that spends immature coinbase on a fork. - // - // ... -> b13(4) -> b15(5) - // \-> b21(5) -> b22(7) - g.setTip("b13") - g.nextBlock("b21", outs[5]) - acceptedToSideChainWithExpectedTip("b15") - - g.nextBlock("b22", outs[7]) - rejected(blockdag.ErrImmatureSpend) - - // --------------------------------------------------------------------- - // Max block size tests. - // --------------------------------------------------------------------- - - // Create block that is the max allowed size. - // - // ... -> b15(5) -> b23(6) - g.setTip("b15") - g.nextBlock("b23", outs[6], func(b *wire.MsgBlock) { - bytesToMaxSize := maxBlockSize - b.SerializeSize() - 3 - sizePadScript := repeatOpcode(0x00, bytesToMaxSize) - replaceSpendScript(sizePadScript)(b) - }) - g.assertTipBlockSize(maxBlockSize) - accepted() - - // Create block that is the one byte larger than max allowed size. This - // is done on a fork and should be rejected regardless. - // - // ... -> b15(5) -> b23(6) - // \-> b24(6) -> b25(7) - g.setTip("b15") - g.nextBlock("b24", outs[6], func(b *wire.MsgBlock) { - bytesToMaxSize := maxBlockSize - b.SerializeSize() - 3 - sizePadScript := repeatOpcode(0x00, bytesToMaxSize+1) - replaceSpendScript(sizePadScript)(b) - }) - g.assertTipBlockSize(maxBlockSize + 1) - rejected(blockdag.ErrBlockMassTooHigh) - - // Parent was rejected, so this block must either be an orphan or - // outright rejected due to an invalid parent. - g.nextBlock("b25", outs[7]) - orphanedOrRejected() - - // --------------------------------------------------------------------- - // Coinbase script length limits tests. - // --------------------------------------------------------------------- - - // Create block that has a coinbase script that is smaller than the - // required length. This is done on a fork and should be rejected - // regardless. Also, create a block that builds on the rejected block. - // - // ... -> b15(5) -> b23(6) - // \-> b26(6) -> b27(7) - g.setTip("b15") - tooSmallCbScript := repeatOpcode(0x00, minCoinbaseScriptLen-1) - g.nextBlock("b26", outs[6], replaceCoinbaseSigScript(tooSmallCbScript)) - rejected(blockdag.ErrBadCoinbasePayloadLen) - - // Parent was rejected, so this block must either be an orphan or - // outright rejected due to an invalid parent. - g.nextBlock("b27", outs[7]) - orphanedOrRejected() - - // Create block that has a coinbase script that is larger than the - // allowed length. This is done on a fork and should be rejected - // regardless. Also, create a block that builds on the rejected block. - // - // ... -> b15(5) -> b23(6) - // \-> b28(6) -> b29(7) - g.setTip("b15") - tooLargeCbScript := repeatOpcode(0x00, maxCoinbaseScriptLen+1) - g.nextBlock("b28", outs[6], replaceCoinbaseSigScript(tooLargeCbScript)) - rejected(blockdag.ErrBadCoinbasePayloadLen) - - // Parent was rejected, so this block must either be an orphan or - // outright rejected due to an invalid parent. - g.nextBlock("b29", outs[7]) - orphanedOrRejected() - - // Create block that has a max length coinbase script. - // - // ... -> b23(6) -> b30(7) - g.setTip("b23") - maxSizeCbScript := repeatOpcode(0x00, maxCoinbaseScriptLen) - g.nextBlock("b30", outs[7], replaceCoinbaseSigScript(maxSizeCbScript)) - accepted() - - // --------------------------------------------------------------------- - // Multisig[Verify]/ChecksigVerifiy signature operation count tests. - // --------------------------------------------------------------------- - - // Create block with max signature operations as OP_CHECKMULTISIG. - // - // ... -> b30(7) -> b31(8) - // - // OP_CHECKMULTISIG counts for 20 sigops. - manySigOps = repeatOpcode(txscript.OpCheckMultiSig, maxBlockSigOps/20) - g.nextBlock("b31", outs[8], replaceSpendScript(manySigOps)) - g.assertTipBlockSigOpsCount(maxBlockSigOps) - accepted() - - // Create block with more than max allowed signature operations using - // OP_CHECKMULTISIG. - // - // ... -> b31(8) - // \-> b32(9) - // - // OP_CHECKMULTISIG counts for 20 sigops. - tooManySigOps = repeatOpcode(txscript.OpCheckMultiSig, maxBlockSigOps/20) - tooManySigOps = append(manySigOps, txscript.OpCheckSig) - g.nextBlock("b32", outs[9], replaceSpendScript(tooManySigOps)) - g.assertTipBlockSigOpsCount(maxBlockSigOps + 1) - rejected(blockdag.ErrTooManySigOps) - - // Create block with max signature operations as OP_CHECKMULTISIGVERIFY. - // - // ... -> b31(8) -> b33(9) - g.setTip("b31") - manySigOps = repeatOpcode(txscript.OpCheckMultiSigVerify, maxBlockSigOps/20) - g.nextBlock("b33", outs[9], replaceSpendScript(manySigOps)) - g.assertTipBlockSigOpsCount(maxBlockSigOps) - accepted() - - // Create block with more than max allowed signature operations using - // OP_CHECKMULTISIGVERIFY. - // - // ... -> b33(9) - // \-> b34(10) - // - tooManySigOps = repeatOpcode(txscript.OpCheckMultiSigVerify, maxBlockSigOps/20) - tooManySigOps = append(manySigOps, txscript.OpCheckSig) - g.nextBlock("b34", outs[10], replaceSpendScript(tooManySigOps)) - g.assertTipBlockSigOpsCount(maxBlockSigOps + 1) - rejected(blockdag.ErrTooManySigOps) - - // Create block with max signature operations as OP_CHECKSIGVERIFY. - // - // ... -> b33(9) -> b35(10) - // - g.setTip("b33") - manySigOps = repeatOpcode(txscript.OpCheckSigVerify, maxBlockSigOps) - g.nextBlock("b35", outs[10], replaceSpendScript(manySigOps)) - g.assertTipBlockSigOpsCount(maxBlockSigOps) - accepted() - - // Create block with more than max allowed signature operations using - // OP_CHECKSIGVERIFY. - // - // ... -> b35(10) - // \-> b36(11) - // - tooManySigOps = repeatOpcode(txscript.OpCheckSigVerify, maxBlockSigOps+1) - g.nextBlock("b36", outs[11], replaceSpendScript(tooManySigOps)) - g.assertTipBlockSigOpsCount(maxBlockSigOps + 1) - rejected(blockdag.ErrTooManySigOps) - - // --------------------------------------------------------------------- - // Spending of tx outputs in block that failed to connect tests. - // --------------------------------------------------------------------- - - // Create block that spends a transaction from a block that failed to - // connect (due to containing a double spend). - // - // ... -> b35(10) - // \-> b37(11) - // \-> b38(b37.tx[1]) - // - g.setTip("b35") - doubleSpendTx := createSpendTx(outs[11], lowFee) - g.nextBlock("b37", outs[11], additionalTx(doubleSpendTx)) - b37Tx1Out := makeSpendableOut(g.tip, 1, 0) - rejected(blockdag.ErrMissingTxOut) - - g.setTip("b35") - g.nextBlock("b38", &b37Tx1Out) - rejected(blockdag.ErrMissingTxOut) - - // --------------------------------------------------------------------- - // Pay-to-script-hash signature operation count tests. - // --------------------------------------------------------------------- - - // Create a pay-to-script-hash redeem script that consists of 9 - // signature operations to be used in the next three blocks. - const redeemScriptSigOps = 9 - redeemScript := pushDataScript(g.privKey.PubKey().SerializeCompressed()) - redeemScript = append(redeemScript, bytes.Repeat([]byte{txscript.Op2Dup, - txscript.OpCheckSigVerify}, redeemScriptSigOps-1)...) - redeemScript = append(redeemScript, txscript.OpCheckSig) - assertScriptSigOpsCount(redeemScript, redeemScriptSigOps) - - // Create a block that has enough pay-to-script-hash outputs such that - // another block can be created that consumes them all and exceeds the - // max allowed signature operations per block. - // - // ... -> b35(10) -> b39(11) - g.setTip("b35") - b39 := g.nextBlock("b39", outs[11], func(b *wire.MsgBlock) { - // Create a chain of transactions each spending from the - // previous one such that each contains an output that pays to - // the redeem script and the total number of signature - // operations in those redeem scripts will be more than the - // max allowed per block. - p2shScript := payToScriptHashScript(redeemScript) - txnsNeeded := (maxBlockSigOps / redeemScriptSigOps) + 1 - prevTx := b.Transactions[1] - for i := 0; i < txnsNeeded; i++ { - prevTx = createSpendTxForTx(prevTx, lowFee) - prevTx.TxOut[0].Value -= 2 - prevTx.AddTxOut(wire.NewTxOut(2, p2shScript)) - b.AddTransaction(prevTx) - } - }) - g.assertTipBlockNumTxns((maxBlockSigOps / redeemScriptSigOps) + 3) - accepted() - - // Create a block with more than max allowed signature operations where - // the majority of them are in pay-to-script-hash scripts. - // - // ... -> b35(10) -> b39(11) - // \-> b40(12) - g.setTip("b39") - g.nextBlock("b40", outs[12], func(b *wire.MsgBlock) { - txnsNeeded := (maxBlockSigOps / redeemScriptSigOps) - for i := 0; i < txnsNeeded; i++ { - // Create a signed transaction that spends from the - // associated p2sh output in b39. - spend := makeSpendableOutForTx(b39.Transactions[i+2], 2) - tx := createSpendTx(&spend, lowFee) - sig, err := txscript.RawTxInSignature(tx, 0, - redeemScript, txscript.SigHashAll, g.privKey) - if err != nil { - panic(err) - } - tx.TxIn[0].SignatureScript = pushDataScript(sig, - redeemScript) - b.AddTransaction(tx) - } - - // Create a final tx that includes a non-pay-to-script-hash - // output with the number of signature operations needed to push - // the block one over the max allowed. - fill := maxBlockSigOps - (txnsNeeded * redeemScriptSigOps) + 1 - finalTx := b.Transactions[len(b.Transactions)-1] - tx := createSpendTxForTx(finalTx, lowFee) - tx.TxOut[0].ScriptPubKey = repeatOpcode(txscript.OpCheckSig, fill) - b.AddTransaction(tx) - }) - rejected(blockdag.ErrTooManySigOps) - - // Create a block with the max allowed signature operations where the - // majority of them are in pay-to-script-hash scripts. - // - // ... -> b35(10) -> b39(11) -> b41(12) - g.setTip("b39") - g.nextBlock("b41", outs[12], func(b *wire.MsgBlock) { - txnsNeeded := (maxBlockSigOps / redeemScriptSigOps) - for i := 0; i < txnsNeeded; i++ { - spend := makeSpendableOutForTx(b39.Transactions[i+2], 2) - tx := createSpendTx(&spend, lowFee) - sig, err := txscript.RawTxInSignature(tx, 0, - redeemScript, txscript.SigHashAll, g.privKey) - if err != nil { - panic(err) - } - tx.TxIn[0].SignatureScript = pushDataScript(sig, - redeemScript) - b.AddTransaction(tx) - } - - // Create a final tx that includes a non-pay-to-script-hash - // output with the number of signature operations needed to push - // the block to exactly the max allowed. - fill := maxBlockSigOps - (txnsNeeded * redeemScriptSigOps) - if fill == 0 { - return - } - finalTx := b.Transactions[len(b.Transactions)-1] - tx := createSpendTxForTx(finalTx, lowFee) - tx.TxOut[0].ScriptPubKey = repeatOpcode(txscript.OpCheckSig, fill) - b.AddTransaction(tx) - }) - accepted() - - // --------------------------------------------------------------------- - // Reset the chain to a stable base. - // - // ... -> b35(10) -> b39(11) -> b42(12) -> b43(13) - // \-> b41(12) - // --------------------------------------------------------------------- - - g.setTip("b39") - g.nextBlock("b42", outs[12]) - acceptedToSideChainWithExpectedTip("b41") - - g.nextBlock("b43", outs[13]) - accepted() - - // --------------------------------------------------------------------- - // Various malformed block tests. - // --------------------------------------------------------------------- - - // Create block with an otherwise valid transaction in place of where - // the coinbase must be. - // - // ... -> b43(13) - // \-> b44(14) - g.nextBlock("b44", nil, func(b *wire.MsgBlock) { - nonCoinbaseTx := createSpendTx(outs[14], lowFee) - b.Transactions[0] = nonCoinbaseTx - }) - rejected(blockdag.ErrFirstTxNotCoinbase) - - // Create block with no transactions. - // - // ... -> b43(13) - // \-> b45(_) - g.setTip("b43") - g.nextBlock("b45", nil, func(b *wire.MsgBlock) { - b.Transactions = nil - }) - rejected(blockdag.ErrNoTransactions) - - // Create block with invalid proof of work. - // - // ... -> b43(13) - // \-> b46(14) - g.setTip("b43") - b46 := g.nextBlock("b46", outs[14]) - // This can't be done inside a munge function passed to nextBlock - // because the block is solved after the function returns and this test - // requires an unsolved block. - { - origHash := b46.BlockHash() - for { - // Keep incrementing the nonce until the hash treated as - // a uint256 is higher than the limit. - b46.Header.Nonce++ - blockHash := b46.BlockHash() - hashNum := daghash.HashToBig(blockHash) - if hashNum.Cmp(g.params.PowMax) >= 0 { - break - } - } - g.updateBlockState("b46", origHash, "b46", b46) - } - rejected(blockdag.ErrHighHash) - - // Create block with a timestamp too far in the future. - // - // ... -> b43(13) - // \-> b47(14) - g.setTip("b43") - g.nextBlock("b47", outs[14], func(b *wire.MsgBlock) { - // 3 hours in the future clamped to 1 second precision. - nowPlus3Hours := time.Now().Add(time.Hour * 3) - b.Header.Timestamp = time.Unix(nowPlus3Hours.Unix(), 0) - }) - rejected(blockdag.ErrTimeTooNew) - - // Create block with an invalid merkle root. - // - // ... -> b43(13) - // \-> b48(14) - g.setTip("b43") - g.nextBlock("b48", outs[14], func(b *wire.MsgBlock) { - b.Header.HashMerkleRoot = &daghash.Hash{} - }) - rejected(blockdag.ErrBadMerkleRoot) - - // Create block with an invalid proof-of-work limit. - // - // ... -> b43(13) - // \-> b49(14) - g.setTip("b43") - g.nextBlock("b49", outs[14], func(b *wire.MsgBlock) { - b.Header.Bits-- - }) - rejected(blockdag.ErrUnexpectedDifficulty) - - // Create block with an invalid negative proof-of-work limit. - // - // ... -> b43(13) - // \-> b49a(14) - g.setTip("b43") - b49a := g.nextBlock("b49a", outs[14]) - // This can't be done inside a munge function passed to nextBlock - // because the block is solved after the function returns and this test - // involves an unsolvable block. - { - origHash := b49a.BlockHash() - b49a.Header.Bits = 0x01810000 // -1 in compact form. - g.updateBlockState("b49a", origHash, "b49a", b49a) - } - rejected(blockdag.ErrUnexpectedDifficulty) - - // Create block with two coinbase transactions. - // - // ... -> b43(13) - // \-> b50(14) - g.setTip("b43") - coinbaseTx := g.createCoinbaseTx(g.tipHeight + 1) - g.nextBlock("b50", outs[14], additionalTx(coinbaseTx)) - rejected(blockdag.ErrMultipleCoinbases) - - // Create block with duplicate transactions. - // - // This test relies on the shape of the shape of the merkle tree to test - // the intended condition and thus is asserted below. - // - // ... -> b43(13) - // \-> b51(14) - g.setTip("b43") - g.nextBlock("b51", outs[14], func(b *wire.MsgBlock) { - b.AddTransaction(b.Transactions[1]) - }) - g.assertTipBlockNumTxns(3) - rejected(blockdag.ErrDuplicateTx) - - // Create a block that spends a transaction that does not exist. - // - // ... -> b43(13) - // \-> b52(14) - g.setTip("b43") - g.nextBlock("b52", outs[14], func(b *wire.MsgBlock) { - txID := newTxIDFromStr("00000000000000000000000000000000" + - "00000000000000000123456789abcdef") - b.Transactions[1].TxIn[0].PreviousOutpoint.TxID = *txID - b.Transactions[1].TxIn[0].PreviousOutpoint.Index = 0 - }) - rejected(blockdag.ErrMissingTxOut) - - // --------------------------------------------------------------------- - // Block header median time tests. - // --------------------------------------------------------------------- - - // Reset the chain to a stable base. - // - // ... -> b33(9) -> b35(10) -> b39(11) -> b42(12) -> b43(13) -> b53(14) - g.setTip("b43") - g.nextBlock("b53", outs[14]) - accepted() - - // Create a block with a timestamp that is exactly the median time. The - // block must be rejected. - // - // ... -> b33(9) -> b35(10) -> b39(11) -> b42(12) -> b43(13) -> b53(14) - // \-> b54(15) - g.nextBlock("b54", outs[15], func(b *wire.MsgBlock) { - // TODO: (Evgeny) This is wrong. Modified only to satisfy compilation. - medianBlock := g.blocks[*b.Header.ParentHashes[0]] - for i := 0; i < medianTimeBlocks/2; i++ { - // TODO: (Evgeny) This is wrong. Modified only to satisfy compilation. - medianBlock = g.blocks[*medianBlock.Header.ParentHashes[0]] - } - b.Header.Timestamp = medianBlock.Header.Timestamp - }) - rejected(blockdag.ErrTimeTooOld) - - // Create a block with a timestamp that is one second after the median - // time. The block must be accepted. - // - // ... -> b33(9) -> b35(10) -> b39(11) -> b42(12) -> b43(13) -> b53(14) -> b55(15) - g.setTip("b53") - g.nextBlock("b55", outs[15], func(b *wire.MsgBlock) { - // TODO: (Evgeny) This is wrong. Modified only to satisfy compilation. - medianBlock := g.blocks[*b.Header.ParentHashes[0]] - for i := 0; i < medianTimeBlocks/2; i++ { - // TODO: (Evgeny) This is wrong. Modified only to satisfy compilation. - medianBlock = g.blocks[*medianBlock.Header.ParentHashes[0]] - } - medianBlockTime := medianBlock.Header.Timestamp - b.Header.Timestamp = medianBlockTime.Add(time.Second) - }) - accepted() - - // --------------------------------------------------------------------- - // CVE-2012-2459 (block hash collision due to merkle tree algo) tests. - // --------------------------------------------------------------------- - - // Create two blocks that have the same hash via merkle tree tricks to - // ensure that the valid block is accepted even though it has the same - // hash as the invalid block that was rejected first. - // - // This is accomplished by building the blocks as follows: - // - // b57 (valid block): - // - // root = h1234 = h(h12 || h34) - // // \\ - // h12 = h(h(cb) || h(tx2)) h34 = h(h(tx3) || h(tx3)) - // // \\ // \\ - // coinbase tx2 tx3 nil - // - // transactions: coinbase, tx2, tx3 - // merkle tree level 1: h12 = h(h(cb) || h(tx2)) - // h34 = h(h(tx3) || h(tx3)) // Algo reuses tx3 - // merkle tree root: h(h12 || h34) - // - // b56 (invalid block with the same hash): - // - // root = h1234 = h(h12 || h34) - // // \\ - // h12 = h(h(cb) || h(tx2)) h34 = h(h(tx3) || h(tx3)) - // // \\ // \\ - // coinbase tx2 tx3 tx3 - // - // transactions: coinbase, tx2, tx3, tx3 - // merkle tree level 1: h12 = h(h(cb) || h(tx2)) - // h34 = h(h(tx3) || h(tx3)) // real tx3 dup - // merkle tree root: h(h12 || h34) - // - // ... -> b55(15) -> b57(16) - // \-> b56(16) - g.setTip("b55") - b57 := g.nextBlock("b57", outs[16], func(b *wire.MsgBlock) { - tx2 := b.Transactions[1] - tx3 := createSpendTxForTx(tx2, lowFee) - b.AddTransaction(tx3) - }) - g.assertTipBlockNumTxns(3) - - g.setTip("b55") - b56 := g.nextBlock("b56", nil, func(b *wire.MsgBlock) { - *b = cloneBlock(b57) - b.AddTransaction(b.Transactions[2]) - }) - g.assertTipBlockNumTxns(4) - g.assertTipBlockHash(b57.BlockHash()) - g.assertTipBlockMerkleRoot(b57.Header.HashMerkleRoot) - rejected(blockdag.ErrDuplicateTx) - - // Since the two blocks have the same hash and the generator state now - // has b56 associated with the hash, manually remove b56, replace it - // with b57, and then reset the tip to it. - g.updateBlockState("b56", b56.BlockHash(), "b57", b57) - g.setTip("b57") - accepted() - - // Create a block that contains two duplicate txns that are not in a - // consecutive position within the merkle tree. - // - // This is accomplished by building the block as follows: - // - // transactions: coinbase, tx2, tx3, tx4, tx5, tx6, tx3, tx4 - // merkle tree level 2: h12 = h(h(cb) || h(tx2)) - // h34 = h(h(tx3) || h(tx4)) - // h56 = h(h(tx5) || h(tx6)) - // h78 = h(h(tx3) || h(tx4)) // Same as h34 - // merkle tree level 1: h1234 = h(h12 || h34) - // h5678 = h(h56 || h78) - // merkle tree root: h(h1234 || h5678) - // - // - // ... -> b55(15) -> b57(16) - // \-> b56p2(16) - g.setTip("b55") - g.nextBlock("b56p2", outs[16], func(b *wire.MsgBlock) { - // Create 4 transactions that each spend from the previous tx - // in the block. - spendTx := b.Transactions[1] - for i := 0; i < 4; i++ { - spendTx = createSpendTxForTx(spendTx, lowFee) - b.AddTransaction(spendTx) - } - - // Add the duplicate transactions (3rd and 4th). - b.AddTransaction(b.Transactions[2]) - b.AddTransaction(b.Transactions[3]) - }) - g.assertTipBlockNumTxns(8) - rejected(blockdag.ErrDuplicateTx) - - // --------------------------------------------------------------------- - // Invalid transaction type tests. - // --------------------------------------------------------------------- - - // Create block with a transaction that tries to spend from an index - // that is out of range from an otherwise valid and existing tx. - // - // ... -> b57(16) - // \-> b58(17) - g.setTip("b57") - g.nextBlock("b58", outs[17], func(b *wire.MsgBlock) { - b.Transactions[1].TxIn[0].PreviousOutpoint.Index = 42 - }) - rejected(blockdag.ErrMissingTxOut) - - // Create block with transaction that pays more than its inputs. - // - // ... -> b57(16) - // \-> b59(17) - g.setTip("b57") - g.nextBlock("b59", outs[17], func(b *wire.MsgBlock) { - b.Transactions[1].TxOut[0].Value = uint64(outs[17].amount) + 1 - }) - rejected(blockdag.ErrSpendTooHigh) - - // --------------------------------------------------------------------- - // BIP0030 tests. - // --------------------------------------------------------------------- - - // Create a good block to reset the chain to a stable base. - // - // ... -> b57(16) -> b60(17) - g.setTip("b57") - g.nextBlock("b60", outs[17]) - accepted() - - // Create block that has a tx with the same hash as an existing tx that - // has not been fully spent. - // - // ... -> b60(17) - // \-> b61(18) - g.nextBlock("b61", outs[18], func(b *wire.MsgBlock) { - // Duplicate the coinbase of the parent block to force the - // condition. - // TODO: (Evgeny) This is wrong. Modified only to satisfy compilation. - parent := g.blocks[*b.Header.ParentHashes[0]] - b.Transactions[0] = parent.Transactions[0] - }) - rejected(blockdag.ErrOverwriteTx) - - // --------------------------------------------------------------------- - // Blocks with non-final transaction tests. - // --------------------------------------------------------------------- - - // Create block that contains a non-final non-coinbase transaction. - // - // ... -> b60(17) - // \-> b62(18) - g.setTip("b60") - g.nextBlock("b62", outs[18], func(b *wire.MsgBlock) { - // A non-final transaction must have at least one input with a - // non-final sequence number in addition to a non-final lock - // time. - b.Transactions[1].LockTime = 0xffffffff - b.Transactions[1].TxIn[0].Sequence = 0 - }) - rejected(blockdag.ErrUnfinalizedTx) - - // Create block that contains a non-final coinbase transaction. - // - // ... -> b60(17) - // \-> b63(18) - g.setTip("b60") - g.nextBlock("b63", outs[18], func(b *wire.MsgBlock) { - // A non-final transaction must have at least one input with a - // non-final sequence number in addition to a non-final lock - // time. - b.Transactions[0].LockTime = 0xffffffff - b.Transactions[0].TxIn[0].Sequence = 0 - }) - rejected(blockdag.ErrUnfinalizedTx) - - // --------------------------------------------------------------------- - // Non-canonical variable-length integer tests. - // --------------------------------------------------------------------- - - // Create a max size block with the variable-length integer for the - // number of transactions replaced with a larger non-canonical version - // that causes the block size to exceed the max allowed size. Then, - // create another block that is identical except with the canonical - // encoding and ensure it is accepted. The intent is to verify the - // implementation does not reject the second block, which will have the - // same hash, due to the first one already being rejected. - // - // ... -> b60(17) -> b64(18) - // \-> b64a(18) - g.setTip("b60") - b64a := g.nextBlock("b64a", outs[18], func(b *wire.MsgBlock) { - bytesToMaxSize := maxBlockSize - b.SerializeSize() - 3 - sizePadScript := repeatOpcode(0x00, bytesToMaxSize) - replaceSpendScript(sizePadScript)(b) - }) - g.assertTipNonCanonicalBlockSize(maxBlockSize + 8) - rejectedNonCanonical() - - g.setTip("b60") - b64 := g.nextBlock("b64", outs[18], func(b *wire.MsgBlock) { - *b = cloneBlock(b64a) - }) - // Since the two blocks have the same hash and the generator state now - // has b64a associated with the hash, manually remove b64a, replace it - // with b64, and then reset the tip to it. - g.updateBlockState("b64a", b64a.BlockHash(), "b64", b64) - g.setTip("b64") - g.assertTipBlockHash(b64a.BlockHash()) - g.assertTipBlockSize(maxBlockSize) - accepted() - - // --------------------------------------------------------------------- - // Same block transaction spend tests. - // --------------------------------------------------------------------- - - // Create block that spends an output created earlier in the same block. - // - // ... b64(18) -> b65(19) - g.setTip("b64") - g.nextBlock("b65", outs[19], func(b *wire.MsgBlock) { - tx3 := createSpendTxForTx(b.Transactions[1], lowFee) - b.AddTransaction(tx3) - }) - accepted() - - // Create block that spends an output created later in the same block. - // - // ... -> b65(19) - // \-> b66(20) - g.nextBlock("b66", nil, func(b *wire.MsgBlock) { - tx2 := createSpendTx(outs[20], lowFee) - tx3 := createSpendTxForTx(tx2, lowFee) - b.AddTransaction(tx3) - b.AddTransaction(tx2) - }) - rejected(blockdag.ErrMissingTxOut) - - // Create block that double spends a transaction created in the same - // block. - // - // ... -> b65(19) - // \-> b67(20) - g.setTip("b65") - g.nextBlock("b67", outs[20], func(b *wire.MsgBlock) { - tx2 := b.Transactions[1] - tx3 := createSpendTxForTx(tx2, lowFee) - tx4 := createSpendTxForTx(tx2, lowFee) - b.AddTransaction(tx3) - b.AddTransaction(tx4) - }) - rejected(blockdag.ErrMissingTxOut) - - // --------------------------------------------------------------------- - // Extra subsidy tests. - // --------------------------------------------------------------------- - - // Create block that pays 10 extra to the coinbase and a tx that only - // pays 9 fee. - // - // ... -> b65(19) - // \-> b68(20) - g.setTip("b65") - g.nextBlock("b68", outs[20], additionalCoinbase(10), additionalSpendFee(9)) - rejected(blockdag.ErrBadCoinbaseTransaction) - - // Create block that pays 10 extra to the coinbase and a tx that pays - // the extra 10 fee. - // - // ... -> b65(19) -> b69(20) - g.setTip("b65") - g.nextBlock("b69", outs[20], additionalCoinbase(10), additionalSpendFee(10)) - accepted() - - // --------------------------------------------------------------------- - // More signature operations counting tests. - // - // The next several tests ensure signature operations are counted before - // script elements that cause parse failure while those after are - // ignored and that signature operations after script elements that - // successfully parse even if that element will fail at run-time are - // counted. - // --------------------------------------------------------------------- - - // Create block with more than max allowed signature operations such - // that the signature operation that pushes it over the limit is after - // a push data with a script element size that is larger than the max - // allowed size when executed. The block must be rejected because the - // signature operation after the script element must be counted since - // the script parses validly. - // - // The script generated consists of the following form: - // - // Comment assumptions: - // maxBlockSigOps = 20000 - // maxScriptElementSize = 520 - // - // [0-19999] : OP_CHECKSIG - // [20000] : OP_PUSHDATA4 - // [20001-20004]: 521 (little-endian encoded maxScriptElementSize+1) - // [20005-20525]: too large script element - // [20526] : OP_CHECKSIG (goes over the limit) - // - // ... -> b69(20) - // \-> b70(21) - scriptSize := maxBlockSigOps + 5 + (maxScriptElementSize + 1) + 1 - tooManySigOps = repeatOpcode(txscript.OpCheckSig, scriptSize) - tooManySigOps[maxBlockSigOps] = txscript.OpPushData4 - binary.LittleEndian.PutUint32(tooManySigOps[maxBlockSigOps+1:], - maxScriptElementSize+1) - g.nextBlock("b70", outs[21], replaceSpendScript(tooManySigOps)) - g.assertTipBlockSigOpsCount(maxBlockSigOps + 1) - rejected(blockdag.ErrTooManySigOps) - - // Create block with more than max allowed signature operations such - // that the signature operation that pushes it over the limit is before - // an invalid push data that claims a large amount of data even though - // that much data is not provided. - // - // ... -> b69(20) - // \-> b71(21) - g.setTip("b69") - scriptSize = maxBlockSigOps + 5 + maxScriptElementSize + 1 - tooManySigOps = repeatOpcode(txscript.OpCheckSig, scriptSize) - tooManySigOps[maxBlockSigOps+1] = txscript.OpPushData4 - binary.LittleEndian.PutUint32(tooManySigOps[maxBlockSigOps+2:], 0xffffffff) - g.nextBlock("b71", outs[21], replaceSpendScript(tooManySigOps)) - g.assertTipBlockSigOpsCount(maxBlockSigOps + 1) - rejected(blockdag.ErrTooManySigOps) - - // Create block with the max allowed signature operations such that all - // counted signature operations are before an invalid push data that - // claims a large amount of data even though that much data is not - // provided. The pushed data itself consists of OP_CHECKSIG so the - // block would be rejected if any of them were counted. - // - // ... -> b69(20) -> b72(21) - g.setTip("b69") - scriptSize = maxBlockSigOps + 5 + maxScriptElementSize - manySigOps = repeatOpcode(txscript.OpCheckSig, scriptSize) - manySigOps[maxBlockSigOps] = txscript.OpPushData4 - binary.LittleEndian.PutUint32(manySigOps[maxBlockSigOps+1:], 0xffffffff) - g.nextBlock("b72", outs[21], replaceSpendScript(manySigOps)) - g.assertTipBlockSigOpsCount(maxBlockSigOps) - accepted() - - // Create block with the max allowed signature operations such that all - // counted signature operations are before an invalid push data that - // contains OP_CHECKSIG in the number of bytes to push. The block would - // be rejected if any of them were counted. - // - // ... -> b72(21) -> b73(22) - scriptSize = maxBlockSigOps + 5 + (maxScriptElementSize + 1) - manySigOps = repeatOpcode(txscript.OpCheckSig, scriptSize) - manySigOps[maxBlockSigOps] = txscript.OpPushData4 - g.nextBlock("b73", outs[22], replaceSpendScript(manySigOps)) - g.assertTipBlockSigOpsCount(maxBlockSigOps) - accepted() - - // --------------------------------------------------------------------- - // Dead execution path tests. - // --------------------------------------------------------------------- - - // Create block with an invalid opcode in a dead execution path. - // - // ... -> b73(22) -> b74(23) - script := []byte{txscript.OpIf, txscript.OpInvalidOpCode, - txscript.OpElse, txscript.OpTrue, txscript.OpEndIf} - g.nextBlock("b74", outs[23], replaceSpendScript(script), func(b *wire.MsgBlock) { - tx2 := b.Transactions[1] - tx3 := createSpendTxForTx(tx2, lowFee) - tx3.TxIn[0].SignatureScript = []byte{txscript.OpFalse} - b.AddTransaction(tx3) - }) - accepted() - - // --------------------------------------------------------------------- - // Various OP_RETURN tests. - // --------------------------------------------------------------------- - - // Create a block that has multiple transactions each with a single - // OP_RETURN output. - // - // ... -> b74(23) -> b75(24) - g.nextBlock("b75", outs[24], func(b *wire.MsgBlock) { - // Add 4 outputs to the spending transaction that are spent - // below. - const numAdditionalOutputs = 4 - const zeroCoin = uint64(0) - spendTx := b.Transactions[1] - for i := 0; i < numAdditionalOutputs; i++ { - spendTx.AddTxOut(wire.NewTxOut(zeroCoin, opTrueScript)) - } - - // Add transactions spending from the outputs added above that - // each contain an OP_RETURN output. - // - // NOTE: The createSpendTx func adds the OP_RETURN output. - zeroFee := util.Amount(0) - for i := uint32(0); i < numAdditionalOutputs; i++ { - spend := makeSpendableOut(b, 1, i+2) - tx := createSpendTx(&spend, zeroFee) - b.AddTransaction(tx) - } - }) - g.assertTipBlockNumTxns(6) - g.assertTipBlockTxOutOpReturn(5, 1) - b75OpReturnOut := makeSpendableOut(g.tip, 5, 1) - accepted() - - // Reorg to a side chain that does not contain the OP_RETURNs. - // - // ... -> b74(23) -> b75(24) - // \-> b76(24) -> b77(25) - g.setTip("b74") - g.nextBlock("b76", outs[24]) - acceptedToSideChainWithExpectedTip("b75") - - g.nextBlock("b77", outs[25]) - accepted() - - // Reorg back to the original chain that contains the OP_RETURNs. - // - // ... -> b74(23) -> b75(24) -> b78(25) -> b79(26) - // \-> b76(24) -> b77(25) - g.setTip("b75") - g.nextBlock("b78", outs[25]) - acceptedToSideChainWithExpectedTip("b77") - - g.nextBlock("b79", outs[26]) - accepted() - - // Create a block that spends an OP_RETURN. - // - // ... -> b74(23) -> b75(24) -> b78(25) -> b79(26) - // \-> b76(24) -> b77(25) \-> b80(b75.tx[5].out[1]) - // - // An OP_RETURN output doesn't have any value and the default behavior - // of nextBlock is to assign a fee of one, so increment the amount here - // to effective negate that behavior. - b75OpReturnOut.amount++ - g.nextBlock("b80", &b75OpReturnOut) - rejected(blockdag.ErrMissingTxOut) - - // Create a block that has a transaction with multiple OP_RETURNs. Even - // though it's not considered a standard transaction, it is still valid - // by the consensus rules. - // - // ... -> b79(26) -> b81(27) - // - g.setTip("b79") - g.nextBlock("b81", outs[27], func(b *wire.MsgBlock) { - const numAdditionalOutputs = 4 - const zeroCoin = uint64(0) - spendTx := b.Transactions[1] - for i := 0; i < numAdditionalOutputs; i++ { - opRetScript := uniqueOpReturnScript() - spendTx.AddTxOut(wire.NewTxOut(zeroCoin, opRetScript)) - } - }) - for i := uint32(2); i < 6; i++ { - g.assertTipBlockTxOutOpReturn(1, i) - } - accepted() - - // --------------------------------------------------------------------- - // Large block re-org test. - // --------------------------------------------------------------------- - - if !includeLargeReorg { - return tests, nil - } - - // Ensure the tip the re-org test builds on is the best chain tip. - // - // ... -> b81(27) -> ... - g.setTip("b81") - - // Collect all of the spendable coinbase outputs from the previous - // collection point up to the current tip. - g.saveSpendableCoinbaseOuts() - spendableOutOffset := g.tipHeight - coinbaseMaturity - - // Extend the main chain by a large number of max size blocks. - // - // ... -> br0 -> br1 -> ... -> br# - testInstances = nil - reorgSpend := *outs[spendableOutOffset] - reorgStartBlockName := g.tipName - chain1TipName := g.tipName - for i := uint64(0); i < numLargeReorgBlocks; i++ { - chain1TipName = fmt.Sprintf("br%d", i) - g.nextBlock(chain1TipName, &reorgSpend, func(b *wire.MsgBlock) { - bytesToMaxSize := maxBlockSize - b.SerializeSize() - 3 - sizePadScript := repeatOpcode(0x00, bytesToMaxSize) - replaceSpendScript(sizePadScript)(b) - }) - g.assertTipBlockSize(maxBlockSize) - g.saveTipCoinbaseOut() - testInstances = append(testInstances, acceptBlock(g.tipName, - g.tip, false)) - - // Use the next available spendable output. First use up any - // remaining spendable outputs that were already popped into the - // outs slice, then just pop them from the stack. - if spendableOutOffset+1+i < uint64(len(outs)) { - reorgSpend = *outs[spendableOutOffset+1+i] - } else { - reorgSpend = g.oldestCoinbaseOut() - } - } - tests = append(tests, testInstances) - - // Create a side chain that has the same length. - // - // ... -> br0 -> ... -> br# - // \-> bralt0 -> ... -> bralt# - g.setTip(reorgStartBlockName) - testInstances = nil - chain2TipName := g.tipName - for i := uint16(0); i < numLargeReorgBlocks; i++ { - chain2TipName = fmt.Sprintf("bralt%d", i) - g.nextBlock(chain2TipName, nil) - testInstances = append(testInstances, acceptBlock(g.tipName, - g.tip, false)) - } - testInstances = append(testInstances, expectTipBlock(chain1TipName, - g.blocksByName[chain1TipName])) - tests = append(tests, testInstances) - - // Extend the side chain by one to force the large reorg. - // - // ... -> bralt0 -> ... -> bralt# -> bralt#+1 - // \-> br0 -> ... -> br# - g.nextBlock(fmt.Sprintf("bralt%d", g.tipHeight+1), nil) - chain2TipName = g.tipName - accepted() - - // Extend the first chain by two to force a large reorg back to it. - // - // ... -> br0 -> ... -> br# -> br#+1 -> br#+2 - // \-> bralt0 -> ... -> bralt# -> bralt#+1 - g.setTip(chain1TipName) - g.nextBlock(fmt.Sprintf("br%d", g.tipHeight+1), nil) - chain1TipName = g.tipName - acceptedToSideChainWithExpectedTip(chain2TipName) - - g.nextBlock(fmt.Sprintf("br%d", g.tipHeight+2), nil) - chain1TipName = g.tipName - accepted() - - return tests, nil -} diff --git a/blockdag/fullblocktests/params.go b/blockdag/fullblocktests/params.go deleted file mode 100644 index 749f2c55a..000000000 --- a/blockdag/fullblocktests/params.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright (c) 2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package fullblocktests - -import ( - "encoding/hex" - "math" - "math/big" - "time" - - "github.com/kaspanet/kaspad/util/hdkeychain" - "github.com/kaspanet/kaspad/util/subnetworkid" - - "github.com/kaspanet/kaspad/dagconfig" - "github.com/kaspanet/kaspad/util/daghash" - "github.com/kaspanet/kaspad/wire" -) - -// newHashFromStr converts the passed big-endian hex string into a -// wire.Hash. It only differs from the one available in daghash in that -// it panics on an error since it will only (and must only) be called with -// hard-coded, and therefore known good, hashes. -func newHashFromStr(hexStr string) *daghash.Hash { - hash, err := daghash.NewHashFromStr(hexStr) - if err != nil { - panic(err) - } - return hash -} - -// newTxIDFromStr converts the passed big-endian hex string into a -// wire.TxID. It only differs from the one available in daghash in that -// it panics on an error since it will only (and must only) be called with -// hard-coded, and therefore known good, hashes. -func newTxIDFromStr(hexStr string) *daghash.TxID { - txID, err := daghash.NewTxIDFromStr(hexStr) - if err != nil { - panic(err) - } - return txID -} - -// fromHex converts the passed hex string into a byte slice and will panic if -// there is an error. This is only provided for the hard-coded constants so -// errors in the source code can be detected. It will only (and must only) be -// called for initialization purposes. -func fromHex(s string) []byte { - r, err := hex.DecodeString(s) - if err != nil { - panic("invalid hex in source file: " + s) - } - return r -} - -var ( - // bigOne is 1 represented as a big.Int. It is defined here to avoid - // the overhead of creating it multiple times. - bigOne = big.NewInt(1) - - // regressionPowLimit is the highest proof of work value a kaspa block - // can have for the regression test network. It is the value 2^255 - 1. - regressionPowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 255), bigOne) - - // regTestGenesisBlock defines the genesis block of the block chain which serves - // as the public transaction ledger for the regression test network. - regTestGenesisBlock = wire.MsgBlock{ - Header: wire.BlockHeader{ - Version: 1, - ParentHashes: []*daghash.Hash{}, - HashMerkleRoot: newHashFromStr("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"), - Timestamp: time.Unix(0x5b28c636, 0), // 2018-06-19 09:00:38 +0000 UTC - Bits: 0x207fffff, // 545259519 [7fffff0000000000000000000000000000000000000000000000000000000000] - Nonce: 1, - }, - Transactions: []*wire.MsgTx{{ - Version: 1, - TxIn: []*wire.TxIn{{ - PreviousOutpoint: wire.Outpoint{ - TxID: daghash.TxID{}, - Index: 0xffffffff, - }, - SignatureScript: fromHex("04ffff001d010445" + - "5468652054696d65732030332f4a616e2f" + - "32303039204368616e63656c6c6f72206f" + - "6e206272696e6b206f66207365636f6e64" + - "206261696c6f757420666f72206261686b73"), - Sequence: math.MaxUint64, - }}, - TxOut: []*wire.TxOut{{ - Value: 0, - ScriptPubKey: fromHex("4104678afdb0fe5548271967f1" + - "a67130b7105cd6a828e03909a67962e0ea1f" + - "61deb649f6bc3f4cef38c4f35504e51ec138" + - "c4f35504e51ec112de5c384df7ba0b8d578a" + - "4c702b6bf11d5fac"), - }}, - LockTime: 0, - SubnetworkID: *subnetworkid.SubnetworkIDNative, - }}, - } -) - -// regressionNetParams defines the network parameters for the regression test -// network. -// -// NOTE: The test generator intentionally does not use the existing definitions -// in the dagconfig package since the intent is to be able to generate known -// good tests which exercise that code. Using the dagconfig parameters would -// allow them to change out from under the tests potentially invalidating them. -var regressionNetParams = &dagconfig.Params{ - Name: "regtest", - Net: wire.RegTest, - DefaultPort: "18444", - - // DAG parameters - GenesisBlock: ®TestGenesisBlock, - GenesisHash: newHashFromStr("5bec7567af40504e0994db3b573c186fffcc4edefe096ff2e58d00523bd7e8a6"), - PowMax: regressionPowLimit, - BlockCoinbaseMaturity: 100, - SubsidyReductionInterval: 150, - TargetTimePerBlock: time.Second * 10, // 10 seconds - DifficultyAdjustmentWindowSize: 2640, - TimestampDeviationTolerance: 132, - GenerateSupported: true, - - // Mempool parameters - RelayNonStdTxs: true, - - // Address encoding magics - PrivateKeyID: 0xef, // starts with 9 (uncompressed) or c (compressed) - - // BIP32 hierarchical deterministic extended key magics - HDKeyIDPair: hdkeychain.HDKeyPairRegressionNet, - - // BIP44 coin type used in the hierarchical deterministic path for - // address generation. - HDCoinType: 1, -}