[DEV-337] enusre transaction not overuse gas (#152)

* [DEV-312] Take in account subnetwork's GAS limit, when adding
transactions to block. Try to do that optimally.

* [DEV-312] Fixed GAS overusage calculation

* [DEV-337] Make sure that a transaction that uses more gas than the total allowed for sub-network

* [DEV-337] Moved transaction GAS check to mempool

* [DEV-337] Added Unit test for gas usage in transaction

* [DEV-337] Fixed build

* [DEV-337] Fixed tests stuff

* [DEV-337] Removed TODO comment
This commit is contained in:
Evgeny Khirin 2019-01-09 15:54:48 +02:00 committed by stasatdaglabs
parent 41d1a08365
commit e76ce06acd
10 changed files with 281 additions and 169 deletions

View File

@ -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
}

View File

@ -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.

View File

@ -57,6 +57,7 @@ func TestErrorCodeStringer(t *testing.T) {
{ErrWithDiff, "ErrWithDiff"},
{ErrFinality, "ErrFinality"},
{ErrTransactionsNotSorted, "ErrTransactionsNotSorted"},
{ErrInvalidGas, "ErrInvalidGas"},
{0xffff, "Unknown ErrorCode (65535)"},
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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

View File

@ -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)

View File

@ -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",

View File

@ -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")
}
}

View File

@ -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 {