@@ -47,6 +47,22 @@ def add_output_to_create_multi_result(self, result, output_value=0):
47
47
48
48
result ["new_utxos" ].append ({"txid" : new_txid , "vout" : len (result ["tx" ].vout ) - 1 , "value" : Decimal (output_value ) / COIN , "height" : 0 , "coinbase" : False , "confirmations" : 0 })
49
49
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
+
50
66
def run_test (self ):
51
67
52
68
node = self .nodes [0 ]
@@ -67,11 +83,7 @@ def test_normal_dust(self):
67
83
self .log .info ("Create 0-value dusty output, show that it works inside truc when spent in package" )
68
84
69
85
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 )
75
87
76
88
# Test doesn't work because lack of package feerates
77
89
test_res = self .nodes [0 ].testmempoolaccept ([dusty_tx ["hex" ], sweep_tx ["hex" ]])
@@ -107,11 +119,7 @@ def test_node_restart(self):
107
119
self .log .info ("Test that an ephemeral package is rejected on restart due to individual evaluation" )
108
120
109
121
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 )
115
123
116
124
res = self .nodes [0 ].submitpackage ([dusty_tx ["hex" ], sweep_tx ["hex" ]])
117
125
assert_equal (res ["package_msg" ], "success" )
@@ -132,14 +140,11 @@ def test_fee_having_parent(self):
132
140
assert_equal (self .nodes [0 ].getrawmempool (), [])
133
141
134
142
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 )
137
144
assert_equal (int (COIN * dusty_tx ["fee" ]), sats_fee ) # has fees
138
145
assert_greater_than (dusty_tx ["tx" ].vout [0 ].nValue , 330 ) # main output is not dust
139
146
assert_equal (dusty_tx ["tx" ].vout [1 ].nValue , 0 ) # added one is dust
140
147
141
- sweep_tx = self .wallet .create_self_transfer_multi (utxos_to_spend = dusty_tx ["new_utxos" ], version = 3 )
142
-
143
148
# When base fee is non-0, we report dust like usual
144
149
res = self .nodes [0 ].submitpackage ([dusty_tx ["hex" ], sweep_tx ["hex" ]])
145
150
assert_equal (res ["package_msg" ], "transaction failed" )
@@ -153,10 +158,7 @@ def test_fee_having_parent(self):
153
158
assert_equal (res ["tx-results" ][dusty_tx ["wtxid" ]]["error" ], "dust, tx with dust output must be 0-fee" )
154
159
155
160
# 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 )
160
162
161
163
self .nodes [0 ].prioritisetransaction (txid = dusty_tx ["txid" ], dummy = 0 , fee_delta = 1000 )
162
164
self .nodes [1 ].prioritisetransaction (txid = dusty_tx ["txid" ], dummy = 0 , fee_delta = 1000 )
@@ -177,12 +179,7 @@ def test_multidust(self):
177
179
self .log .info ("Test that a transaction with multiple ephemeral dusts is not allowed" )
178
180
179
181
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 )
186
183
187
184
res = self .nodes [0 ].submitpackage ([dusty_tx ["hex" ], sweep_tx ["hex" ]])
188
185
assert_equal (res ["package_msg" ], "transaction failed" )
@@ -200,10 +197,7 @@ def test_nonzero_dust(self):
200
197
# 330 is dust threshold for taproot outputs
201
198
for value in [1 , 329 , 330 ]:
202
199
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 )
207
201
test_res = self .nodes [0 ].testmempoolaccept ([dusty_tx ["hex" ]])
208
202
assert test_res [0 ]["allowed" ]
209
203
@@ -217,11 +211,7 @@ def test_non_truc(self):
217
211
self .log .info ("Test that v2 dust-having transaction is rejected even if spent, because of min relay requirement" )
218
212
219
213
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 )
225
215
226
216
res = self .nodes [0 ].submitpackage ([dusty_tx ["hex" ], sweep_tx ["hex" ]])
227
217
assert_equal (res ["package_msg" ], "transaction failed" )
@@ -233,12 +223,9 @@ def test_unspent_ephemeral(self):
233
223
self .log .info ("Test that spending from a tx with ephemeral outputs is only allowed if dust is spent as well" )
234
224
235
225
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 )
239
227
240
228
# 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 )
242
229
self .nodes [0 ].submitpackage ([dusty_tx ["hex" ], sweep_tx ["hex" ]])
243
230
assert_mempool_contents (self , self .nodes [0 ], expected = [dusty_tx ["tx" ], sweep_tx ["tx" ]])
244
231
@@ -260,8 +247,7 @@ def test_unspent_ephemeral(self):
260
247
self .generate (self .nodes [0 ], 1 )
261
248
assert_equal (self .nodes [0 ].getrawmempool (), [])
262
249
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 )
265
251
266
252
# Spend non-dust only
267
253
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):
286
272
self .log .info ("Test that dust txn is not evicted when it becomes childless, but won't be mined" )
287
273
288
274
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
-
297
275
sponsor_coin = self .wallet .get_utxo ()
298
-
299
276
# 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 ])
301
278
302
279
res = self .nodes [0 ].submitpackage ([dusty_tx ["hex" ], sweep_tx ["hex" ]])
303
280
assert_equal (res ["package_msg" ], "success" )
@@ -345,8 +322,7 @@ def test_reorgs(self):
345
322
346
323
# Get dusty tx mined, then check that it makes it back into mempool on reorg
347
324
# 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 )
350
326
assert_raises_rpc_error (- 26 , "min relay fee not met" , self .nodes [0 ].sendrawtransaction , dusty_tx ["hex" ])
351
327
352
328
block_res = self .nodes [0 ].rpc .generateblock (self .wallet .get_address (), [dusty_tx ["hex" ]])
@@ -380,18 +356,13 @@ def test_reorgs(self):
380
356
assert_equal (self .nodes [0 ].getrawmempool (), [])
381
357
382
358
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 )
387
360
block_res = self .nodes [0 ].rpc .generateblock (self .wallet .get_address (), [multi_dusty_tx ["hex" ]])
388
361
self .nodes [0 ].invalidateblock (block_res ["hash" ])
389
362
assert_equal (self .nodes [0 ].getrawmempool (), [])
390
363
391
364
# 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 )
395
366
block_res = self .nodes [0 ].rpc .generateblock (self .wallet .get_address (), [dusty_fee_tx ["hex" ]])
396
367
self .nodes [0 ].invalidateblock (block_res ["hash" ])
397
368
assert_equal (self .nodes [0 ].getrawmempool (), [])
@@ -410,11 +381,7 @@ def test_no_minrelay_fee(self):
410
381
self .connect_nodes (0 , 1 )
411
382
412
383
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 )
418
385
419
386
self .nodes [0 ].submitpackage ([dusty_tx ["hex" ], sweep_tx ["hex" ]])
420
387
0 commit comments