@@ -47,6 +47,22 @@ def add_output_to_create_multi_result(self, result, output_value=0):
4747
4848 result ["new_utxos" ].append ({"txid" : new_txid , "vout" : len (result ["tx" ].vout ) - 1 , "value" : Decimal (output_value ) / COIN , "height" : 0 , "coinbase" : False , "confirmations" : 0 })
4949
50+ def create_ephemeral_dust_package (self , * , tx_version , dust_tx_fee = 0 , dust_value = 0 , num_dust_outputs = 1 , extra_sponsors = None ):
51+ """Creates a 1P1C package containing ephemeral dust. By default, the parent transaction
52+ is zero-fee and creates a single zero-value dust output, and all of its outputs are
53+ spent by the child."""
54+ dusty_tx = self .wallet .create_self_transfer_multi (fee_per_output = dust_tx_fee , version = tx_version )
55+ for _ in range (num_dust_outputs ):
56+ self .add_output_to_create_multi_result (dusty_tx , dust_value )
57+
58+ extra_sponsors = extra_sponsors or []
59+ sweep_tx = self .wallet .create_self_transfer_multi (
60+ utxos_to_spend = dusty_tx ["new_utxos" ] + extra_sponsors ,
61+ version = tx_version ,
62+ )
63+
64+ return dusty_tx , sweep_tx
65+
5066 def run_test (self ):
5167
5268 node = self .nodes [0 ]
@@ -67,11 +83,7 @@ def test_normal_dust(self):
6783 self .log .info ("Create 0-value dusty output, show that it works inside truc when spent in package" )
6884
6985 assert_equal (self .nodes [0 ].getrawmempool (), [])
70-
71- dusty_tx = self .wallet .create_self_transfer_multi (fee_per_output = 0 , version = 3 )
72- self .add_output_to_create_multi_result (dusty_tx )
73-
74- sweep_tx = self .wallet .create_self_transfer_multi (utxos_to_spend = dusty_tx ["new_utxos" ], version = 3 )
86+ dusty_tx , sweep_tx = self .create_ephemeral_dust_package (tx_version = 3 )
7587
7688 # Test doesn't work because lack of package feerates
7789 test_res = self .nodes [0 ].testmempoolaccept ([dusty_tx ["hex" ], sweep_tx ["hex" ]])
@@ -107,11 +119,7 @@ def test_node_restart(self):
107119 self .log .info ("Test that an ephemeral package is rejected on restart due to individual evaluation" )
108120
109121 assert_equal (self .nodes [0 ].getrawmempool (), [])
110-
111- dusty_tx = self .wallet .create_self_transfer_multi (fee_per_output = 0 , version = 3 )
112- self .add_output_to_create_multi_result (dusty_tx )
113-
114- sweep_tx = self .wallet .create_self_transfer_multi (utxos_to_spend = dusty_tx ["new_utxos" ], version = 3 )
122+ dusty_tx , sweep_tx = self .create_ephemeral_dust_package (tx_version = 3 )
115123
116124 res = self .nodes [0 ].submitpackage ([dusty_tx ["hex" ], sweep_tx ["hex" ]])
117125 assert_equal (res ["package_msg" ], "success" )
@@ -132,14 +140,11 @@ def test_fee_having_parent(self):
132140 assert_equal (self .nodes [0 ].getrawmempool (), [])
133141
134142 sats_fee = 1
135- dusty_tx = self .wallet .create_self_transfer_multi (fee_per_output = sats_fee , version = 3 )
136- self .add_output_to_create_multi_result (dusty_tx )
143+ dusty_tx , sweep_tx = self .create_ephemeral_dust_package (tx_version = 3 , dust_tx_fee = sats_fee )
137144 assert_equal (int (COIN * dusty_tx ["fee" ]), sats_fee ) # has fees
138145 assert_greater_than (dusty_tx ["tx" ].vout [0 ].nValue , 330 ) # main output is not dust
139146 assert_equal (dusty_tx ["tx" ].vout [1 ].nValue , 0 ) # added one is dust
140147
141- sweep_tx = self .wallet .create_self_transfer_multi (utxos_to_spend = dusty_tx ["new_utxos" ], version = 3 )
142-
143148 # When base fee is non-0, we report dust like usual
144149 res = self .nodes [0 ].submitpackage ([dusty_tx ["hex" ], sweep_tx ["hex" ]])
145150 assert_equal (res ["package_msg" ], "transaction failed" )
@@ -153,10 +158,7 @@ def test_fee_having_parent(self):
153158 assert_equal (res ["tx-results" ][dusty_tx ["wtxid" ]]["error" ], "dust, tx with dust output must be 0-fee" )
154159
155160 # Will not be accepted if base fee is 0 with modified fee of non-0
156- dusty_tx = self .wallet .create_self_transfer_multi (fee_per_output = 0 , version = 3 )
157- self .add_output_to_create_multi_result (dusty_tx )
158-
159- sweep_tx = self .wallet .create_self_transfer_multi (utxos_to_spend = dusty_tx ["new_utxos" ], version = 3 )
161+ dusty_tx , sweep_tx = self .create_ephemeral_dust_package (tx_version = 3 )
160162
161163 self .nodes [0 ].prioritisetransaction (txid = dusty_tx ["txid" ], dummy = 0 , fee_delta = 1000 )
162164 self .nodes [1 ].prioritisetransaction (txid = dusty_tx ["txid" ], dummy = 0 , fee_delta = 1000 )
@@ -177,12 +179,7 @@ def test_multidust(self):
177179 self .log .info ("Test that a transaction with multiple ephemeral dusts is not allowed" )
178180
179181 assert_mempool_contents (self , self .nodes [0 ], expected = [])
180-
181- dusty_tx = self .wallet .create_self_transfer_multi (fee_per_output = 0 , version = 3 )
182- self .add_output_to_create_multi_result (dusty_tx )
183- self .add_output_to_create_multi_result (dusty_tx )
184-
185- sweep_tx = self .wallet .create_self_transfer_multi (utxos_to_spend = dusty_tx ["new_utxos" ], version = 3 )
182+ dusty_tx , sweep_tx = self .create_ephemeral_dust_package (tx_version = 3 , num_dust_outputs = 2 )
186183
187184 res = self .nodes [0 ].submitpackage ([dusty_tx ["hex" ], sweep_tx ["hex" ]])
188185 assert_equal (res ["package_msg" ], "transaction failed" )
@@ -200,10 +197,7 @@ def test_nonzero_dust(self):
200197 # 330 is dust threshold for taproot outputs
201198 for value in [1 , 329 , 330 ]:
202199 assert_equal (self .nodes [0 ].getrawmempool (), [])
203-
204- dusty_tx = self .wallet .create_self_transfer_multi (fee_per_output = 0 , version = 3 )
205- self .add_output_to_create_multi_result (dusty_tx , value )
206-
200+ dusty_tx , _ = self .create_ephemeral_dust_package (tx_version = 3 , dust_value = value )
207201 test_res = self .nodes [0 ].testmempoolaccept ([dusty_tx ["hex" ]])
208202 assert test_res [0 ]["allowed" ]
209203
@@ -217,11 +211,7 @@ def test_non_truc(self):
217211 self .log .info ("Test that v2 dust-having transaction is rejected even if spent, because of min relay requirement" )
218212
219213 assert_equal (self .nodes [0 ].getrawmempool (), [])
220-
221- dusty_tx = self .wallet .create_self_transfer_multi (fee_per_output = 0 , version = 2 )
222- self .add_output_to_create_multi_result (dusty_tx )
223-
224- sweep_tx = self .wallet .create_self_transfer_multi (utxos_to_spend = dusty_tx ["new_utxos" ], version = 2 )
214+ dusty_tx , sweep_tx = self .create_ephemeral_dust_package (tx_version = 2 )
225215
226216 res = self .nodes [0 ].submitpackage ([dusty_tx ["hex" ], sweep_tx ["hex" ]])
227217 assert_equal (res ["package_msg" ], "transaction failed" )
@@ -233,12 +223,9 @@ def test_unspent_ephemeral(self):
233223 self .log .info ("Test that spending from a tx with ephemeral outputs is only allowed if dust is spent as well" )
234224
235225 assert_equal (self .nodes [0 ].getrawmempool (), [])
236-
237- dusty_tx = self .wallet .create_self_transfer_multi (fee_per_output = 0 , version = 3 )
238- self .add_output_to_create_multi_result (dusty_tx , 329 )
226+ dusty_tx , sweep_tx = self .create_ephemeral_dust_package (tx_version = 3 , dust_value = 329 )
239227
240228 # Valid sweep we will RBF incorrectly by not spending dust as well
241- sweep_tx = self .wallet .create_self_transfer_multi (utxos_to_spend = dusty_tx ["new_utxos" ], version = 3 )
242229 self .nodes [0 ].submitpackage ([dusty_tx ["hex" ], sweep_tx ["hex" ]])
243230 assert_mempool_contents (self , self .nodes [0 ], expected = [dusty_tx ["tx" ], sweep_tx ["tx" ]])
244231
@@ -260,8 +247,7 @@ def test_unspent_ephemeral(self):
260247 self .generate (self .nodes [0 ], 1 )
261248 assert_equal (self .nodes [0 ].getrawmempool (), [])
262249
263- dusty_tx = self .wallet .create_self_transfer_multi (fee_per_output = 0 , version = 3 )
264- self .add_output_to_create_multi_result (dusty_tx , 329 )
250+ dusty_tx , _ = self .create_ephemeral_dust_package (tx_version = 3 , dust_value = 329 )
265251
266252 # Spend non-dust only
267253 unspent_sweep_tx = self .wallet .create_self_transfer_multi (utxos_to_spend = [dusty_tx ["new_utxos" ][0 ]], version = 3 )
@@ -286,18 +272,9 @@ def test_sponsor_cycle(self):
286272 self .log .info ("Test that dust txn is not evicted when it becomes childless, but won't be mined" )
287273
288274 assert_equal (self .nodes [0 ].getrawmempool (), [])
289-
290- dusty_tx = self .wallet .create_self_transfer_multi (
291- fee_per_output = 0 ,
292- version = 3
293- )
294-
295- self .add_output_to_create_multi_result (dusty_tx )
296-
297275 sponsor_coin = self .wallet .get_utxo ()
298-
299276 # Bring "fee" input that can be double-spend separately
300- sweep_tx = self .wallet . create_self_transfer_multi ( utxos_to_spend = dusty_tx [ "new_utxos" ] + [sponsor_coin ], version = 3 )
277+ dusty_tx , sweep_tx = self .create_ephemeral_dust_package ( tx_version = 3 , extra_sponsors = [sponsor_coin ])
301278
302279 res = self .nodes [0 ].submitpackage ([dusty_tx ["hex" ], sweep_tx ["hex" ]])
303280 assert_equal (res ["package_msg" ], "success" )
@@ -345,8 +322,7 @@ def test_reorgs(self):
345322
346323 # Get dusty tx mined, then check that it makes it back into mempool on reorg
347324 # due to bypass_limits allowing 0-fee individually
348- dusty_tx = self .wallet .create_self_transfer_multi (fee_per_output = 0 , version = 3 )
349- self .add_output_to_create_multi_result (dusty_tx )
325+ dusty_tx , _ = self .create_ephemeral_dust_package (tx_version = 3 )
350326 assert_raises_rpc_error (- 26 , "min relay fee not met" , self .nodes [0 ].sendrawtransaction , dusty_tx ["hex" ])
351327
352328 block_res = self .nodes [0 ].rpc .generateblock (self .wallet .get_address (), [dusty_tx ["hex" ]])
@@ -380,18 +356,13 @@ def test_reorgs(self):
380356 assert_equal (self .nodes [0 ].getrawmempool (), [])
381357
382358 self .log .info ("Test that ephemeral dust tx with fees or multi dust don't enter mempool via reorg" )
383- multi_dusty_tx = self .wallet .create_self_transfer_multi (fee_per_output = 0 , version = 3 )
384- self .add_output_to_create_multi_result (multi_dusty_tx )
385- self .add_output_to_create_multi_result (multi_dusty_tx )
386-
359+ multi_dusty_tx , _ = self .create_ephemeral_dust_package (tx_version = 3 , num_dust_outputs = 2 )
387360 block_res = self .nodes [0 ].rpc .generateblock (self .wallet .get_address (), [multi_dusty_tx ["hex" ]])
388361 self .nodes [0 ].invalidateblock (block_res ["hash" ])
389362 assert_equal (self .nodes [0 ].getrawmempool (), [])
390363
391364 # With fee and one dust
392- dusty_fee_tx = self .wallet .create_self_transfer_multi (fee_per_output = 1 , version = 3 )
393- self .add_output_to_create_multi_result (dusty_fee_tx )
394-
365+ dusty_fee_tx , _ = self .create_ephemeral_dust_package (tx_version = 3 , dust_tx_fee = 1 )
395366 block_res = self .nodes [0 ].rpc .generateblock (self .wallet .get_address (), [dusty_fee_tx ["hex" ]])
396367 self .nodes [0 ].invalidateblock (block_res ["hash" ])
397368 assert_equal (self .nodes [0 ].getrawmempool (), [])
@@ -410,11 +381,7 @@ def test_no_minrelay_fee(self):
410381 self .connect_nodes (0 , 1 )
411382
412383 assert_equal (self .nodes [0 ].getrawmempool (), [])
413-
414- dusty_tx = self .wallet .create_self_transfer_multi (fee_per_output = 0 , version = 2 )
415- self .add_output_to_create_multi_result (dusty_tx )
416-
417- sweep_tx = self .wallet .create_self_transfer_multi (utxos_to_spend = dusty_tx ["new_utxos" ], version = 2 )
384+ dusty_tx , sweep_tx = self .create_ephemeral_dust_package (tx_version = 2 )
418385
419386 self .nodes [0 ].submitpackage ([dusty_tx ["hex" ], sweep_tx ["hex" ]])
420387
0 commit comments