77from decimal import Decimal
88import random
99
10- from test_framework .address import ADDRESS_BCRT1_P2SH_OP_TRUE
11- from test_framework .test_framework import BitcoinTestFramework
10+ from test_framework .blocktools import COINBASE_MATURITY
1211from test_framework .messages import (
1312 tx_from_hex ,
1413)
15- from test_framework .script import (
16- CScript ,
17- OP_TRUE ,
18- )
14+ from test_framework .test_framework import BitcoinTestFramework
1915from test_framework .util import (
2016 assert_equal ,
2117)
2218from test_framework .wallet import (
23- create_child_with_parents ,
24- create_raw_chain ,
25- make_chain ,
19+ MiniWallet ,
2620)
2721
22+
2823class RPCPackagesTest (BitcoinTestFramework ):
2924 def set_test_params (self ):
3025 self .num_nodes = 1
@@ -42,52 +37,53 @@ def assert_testres_equal(self, package_hex, testres_expected):
4237 assert_equal (shuffled_testres , self .nodes [0 ].testmempoolaccept (shuffled_package ))
4338
4439 def run_test (self ):
45- self .log .info ("Generate blocks to create UTXOs" )
4640 node = self .nodes [0 ]
47- self .privkeys = [node .get_deterministic_priv_key ().key ]
48- self .address = node .get_deterministic_priv_key ().address
49- self .coins = []
50- # The last 100 coinbase transactions are premature
51- for b in self .generatetoaddress (node , 200 , self .address )[:100 ]:
52- coinbase = node .getblock (blockhash = b , verbosity = 2 )["tx" ][0 ]
53- self .coins .append ({
41+
42+ # get an UTXO that requires signature to be spent
43+ deterministic_address = node .get_deterministic_priv_key ().address
44+ blockhash = self .generatetoaddress (node , 1 , deterministic_address )[0 ]
45+ coinbase = node .getblock (blockhash = blockhash , verbosity = 2 )["tx" ][0 ]
46+ coin = {
5447 "txid" : coinbase ["txid" ],
5548 "amount" : coinbase ["vout" ][0 ]["value" ],
5649 "scriptPubKey" : coinbase ["vout" ][0 ]["scriptPubKey" ],
57- })
50+ "vout" : 0 ,
51+ "height" : 0
52+ }
53+
54+ self .wallet = MiniWallet (self .nodes [0 ])
55+ self .generate (self .wallet , COINBASE_MATURITY + 100 ) # blocks generated for inputs
5856
57+ self .log .info ("Create some transactions" )
5958 # Create some transactions that can be reused throughout the test. Never submit these to mempool.
6059 self .independent_txns_hex = []
6160 self .independent_txns_testres = []
6261 for _ in range (3 ):
63- coin = self .coins .pop ()
64- rawtx = node .createrawtransaction ([{"txid" : coin ["txid" ], "vout" : 0 }],
65- {self .address : coin ["amount" ] - Decimal ("0.0001" )})
66- signedtx = node .signrawtransactionwithkey (hexstring = rawtx , privkeys = self .privkeys )
67- assert signedtx ["complete" ]
68- testres = node .testmempoolaccept ([signedtx ["hex" ]])
62+ tx_hex = self .wallet .create_self_transfer (fee_rate = Decimal ("0.0001" ))["hex" ]
63+ testres = self .nodes [0 ].testmempoolaccept ([tx_hex ])
6964 assert testres [0 ]["allowed" ]
70- self .independent_txns_hex .append (signedtx [ "hex" ] )
65+ self .independent_txns_hex .append (tx_hex )
7166 # testmempoolaccept returns a list of length one, avoid creating a 2D list
7267 self .independent_txns_testres .append (testres [0 ])
7368 self .independent_txns_testres_blank = [{
7469 "txid" : res ["txid" ]} for res in self .independent_txns_testres ]
7570
76- self .test_independent ()
71+ self .test_independent (coin )
7772 self .test_chain ()
7873 self .test_multiple_children ()
7974 self .test_multiple_parents ()
8075 self .test_conflicting ()
8176
8277
83- def test_independent (self ):
78+ def test_independent (self , coin ):
8479 self .log .info ("Test multiple independent transactions in a package" )
8580 node = self .nodes [0 ]
8681 # For independent transactions, order doesn't matter.
8782 self .assert_testres_equal (self .independent_txns_hex , self .independent_txns_testres )
8883
8984 self .log .info ("Test an otherwise valid package with an extra garbage tx appended" )
90- garbage_tx = node .createrawtransaction ([{"txid" : "00" * 32 , "vout" : 5 }], {self .address : 1 })
85+ address = node .get_deterministic_priv_key ().address
86+ garbage_tx = node .createrawtransaction ([{"txid" : "00" * 32 , "vout" : 5 }], {address : 1 })
9187 tx = tx_from_hex (garbage_tx )
9288 # Only the txid is returned because validation is incomplete for the independent txns.
9389 # Package validation is atomic: if the node cannot find a UTXO for any single tx in the package,
@@ -97,9 +93,8 @@ def test_independent(self):
9793 self .assert_testres_equal (package_bad , testres_bad )
9894
9995 self .log .info ("Check testmempoolaccept tells us when some transactions completed validation successfully" )
100- coin = self .coins .pop ()
10196 tx_bad_sig_hex = node .createrawtransaction ([{"txid" : coin ["txid" ], "vout" : 0 }],
102- {self . address : coin ["amount" ] - Decimal ("0.0001" )})
97+ {address : coin ["amount" ] - Decimal ("0.0001" )})
10398 tx_bad_sig = tx_from_hex (tx_bad_sig_hex )
10499 tx_bad_sig_hex = tx_bad_sig .serialize ().hex ()
105100 testres_bad_sig = node .testmempoolaccept (self .independent_txns_hex + [tx_bad_sig_hex ])
@@ -113,24 +108,22 @@ def test_independent(self):
113108 }])
114109
115110 self .log .info ("Check testmempoolaccept reports txns in packages that exceed max feerate" )
116- coin = self .coins .pop ()
117- tx_high_fee_raw = node .createrawtransaction ([{"txid" : coin ["txid" ], "vout" : 0 }],
118- {self .address : coin ["amount" ] - Decimal ("0.999" )})
119- tx_high_fee_signed = node .signrawtransactionwithkey (hexstring = tx_high_fee_raw , privkeys = self .privkeys )
120- assert tx_high_fee_signed ["complete" ]
121- tx_high_fee = tx_from_hex (tx_high_fee_signed ["hex" ])
122- testres_high_fee = node .testmempoolaccept ([tx_high_fee_signed ["hex" ]])
111+ tx_high_fee = self .wallet .create_self_transfer (fee = Decimal ("0.999" ))
112+ testres_high_fee = node .testmempoolaccept ([tx_high_fee ["hex" ]])
123113 assert_equal (testres_high_fee , [
124- {"txid" : tx_high_fee . rehash () , "allowed" : False , "reject-reason" : "max-fee-exceeded" }
114+ {"txid" : tx_high_fee [ "txid" ] , "allowed" : False , "reject-reason" : "max-fee-exceeded" }
125115 ])
126- package_high_fee = [tx_high_fee_signed ["hex" ]] + self .independent_txns_hex
116+ package_high_fee = [tx_high_fee ["hex" ]] + self .independent_txns_hex
127117 testres_package_high_fee = node .testmempoolaccept (package_high_fee )
128118 assert_equal (testres_package_high_fee , testres_high_fee + self .independent_txns_testres_blank )
129119
130120 def test_chain (self ):
131121 node = self .nodes [0 ]
132- first_coin = self .coins .pop ()
133- (chain_hex , chain_txns ) = create_raw_chain (node , first_coin , self .address , self .privkeys )
122+
123+ chain = self .wallet .create_self_transfer_chain (chain_length = 25 )
124+ chain_hex = chain ["chain_hex" ]
125+ chain_txns = chain ["chain_txns" ]
126+
134127 self .log .info ("Check that testmempoolaccept requires packages to be sorted by dependency" )
135128 assert_equal (node .testmempoolaccept (rawtxs = chain_hex [::- 1 ]),
136129 [{"txid" : tx .rehash (), "package-error" : "package-not-sorted" } for tx in chain_txns [::- 1 ]])
@@ -152,116 +145,88 @@ def test_chain(self):
152145
153146 def test_multiple_children (self ):
154147 node = self .nodes [0 ]
155-
156148 self .log .info ("Testmempoolaccept a package in which a transaction has two children within the package" )
157- first_coin = self .coins .pop ()
158- value = (first_coin ["amount" ] - Decimal ("0.0002" )) / 2 # Deduct reasonable fee and make 2 outputs
159- inputs = [{"txid" : first_coin ["txid" ], "vout" : 0 }]
160- outputs = [{self .address : value }, {ADDRESS_BCRT1_P2SH_OP_TRUE : value }]
161- rawtx = node .createrawtransaction (inputs , outputs )
162-
163- parent_signed = node .signrawtransactionwithkey (hexstring = rawtx , privkeys = self .privkeys )
164- parent_tx = tx_from_hex (parent_signed ["hex" ])
165- assert parent_signed ["complete" ]
166- parent_txid = parent_tx .rehash ()
167- assert node .testmempoolaccept ([parent_signed ["hex" ]])[0 ]["allowed" ]
168149
169- parent_locking_script_a = parent_tx . vout [ 0 ]. scriptPubKey . hex ( )
170- child_value = value - Decimal ( "0.0001" )
150+ parent_tx = self . wallet . create_self_transfer_multi ( num_outputs = 2 )
151+ assert node . testmempoolaccept ([ parent_tx [ "hex" ]])[ 0 ][ "allowed" ]
171152
172153 # Child A
173- ( _ , tx_child_a_hex , _ , _ ) = make_chain ( node , self .address , self . privkeys , parent_txid , child_value , 0 , parent_locking_script_a )
174- assert not node .testmempoolaccept ([tx_child_a_hex ])[0 ]["allowed" ]
154+ child_a_tx = self .wallet . create_self_transfer ( utxo_to_spend = parent_tx [ "new_utxos" ][ 0 ] )
155+ assert not node .testmempoolaccept ([child_a_tx [ "hex" ] ])[0 ]["allowed" ]
175156
176157 # Child B
177- rawtx_b = node .createrawtransaction ([{"txid" : parent_txid , "vout" : 1 }], {self .address : child_value })
178- tx_child_b = tx_from_hex (rawtx_b )
179- tx_child_b .vin [0 ].scriptSig = CScript ([CScript ([OP_TRUE ])])
180- tx_child_b_hex = tx_child_b .serialize ().hex ()
181- assert not node .testmempoolaccept ([tx_child_b_hex ])[0 ]["allowed" ]
158+ child_b_tx = self .wallet .create_self_transfer (utxo_to_spend = parent_tx ["new_utxos" ][1 ])
159+ assert not node .testmempoolaccept ([child_b_tx ["hex" ]])[0 ]["allowed" ]
182160
183161 self .log .info ("Testmempoolaccept with entire package, should work with children in either order" )
184- testres_multiple_ab = node .testmempoolaccept (rawtxs = [parent_signed ["hex" ], tx_child_a_hex , tx_child_b_hex ])
185- testres_multiple_ba = node .testmempoolaccept (rawtxs = [parent_signed ["hex" ], tx_child_b_hex , tx_child_a_hex ])
162+ testres_multiple_ab = node .testmempoolaccept (rawtxs = [parent_tx ["hex" ], child_a_tx [ "hex" ], child_b_tx [ "hex" ] ])
163+ testres_multiple_ba = node .testmempoolaccept (rawtxs = [parent_tx ["hex" ], child_b_tx [ "hex" ], child_a_tx [ "hex" ] ])
186164 assert all ([testres ["allowed" ] for testres in testres_multiple_ab + testres_multiple_ba ])
187165
188166 testres_single = []
189167 # Test accept and then submit each one individually, which should be identical to package testaccept
190- for rawtx in [parent_signed ["hex" ], tx_child_a_hex , tx_child_b_hex ]:
168+ for rawtx in [parent_tx ["hex" ], child_a_tx [ "hex" ], child_b_tx [ "hex" ] ]:
191169 testres = node .testmempoolaccept ([rawtx ])
192170 testres_single .append (testres [0 ])
193171 # Submit the transaction now so its child should have no problem validating
194172 node .sendrawtransaction (rawtx )
195173 assert_equal (testres_single , testres_multiple_ab )
196174
197-
198175 def test_multiple_parents (self ):
199176 node = self .nodes [0 ]
200-
201177 self .log .info ("Testmempoolaccept a package in which a transaction has multiple parents within the package" )
178+
202179 for num_parents in [2 , 10 , 24 ]:
203180 # Test a package with num_parents parents and 1 child transaction.
181+ parent_coins = []
204182 package_hex = []
205- parents_tx = []
206- values = []
207- parent_locking_scripts = []
183+
208184 for _ in range (num_parents ):
209- parent_coin = self .coins .pop ()
210- value = parent_coin ["amount" ]
211- (tx , txhex , value , parent_locking_script ) = make_chain (node , self .address , self .privkeys , parent_coin ["txid" ], value )
212- package_hex .append (txhex )
213- parents_tx .append (tx )
214- values .append (value )
215- parent_locking_scripts .append (parent_locking_script )
216- child_hex = create_child_with_parents (node , self .address , self .privkeys , parents_tx , values , parent_locking_scripts )
217- # Package accept should work with the parents in any order (as long as parents come before child)
185+ # Package accept should work with the parents in any order (as long as parents come before child)
186+ parent_tx = self .wallet .create_self_transfer ()
187+ parent_coins .append (parent_tx ["new_utxo" ])
188+ package_hex .append (parent_tx ["hex" ])
189+
190+ child_tx = self .wallet .create_self_transfer_multi (utxos_to_spend = parent_coins , fee_per_output = 2000 )
218191 for _ in range (10 ):
219192 random .shuffle (package_hex )
220- testres_multiple = node .testmempoolaccept (rawtxs = package_hex + [child_hex ])
193+ testres_multiple = node .testmempoolaccept (rawtxs = package_hex + [child_tx [ 'hex' ] ])
221194 assert all ([testres ["allowed" ] for testres in testres_multiple ])
222195
223196 testres_single = []
224197 # Test accept and then submit each one individually, which should be identical to package testaccept
225- for rawtx in package_hex + [child_hex ]:
198+ for rawtx in package_hex + [child_tx [ "hex" ] ]:
226199 testres_single .append (node .testmempoolaccept ([rawtx ])[0 ])
227200 # Submit the transaction now so its child should have no problem validating
228201 node .sendrawtransaction (rawtx )
229202 assert_equal (testres_single , testres_multiple )
230203
231204 def test_conflicting (self ):
232205 node = self .nodes [0 ]
233- prevtx = self .coins .pop ()
234- inputs = [{"txid" : prevtx ["txid" ], "vout" : 0 }]
235- output1 = {node .get_deterministic_priv_key ().address : 500 - 0.00125 }
236- output2 = {ADDRESS_BCRT1_P2SH_OP_TRUE : 500 - 0.00125 }
206+ coin = self .wallet .get_utxo ()
237207
238208 # tx1 and tx2 share the same inputs
239- rawtx1 = node .createrawtransaction (inputs , output1 )
240- rawtx2 = node .createrawtransaction (inputs , output2 )
241- signedtx1 = node .signrawtransactionwithkey (hexstring = rawtx1 , privkeys = self .privkeys )
242- signedtx2 = node .signrawtransactionwithkey (hexstring = rawtx2 , privkeys = self .privkeys )
243- tx1 = tx_from_hex (signedtx1 ["hex" ])
244- tx2 = tx_from_hex (signedtx2 ["hex" ])
245- assert signedtx1 ["complete" ]
246- assert signedtx2 ["complete" ]
209+ tx1 = self .wallet .create_self_transfer (utxo_to_spend = coin )
210+ tx2 = self .wallet .create_self_transfer (utxo_to_spend = coin )
247211
248212 # Ensure tx1 and tx2 are valid by themselves
249- assert node .testmempoolaccept ([signedtx1 ["hex" ]])[0 ]["allowed" ]
250- assert node .testmempoolaccept ([signedtx2 ["hex" ]])[0 ]["allowed" ]
213+ assert node .testmempoolaccept ([tx1 ["hex" ]])[0 ]["allowed" ]
214+ assert node .testmempoolaccept ([tx2 ["hex" ]])[0 ]["allowed" ]
251215
252216 self .log .info ("Test duplicate transactions in the same package" )
253- testres = node .testmempoolaccept ([signedtx1 ["hex" ], signedtx1 ["hex" ]])
217+ testres = node .testmempoolaccept ([tx1 ["hex" ], tx1 ["hex" ]])
254218 assert_equal (testres , [
255- {"txid" : tx1 . rehash () , "package-error" : "conflict-in-package" },
256- {"txid" : tx1 . rehash () , "package-error" : "conflict-in-package" }
219+ {"txid" : tx1 [ "txid" ] , "package-error" : "conflict-in-package" },
220+ {"txid" : tx1 [ "txid" ] , "package-error" : "conflict-in-package" }
257221 ])
258222
259223 self .log .info ("Test conflicting transactions in the same package" )
260- testres = node .testmempoolaccept ([signedtx1 ["hex" ], signedtx2 ["hex" ]])
224+ testres = node .testmempoolaccept ([tx1 ["hex" ], tx2 ["hex" ]])
261225 assert_equal (testres , [
262- {"txid" : tx1 . rehash () , "package-error" : "conflict-in-package" },
263- {"txid" : tx2 . rehash () , "package-error" : "conflict-in-package" }
226+ {"txid" : tx1 [ "txid" ] , "package-error" : "conflict-in-package" },
227+ {"txid" : tx2 [ "txid" ] , "package-error" : "conflict-in-package" }
264228 ])
265229
230+
266231if __name__ == "__main__" :
267232 RPCPackagesTest ().main ()
0 commit comments