@@ -28,49 +28,43 @@ pub fn sort_outputs<T, C : Fn(&T, &T) -> Ordering>(outputs: &mut Vec<(TxOut, T)>
28
28
} ) ;
29
29
}
30
30
31
- fn get_dust_value ( output_script : & Script ) -> u64 {
32
- //TODO: This belongs in rust-bitcoin (https://github.com/rust-bitcoin/rust-bitcoin/pull/566)
33
- if output_script. is_op_return ( ) {
34
- 0
35
- } else if output_script. is_witness_program ( ) {
36
- 294
37
- } else {
38
- 546
39
- }
40
- }
41
-
42
31
/// Possibly adds a change output to the given transaction, always doing so if there are excess
43
32
/// funds available beyond the requested feerate.
44
33
/// Assumes at least one input will have a witness (ie spends a segwit output).
45
34
/// Returns an Err(()) if the requested feerate cannot be met.
46
- pub ( crate ) fn maybe_add_change_output ( tx : & mut Transaction , input_value : u64 , witness_max_weight : usize , feerate_sat_per_1000_weight : u32 , change_destination_script : Script ) -> Result < ( ) , ( ) > {
35
+ /// Returns the expected maximum weight of the fully signed transaction on success.
36
+ pub ( crate ) fn maybe_add_change_output ( tx : & mut Transaction , input_value : u64 , witness_max_weight : usize , feerate_sat_per_1000_weight : u32 , change_destination_script : Script ) -> Result < usize , ( ) > {
47
37
if input_value > MAX_VALUE_MSAT / 1000 { return Err ( ( ) ) ; }
48
38
39
+ const WITNESS_FLAG_BYTES : i64 = 2 ;
40
+
49
41
let mut output_value = 0 ;
50
42
for output in tx. output . iter ( ) {
51
43
output_value += output. value ;
52
44
if output_value >= input_value { return Err ( ( ) ) ; }
53
45
}
54
46
55
- let dust_value = get_dust_value ( & change_destination_script) ;
47
+ let dust_value = change_destination_script. dust_value ( ) ;
56
48
let mut change_output = TxOut {
57
49
script_pubkey : change_destination_script,
58
50
value : 0 ,
59
51
} ;
60
52
let change_len = change_output. consensus_encode ( & mut sink ( ) ) . unwrap ( ) ;
61
- let mut weight_with_change: i64 = tx. get_weight ( ) as i64 + 2 + witness_max_weight as i64 + change_len as i64 * 4 ;
53
+ let starting_weight = tx. get_weight ( ) + WITNESS_FLAG_BYTES as usize + witness_max_weight;
54
+ let mut weight_with_change: i64 = starting_weight as i64 + change_len as i64 * 4 ;
62
55
// Include any extra bytes required to push an extra output.
63
56
weight_with_change += ( VarInt ( tx. output . len ( ) as u64 + 1 ) . len ( ) - VarInt ( tx. output . len ( ) as u64 ) . len ( ) ) as i64 * 4 ;
64
57
// When calculating weight, add two for the flag bytes
65
58
let change_value: i64 = ( input_value - output_value) as i64 - weight_with_change * feerate_sat_per_1000_weight as i64 / 1000 ;
66
- if change_value >= dust_value as i64 {
59
+ if change_value >= dust_value. as_sat ( ) as i64 {
67
60
change_output. value = change_value as u64 ;
68
61
tx. output . push ( change_output) ;
69
- } else if ( input_value - output_value) as i64 - ( tx. get_weight ( ) as i64 + 2 + witness_max_weight as i64 ) * feerate_sat_per_1000_weight as i64 / 1000 < 0 {
70
- return Err ( ( ) ) ;
62
+ Ok ( weight_with_change as usize )
63
+ } else if ( input_value - output_value) as i64 - ( starting_weight as i64 ) * feerate_sat_per_1000_weight as i64 / 1000 < 0 {
64
+ Err ( ( ) )
65
+ } else {
66
+ Ok ( starting_weight)
71
67
}
72
-
73
- Ok ( ( ) )
74
68
}
75
69
76
70
#[ cfg( test) ]
@@ -79,9 +73,10 @@ mod tests {
79
73
80
74
use bitcoin:: blockdata:: transaction:: { Transaction , TxOut , TxIn , OutPoint } ;
81
75
use bitcoin:: blockdata:: script:: { Script , Builder } ;
82
- use bitcoin:: hash_types:: Txid ;
76
+ use bitcoin:: hash_types:: { PubkeyHash , Txid } ;
83
77
84
78
use bitcoin:: hashes:: sha256d:: Hash as Sha256dHash ;
79
+ use bitcoin:: hashes:: Hash ;
85
80
86
81
use hex:: decode;
87
82
@@ -232,28 +227,30 @@ mod tests {
232
227
// Check that we never add dust outputs
233
228
let mut tx = Transaction { version : 2 , lock_time : 0 , input : Vec :: new ( ) , output : Vec :: new ( ) } ;
234
229
let orig_wtxid = tx. wtxid ( ) ;
230
+ let output_spk = Script :: new_p2pkh ( & PubkeyHash :: hash ( & [ 0 ; 0 ] ) ) ;
231
+ assert_eq ! ( output_spk. dust_value( ) . as_sat( ) , 546 ) ;
235
232
// 9 sats isn't enough to pay fee on a dummy transaction...
236
233
assert_eq ! ( tx. get_weight( ) as u64 , 40 ) ; // ie 10 vbytes
237
- assert ! ( maybe_add_change_output( & mut tx, 9 , 0 , 253 , Script :: new ( ) ) . is_err( ) ) ;
234
+ assert ! ( maybe_add_change_output( & mut tx, 9 , 0 , 250 , output_spk . clone ( ) ) . is_err( ) ) ;
238
235
assert_eq ! ( tx. wtxid( ) , orig_wtxid) ; // Failure doesn't change the transaction
239
236
// but 10-564 is, just not enough to add a change output...
240
- assert ! ( maybe_add_change_output( & mut tx, 10 , 0 , 253 , Script :: new ( ) ) . is_ok( ) ) ;
237
+ assert ! ( maybe_add_change_output( & mut tx, 10 , 0 , 250 , output_spk . clone ( ) ) . is_ok( ) ) ;
241
238
assert_eq ! ( tx. output. len( ) , 0 ) ;
242
239
assert_eq ! ( tx. wtxid( ) , orig_wtxid) ; // If we don't add an output, we don't change the transaction
243
- assert ! ( maybe_add_change_output( & mut tx, 564 , 0 , 253 , Script :: new ( ) ) . is_ok( ) ) ;
240
+ assert ! ( maybe_add_change_output( & mut tx, 549 , 0 , 250 , output_spk . clone ( ) ) . is_ok( ) ) ;
244
241
assert_eq ! ( tx. output. len( ) , 0 ) ;
245
242
assert_eq ! ( tx. wtxid( ) , orig_wtxid) ; // If we don't add an output, we don't change the transaction
246
- // 565 is also not enough, if we anticipate 2 more weight units pushing us up to the next vbyte
243
+ // 590 is also not enough, if we anticipate 2 more weight units pushing us up to the next vbyte
247
244
// (considering the two bytes for segwit flags)
248
- assert ! ( maybe_add_change_output( & mut tx, 565 , 2 , 253 , Script :: new ( ) ) . is_ok( ) ) ;
245
+ assert ! ( maybe_add_change_output( & mut tx, 590 , 2 , 250 , output_spk . clone ( ) ) . is_ok( ) ) ;
249
246
assert_eq ! ( tx. output. len( ) , 0 ) ;
250
247
assert_eq ! ( tx. wtxid( ) , orig_wtxid) ; // If we don't add an output, we don't change the transaction
251
- // at 565 we can afford the change output at the dust limit (546)
252
- assert ! ( maybe_add_change_output( & mut tx, 565 , 0 , 253 , Script :: new ( ) ) . is_ok( ) ) ;
248
+ // at 590 we can afford the change output at the dust limit (546)
249
+ assert ! ( maybe_add_change_output( & mut tx, 590 , 0 , 250 , output_spk . clone ( ) ) . is_ok( ) ) ;
253
250
assert_eq ! ( tx. output. len( ) , 1 ) ;
254
251
assert_eq ! ( tx. output[ 0 ] . value, 546 ) ;
255
- assert_eq ! ( tx. output[ 0 ] . script_pubkey, Script :: new ( ) ) ;
256
- assert_eq ! ( tx. get_weight( ) / 4 , 565 -546 ) ; // New weight is exactly the fee we wanted.
252
+ assert_eq ! ( tx. output[ 0 ] . script_pubkey, output_spk ) ;
253
+ assert_eq ! ( tx. get_weight( ) / 4 , 590 -546 ) ; // New weight is exactly the fee we wanted.
257
254
258
255
tx. output . pop ( ) ;
259
256
assert_eq ! ( tx. wtxid( ) , orig_wtxid) ; // The only change is the addition of one output.
@@ -271,19 +268,21 @@ mod tests {
271
268
let orig_weight = tx. get_weight ( ) ;
272
269
assert_eq ! ( orig_weight / 4 , 61 ) ;
273
270
271
+ assert_eq ! ( Builder :: new( ) . push_int( 2 ) . into_script( ) . dust_value( ) . as_sat( ) , 474 ) ;
272
+
274
273
// Input value of the output value + fee - 1 should fail:
275
274
assert ! ( maybe_add_change_output( & mut tx, 1000 + 61 + 100 - 1 , 400 , 250 , Builder :: new( ) . push_int( 2 ) . into_script( ) ) . is_err( ) ) ;
276
275
assert_eq ! ( tx. wtxid( ) , orig_wtxid) ; // Failure doesn't change the transaction
277
276
// but one more input sat should succeed, without changing the transaction
278
277
assert ! ( maybe_add_change_output( & mut tx, 1000 + 61 + 100 , 400 , 250 , Builder :: new( ) . push_int( 2 ) . into_script( ) ) . is_ok( ) ) ;
279
278
assert_eq ! ( tx. wtxid( ) , orig_wtxid) ; // If we don't add an output, we don't change the transaction
280
- // In order to get a change output, we need to add 546 plus the output's weight / 4 (10)...
281
- assert ! ( maybe_add_change_output( & mut tx, 1000 + 61 + 100 + 546 + 9 , 400 , 250 , Builder :: new( ) . push_int( 2 ) . into_script( ) ) . is_ok( ) ) ;
279
+ // In order to get a change output, we need to add 474 plus the output's weight / 4 (10)...
280
+ assert ! ( maybe_add_change_output( & mut tx, 1000 + 61 + 100 + 474 + 9 , 400 , 250 , Builder :: new( ) . push_int( 2 ) . into_script( ) ) . is_ok( ) ) ;
282
281
assert_eq ! ( tx. wtxid( ) , orig_wtxid) ; // If we don't add an output, we don't change the transaction
283
282
284
- assert ! ( maybe_add_change_output( & mut tx, 1000 + 61 + 100 + 546 + 10 , 400 , 250 , Builder :: new( ) . push_int( 2 ) . into_script( ) ) . is_ok( ) ) ;
283
+ assert ! ( maybe_add_change_output( & mut tx, 1000 + 61 + 100 + 474 + 10 , 400 , 250 , Builder :: new( ) . push_int( 2 ) . into_script( ) ) . is_ok( ) ) ;
285
284
assert_eq ! ( tx. output. len( ) , 2 ) ;
286
- assert_eq ! ( tx. output[ 1 ] . value, 546 ) ;
285
+ assert_eq ! ( tx. output[ 1 ] . value, 474 ) ;
287
286
assert_eq ! ( tx. output[ 1 ] . script_pubkey, Builder :: new( ) . push_int( 2 ) . into_script( ) ) ;
288
287
assert_eq ! ( tx. get_weight( ) - orig_weight, 40 ) ; // Weight difference matches what we had to add above
289
288
tx. output . pop ( ) ;
0 commit comments