mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-10-13 16:49:24 +00:00
[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:
parent
41d1a08365
commit
e76ce06acd
@ -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
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -57,6 +57,7 @@ func TestErrorCodeStringer(t *testing.T) {
|
||||
{ErrWithDiff, "ErrWithDiff"},
|
||||
{ErrFinality, "ErrFinality"},
|
||||
{ErrTransactionsNotSorted, "ErrTransactionsNotSorted"},
|
||||
{ErrInvalidGas, "ErrInvalidGas"},
|
||||
{0xffff, "Unknown ErrorCode (65535)"},
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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",
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user