@@ -52,16 +52,19 @@ pub(crate) fn maybe_add_change_output(tx: &mut Transaction, input_value: Amount,
52
52
} ;
53
53
let change_len = change_output. consensus_encode ( & mut sink ( ) ) . unwrap ( ) ;
54
54
let starting_weight = tx. weight ( ) . to_wu ( ) + WITNESS_FLAG_BYTES + witness_max_weight as u64 ;
55
+ let starting_fees = ( starting_weight as i64 ) * feerate_sat_per_1000_weight as i64 / 1000 ;
55
56
let mut weight_with_change: i64 = starting_weight as i64 + change_len as i64 * 4 ;
56
57
// Include any extra bytes required to push an extra output.
57
- weight_with_change += ( VarInt ( tx. output . len ( ) as u64 + 1 ) . size ( ) - VarInt ( tx. output . len ( ) as u64 ) . size ( ) ) as i64 * 4 ;
58
+ let num_outputs = tx. output . len ( ) as u64 ;
59
+ weight_with_change += ( VarInt ( num_outputs + 1 ) . size ( ) - VarInt ( num_outputs) . size ( ) ) as i64 * 4 ;
58
60
// When calculating weight, add two for the flag bytes
59
- let change_value: i64 = ( input_value - output_value) . to_sat ( ) as i64 - weight_with_change * feerate_sat_per_1000_weight as i64 / 1000 ;
61
+ let fees_with_change = weight_with_change * feerate_sat_per_1000_weight as i64 / 1000 ;
62
+ let change_value: i64 = ( input_value - output_value) . to_sat ( ) as i64 - fees_with_change;
60
63
if change_value >= dust_value. to_sat ( ) as i64 {
61
64
change_output. value = Amount :: from_sat ( change_value as u64 ) ;
62
65
tx. output . push ( change_output) ;
63
66
Ok ( weight_with_change as u64 )
64
- } else if ( input_value - output_value) . to_sat ( ) as i64 - ( starting_weight as i64 ) * feerate_sat_per_1000_weight as i64 / 1000 < 0 {
67
+ } else if ( input_value - output_value) . to_sat ( ) as i64 - starting_fees < 0 {
65
68
Err ( ( ) )
66
69
} else {
67
70
Ok ( starting_weight)
@@ -215,18 +218,28 @@ mod tests {
215
218
#[ test]
216
219
fn test_tx_value_overrun ( ) {
217
220
// If we have a bogus input amount or outputs valued more than inputs, we should fail
218
- let mut tx = Transaction { version : Version :: TWO , lock_time : LockTime :: ZERO , input : Vec :: new ( ) , output : vec ! [ TxOut {
219
- script_pubkey: ScriptBuf :: new( ) , value: Amount :: from_sat( 1000 )
220
- } ] } ;
221
- assert ! ( maybe_add_change_output( & mut tx, Amount :: from_sat( 21_000_000_0000_0001 ) , 0 , 253 , ScriptBuf :: new( ) ) . is_err( ) ) ;
222
- assert ! ( maybe_add_change_output( & mut tx, Amount :: from_sat( 400 ) , 0 , 253 , ScriptBuf :: new( ) ) . is_err( ) ) ;
223
- assert ! ( maybe_add_change_output( & mut tx, Amount :: from_sat( 4000 ) , 0 , 253 , ScriptBuf :: new( ) ) . is_ok( ) ) ;
221
+ let version = Version :: TWO ;
222
+ let lock_time = LockTime :: ZERO ;
223
+ let input = Vec :: new ( ) ;
224
+ let tx_out = TxOut { script_pubkey : ScriptBuf :: new ( ) , value : Amount :: from_sat ( 1000 ) } ;
225
+ let output = vec ! [ tx_out] ;
226
+ let mut tx = Transaction { version, lock_time, input, output } ;
227
+ let amount = Amount :: from_sat ( 21_000_000_0000_0001 ) ;
228
+ assert ! ( maybe_add_change_output( & mut tx, amount, 0 , 253 , ScriptBuf :: new( ) ) . is_err( ) ) ;
229
+ let amount = Amount :: from_sat ( 400 ) ;
230
+ assert ! ( maybe_add_change_output( & mut tx, amount, 0 , 253 , ScriptBuf :: new( ) ) . is_err( ) ) ;
231
+ let amount = Amount :: from_sat ( 4000 ) ;
232
+ assert ! ( maybe_add_change_output( & mut tx, amount, 0 , 253 , ScriptBuf :: new( ) ) . is_ok( ) ) ;
224
233
}
225
234
226
235
#[ test]
227
236
fn test_tx_change_edge ( ) {
228
237
// Check that we never add dust outputs
229
- let mut tx = Transaction { version : Version :: TWO , lock_time : LockTime :: ZERO , input : Vec :: new ( ) , output : Vec :: new ( ) } ;
238
+ let version = Version :: TWO ;
239
+ let lock_time = LockTime :: ZERO ;
240
+ let input = Vec :: new ( ) ;
241
+ let output = Vec :: new ( ) ;
242
+ let mut tx = Transaction { version, lock_time, input, output } ;
230
243
let orig_wtxid = tx. compute_wtxid ( ) ;
231
244
let output_spk = ScriptBuf :: new_p2pkh ( & PubkeyHash :: hash ( & [ 0 ; 0 ] ) ) ;
232
245
assert_eq ! ( output_spk. minimal_non_dust( ) . to_sat( ) , 546 ) ;
@@ -235,60 +248,91 @@ mod tests {
235
248
// weight = 3 * base size + total size = 3 * (4 + 1 + 0 + 1 + 0 + 4) + (4 + 1 + 1 + 1 + 0 + 1 + 0 + 4) = 3 * 10 + 12 = 42
236
249
assert_eq ! ( tx. weight( ) . to_wu( ) , 42 ) ;
237
250
// 10 sats isn't enough to pay fee on a dummy transaction...
238
- assert ! ( maybe_add_change_output( & mut tx, Amount :: from_sat( 10 ) , 0 , 250 , output_spk. clone( ) ) . is_err( ) ) ;
239
- assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ; // Failure doesn't change the transaction
251
+ let amount = Amount :: from_sat ( 10 ) ;
252
+ assert ! ( maybe_add_change_output( & mut tx, amount, 0 , 250 , output_spk. clone( ) ) . is_err( ) ) ;
253
+ // Failure doesn't change the transaction
254
+ assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ;
240
255
// but 11 (= ceil(42 * 250 / 1000)) is, just not enough to add a change output...
241
- assert ! ( maybe_add_change_output( & mut tx, Amount :: from_sat( 11 ) , 0 , 250 , output_spk. clone( ) ) . is_ok( ) ) ;
256
+ let amount = Amount :: from_sat ( 11 ) ;
257
+ assert ! ( maybe_add_change_output( & mut tx, amount, 0 , 250 , output_spk. clone( ) ) . is_ok( ) ) ;
242
258
assert_eq ! ( tx. output. len( ) , 0 ) ;
243
- assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ; // If we don't add an output, we don't change the transaction
244
- assert ! ( maybe_add_change_output( & mut tx, Amount :: from_sat( 549 ) , 0 , 250 , output_spk. clone( ) ) . is_ok( ) ) ;
259
+ // If we don't add an output, we don't change the transaction
260
+ assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ;
261
+ let amount = Amount :: from_sat ( 549 ) ;
262
+ assert ! ( maybe_add_change_output( & mut tx, amount, 0 , 250 , output_spk. clone( ) ) . is_ok( ) ) ;
245
263
assert_eq ! ( tx. output. len( ) , 0 ) ;
246
- assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ; // If we don't add an output, we don't change the transaction
264
+ // If we don't add an output, we don't change the transaction
265
+ assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ;
247
266
// 590 is also not enough
248
- assert ! ( maybe_add_change_output( & mut tx, Amount :: from_sat( 590 ) , 0 , 250 , output_spk. clone( ) ) . is_ok( ) ) ;
267
+ let amount = Amount :: from_sat ( 590 ) ;
268
+ assert ! ( maybe_add_change_output( & mut tx, amount, 0 , 250 , output_spk. clone( ) ) . is_ok( ) ) ;
249
269
assert_eq ! ( tx. output. len( ) , 0 ) ;
250
- assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ; // If we don't add an output, we don't change the transaction
270
+ // If we don't add an output, we don't change the transaction
271
+ assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ;
251
272
// at 591 we can afford the change output at the dust limit (546)
252
- assert ! ( maybe_add_change_output( & mut tx, Amount :: from_sat( 591 ) , 0 , 250 , output_spk. clone( ) ) . is_ok( ) ) ;
273
+ let amount = Amount :: from_sat ( 591 ) ;
274
+ assert ! ( maybe_add_change_output( & mut tx, amount, 0 , 250 , output_spk. clone( ) ) . is_ok( ) ) ;
253
275
assert_eq ! ( tx. output. len( ) , 1 ) ;
254
276
assert_eq ! ( tx. output[ 0 ] . value. to_sat( ) , 546 ) ;
255
277
assert_eq ! ( tx. output[ 0 ] . script_pubkey, output_spk) ;
256
- assert_eq ! ( tx. weight( ) . to_wu( ) / 4 , 590 -546 ) ; // New weight is exactly the fee we wanted.
278
+ // New weight is exactly the fee we wanted.
279
+ assert_eq ! ( tx. weight( ) . to_wu( ) / 4 , 590 -546 ) ;
257
280
258
281
tx. output . pop ( ) ;
259
- assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ; // The only change is the addition of one output.
282
+ // The only change is the addition of one output.
283
+ assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ;
260
284
}
261
285
262
286
#[ test]
263
287
fn test_tx_extra_outputs ( ) {
264
288
// Check that we correctly handle existing outputs
265
- let mut tx = Transaction { version : Version :: TWO , lock_time : LockTime :: ZERO , input : vec ! [ TxIn {
266
- previous_output: OutPoint :: new( Txid :: all_zeros( ) , 0 ) , script_sig: ScriptBuf :: new( ) , witness: Witness :: new( ) , sequence: Sequence :: ZERO ,
267
- } ] , output : vec ! [ TxOut {
268
- script_pubkey: Builder :: new( ) . push_int( 1 ) . into_script( ) , value: Amount :: from_sat( 1000 )
269
- } ] } ;
289
+ let script_pubkey = Builder :: new ( ) . push_int ( 1 ) . into_script ( ) ;
290
+ let tx_out = TxOut { script_pubkey, value : Amount :: from_sat ( 1000 ) } ;
291
+ let previous_output = OutPoint :: new ( Txid :: all_zeros ( ) , 0 ) ;
292
+ let script_sig = ScriptBuf :: new ( ) ;
293
+ let witness = Witness :: new ( ) ;
294
+ let sequence = Sequence :: ZERO ;
295
+ let tx_in = TxIn { previous_output, script_sig, witness, sequence } ;
296
+ let version = Version :: TWO ;
297
+ let lock_time = LockTime :: ZERO ;
298
+ let input = vec ! [ tx_in] ;
299
+ let output = vec ! [ tx_out] ;
300
+ let mut tx = Transaction { version, lock_time, input, output } ;
270
301
let orig_wtxid = tx. compute_wtxid ( ) ;
271
302
let orig_weight = tx. weight ( ) . to_wu ( ) ;
272
303
assert_eq ! ( orig_weight / 4 , 61 ) ;
273
304
274
305
assert_eq ! ( Builder :: new( ) . push_int( 2 ) . into_script( ) . minimal_non_dust( ) . to_sat( ) , 474 ) ;
275
306
276
307
// Input value of the output value + fee - 1 should fail:
277
- assert ! ( maybe_add_change_output( & mut tx, Amount :: from_sat( 1000 + 61 + 100 - 1 ) , 400 , 250 , Builder :: new( ) . push_int( 2 ) . into_script( ) ) . is_err( ) ) ;
278
- assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ; // Failure doesn't change the transaction
308
+ let amount = Amount :: from_sat ( 1000 + 61 + 100 - 1 ) ;
309
+ let script = Builder :: new ( ) . push_int ( 2 ) . into_script ( ) ;
310
+ assert ! ( maybe_add_change_output( & mut tx, amount, 400 , 250 , script) . is_err( ) ) ;
311
+ // Failure doesn't change the transaction
312
+ assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ;
279
313
// but one more input sat should succeed, without changing the transaction
280
- assert ! ( maybe_add_change_output( & mut tx, Amount :: from_sat( 1000 + 61 + 100 ) , 400 , 250 , Builder :: new( ) . push_int( 2 ) . into_script( ) ) . is_ok( ) ) ;
281
- assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ; // If we don't add an output, we don't change the transaction
314
+ let amount = Amount :: from_sat ( 1000 + 61 + 100 ) ;
315
+ let script = Builder :: new ( ) . push_int ( 2 ) . into_script ( ) ;
316
+ assert ! ( maybe_add_change_output( & mut tx, amount, 400 , 250 , script) . is_ok( ) ) ;
317
+ // If we don't add an output, we don't change the transaction
318
+ assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ;
282
319
// In order to get a change output, we need to add 474 plus the output's weight / 4 (10)...
283
- assert ! ( maybe_add_change_output( & mut tx, Amount :: from_sat( 1000 + 61 + 100 + 474 + 9 ) , 400 , 250 , Builder :: new( ) . push_int( 2 ) . into_script( ) ) . is_ok( ) ) ;
284
- assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ; // If we don't add an output, we don't change the transaction
285
-
286
- assert ! ( maybe_add_change_output( & mut tx, Amount :: from_sat( 1000 + 61 + 100 + 474 + 10 ) , 400 , 250 , Builder :: new( ) . push_int( 2 ) . into_script( ) ) . is_ok( ) ) ;
320
+ let amount = Amount :: from_sat ( 1000 + 61 + 100 + 474 + 9 ) ;
321
+ let script = Builder :: new ( ) . push_int ( 2 ) . into_script ( ) ;
322
+ assert ! ( maybe_add_change_output( & mut tx, amount, 400 , 250 , script) . is_ok( ) ) ;
323
+ // If we don't add an output, we don't change the transaction
324
+ assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ;
325
+
326
+ let amount = Amount :: from_sat ( 1000 + 61 + 100 + 474 + 10 ) ;
327
+ let script = Builder :: new ( ) . push_int ( 2 ) . into_script ( ) ;
328
+ assert ! ( maybe_add_change_output( & mut tx, amount, 400 , 250 , script) . is_ok( ) ) ;
287
329
assert_eq ! ( tx. output. len( ) , 2 ) ;
288
330
assert_eq ! ( tx. output[ 1 ] . value. to_sat( ) , 474 ) ;
289
331
assert_eq ! ( tx. output[ 1 ] . script_pubkey, Builder :: new( ) . push_int( 2 ) . into_script( ) ) ;
290
- assert_eq ! ( tx. weight( ) . to_wu( ) - orig_weight, 40 ) ; // Weight difference matches what we had to add above
332
+ // Weight difference matches what we had to add above
333
+ assert_eq ! ( tx. weight( ) . to_wu( ) - orig_weight, 40 ) ;
291
334
tx. output . pop ( ) ;
292
- assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ; // The only change is the addition of one output.
335
+ // The only change is the addition of one output.
336
+ assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ;
293
337
}
294
338
}
0 commit comments