4
4
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
5
"""Test mempool acceptance of raw transactions."""
6
6
7
+ from copy import deepcopy
7
8
from decimal import Decimal
8
9
import math
9
10
34
35
assert_equal ,
35
36
assert_raises_rpc_error ,
36
37
)
38
+ from test_framework .wallet import MiniWallet
37
39
38
40
39
41
class MempoolAcceptanceTest (BitcoinTestFramework ):
@@ -44,9 +46,6 @@ def set_test_params(self):
44
46
]] * self .num_nodes
45
47
self .supports_cli = False
46
48
47
- def skip_test_if_missing_module (self ):
48
- self .skip_if_no_wallet ()
49
-
50
49
def check_mempool_result (self , result_expected , * args , ** kwargs ):
51
50
"""Wrapper to check result of testmempoolaccept on node_0's mempool"""
52
51
result_test = self .nodes [0 ].testmempoolaccept (* args , ** kwargs )
@@ -57,12 +56,13 @@ def check_mempool_result(self, result_expected, *args, **kwargs):
57
56
58
57
def run_test (self ):
59
58
node = self .nodes [0 ]
59
+ self .wallet = MiniWallet (node )
60
+ self .wallet .rescan_utxos ()
60
61
61
62
self .log .info ('Start with empty mempool, and 200 blocks' )
62
63
self .mempool_size = 0
63
64
assert_equal (node .getblockcount (), 200 )
64
65
assert_equal (node .getmempoolinfo ()['size' ], self .mempool_size )
65
- coins = node .listunspent ()
66
66
67
67
self .log .info ('Should not accept garbage to testmempoolaccept' )
68
68
assert_raises_rpc_error (- 3 , 'Expected type array, got string' , lambda : node .testmempoolaccept (rawtxs = 'ff00baar' ))
@@ -71,12 +71,12 @@ def run_test(self):
71
71
assert_raises_rpc_error (- 22 , 'TX decode failed' , lambda : node .testmempoolaccept (rawtxs = ['ff00baar' ]))
72
72
73
73
self .log .info ('A transaction already in the blockchain' )
74
- coin = coins . pop () # Pick a random coin(base) to spend
75
- raw_tx_in_block = node . signrawtransactionwithwallet ( node . createrawtransaction (
76
- inputs = [{ 'txid' : coin [ 'txid' ], 'vout' : coin [ 'vout' ]}],
77
- outputs = [{ node . getnewaddress (): 0.3 }, { node . getnewaddress (): 49 }],
78
- ))[ ' hex' ]
79
- txid_in_block = node . sendrawtransaction (hexstring = raw_tx_in_block , maxfeerate = 0 )
74
+ tx = self . wallet . create_self_transfer ()[ 'tx' ] # Pick a random coin(base) to spend
75
+ tx . vout . append ( deepcopy ( tx . vout [ 0 ]))
76
+ tx . vout [ 0 ]. nValue = int ( 0.3 * COIN )
77
+ tx . vout [ 1 ]. nValue = int ( 49 * COIN )
78
+ raw_tx_in_block = tx . serialize (). hex ()
79
+ txid_in_block = self . wallet . sendrawtransaction (from_node = node , tx_hex = raw_tx_in_block , maxfeerate = 0 )
80
80
self .generate (node , 1 )
81
81
self .mempool_size = 0
82
82
self .check_mempool_result (
@@ -86,27 +86,26 @@ def run_test(self):
86
86
87
87
self .log .info ('A transaction not in the mempool' )
88
88
fee = Decimal ('0.000007' )
89
- raw_tx_0 = node .signrawtransactionwithwallet (node .createrawtransaction (
90
- inputs = [{"txid" : txid_in_block , "vout" : 0 , "sequence" : BIP125_SEQUENCE_NUMBER }], # RBF is used later
91
- outputs = [{node .getnewaddress (): Decimal ('0.3' ) - fee }],
92
- ))['hex' ]
93
- tx = tx_from_hex (raw_tx_0 )
89
+ utxo_to_spend = self .wallet .get_utxo (txid = txid_in_block ) # use 0.3 BTC UTXO
90
+ tx = self .wallet .create_self_transfer (utxo_to_spend = utxo_to_spend , sequence = BIP125_SEQUENCE_NUMBER )['tx' ]
91
+ tx .vout [0 ].nValue = int ((Decimal ('0.3' ) - fee ) * COIN )
92
+ raw_tx_0 = tx .serialize ().hex ()
94
93
txid_0 = tx .rehash ()
95
94
self .check_mempool_result (
96
95
result_expected = [{'txid' : txid_0 , 'allowed' : True , 'vsize' : tx .get_vsize (), 'fees' : {'base' : fee }}],
97
96
rawtxs = [raw_tx_0 ],
98
97
)
99
98
100
99
self .log .info ('A final transaction not in the mempool' )
101
- coin = coins .pop () # Pick a random coin(base) to spend
102
100
output_amount = Decimal ('0.025' )
103
- raw_tx_final = node .signrawtransactionwithwallet (node .createrawtransaction (
104
- inputs = [{'txid' : coin ['txid' ], 'vout' : coin ['vout' ], "sequence" : SEQUENCE_FINAL }],
105
- outputs = [{node .getnewaddress (): output_amount }],
101
+ tx = self .wallet .create_self_transfer (
102
+ sequence = SEQUENCE_FINAL ,
106
103
locktime = node .getblockcount () + 2000 , # Can be anything
107
- ))['hex' ]
104
+ )['tx' ]
105
+ tx .vout [0 ].nValue = int (output_amount * COIN )
106
+ raw_tx_final = tx .serialize ().hex ()
108
107
tx = tx_from_hex (raw_tx_final )
109
- fee_expected = coin [ 'amount' ] - output_amount
108
+ fee_expected = Decimal ( '50.0' ) - output_amount
110
109
self .check_mempool_result (
111
110
result_expected = [{'txid' : tx .rehash (), 'allowed' : True , 'vsize' : tx .get_vsize (), 'fees' : {'base' : fee_expected }}],
112
111
rawtxs = [tx .serialize ().hex ()],
@@ -127,8 +126,7 @@ def run_test(self):
127
126
tx = tx_from_hex (raw_tx_0 )
128
127
tx .vout [0 ].nValue -= int (fee * COIN ) # Double the fee
129
128
tx .vin [0 ].nSequence = BIP125_SEQUENCE_NUMBER + 1 # Now, opt out of RBF
130
- raw_tx_0 = node .signrawtransactionwithwallet (tx .serialize ().hex ())['hex' ]
131
- tx = tx_from_hex (raw_tx_0 )
129
+ raw_tx_0 = tx .serialize ().hex ()
132
130
txid_0 = tx .rehash ()
133
131
self .check_mempool_result (
134
132
result_expected = [{'txid' : txid_0 , 'allowed' : True , 'vsize' : tx .get_vsize (), 'fees' : {'base' : (2 * fee )}}],
@@ -141,7 +139,6 @@ def run_test(self):
141
139
# take original raw_tx_0
142
140
tx = tx_from_hex (raw_tx_0 )
143
141
tx .vout [0 ].nValue -= int (4 * fee * COIN ) # Set more fee
144
- # skip re-signing the tx
145
142
self .check_mempool_result (
146
143
result_expected = [{'txid' : tx .rehash (), 'allowed' : False , 'reject-reason' : 'txn-mempool-conflict' }],
147
144
rawtxs = [tx .serialize ().hex ()],
@@ -151,7 +148,6 @@ def run_test(self):
151
148
self .log .info ('A transaction with missing inputs, that never existed' )
152
149
tx = tx_from_hex (raw_tx_0 )
153
150
tx .vin [0 ].prevout = COutPoint (hash = int ('ff' * 32 , 16 ), n = 14 )
154
- # skip re-signing the tx
155
151
self .check_mempool_result (
156
152
result_expected = [{'txid' : tx .rehash (), 'allowed' : False , 'reject-reason' : 'missing-inputs' }],
157
153
rawtxs = [tx .serialize ().hex ()],
@@ -160,17 +156,17 @@ def run_test(self):
160
156
self .log .info ('A transaction with missing inputs, that existed once in the past' )
161
157
tx = tx_from_hex (raw_tx_0 )
162
158
tx .vin [0 ].prevout .n = 1 # Set vout to 1, to spend the other outpoint (49 coins) of the in-chain-tx we want to double spend
163
- raw_tx_1 = node . signrawtransactionwithwallet ( tx .serialize ().hex ())[ 'hex' ]
159
+ raw_tx_1 = tx .serialize ().hex ()
164
160
txid_1 = node .sendrawtransaction (hexstring = raw_tx_1 , maxfeerate = 0 )
165
161
# Now spend both to "clearly hide" the outputs, ie. remove the coins from the utxo set by spending them
166
- raw_tx_spend_both = node . signrawtransactionwithwallet ( node . createrawtransaction (
167
- inputs = [
168
- { 'txid' : txid_0 , 'vout' : 0 },
169
- { 'txid' : txid_1 , 'vout' : 0 },
170
- ],
171
- outputs = [{ node . getnewaddress (): 0.1 }]
172
- ))[ ' hex' ]
173
- txid_spend_both = node . sendrawtransaction (hexstring = raw_tx_spend_both , maxfeerate = 0 )
162
+ tx = self . wallet . create_self_transfer ()[ 'tx' ]
163
+ tx . vin . append ( deepcopy ( tx . vin [ 0 ]))
164
+ tx . wit . vtxinwit . append ( deepcopy ( tx . wit . vtxinwit [ 0 ]))
165
+ tx . vin [ 0 ]. prevout = COutPoint ( hash = int ( txid_0 , 16 ), n = 0 )
166
+ tx . vin [ 1 ]. prevout = COutPoint ( hash = int ( txid_1 , 16 ), n = 0 )
167
+ tx . vout [ 0 ]. nValue = int ( 0.1 * COIN )
168
+ raw_tx_spend_both = tx . serialize (). hex ()
169
+ txid_spend_both = self . wallet . sendrawtransaction (from_node = node , tx_hex = raw_tx_spend_both , maxfeerate = 0 )
174
170
self .generate (node , 1 )
175
171
self .mempool_size = 0
176
172
# Now see if we can add the coins back to the utxo set by sending the exact txs again
@@ -183,12 +179,11 @@ def run_test(self):
183
179
rawtxs = [raw_tx_1 ],
184
180
)
185
181
186
- self .log .info ('Create a signed "reference" tx for later use' )
187
- raw_tx_reference = node .signrawtransactionwithwallet (node .createrawtransaction (
188
- inputs = [{'txid' : txid_spend_both , 'vout' : 0 }],
189
- outputs = [{node .getnewaddress (): 0.05 }],
190
- ))['hex' ]
191
- tx = tx_from_hex (raw_tx_reference )
182
+ self .log .info ('Create a "reference" tx for later use' )
183
+ utxo_to_spend = self .wallet .get_utxo (txid = txid_spend_both )
184
+ tx = self .wallet .create_self_transfer (utxo_to_spend = utxo_to_spend , sequence = SEQUENCE_FINAL )['tx' ]
185
+ tx .vout [0 ].nValue = int (0.05 * COIN )
186
+ raw_tx_reference = tx .serialize ().hex ()
192
187
# Reference tx should be valid on itself
193
188
self .check_mempool_result (
194
189
result_expected = [{'txid' : tx .rehash (), 'allowed' : True , 'vsize' : tx .get_vsize (), 'fees' : { 'base' : Decimal ('0.1' ) - Decimal ('0.05' )}}],
@@ -199,8 +194,6 @@ def run_test(self):
199
194
self .log .info ('A transaction with no outputs' )
200
195
tx = tx_from_hex (raw_tx_reference )
201
196
tx .vout = []
202
- # Skip re-signing the transaction for context independent checks from now on
203
- # tx = tx_from_hex(node.signrawtransactionwithwallet(tx.serialize().hex())['hex'])
204
197
self .check_mempool_result (
205
198
result_expected = [{'txid' : tx .rehash (), 'allowed' : False , 'reject-reason' : 'bad-txns-vout-empty' }],
206
199
rawtxs = [tx .serialize ().hex ()],
@@ -257,7 +250,7 @@ def run_test(self):
257
250
)
258
251
259
252
self .log .info ('A coinbase transaction' )
260
- # Pick the input of the first tx we signed , so it has to be a coinbase tx
253
+ # Pick the input of the first tx we created , so it has to be a coinbase tx
261
254
raw_tx_coinbase_spent = node .getrawtransaction (txid = node .decoderawtransaction (hexstring = raw_tx_in_block )['vin' ][0 ]['txid' ])
262
255
tx = tx_from_hex (raw_tx_coinbase_spent )
263
256
self .check_mempool_result (
@@ -334,7 +327,6 @@ def run_test(self):
334
327
self .log .info ('A transaction that is locked by BIP68 sequence logic' )
335
328
tx = tx_from_hex (raw_tx_reference )
336
329
tx .vin [0 ].nSequence = 2 # We could include it in the second block mined from now, but not the very next one
337
- # Can skip re-signing the tx because of early rejection
338
330
self .check_mempool_result (
339
331
result_expected = [{'txid' : tx .rehash (), 'allowed' : False , 'reject-reason' : 'non-BIP68-final' }],
340
332
rawtxs = [tx .serialize ().hex ()],
0 commit comments