mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-09 07:36:43 +00:00
[NOD-909] Add tests for double spends (#694)
* [NOD-909] Add tests for double spends * [NOD-909] Add prepareAndProcessBlock that gets parent hashes and transactions as argument * [NOD-909] Use PrepareAndProcessBlockForTest where possible * [NOD-909] Use more meaningful names * [NOD-909] Change a comment * [NOD-909] Fix comment * [NOD-909] Fix comment
This commit is contained in:
parent
fe91b4c878
commit
d015286f65
@ -172,28 +172,12 @@ func checkRuleError(gotErr, wantErr error) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareAndProcessBlock(t *testing.T, dag *BlockDAG, parents ...*wire.MsgBlock) *wire.MsgBlock {
|
func prepareAndProcessBlockByParentMsgBlocks(t *testing.T, dag *BlockDAG, parents ...*wire.MsgBlock) *wire.MsgBlock {
|
||||||
parentHashes := make([]*daghash.Hash, len(parents))
|
parentHashes := make([]*daghash.Hash, len(parents))
|
||||||
for i, parent := range parents {
|
for i, parent := range parents {
|
||||||
parentHashes[i] = parent.BlockHash()
|
parentHashes[i] = parent.BlockHash()
|
||||||
}
|
}
|
||||||
daghash.Sort(parentHashes)
|
return PrepareAndProcessBlockForTest(t, dag, parentHashes, nil)
|
||||||
block, err := PrepareBlockForTest(dag, parentHashes, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error in PrepareBlockForTest: %s", err)
|
|
||||||
}
|
|
||||||
utilBlock := util.NewBlock(block)
|
|
||||||
isOrphan, isDelayed, err := dag.ProcessBlock(utilBlock, BFNoPoWCheck)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error in ProcessBlock: %s", err)
|
|
||||||
}
|
|
||||||
if isDelayed {
|
|
||||||
t.Fatalf("block is too far in the future")
|
|
||||||
}
|
|
||||||
if isOrphan {
|
|
||||||
t.Fatalf("block was unexpectedly orphan")
|
|
||||||
}
|
|
||||||
return block
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func nodeByMsgBlock(t *testing.T, dag *BlockDAG, block *wire.MsgBlock) *blockNode {
|
func nodeByMsgBlock(t *testing.T, dag *BlockDAG, block *wire.MsgBlock) *blockNode {
|
||||||
|
@ -688,7 +688,7 @@ func TestConfirmations(t *testing.T) {
|
|||||||
chainBlocks := make([]*wire.MsgBlock, 5)
|
chainBlocks := make([]*wire.MsgBlock, 5)
|
||||||
chainBlocks[0] = dag.dagParams.GenesisBlock
|
chainBlocks[0] = dag.dagParams.GenesisBlock
|
||||||
for i := uint32(1); i < 5; i++ {
|
for i := uint32(1); i < 5; i++ {
|
||||||
chainBlocks[i] = prepareAndProcessBlock(t, dag, chainBlocks[i-1])
|
chainBlocks[i] = prepareAndProcessBlockByParentMsgBlocks(t, dag, chainBlocks[i-1])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that each one of the chain blocks has the expected confirmations number
|
// Make sure that each one of the chain blocks has the expected confirmations number
|
||||||
@ -707,8 +707,8 @@ func TestConfirmations(t *testing.T) {
|
|||||||
|
|
||||||
branchingBlocks := make([]*wire.MsgBlock, 2)
|
branchingBlocks := make([]*wire.MsgBlock, 2)
|
||||||
// Add two branching blocks
|
// Add two branching blocks
|
||||||
branchingBlocks[0] = prepareAndProcessBlock(t, dag, chainBlocks[1])
|
branchingBlocks[0] = prepareAndProcessBlockByParentMsgBlocks(t, dag, chainBlocks[1])
|
||||||
branchingBlocks[1] = prepareAndProcessBlock(t, dag, branchingBlocks[0])
|
branchingBlocks[1] = prepareAndProcessBlockByParentMsgBlocks(t, dag, branchingBlocks[0])
|
||||||
|
|
||||||
// Check that the genesis has a confirmations number == len(chainBlocks)
|
// Check that the genesis has a confirmations number == len(chainBlocks)
|
||||||
genesisConfirmations, err = dag.blockConfirmations(dag.genesis)
|
genesisConfirmations, err = dag.blockConfirmations(dag.genesis)
|
||||||
@ -738,7 +738,7 @@ func TestConfirmations(t *testing.T) {
|
|||||||
// Generate 100 blocks to force the "main" chain to become red
|
// Generate 100 blocks to force the "main" chain to become red
|
||||||
branchingChainTip := branchingBlocks[1]
|
branchingChainTip := branchingBlocks[1]
|
||||||
for i := uint32(0); i < 100; i++ {
|
for i := uint32(0); i < 100; i++ {
|
||||||
nextBranchingChainTip := prepareAndProcessBlock(t, dag, branchingChainTip)
|
nextBranchingChainTip := prepareAndProcessBlockByParentMsgBlocks(t, dag, branchingChainTip)
|
||||||
branchingChainTip = nextBranchingChainTip
|
branchingChainTip = nextBranchingChainTip
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -797,7 +797,7 @@ func TestAcceptingBlock(t *testing.T) {
|
|||||||
chainBlocks := make([]*wire.MsgBlock, numChainBlocks)
|
chainBlocks := make([]*wire.MsgBlock, numChainBlocks)
|
||||||
chainBlocks[0] = dag.dagParams.GenesisBlock
|
chainBlocks[0] = dag.dagParams.GenesisBlock
|
||||||
for i := uint32(1); i <= numChainBlocks-1; i++ {
|
for i := uint32(1); i <= numChainBlocks-1; i++ {
|
||||||
chainBlocks[i] = prepareAndProcessBlock(t, dag, chainBlocks[i-1])
|
chainBlocks[i] = prepareAndProcessBlockByParentMsgBlocks(t, dag, chainBlocks[i-1])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that each chain block (including the genesis) is accepted by its child
|
// Make sure that each chain block (including the genesis) is accepted by its child
|
||||||
@ -825,7 +825,7 @@ func TestAcceptingBlock(t *testing.T) {
|
|||||||
|
|
||||||
// Generate a chain tip that will be in the anticone of the selected tip and
|
// Generate a chain tip that will be in the anticone of the selected tip and
|
||||||
// in dag.virtual.blues.
|
// in dag.virtual.blues.
|
||||||
branchingChainTip := prepareAndProcessBlock(t, dag, chainBlocks[len(chainBlocks)-3])
|
branchingChainTip := prepareAndProcessBlockByParentMsgBlocks(t, dag, chainBlocks[len(chainBlocks)-3])
|
||||||
|
|
||||||
// Make sure that branchingChainTip is not in the selected parent chain
|
// Make sure that branchingChainTip is not in the selected parent chain
|
||||||
isBranchingChainTipInSelectedParentChain, err := dag.IsInSelectedParentChain(branchingChainTip.BlockHash())
|
isBranchingChainTipInSelectedParentChain, err := dag.IsInSelectedParentChain(branchingChainTip.BlockHash())
|
||||||
@ -863,7 +863,7 @@ func TestAcceptingBlock(t *testing.T) {
|
|||||||
intersectionBlock := chainBlocks[1]
|
intersectionBlock := chainBlocks[1]
|
||||||
sideChainTip := intersectionBlock
|
sideChainTip := intersectionBlock
|
||||||
for i := 0; i < len(chainBlocks)-3; i++ {
|
for i := 0; i < len(chainBlocks)-3; i++ {
|
||||||
sideChainTip = prepareAndProcessBlock(t, dag, sideChainTip)
|
sideChainTip = prepareAndProcessBlockByParentMsgBlocks(t, dag, sideChainTip)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that the accepting block of the parent of the branching block didn't change
|
// Make sure that the accepting block of the parent of the branching block didn't change
|
||||||
@ -879,7 +879,7 @@ func TestAcceptingBlock(t *testing.T) {
|
|||||||
|
|
||||||
// Make sure that a block that is found in the red set of the selected tip
|
// Make sure that a block that is found in the red set of the selected tip
|
||||||
// doesn't have an accepting block
|
// doesn't have an accepting block
|
||||||
prepareAndProcessBlock(t, dag, sideChainTip, chainBlocks[len(chainBlocks)-1])
|
prepareAndProcessBlockByParentMsgBlocks(t, dag, sideChainTip, chainBlocks[len(chainBlocks)-1])
|
||||||
|
|
||||||
sideChainTipAcceptingBlock, err := acceptingBlockByMsgBlock(sideChainTip)
|
sideChainTipAcceptingBlock, err := acceptingBlockByMsgBlock(sideChainTip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1117,3 +1117,132 @@ func TestIsDAGCurrentMaxDiff(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDoubleSpends(t *testing.T) {
|
||||||
|
params := dagconfig.SimnetParams
|
||||||
|
params.BlockCoinbaseMaturity = 0
|
||||||
|
// Create a new database and dag instance to run tests against.
|
||||||
|
dag, teardownFunc, err := DAGSetup("TestDoubleSpends", true, Config{
|
||||||
|
DAGParams: ¶ms,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to setup dag instance: %v", err)
|
||||||
|
}
|
||||||
|
defer teardownFunc()
|
||||||
|
|
||||||
|
fundingBlock := PrepareAndProcessBlockForTest(t, dag, []*daghash.Hash{params.GenesisHash}, nil)
|
||||||
|
cbTx := fundingBlock.Transactions[0]
|
||||||
|
|
||||||
|
signatureScript, err := txscript.PayToScriptHashSignatureScript(OpTrueScript, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to build signature script: %s", err)
|
||||||
|
}
|
||||||
|
txIn := &wire.TxIn{
|
||||||
|
PreviousOutpoint: wire.Outpoint{TxID: *cbTx.TxID(), Index: 0},
|
||||||
|
SignatureScript: signatureScript,
|
||||||
|
Sequence: wire.MaxTxInSequenceNum,
|
||||||
|
}
|
||||||
|
txOut := &wire.TxOut{
|
||||||
|
ScriptPubKey: OpTrueScript,
|
||||||
|
Value: uint64(1),
|
||||||
|
}
|
||||||
|
tx1 := wire.NewNativeMsgTx(wire.TxVersion, []*wire.TxIn{txIn}, []*wire.TxOut{txOut})
|
||||||
|
|
||||||
|
doubleSpendTxOut := &wire.TxOut{
|
||||||
|
ScriptPubKey: OpTrueScript,
|
||||||
|
Value: uint64(2),
|
||||||
|
}
|
||||||
|
doubleSpendTx1 := wire.NewNativeMsgTx(wire.TxVersion, []*wire.TxIn{txIn}, []*wire.TxOut{doubleSpendTxOut})
|
||||||
|
|
||||||
|
blockWithTx1 := PrepareAndProcessBlockForTest(t, dag, []*daghash.Hash{fundingBlock.BlockHash()}, []*wire.MsgTx{tx1})
|
||||||
|
|
||||||
|
// Check that a block will be rejected if it has a transaction that already exists in its past.
|
||||||
|
anotherBlockWithTx1, err := PrepareBlockForTest(dag, []*daghash.Hash{blockWithTx1.BlockHash()}, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("PrepareBlockForTest: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manually add tx1.
|
||||||
|
anotherBlockWithTx1.Transactions = append(anotherBlockWithTx1.Transactions, tx1)
|
||||||
|
anotherBlockWithTx1UtilTxs := make([]*util.Tx, len(anotherBlockWithTx1.Transactions))
|
||||||
|
for i, tx := range anotherBlockWithTx1.Transactions {
|
||||||
|
anotherBlockWithTx1UtilTxs[i] = util.NewTx(tx)
|
||||||
|
}
|
||||||
|
anotherBlockWithTx1.Header.HashMerkleRoot = BuildHashMerkleTreeStore(anotherBlockWithTx1UtilTxs).Root()
|
||||||
|
|
||||||
|
isOrphan, isDelayed, err := dag.ProcessBlock(util.NewBlock(anotherBlockWithTx1), BFNoPoWCheck)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("ProcessBlock expected an error")
|
||||||
|
} else {
|
||||||
|
var ruleErr RuleError
|
||||||
|
if ok := errors.As(err, &ruleErr); ok {
|
||||||
|
if ruleErr.ErrorCode != ErrOverwriteTx {
|
||||||
|
t.Errorf("ProcessBlock expected an %v error code but got %v", ErrOverwriteTx, ruleErr.ErrorCode)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("ProcessBlock expected a blockdag.RuleError but got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isDelayed {
|
||||||
|
t.Fatalf("ProcessBlock: anotherBlockWithTx1 " +
|
||||||
|
"is too far in the future")
|
||||||
|
}
|
||||||
|
if isOrphan {
|
||||||
|
t.Fatalf("ProcessBlock: anotherBlockWithTx1 got unexpectedly orphaned")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that a block will be rejected if it has a transaction that double spends
|
||||||
|
// a transaction from its past.
|
||||||
|
blockWithDoubleSpendForTx1, err := PrepareBlockForTest(dag, []*daghash.Hash{blockWithTx1.BlockHash()}, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("PrepareBlockForTest: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manually add a transaction that double spends the block past.
|
||||||
|
blockWithDoubleSpendForTx1.Transactions = append(blockWithDoubleSpendForTx1.Transactions, doubleSpendTx1)
|
||||||
|
blockWithDoubleSpendForTx1UtilTxs := make([]*util.Tx, len(blockWithDoubleSpendForTx1.Transactions))
|
||||||
|
for i, tx := range blockWithDoubleSpendForTx1.Transactions {
|
||||||
|
blockWithDoubleSpendForTx1UtilTxs[i] = util.NewTx(tx)
|
||||||
|
}
|
||||||
|
blockWithDoubleSpendForTx1.Header.HashMerkleRoot = BuildHashMerkleTreeStore(blockWithDoubleSpendForTx1UtilTxs).Root()
|
||||||
|
|
||||||
|
isOrphan, isDelayed, err = dag.ProcessBlock(util.NewBlock(blockWithDoubleSpendForTx1), BFNoPoWCheck)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("ProcessBlock expected an error")
|
||||||
|
} else {
|
||||||
|
var ruleErr RuleError
|
||||||
|
if ok := errors.As(err, &ruleErr); ok {
|
||||||
|
if ruleErr.ErrorCode != ErrMissingTxOut {
|
||||||
|
t.Errorf("ProcessBlock expected an %v error code but got %v", ErrMissingTxOut, ruleErr.ErrorCode)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("ProcessBlock expected a blockdag.RuleError but got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isDelayed {
|
||||||
|
t.Fatalf("ProcessBlock: blockWithDoubleSpendForTx1 " +
|
||||||
|
"is too far in the future")
|
||||||
|
}
|
||||||
|
if isOrphan {
|
||||||
|
t.Fatalf("ProcessBlock: blockWithDoubleSpendForTx1 got unexpectedly orphaned")
|
||||||
|
}
|
||||||
|
|
||||||
|
blockInAnticoneOfBlockWithTx1, err := PrepareBlockForTest(dag, []*daghash.Hash{fundingBlock.BlockHash()}, []*wire.MsgTx{doubleSpendTx1})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("PrepareBlockForTest: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that a block will not get rejected if it has a transaction that double spends
|
||||||
|
// a transaction from its anticone.
|
||||||
|
isOrphan, isDelayed, err = dag.ProcessBlock(util.NewBlock(blockInAnticoneOfBlockWithTx1), BFNoPoWCheck)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ProcessBlock: %v", err)
|
||||||
|
}
|
||||||
|
if isDelayed {
|
||||||
|
t.Fatalf("ProcessBlock: blockInAnticoneOfBlockWithTx1 " +
|
||||||
|
"is too far in the future")
|
||||||
|
}
|
||||||
|
if isOrphan {
|
||||||
|
t.Fatalf("ProcessBlock: blockInAnticoneOfBlockWithTx1 got unexpectedly orphaned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -293,14 +293,14 @@ func TestBlueAnticoneSizeErrors(t *testing.T) {
|
|||||||
// Prepare a block chain with size K beginning with the genesis block
|
// Prepare a block chain with size K beginning with the genesis block
|
||||||
currentBlockA := dag.dagParams.GenesisBlock
|
currentBlockA := dag.dagParams.GenesisBlock
|
||||||
for i := dagconfig.KType(0); i < dag.dagParams.K; i++ {
|
for i := dagconfig.KType(0); i < dag.dagParams.K; i++ {
|
||||||
newBlock := prepareAndProcessBlock(t, dag, currentBlockA)
|
newBlock := prepareAndProcessBlockByParentMsgBlocks(t, dag, currentBlockA)
|
||||||
currentBlockA = newBlock
|
currentBlockA = newBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare another block chain with size K beginning with the genesis block
|
// Prepare another block chain with size K beginning with the genesis block
|
||||||
currentBlockB := dag.dagParams.GenesisBlock
|
currentBlockB := dag.dagParams.GenesisBlock
|
||||||
for i := dagconfig.KType(0); i < dag.dagParams.K; i++ {
|
for i := dagconfig.KType(0); i < dag.dagParams.K; i++ {
|
||||||
newBlock := prepareAndProcessBlock(t, dag, currentBlockB)
|
newBlock := prepareAndProcessBlockByParentMsgBlocks(t, dag, currentBlockB)
|
||||||
currentBlockB = newBlock
|
currentBlockB = newBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,11 +332,11 @@ func TestGHOSTDAGErrors(t *testing.T) {
|
|||||||
defer teardownFunc()
|
defer teardownFunc()
|
||||||
|
|
||||||
// Add two child blocks to the genesis
|
// Add two child blocks to the genesis
|
||||||
block1 := prepareAndProcessBlock(t, dag, dag.dagParams.GenesisBlock)
|
block1 := prepareAndProcessBlockByParentMsgBlocks(t, dag, dag.dagParams.GenesisBlock)
|
||||||
block2 := prepareAndProcessBlock(t, dag, dag.dagParams.GenesisBlock)
|
block2 := prepareAndProcessBlockByParentMsgBlocks(t, dag, dag.dagParams.GenesisBlock)
|
||||||
|
|
||||||
// Add a child block to the previous two blocks
|
// Add a child block to the previous two blocks
|
||||||
block3 := prepareAndProcessBlock(t, dag, block1, block2)
|
block3 := prepareAndProcessBlockByParentMsgBlocks(t, dag, block1, block2)
|
||||||
|
|
||||||
// Clear the reachability store
|
// Clear the reachability store
|
||||||
dag.reachabilityStore.loaded = map[daghash.Hash]*reachabilityData{}
|
dag.reachabilityStore.loaded = map[daghash.Hash]*reachabilityData{}
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/util/subnetworkid"
|
"github.com/kaspanet/kaspad/util/subnetworkid"
|
||||||
|
|
||||||
@ -279,6 +280,28 @@ func PrepareBlockForTest(dag *BlockDAG, parentHashes []*daghash.Hash, transactio
|
|||||||
return block, nil
|
return block, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrepareAndProcessBlockForTest prepares a block that points to the given parent
|
||||||
|
// hashes and process it.
|
||||||
|
func PrepareAndProcessBlockForTest(t *testing.T, dag *BlockDAG, parentHashes []*daghash.Hash, transactions []*wire.MsgTx) *wire.MsgBlock {
|
||||||
|
daghash.Sort(parentHashes)
|
||||||
|
block, err := PrepareBlockForTest(dag, parentHashes, transactions)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error in PrepareBlockForTest: %s", err)
|
||||||
|
}
|
||||||
|
utilBlock := util.NewBlock(block)
|
||||||
|
isOrphan, isDelayed, err := dag.ProcessBlock(utilBlock, BFNoPoWCheck)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error in ProcessBlock: %s", err)
|
||||||
|
}
|
||||||
|
if isDelayed {
|
||||||
|
t.Fatalf("block is too far in the future")
|
||||||
|
}
|
||||||
|
if isOrphan {
|
||||||
|
t.Fatalf("block was unexpectedly orphan")
|
||||||
|
}
|
||||||
|
return block
|
||||||
|
}
|
||||||
|
|
||||||
// generateDeterministicExtraNonceForTest returns a unique deterministic extra nonce for coinbase data, in order to create unique coinbase transactions.
|
// generateDeterministicExtraNonceForTest returns a unique deterministic extra nonce for coinbase data, in order to create unique coinbase transactions.
|
||||||
func generateDeterministicExtraNonceForTest() uint64 {
|
func generateDeterministicExtraNonceForTest() uint64 {
|
||||||
extraNonceForTest++
|
extraNonceForTest++
|
||||||
|
@ -570,9 +570,9 @@ func TestValidateParents(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer teardownFunc()
|
defer teardownFunc()
|
||||||
|
|
||||||
a := prepareAndProcessBlock(t, dag, dag.dagParams.GenesisBlock)
|
a := prepareAndProcessBlockByParentMsgBlocks(t, dag, dag.dagParams.GenesisBlock)
|
||||||
b := prepareAndProcessBlock(t, dag, a)
|
b := prepareAndProcessBlockByParentMsgBlocks(t, dag, a)
|
||||||
c := prepareAndProcessBlock(t, dag, dag.dagParams.GenesisBlock)
|
c := prepareAndProcessBlockByParentMsgBlocks(t, dag, dag.dagParams.GenesisBlock)
|
||||||
|
|
||||||
aNode := nodeByMsgBlock(t, dag, a)
|
aNode := nodeByMsgBlock(t, dag, a)
|
||||||
bNode := nodeByMsgBlock(t, dag, b)
|
bNode := nodeByMsgBlock(t, dag, b)
|
||||||
|
@ -795,6 +795,57 @@ func TestDoubleSpends(t *testing.T) {
|
|||||||
testPoolMembership(tc, tx2, false, true, false)
|
testPoolMembership(tc, tx2, false, true, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDoubleSpendsFromDAG(t *testing.T) {
|
||||||
|
tc, spendableOuts, teardownFunc, err := newPoolHarness(t, &dagconfig.SimnetParams, 2, "TestDoubleSpendsFromDAG")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create test pool: %v", err)
|
||||||
|
}
|
||||||
|
defer teardownFunc()
|
||||||
|
harness := tc.harness
|
||||||
|
|
||||||
|
tx, err := harness.createTx(spendableOuts[0], uint64(txRelayFeeForTest), 1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create transaction: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dag := harness.txPool.cfg.DAG
|
||||||
|
blockdag.PrepareAndProcessBlockForTest(t, dag, dag.TipHashes(), []*wire.MsgTx{tx.MsgTx()})
|
||||||
|
|
||||||
|
// Check that a transaction that double spends the DAG UTXO set is orphaned.
|
||||||
|
doubleSpendTx, err := harness.createTx(spendableOuts[0], uint64(txRelayFeeForTest), 2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create transaction: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = harness.txPool.ProcessTransaction(doubleSpendTx, true, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ProcessTransaction: %s", err)
|
||||||
|
}
|
||||||
|
testPoolMembership(tc, doubleSpendTx, true, false, false)
|
||||||
|
|
||||||
|
// If you send a transaction that some of its outputs exist in the DAG UTXO
|
||||||
|
// set, it won't be added to the orphan pool, and will completely get rejected
|
||||||
|
// from the mempool.
|
||||||
|
// This happens because transactions with the same ID as old transactions
|
||||||
|
// are not allowed as long as some of the old transaction outputs exist
|
||||||
|
// in the UTXO.
|
||||||
|
_, err = harness.txPool.ProcessTransaction(tx, true, 0)
|
||||||
|
var ruleErr RuleError
|
||||||
|
if ok := errors.As(err, &ruleErr); ok {
|
||||||
|
var txRuleErr TxRuleError
|
||||||
|
if ok := errors.As(ruleErr.Err, &txRuleErr); ok {
|
||||||
|
if txRuleErr.RejectCode != wire.RejectDuplicate {
|
||||||
|
t.Errorf("ProcessTransaction expected an %v reject code but got %v", wire.RejectDuplicate, txRuleErr.RejectCode)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("ProcessTransaction expected a ruleErr.Err to be a TxRuleError but got %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("ProcessTransaction expected a RuleError but got %v", err)
|
||||||
|
}
|
||||||
|
testPoolMembership(tc, tx, false, false, false)
|
||||||
|
}
|
||||||
|
|
||||||
//TestFetchTransaction checks that FetchTransaction
|
//TestFetchTransaction checks that FetchTransaction
|
||||||
//returns only transaction from the main pool and not from the orphan pool
|
//returns only transaction from the main pool and not from the orphan pool
|
||||||
func TestFetchTransaction(t *testing.T) {
|
func TestFetchTransaction(t *testing.T) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user