diff --git a/blockdag/dag_test.go b/blockdag/dag_test.go index 891d95416..8f64a5b4c 100644 --- a/blockdag/dag_test.go +++ b/blockdag/dag_test.go @@ -894,7 +894,7 @@ func TestFinality(t *testing.T) { } msgBlock := wire.NewMsgBlock(bh) blockHeight := parents.maxHeight() + 1 - coinbaseTx, err := createCoinbaseTx(blockHeight, 1, extraNonce, dag.dagParams) + coinbaseTx, err := createCoinbaseTxForTest(blockHeight, 1, extraNonce, dag.dagParams) if err != nil { return nil, err } diff --git a/blockdag/error.go b/blockdag/error.go index 02d97fa17..0c325a257 100644 --- a/blockdag/error.go +++ b/blockdag/error.go @@ -216,6 +216,10 @@ const ( // sorted by sub-network ErrTransactionsNotSorted + // ErrInvalidGas transaction wants to use more GAS than allowed + // by subnetwork + ErrInvalidGas + // ErrSubNetwork indicates that a block doesn't adhere to the sub-network // registry rules ErrSubNetworkRegistry @@ -265,6 +269,7 @@ var errorCodeStrings = map[ErrorCode]string{ ErrWithDiff: "ErrWithDiff", ErrFinality: "ErrFinality", ErrTransactionsNotSorted: "ErrTransactionsNotSorted", + ErrInvalidGas: "ErrInvalidGas", } // String returns the ErrorCode as a human-readable name. diff --git a/blockdag/error_test.go b/blockdag/error_test.go index 0e0efe02a..218a86a96 100644 --- a/blockdag/error_test.go +++ b/blockdag/error_test.go @@ -57,6 +57,7 @@ func TestErrorCodeStringer(t *testing.T) { {ErrWithDiff, "ErrWithDiff"}, {ErrFinality, "ErrFinality"}, {ErrTransactionsNotSorted, "ErrTransactionsNotSorted"}, + {ErrInvalidGas, "ErrInvalidGas"}, {0xffff, "Unknown ErrorCode (65535)"}, } diff --git a/blockdag/subnetworks_test.go b/blockdag/subnetworks_test.go index 5571b9f6a..e5fd370b6 100644 --- a/blockdag/subnetworks_test.go +++ b/blockdag/subnetworks_test.go @@ -2,13 +2,12 @@ package blockdag import ( "encoding/binary" - "github.com/daglabs/btcd/dagconfig" - "github.com/daglabs/btcd/util" - "github.com/daglabs/btcd/wire" "math" "reflect" "testing" - "time" + + "github.com/daglabs/btcd/dagconfig" + "github.com/daglabs/btcd/wire" ) // TestSubNetworkRegistry tests the full sub-network registry flow. In this test: @@ -25,89 +24,11 @@ func TestSubNetworkRegistry(t *testing.T) { t.Fatalf("Failed to setup DAG instance: %v", err) } defer teardownFunc() - blockTime := time.Unix(dag.genesis.timestamp, 0) - extraNonce := int64(0) - buildNextBlock := func(parents blockSet, txs []*wire.MsgTx) (*util.Block, error) { - // We need to change the blockTime to keep all block hashes unique - blockTime = blockTime.Add(time.Second) + _, err = RegisterSubnetworkForTest(dag, 12345) - // We need to change the extraNonce to keep coinbase hashes unique - extraNonce++ - - bh := &wire.BlockHeader{ - Version: 1, - Bits: dag.genesis.bits, - ParentHashes: parents.hashes(), - Timestamp: blockTime, - } - msgBlock := wire.NewMsgBlock(bh) - blockHeight := parents.maxHeight() + 1 - coinbaseTx, err := createCoinbaseTx(blockHeight, 1, extraNonce, dag.dagParams) - if err != nil { - return nil, err - } - _ = msgBlock.AddTransaction(coinbaseTx) - - for _, tx := range txs { - _ = msgBlock.AddTransaction(tx) - } - - return util.NewBlock(msgBlock), nil - } - - addBlockToDAG := func(block *util.Block) (*blockNode, error) { - dag.dagLock.Lock() - defer dag.dagLock.Unlock() - - err = dag.maybeAcceptBlock(block, BFNone) - if err != nil { - return nil, err - } - - return dag.index.LookupNode(block.Hash()), nil - } - - currentNode := dag.genesis - - // Create a block with a valid sub-network registry transaction - gasLimit := uint64(12345) - registryTx := wire.NewMsgTx(wire.TxVersion) - registryTx.SubNetworkID = wire.SubNetworkRegistry - registryTx.Payload = make([]byte, 8) - binary.LittleEndian.PutUint64(registryTx.Payload, gasLimit) - - // Add it to the DAG - registryBlock, err := buildNextBlock(setFromSlice(currentNode), []*wire.MsgTx{registryTx}) if err != nil { - t.Fatalf("could not build registry block: %s", err) - } - currentNode, err = addBlockToDAG(registryBlock) - if err != nil { - t.Fatalf("could not add registry block to DAG: %s", err) - } - - // Add 2*finalityInterval to ensure the registry transaction is finalized - for currentNode.blueScore < 2*finalityInterval { - nextBlock, err := buildNextBlock(setFromSlice(currentNode), []*wire.MsgTx{}) - if err != nil { - t.Fatalf("could not create block: %s", err) - } - currentNode, err = addBlockToDAG(nextBlock) - if err != nil { - t.Fatalf("could not add block to DAG: %s", err) - } - } - - // Make sure that the sub-network had been successfully registered by trying - // to retrieve its gas limit. - mostRecentlyRegisteredSubNetworkID := dag.lastSubNetworkID - 1 - limit, err := dag.GasLimit(mostRecentlyRegisteredSubNetworkID) - if err != nil { - t.Fatalf("could not retrieve gas limit: %s", err) - } - if limit != gasLimit { - t.Fatalf("unexpected gas limit. want: %d, got: %d", gasLimit, limit) + t.Fatalf("could not register network: %s", err) } } diff --git a/blockdag/test_utils.go b/blockdag/test_utils.go index 3fcbe36c7..5cb794a6a 100644 --- a/blockdag/test_utils.go +++ b/blockdag/test_utils.go @@ -1,13 +1,18 @@ package blockdag import ( + "encoding/binary" "fmt" "os" "path/filepath" + "time" + "github.com/daglabs/btcd/dagconfig" + "github.com/daglabs/btcd/dagconfig/daghash" "github.com/daglabs/btcd/database" _ "github.com/daglabs/btcd/database/ffldb" // blank import ffldb so that its init() function runs before tests "github.com/daglabs/btcd/txscript" + "github.com/daglabs/btcd/util" "github.com/daglabs/btcd/wire" ) @@ -94,3 +99,134 @@ func DAGSetup(dbName string, config Config) (*BlockDAG, func(), error) { } return dag, teardown, nil } + +// OpTrueScript is script returning TRUE +var OpTrueScript = []byte{txscript.OpTrue} + +// createCoinbaseTxForTest returns a coinbase transaction with the requested number of +// outputs paying an appropriate subsidy based on the passed block height to the +// address associated with the harness. It automatically uses a standard +// signature script that starts with the block height +func createCoinbaseTxForTest(blockHeight int32, numOutputs uint32, extraNonce int64, params *dagconfig.Params) (*wire.MsgTx, error) { + // Create standard coinbase script. + coinbaseScript, err := txscript.NewScriptBuilder(). + AddInt64(int64(blockHeight)).AddInt64(extraNonce).Script() + if err != nil { + return nil, err + } + + tx := wire.NewMsgTx(wire.TxVersion) + tx.AddTxIn(&wire.TxIn{ + // Coinbase transactions have no inputs, so previous outpoint is + // zero hash and max index. + PreviousOutPoint: *wire.NewOutPoint(&daghash.Hash{}, + wire.MaxPrevOutIndex), + SignatureScript: coinbaseScript, + Sequence: wire.MaxTxInSequenceNum, + }) + totalInput := CalcBlockSubsidy(blockHeight, params) + amountPerOutput := totalInput / uint64(numOutputs) + remainder := totalInput - amountPerOutput*uint64(numOutputs) + for i := uint32(0); i < numOutputs; i++ { + // Ensure the final output accounts for any remainder that might + // be left from splitting the input amount. + amount := amountPerOutput + if i == numOutputs-1 { + amount = amountPerOutput + remainder + } + tx.AddTxOut(&wire.TxOut{ + PkScript: OpTrueScript, + Value: amount, + }) + } + + return tx, nil +} + +// RegisterSubnetworkForTest is used to register network on DAG with specified GAS limit. +func RegisterSubnetworkForTest(dag *BlockDAG, gasLimit uint64) (subNetworkID uint64, err error) { + blockTime := time.Unix(dag.genesis.timestamp, 0) + extraNonce := int64(0) + + buildNextBlock := func(parents blockSet, txs []*wire.MsgTx) (*util.Block, error) { + // We need to change the blockTime to keep all block hashes unique + blockTime = blockTime.Add(time.Second) + + // We need to change the extraNonce to keep coinbase hashes unique + extraNonce++ + + bh := &wire.BlockHeader{ + Version: 1, + Bits: dag.genesis.bits, + ParentHashes: parents.hashes(), + Timestamp: blockTime, + } + msgBlock := wire.NewMsgBlock(bh) + blockHeight := parents.maxHeight() + 1 + coinbaseTx, err := createCoinbaseTxForTest(blockHeight, 1, extraNonce, dag.dagParams) + if err != nil { + return nil, err + } + _ = msgBlock.AddTransaction(coinbaseTx) + + for _, tx := range txs { + _ = msgBlock.AddTransaction(tx) + } + + return util.NewBlock(msgBlock), nil + } + + addBlockToDAG := func(block *util.Block) (*blockNode, error) { + dag.dagLock.Lock() + defer dag.dagLock.Unlock() + + err = dag.maybeAcceptBlock(block, BFNone) + if err != nil { + return nil, err + } + + return dag.index.LookupNode(block.Hash()), nil + } + + currentNode := dag.genesis + + // Create a block with a valid sub-network registry transaction + registryTx := wire.NewMsgTx(wire.TxVersion) + registryTx.SubNetworkID = wire.SubNetworkRegistry + registryTx.Payload = make([]byte, 8) + binary.LittleEndian.PutUint64(registryTx.Payload, gasLimit) + + // Add it to the DAG + registryBlock, err := buildNextBlock(setFromSlice(currentNode), []*wire.MsgTx{registryTx}) + if err != nil { + return 0, fmt.Errorf("could not build registry block: %s", err) + } + currentNode, err = addBlockToDAG(registryBlock) + if err != nil { + return 0, fmt.Errorf("could not add registry block to DAG: %s", err) + } + + // Add 2*finalityInterval to ensure the registry transaction is finalized + for currentNode.blueScore < 2*finalityInterval { + nextBlock, err := buildNextBlock(setFromSlice(currentNode), []*wire.MsgTx{}) + if err != nil { + return 0, fmt.Errorf("could not create block: %s", err) + } + currentNode, err = addBlockToDAG(nextBlock) + if err != nil { + return 0, fmt.Errorf("could not add block to DAG: %s", err) + } + } + + // Make sure that the sub-network had been successfully registered by trying + // to retrieve its gas limit. + mostRecentlyRegisteredSubNetworkID := dag.lastSubNetworkID - 1 + limit, err := dag.GasLimit(mostRecentlyRegisteredSubNetworkID) + if err != nil { + return 0, fmt.Errorf("could not retrieve gas limit: %s", err) + } + if limit != gasLimit { + return 0, fmt.Errorf("unexpected gas limit. want: %d, got: %d", gasLimit, limit) + } + return mostRecentlyRegisteredSubNetworkID, nil +} diff --git a/blockdag/utxoset_test.go b/blockdag/utxoset_test.go index 098876701..f465c48d6 100644 --- a/blockdag/utxoset_test.go +++ b/blockdag/utxoset_test.go @@ -7,13 +7,10 @@ import ( "github.com/daglabs/btcd/dagconfig" "github.com/daglabs/btcd/dagconfig/daghash" - "github.com/daglabs/btcd/txscript" "github.com/daglabs/btcd/util" "github.com/daglabs/btcd/wire" ) -var OpTrueScript = []byte{txscript.OpTrue} - // TestUTXOCollection makes sure that utxoCollection cloning and string representations work as expected. func TestUTXOCollection(t *testing.T) { hash0, _ := daghash.NewHashFromStr("0000000000000000000000000000000000000000000000000000000000000000") @@ -799,46 +796,6 @@ func TestDiffUTXOSet_addTx(t *testing.T) { } } -// createCoinbaseTx returns a coinbase transaction with the requested number of -// outputs paying an appropriate subsidy based on the passed block height to the -// address associated with the harness. It automatically uses a standard -// signature script that starts with the block height -func createCoinbaseTx(blockHeight int32, numOutputs uint32, extraNonce int64, params *dagconfig.Params) (*wire.MsgTx, error) { - // Create standard coinbase script. - coinbaseScript, err := txscript.NewScriptBuilder(). - AddInt64(int64(blockHeight)).AddInt64(extraNonce).Script() - if err != nil { - return nil, err - } - - tx := wire.NewMsgTx(wire.TxVersion) - tx.AddTxIn(&wire.TxIn{ - // Coinbase transactions have no inputs, so previous outpoint is - // zero hash and max index. - PreviousOutPoint: *wire.NewOutPoint(&daghash.Hash{}, - wire.MaxPrevOutIndex), - SignatureScript: coinbaseScript, - Sequence: wire.MaxTxInSequenceNum, - }) - totalInput := CalcBlockSubsidy(blockHeight, params) - amountPerOutput := totalInput / uint64(numOutputs) - remainder := totalInput - amountPerOutput*uint64(numOutputs) - for i := uint32(0); i < numOutputs; i++ { - // Ensure the final output accounts for any remainder that might - // be left from splitting the input amount. - amount := amountPerOutput - if i == numOutputs-1 { - amount = amountPerOutput + remainder - } - tx.AddTxOut(&wire.TxOut{ - PkScript: OpTrueScript, - Value: amount, - }) - } - - return tx, nil -} - func TestApplyUTXOChanges(t *testing.T) { // Create a new database and dag instance to run tests against. dag, teardownFunc, err := DAGSetup("TestApplyUTXOChanges", Config{ @@ -857,9 +814,9 @@ func TestApplyUTXOChanges(t *testing.T) { }, } - cbTx, err := createCoinbaseTx(1, 1, 0, dag.dagParams) + cbTx, err := createCoinbaseTxForTest(1, 1, 0, dag.dagParams) if err != nil { - t.Errorf("createCoinbaseTx: %v", err) + t.Errorf("createCoinbaseTxForTest: %v", err) } chainedTx := wire.NewMsgTx(wire.TxVersion) @@ -924,9 +881,9 @@ func TestDiffFromTx(t *testing.T) { fus := &FullUTXOSet{ utxoCollection: utxoCollection{}, } - cbTx, err := createCoinbaseTx(1, 1, 0, &dagconfig.SimNetParams) + cbTx, err := createCoinbaseTxForTest(1, 1, 0, &dagconfig.SimNetParams) if err != nil { - t.Errorf("createCoinbaseTx: %v", err) + t.Errorf("createCoinbaseTxForTest: %v", err) } fus.AddTx(cbTx, 1) node := &blockNode{height: 2} //Fake node diff --git a/blockdag/validate.go b/blockdag/validate.go index 469894929..bf2b30d78 100644 --- a/blockdag/validate.go +++ b/blockdag/validate.go @@ -187,7 +187,7 @@ func CheckTransactionSanity(tx *util.Tx) error { // A transaction must not exceed the maximum allowed block payload when // serialized. - serializedTxSize := tx.MsgTx().SerializeSize() + serializedTxSize := msgTx.SerializeSize() if serializedTxSize > wire.MaxBlockPayload { str := fmt.Sprintf("serialized transaction is too big - got "+ "%d, max %d", serializedTxSize, wire.MaxBlockPayload) diff --git a/mempool/mempool.go b/mempool/mempool.go index 85a8e5bff..9ad5ceab3 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -657,6 +657,24 @@ func (mp *TxPool) maybeAcceptTransaction(tx *util.Tx, isNew, rateLimit, rejectDu return nil, nil, err } + // Check that transaction does not overuse GAS + msgTx := tx.MsgTx() + if msgTx.SubNetworkID == 0 { + return nil, nil, txRuleError(wire.RejectInvalid, "Subnetwork 0 is not permited in transaction") + } else if msgTx.SubNetworkID != wire.SubNetworkDAGCoin { + gasLimit, err := mp.cfg.DAG.GasLimit(msgTx.SubNetworkID) + if err != nil { + return nil, nil, err + } + if msgTx.Gas > gasLimit { + str := fmt.Sprintf("transaction wants more gas %v, than allowed %v", + msgTx.Gas, gasLimit) + return nil, nil, dagRuleError(blockdag.RuleError{ + ErrorCode: blockdag.ErrInvalidGas, + Description: str}) + } + } + // A standalone transaction must not be a coinbase transaction. if blockdag.IsCoinBase(tx) { str := fmt.Sprintf("transaction %v is an individual coinbase", diff --git a/mempool/mempool_test.go b/mempool/mempool_test.go index b631cc9e0..1756e765d 100644 --- a/mempool/mempool_test.go +++ b/mempool/mempool_test.go @@ -155,11 +155,11 @@ func (p *poolHarness) CreateCoinbaseTx(blockHeight int32, numOutputs uint32) (*u return util.NewTx(tx), nil } -// CreateSignedTx creates a new signed transaction that consumes the provided +// CreateSignedTxForSubnetwork creates a new signed transaction that consumes the provided // inputs and generates the provided number of outputs by evenly splitting the // total input amount. All outputs will be to the payment script associated // with the harness and all inputs are assumed to do the same. -func (p *poolHarness) CreateSignedTx(inputs []spendableOutpoint, numOutputs uint32) (*util.Tx, error) { +func (p *poolHarness) CreateSignedTxForSubnetwork(inputs []spendableOutpoint, numOutputs uint32, subnetworkID uint64, gas uint64) (*util.Tx, error) { // Calculate the total input amount and split it amongst the requested // number of outputs. var totalInput util.Amount @@ -170,6 +170,8 @@ func (p *poolHarness) CreateSignedTx(inputs []spendableOutpoint, numOutputs uint remainder := uint64(totalInput) - amountPerOutput*uint64(numOutputs) tx := wire.NewMsgTx(wire.TxVersion) + tx.SubNetworkID = subnetworkID + tx.Gas = gas for _, input := range inputs { tx.AddTxIn(&wire.TxIn{ PreviousOutPoint: input.outPoint, @@ -203,6 +205,14 @@ func (p *poolHarness) CreateSignedTx(inputs []spendableOutpoint, numOutputs uint return util.NewTx(tx), nil } +// CreateSignedTx creates a new signed transaction that consumes the provided +// inputs and generates the provided number of outputs by evenly splitting the +// total input amount. All outputs will be to the payment script associated +// with the harness and all inputs are assumed to do the same. +func (p *poolHarness) CreateSignedTx(inputs []spendableOutpoint, numOutputs uint32) (*util.Tx, error) { + return p.CreateSignedTxForSubnetwork(inputs, numOutputs, wire.SubNetworkDAGCoin, 0) +} + // CreateTxChain creates a chain of zero-fee transactions (each subsequent // transaction spends the entire amount from the previous one) with the first // one spending the provided outpoint. Each transaction spends the entire @@ -248,12 +258,12 @@ func (p *poolHarness) CreateTxChain(firstOutput spendableOutpoint, numTxns uint3 // for testing. Also, the fake chain is populated with the returned spendable // outputs so the caller can easily create new valid transactions which build // off of it. -func newPoolHarness(dagParams *dagconfig.Params, numOutputs uint32, dbName string) (*poolHarness, []spendableOutpoint, error) { +func newPoolHarness(dagParams *dagconfig.Params, numOutputs uint32, dbName string) (*poolHarness, []spendableOutpoint, func(), error) { // Use a hard coded key pair for deterministic results. keyBytes, err := hex.DecodeString("700868df1838811ffbdf918fb482c1f7e" + "ad62db4b97bd7012c23e726485e577d") if err != nil { - return nil, nil, err + return nil, nil, nil, err } signKey, signPub := btcec.PrivKeyFromBytes(btcec.S256(), keyBytes) @@ -262,12 +272,12 @@ func newPoolHarness(dagParams *dagconfig.Params, numOutputs uint32, dbName strin pubKeyBytes := signPub.SerializeCompressed() payPubKeyAddr, err := util.NewAddressPubKey(pubKeyBytes, dagParams.Prefix) if err != nil { - return nil, nil, err + return nil, nil, nil, err } payAddr := payPubKeyAddr.AddressPubKeyHash() pkScript, err := txscript.PayToAddrScript(payAddr) if err != nil { - return nil, nil, err + return nil, nil, nil, err } // Create a new database and chain instance to run tests against. @@ -275,9 +285,13 @@ func newPoolHarness(dagParams *dagconfig.Params, numOutputs uint32, dbName strin DAGParams: &dagconfig.MainNetParams, }) if err != nil { - return nil, nil, fmt.Errorf("Failed to setup DAG instance: %v", err) + return nil, nil, nil, fmt.Errorf("Failed to setup DAG instance: %v", err) } - defer teardownFunc() + defer func() { + if err != nil { + teardownFunc() + } + }() // Create a new fake chain and harness bound to it. chain := &fakeChain{} @@ -317,7 +331,7 @@ func newPoolHarness(dagParams *dagconfig.Params, numOutputs uint32, dbName strin curHeight := harness.chain.BestHeight() coinbase, err := harness.CreateCoinbaseTx(curHeight+1, numOutputs) if err != nil { - return nil, nil, err + return nil, nil, nil, err } harness.txPool.mpUTXOSet.AddTx(coinbase.MsgTx(), curHeight+1) for i := uint32(0); i < numOutputs; i++ { @@ -326,7 +340,7 @@ func newPoolHarness(dagParams *dagconfig.Params, numOutputs uint32, dbName strin harness.chain.SetHeight(int32(dagParams.CoinbaseMaturity) + curHeight) harness.chain.SetMedianTimePast(time.Now()) - return &harness, outpoints, nil + return &harness, outpoints, teardownFunc, nil } // testContext houses a test-related state that is useful to pass to helper @@ -434,10 +448,11 @@ func (p *poolHarness) createTx(outpoint spendableOutpoint, fee uint64, numOutput } func TestProcessTransaction(t *testing.T) { - harness, spendableOuts, err := newPoolHarness(&dagconfig.MainNetParams, 6, "TestProcessTransaction") + harness, spendableOuts, teardownFunc, err := newPoolHarness(&dagconfig.MainNetParams, 6, "TestProcessTransaction") if err != nil { t.Fatalf("unable to create test pool: %v", err) } + defer teardownFunc() tc := &testContext{t, harness} //Checks that a transaction cannot be added to the transaction pool if it's already there @@ -593,7 +608,8 @@ func TestProcessTransaction(t *testing.T) { Value: 5000000000, PkScript: p2shPKScript, }}, - LockTime: 0, + LockTime: 0, + SubNetworkID: wire.SubNetworkDAGCoin, }) harness.txPool.mpUTXOSet.AddTx(p2shTx.MsgTx(), curHeight+1) @@ -608,7 +624,8 @@ func TestProcessTransaction(t *testing.T) { Value: 5000000000, PkScript: dummyPkScript, }}, - LockTime: 0, + LockTime: 0, + SubNetworkID: wire.SubNetworkDAGCoin, }) _, err = harness.txPool.ProcessTransaction(nonStdSigScriptTx, true, false, 0) if err == nil { @@ -651,8 +668,9 @@ func TestProcessTransaction(t *testing.T) { SignatureScript: dummySigScript, Sequence: wire.MaxTxInSequenceNum, }}, - TxOut: []*wire.TxOut{}, - LockTime: 0, + TxOut: []*wire.TxOut{}, + LockTime: 0, + SubNetworkID: wire.SubNetworkDAGCoin, }) _, err = harness.txPool.ProcessTransaction(noOutsTx, true, false, 0) if err == nil { @@ -728,7 +746,8 @@ func TestProcessTransaction(t *testing.T) { Value: 1, PkScript: dummyPkScript, }}, - LockTime: 0, + LockTime: 0, + SubNetworkID: wire.SubNetworkDAGCoin, }) _, err = harness.txPool.ProcessTransaction(tx, true, false, 0) fmt.Println(err) @@ -738,14 +757,14 @@ func TestProcessTransaction(t *testing.T) { if code, _ := extractRejectCode(err); code != wire.RejectNonstandard { t.Errorf("Unexpected error code. Expected %v but got %v", wire.RejectNonstandard, code) } - } func TestAddrIndex(t *testing.T) { - harness, spendableOuts, err := newPoolHarness(&dagconfig.MainNetParams, 2, "TestAddrIndex") + harness, spendableOuts, teardownFunc, err := newPoolHarness(&dagconfig.MainNetParams, 2, "TestAddrIndex") if err != nil { t.Fatalf("unable to create test pool: %v", err) } + defer teardownFunc() harness.txPool.cfg.AddrIndex = &indexers.AddrIndex{} enteredAddUnconfirmedTx := false guard := monkey.Patch((*indexers.AddrIndex).AddUnconfirmedTx, func(idx *indexers.AddrIndex, tx *util.Tx, utxoSet blockdag.UTXOSet) { @@ -782,10 +801,11 @@ func TestAddrIndex(t *testing.T) { } func TestFeeEstimatorCfg(t *testing.T) { - harness, spendableOuts, err := newPoolHarness(&dagconfig.MainNetParams, 2, "TestFeeEstimatorCfg") + harness, spendableOuts, teardownFunc, err := newPoolHarness(&dagconfig.MainNetParams, 2, "TestFeeEstimatorCfg") if err != nil { t.Fatalf("unable to create test pool: %v", err) } + defer teardownFunc() harness.txPool.cfg.FeeEstimator = &FeeEstimator{} enteredObserveTransaction := false guard := monkey.Patch((*FeeEstimator).ObserveTransaction, func(ef *FeeEstimator, t *TxDesc) { @@ -808,10 +828,11 @@ func TestFeeEstimatorCfg(t *testing.T) { } func TestDoubleSpends(t *testing.T) { - harness, spendableOuts, err := newPoolHarness(&dagconfig.MainNetParams, 2, "TestDoubleSpends") + harness, spendableOuts, teardownFunc, err := newPoolHarness(&dagconfig.MainNetParams, 2, "TestDoubleSpends") if err != nil { t.Fatalf("unable to create test pool: %v", err) } + defer teardownFunc() tc := &testContext{t, harness} //Add two transactions to the mempool @@ -857,7 +878,11 @@ func TestDoubleSpends(t *testing.T) { //TestFetchTransaction checks that FetchTransaction //returns only transaction from the main pool and not from the orphan pool func TestFetchTransaction(t *testing.T) { - harness, spendableOuts, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestFetchTransaction") + harness, spendableOuts, teardownFunc, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestFetchTransaction") + if err != nil { + t.Fatalf("unable to create test pool: %v", err) + } + defer teardownFunc() tc := &testContext{t, harness} orphanedTx, err := harness.CreateSignedTx([]spendableOutpoint{{ @@ -899,10 +924,11 @@ func TestFetchTransaction(t *testing.T) { // they are all orphans. Finally, it adds the linking transaction and ensures // the entire orphan chain is moved to the transaction pool. func TestSimpleOrphanChain(t *testing.T) { - harness, spendableOuts, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestSimpleOrphanChain") + harness, spendableOuts, teardownFunc, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestSimpleOrphanChain") if err != nil { t.Fatalf("unable to create test pool: %v", err) } + defer teardownFunc() tc := &testContext{t, harness} // Create a chain of transactions rooted with the first spendable output @@ -960,10 +986,11 @@ func TestSimpleOrphanChain(t *testing.T) { // TestOrphanReject ensures that orphans are properly rejected when the allow // orphans flag is not set on ProcessTransaction. func TestOrphanReject(t *testing.T) { - harness, outputs, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestOrphanReject") + harness, outputs, teardownFunc, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestOrphanReject") if err != nil { t.Fatalf("unable to create test pool: %v", err) } + defer teardownFunc() tc := &testContext{t, harness} // Create a chain of transactions rooted with the first spendable output @@ -1014,10 +1041,11 @@ func TestOrphanReject(t *testing.T) { // it will check if we are beyond nextExpireScan, and if so, it will remove // all expired orphan transactions func TestOrphanExpiration(t *testing.T) { - harness, _, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestOrphanExpiration") + harness, _, teardownFunc, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestOrphanExpiration") if err != nil { t.Fatalf("unable to create test pool: %v", err) } + defer teardownFunc() tc := &testContext{t, harness} expiredTx, err := harness.CreateSignedTx([]spendableOutpoint{{ @@ -1058,10 +1086,11 @@ func TestOrphanExpiration(t *testing.T) { //TestMaxOrphanTxSize ensures that a transaction that is //bigger than MaxOrphanTxSize will get rejected func TestMaxOrphanTxSize(t *testing.T) { - harness, _, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestMaxOrphanTxSize") + harness, _, teardownFunc, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestMaxOrphanTxSize") if err != nil { t.Fatalf("unable to create test pool: %v", err) } + defer teardownFunc() tc := &testContext{t, harness} harness.txPool.cfg.Policy.MaxOrphanTxSize = 1 @@ -1085,10 +1114,11 @@ func TestMaxOrphanTxSize(t *testing.T) { } func TestRemoveTransaction(t *testing.T) { - harness, outputs, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestRemoveTransaction") + harness, outputs, teardownFunc, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestRemoveTransaction") if err != nil { t.Fatalf("unable to create test pool: %v", err) } + defer teardownFunc() tc := &testContext{t, harness} chainedTxns, err := harness.CreateTxChain(outputs[0], 5) if err != nil { @@ -1130,10 +1160,11 @@ func TestRemoveTransaction(t *testing.T) { // TestOrphanEviction ensures that exceeding the maximum number of orphans // evicts entries to make room for the new ones. func TestOrphanEviction(t *testing.T) { - harness, outputs, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestOrphanEviction") + harness, outputs, teardownFunc, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestOrphanEviction") if err != nil { t.Fatalf("unable to create test pool: %v", err) } + defer teardownFunc() tc := &testContext{t, harness} // Create a chain of transactions rooted with the first spendable output @@ -1191,10 +1222,11 @@ func TestOrphanEviction(t *testing.T) { // Attempt to remove orphans by tag, // and ensure the state of all other orphans are unaffected. func TestRemoveOrphansByTag(t *testing.T) { - harness, _, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestRemoveOrphansByTag") + harness, _, teardownFunc, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestRemoveOrphansByTag") if err != nil { t.Fatalf("unable to create test pool: %v", err) } + defer teardownFunc() tc := &testContext{t, harness} orphanedTx1, err := harness.CreateSignedTx([]spendableOutpoint{{ @@ -1247,10 +1279,11 @@ func TestRemoveOrphansByTag(t *testing.T) { // redeems it and when there is not. func TestBasicOrphanRemoval(t *testing.T) { const maxOrphans = 4 - harness, spendableOuts, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestBasicOrphanRemoval") + harness, spendableOuts, teardownFunc, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestBasicOrphanRemoval") if err != nil { t.Fatalf("unable to create test pool: %v", err) } + defer teardownFunc() harness.txPool.cfg.Policy.MaxOrphanTxs = maxOrphans tc := &testContext{t, harness} @@ -1320,10 +1353,11 @@ func TestBasicOrphanRemoval(t *testing.T) { // from other orphans) are removed as expected. func TestOrphanChainRemoval(t *testing.T) { const maxOrphans = 10 - harness, spendableOuts, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestOrphanChainRemoval") + harness, spendableOuts, teardownFunc, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestOrphanChainRemoval") if err != nil { t.Fatalf("unable to create test pool: %v", err) } + defer teardownFunc() harness.txPool.cfg.Policy.MaxOrphanTxs = maxOrphans tc := &testContext{t, harness} @@ -1381,10 +1415,11 @@ func TestOrphanChainRemoval(t *testing.T) { // output that is spend by another transaction entering the pool are removed. func TestMultiInputOrphanDoubleSpend(t *testing.T) { const maxOrphans = 4 - harness, outputs, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestMultiInputOrphanDoubleSpend") + harness, outputs, teardownFunc, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestMultiInputOrphanDoubleSpend") if err != nil { t.Fatalf("unable to create test pool: %v", err) } + defer teardownFunc() harness.txPool.cfg.Policy.MaxOrphanTxs = maxOrphans tc := &testContext{t, harness} @@ -1467,10 +1502,11 @@ func TestMultiInputOrphanDoubleSpend(t *testing.T) { // TestCheckSpend tests that CheckSpend returns the expected spends found in // the mempool. func TestCheckSpend(t *testing.T) { - harness, outputs, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestCheckSpend") + harness, outputs, teardownFunc, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestCheckSpend") if err != nil { t.Fatalf("unable to create test pool: %v", err) } + defer teardownFunc() // The mempool is empty, so none of the spendable outputs should have a // spend there. @@ -1532,10 +1568,11 @@ func TestCheckSpend(t *testing.T) { } func TestCount(t *testing.T) { - harness, outputs, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestCount") + harness, outputs, teardownFunc, err := newPoolHarness(&dagconfig.MainNetParams, 1, "TestCount") if err != nil { t.Fatalf("unable to create test pool: %v", err) } + defer teardownFunc() if harness.txPool.Count() != 0 { t.Errorf("TestCount: txPool should be initialized with 0 transactions") } @@ -1641,10 +1678,11 @@ func TestExtractRejectCode(t *testing.T) { // TestHandleNewBlock func TestHandleNewBlock(t *testing.T) { - harness, spendableOuts, err := newPoolHarness(&dagconfig.MainNetParams, 2, "TestHandleNewBlock") + harness, spendableOuts, teardownFunc, err := newPoolHarness(&dagconfig.MainNetParams, 2, "TestHandleNewBlock") if err != nil { t.Fatalf("unable to create test pool: %v", err) } + defer teardownFunc() tc := &testContext{t, harness} // Create parent transaction for orphan transaction below @@ -1790,7 +1828,43 @@ var dummyBlock = wire.MsgBlock{ }, }, }, - LockTime: 0, + LockTime: 0, + SubNetworkID: wire.SubNetworkDAGCoin, }, }, } + +func TestTransactionGas(t *testing.T) { + harness, spendableOuts, teardownFunc, err := newPoolHarness(&dagconfig.MainNetParams, 6, "TestTransactionGas") + if err != nil { + t.Fatalf("unable to create test pool: %v", err) + } + defer teardownFunc() + // tc := &testContext{t, harness} + + const gasLimit = 10000 + subnetworkID, err := blockdag.RegisterSubnetworkForTest(harness.txPool.cfg.DAG, gasLimit) + if err != nil { + t.Fatalf("unable to register network: %v", err) + } + + // Create valid transaction + tx, err := harness.CreateSignedTxForSubnetwork(spendableOuts[:1], 1, subnetworkID, gasLimit) + if err != nil { + t.Fatalf("unable to create transaction: %v", err) + } + _, err = harness.txPool.ProcessTransaction(tx, true, false, 0) + if err != nil { + t.Errorf("ProcessTransaction: unexpected error: %v", err) + } + + // Create invalid transaction + tx, err = harness.CreateSignedTxForSubnetwork(spendableOuts[1:], 1, subnetworkID, gasLimit+1) + if err != nil { + t.Fatalf("unable to create transaction: %v", err) + } + _, err = harness.txPool.ProcessTransaction(tx, true, false, 0) + if err == nil { + t.Error("ProcessTransaction did not return error, expecting ErrInvalidGas") + } +} diff --git a/mining/mining.go b/mining/mining.go index 8ff4e1172..f181ae100 100644 --- a/mining/mining.go +++ b/mining/mining.go @@ -482,7 +482,7 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe prioItem := heap.Pop(priorityQueue).(*txPrioItem) tx := prioItem.tx - if tx.MsgTx().SubNetworkID > wire.SubNetworkDAGCoin { + if tx.MsgTx().SubNetworkID != wire.SubNetworkDAGCoin { subnetwork := tx.MsgTx().SubNetworkID gasUsage, ok := gasUsageMap[subnetwork] if !ok {