Skip to content

Commit 4b7dbe4

Browse files
committed
integration: add comprehensive tests for v3 transaction support
In this commit, we introduce a comprehensive integration test suite that validates v3 transaction handling across the full stack, from mempool acceptance through mining into blocks. These tests ensure that v3 transactions work correctly in real-world scenarios using the RPC test harness. The test suite covers multiple critical scenarios to ensure robust v3 transaction support. TestV3TransactionPolicy validates that transactions with versions 1, 2, and 3 are accepted while version 4 is properly rejected, confirming our policy enforcement works as intended. The test also verifies that accepted v3 transactions can be successfully mined into blocks, demonstrating end-to-end functionality. TestV3TransactionRPCSubmission specifically tests the RPC pathway, ensuring that v3 transactions created with the WithTxVersion option can be submitted through SendOutputs and properly confirmed in blocks. This validates that the functional options we added to the test infrastructure work correctly in practice. TestV3TransactionChaining goes further by testing that outputs from v3 transactions can be spent by subsequent transactions, confirming that the UTXO management and transaction validation logic properly handles the new version throughout the transaction lifecycle.
1 parent 88a8303 commit 4b7dbe4

File tree

1 file changed

+226
-0
lines changed

1 file changed

+226
-0
lines changed

integration/v3_transaction_test.go

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
//go:build rpctest
2+
// +build rpctest
3+
4+
package integration
5+
6+
import (
7+
"testing"
8+
9+
"github.com/btcsuite/btcd/chaincfg"
10+
"github.com/btcsuite/btcd/integration/rpctest"
11+
"github.com/btcsuite/btcd/txscript"
12+
"github.com/btcsuite/btcd/wire"
13+
"github.com/stretchr/testify/require"
14+
)
15+
16+
// TestV3TransactionPolicy tests that v3 transactions are accepted by the
17+
// mempool and can be mined into blocks.
18+
func TestV3TransactionPolicy(t *testing.T) {
19+
t.Parallel()
20+
21+
// Setup the test harness, and initialize the harness with mature
22+
// coinbase outputs for spending.
23+
btcdCfg := []string{"--rejectnonstd", "--debuglevel=debug"}
24+
r, err := rpctest.New(&chaincfg.SimNetParams, nil, btcdCfg, "")
25+
require.NoError(t, err)
26+
27+
require.NoError(t, r.SetUp(true, 100))
28+
t.Cleanup(func() {
29+
require.NoError(t, r.TearDown())
30+
})
31+
32+
// Make sure that normal v1 transactions are still accepted.
33+
t.Run("v1 transaction accepted", func(t *testing.T) {
34+
testVersionedTransaction(t, r, 1, true)
35+
})
36+
37+
// Make sure that v2 transactions are still accepted.
38+
t.Run("v2 transaction accepted", func(t *testing.T) {
39+
testVersionedTransaction(t, r, 2, true)
40+
})
41+
42+
// Ensure that v3 transactions are accepted.
43+
t.Run("v3 transaction accepted", func(t *testing.T) {
44+
testVersionedTransaction(t, r, 3, true)
45+
})
46+
47+
// Ensure that v4 transactions are rejected.
48+
t.Run("v4 transaction rejected", func(t *testing.T) {
49+
testVersionedTransaction(t, r, 4, false)
50+
})
51+
52+
// Test that v3 transactions can be mined.
53+
t.Run("v3 transaction mined in block", func(t *testing.T) {
54+
testV3TransactionMining(t, r)
55+
})
56+
}
57+
58+
// testVersionedTransaction creates a transaction with the specified version
59+
// and tests whether it's accepted by the mempool.
60+
func testVersionedTransaction(t *testing.T, r *rpctest.Harness, version int32,
61+
shouldBeAccepted bool) {
62+
63+
addr, err := r.NewAddress()
64+
require.NoError(t, err)
65+
66+
script, err := txscript.PayToAddrScript(addr)
67+
require.NoError(t, err)
68+
69+
output := &wire.TxOut{
70+
PkScript: script,
71+
Value: 50000000,
72+
}
73+
74+
tx, err := r.CreateTransaction(
75+
[]*wire.TxOut{output}, 10, true, rpctest.WithTxVersion(version),
76+
)
77+
require.NoError(t, err)
78+
require.Equal(t, version, tx.Version)
79+
80+
// Test mempool acceptance without submission.
81+
results, err := r.Client.TestMempoolAccept([]*wire.MsgTx{tx}, 0)
82+
require.NoError(t, err)
83+
require.Len(t, results, 1)
84+
85+
if shouldBeAccepted {
86+
require.True(
87+
t, results[0].Allowed,
88+
"v%d transaction should be accepted, but got: %s",
89+
version, results[0].RejectReason,
90+
)
91+
92+
// Try to actually submit the transaction.
93+
txHash, err := r.Client.SendRawTransaction(tx, true)
94+
require.NoError(t, err)
95+
require.NotNil(t, txHash)
96+
97+
// Verify it's in the mempool.
98+
mempool, err := r.Client.GetRawMempool()
99+
require.NoError(t, err)
100+
require.Contains(t, mempool, txHash)
101+
102+
return
103+
}
104+
105+
require.False(
106+
t, results[0].Allowed,
107+
"v%d transaction should be rejected", version,
108+
)
109+
require.Contains(
110+
t, results[0].RejectReason, "version",
111+
"rejection reason should mention version",
112+
)
113+
}
114+
115+
// testV3TransactionMining tests that v3 transactions can be successfully mined
116+
// into blocks.
117+
func testV3TransactionMining(t *testing.T, r *rpctest.Harness) {
118+
// Create a new random address to send to.
119+
addr, err := r.NewAddress()
120+
require.NoError(t, err)
121+
script, err := txscript.PayToAddrScript(addr)
122+
require.NoError(t, err)
123+
124+
output := &wire.TxOut{
125+
PkScript: script,
126+
Value: 100000000,
127+
}
128+
129+
// Create a v3 transaction using the WithTxVersion option.
130+
v3Tx, err := r.CreateTransaction(
131+
[]*wire.TxOut{output}, 10, true,
132+
rpctest.WithTxVersion(3),
133+
)
134+
require.NoError(t, err)
135+
require.Equal(t, int32(3), v3Tx.Version)
136+
137+
// Submit the v3 transaction to the mempool, this should work np.
138+
v3TxHash, err := r.Client.SendRawTransaction(v3Tx, true)
139+
require.NoError(t, err)
140+
require.NotNil(t, v3TxHash)
141+
142+
// Verify it's in the mempool.
143+
mempool, err := r.Client.GetRawMempool()
144+
require.NoError(t, err)
145+
require.Contains(t, mempool, v3TxHash)
146+
147+
blocks, err := r.Client.Generate(1)
148+
require.NoError(t, err)
149+
require.Len(t, blocks, 1)
150+
151+
block, err := r.Client.GetBlock(blocks[0])
152+
require.NoError(t, err)
153+
154+
// Verify the v3 transaction is included in the block.
155+
found := false
156+
for _, tx := range block.Transactions {
157+
txHash := tx.TxHash()
158+
if txHash.IsEqual(v3TxHash) {
159+
found = true
160+
161+
// Verify it's still a v3 transaction in the block.
162+
require.Equal(t, int32(3), tx.Version)
163+
break
164+
}
165+
}
166+
require.True(
167+
t, found, "v3 transaction should be included in the mined "+
168+
"block",
169+
)
170+
171+
// Verify the transaction is no longer in the mempool.
172+
mempool, err = r.Client.GetRawMempool()
173+
require.NoError(t, err)
174+
require.NotContains(t, mempool, v3TxHash)
175+
}
176+
177+
// TestV3TransactionRPCSubmission tests v3 transaction submission via RPC.
178+
func TestV3TransactionRPCSubmission(t *testing.T) {
179+
t.Parallel()
180+
181+
// Setup the test harness.
182+
btcdCfg := []string{"--rejectnonstd", "--debuglevel=debug"}
183+
r, err := rpctest.New(&chaincfg.SimNetParams, nil, btcdCfg, "")
184+
require.NoError(t, err)
185+
186+
// Initialize the harness with mature coinbase outputs for spending.
187+
require.NoError(t, r.SetUp(true, 100))
188+
t.Cleanup(func() {
189+
require.NoError(t, r.TearDown())
190+
})
191+
192+
t.Run("v3 transaction via SendOutputs", func(t *testing.T) {
193+
addr, err := r.NewAddress()
194+
require.NoError(t, err)
195+
script, err := txscript.PayToAddrScript(addr)
196+
require.NoError(t, err)
197+
198+
output := &wire.TxOut{
199+
PkScript: script,
200+
Value: 25000000,
201+
}
202+
203+
// Send outputs with v3 transaction version.
204+
txHash, err := r.SendOutputs(
205+
[]*wire.TxOut{output}, 10, rpctest.WithTxVersion(3),
206+
)
207+
require.NoError(t, err)
208+
require.NotNil(t, txHash)
209+
210+
// Get the transaction from mempool and verify its version.
211+
tx, err := r.Client.GetRawTransaction(txHash)
212+
require.NoError(t, err)
213+
require.Equal(t, int32(3), tx.MsgTx().Version)
214+
215+
// Mine a block to confirm, then verify transaction is no
216+
// longer in mempool (meaning it was mined).
217+
blocks, err := r.Client.Generate(1)
218+
require.NoError(t, err)
219+
require.Len(t, blocks, 1)
220+
221+
mempool, err := r.Client.GetRawMempool()
222+
require.NoError(t, err)
223+
require.NotContains(t, mempool, txHash)
224+
})
225+
}
226+

0 commit comments

Comments
 (0)