8
8
// licenses.
9
9
10
10
use bitcoin:: amount:: Amount ;
11
- use bitcoin:: transaction:: { Transaction , TxOut } ;
12
- use bitcoin:: script:: ScriptBuf ;
13
- use bitcoin:: consensus:: Encodable ;
14
11
use bitcoin:: consensus:: encode:: VarInt ;
12
+ use bitcoin:: consensus:: Encodable ;
13
+ use bitcoin:: script:: ScriptBuf ;
14
+ use bitcoin:: transaction:: { Transaction , TxOut } ;
15
15
16
16
#[ allow( unused_imports) ]
17
17
use crate :: prelude:: * ;
18
18
19
19
use crate :: io_extras:: sink;
20
20
use core:: cmp:: Ordering ;
21
21
22
- pub fn sort_outputs < T , C : Fn ( & T , & T ) -> Ordering > ( outputs : & mut Vec < ( TxOut , T ) > , tie_breaker : C ) {
22
+ pub fn sort_outputs < T , C : Fn ( & T , & T ) -> Ordering > ( outputs : & mut Vec < ( TxOut , T ) > , tie_breaker : C ) {
23
23
outputs. sort_unstable_by ( |a, b| {
24
24
a. 0 . value . cmp ( & b. 0 . value ) . then_with ( || {
25
- a. 0 . script_pubkey [ ..] . cmp ( & b. 0 . script_pubkey [ ..] ) . then_with ( || {
26
- tie_breaker ( & a. 1 , & b. 1 )
27
- } )
25
+ a. 0 . script_pubkey [ ..] . cmp ( & b. 0 . script_pubkey [ ..] ) . then_with ( || tie_breaker ( & a. 1 , & b. 1 ) )
28
26
} )
29
27
} ) ;
30
28
}
@@ -34,34 +32,44 @@ pub fn sort_outputs<T, C : Fn(&T, &T) -> Ordering>(outputs: &mut Vec<(TxOut, T)>
34
32
/// Assumes at least one input will have a witness (ie spends a segwit output).
35
33
/// Returns an Err(()) if the requested feerate cannot be met.
36
34
/// Returns the expected maximum weight of the fully signed transaction on success.
37
- pub ( crate ) fn maybe_add_change_output ( tx : & mut Transaction , input_value : Amount , witness_max_weight : u64 , feerate_sat_per_1000_weight : u32 , change_destination_script : ScriptBuf ) -> Result < u64 , ( ) > {
38
- if input_value > Amount :: MAX_MONEY { return Err ( ( ) ) ; }
35
+ pub ( crate ) fn maybe_add_change_output (
36
+ tx : & mut Transaction , input_value : Amount , witness_max_weight : u64 ,
37
+ feerate_sat_per_1000_weight : u32 , change_destination_script : ScriptBuf ,
38
+ ) -> Result < u64 , ( ) > {
39
+ if input_value > Amount :: MAX_MONEY {
40
+ return Err ( ( ) ) ;
41
+ }
39
42
40
43
const WITNESS_FLAG_BYTES : u64 = 2 ;
41
44
42
45
let mut output_value = Amount :: ZERO ;
43
46
for output in tx. output . iter ( ) {
44
47
output_value += output. value ;
45
- if output_value >= input_value { return Err ( ( ) ) ; }
48
+ if output_value >= input_value {
49
+ return Err ( ( ) ) ;
50
+ }
46
51
}
47
52
48
53
let dust_value = change_destination_script. minimal_non_dust ( ) ;
49
- let mut change_output = TxOut {
50
- script_pubkey : change_destination_script,
51
- value : Amount :: ZERO ,
52
- } ;
54
+ let mut change_output = TxOut { script_pubkey : change_destination_script, value : Amount :: ZERO } ;
53
55
let change_len = change_output. consensus_encode ( & mut sink ( ) ) . unwrap ( ) ;
54
56
let starting_weight = tx. weight ( ) . to_wu ( ) + WITNESS_FLAG_BYTES + witness_max_weight as u64 ;
55
57
let mut weight_with_change: i64 = starting_weight as i64 + change_len as i64 * 4 ;
56
58
// 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 ;
59
+ weight_with_change += ( VarInt ( tx. output . len ( ) as u64 + 1 ) . size ( )
60
+ - VarInt ( tx. output . len ( ) as u64 ) . size ( ) ) as i64
61
+ * 4 ;
58
62
// 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 ;
63
+ let change_value: i64 = ( input_value - output_value) . to_sat ( ) as i64
64
+ - weight_with_change * feerate_sat_per_1000_weight as i64 / 1000 ;
60
65
if change_value >= dust_value. to_sat ( ) as i64 {
61
66
change_output. value = Amount :: from_sat ( change_value as u64 ) ;
62
67
tx. output . push ( change_output) ;
63
68
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 {
69
+ } else if ( input_value - output_value) . to_sat ( ) as i64
70
+ - ( starting_weight as i64 ) * feerate_sat_per_1000_weight as i64 / 1000
71
+ < 0
72
+ {
65
73
Err ( ( ) )
66
74
} else {
67
75
Ok ( starting_weight)
@@ -73,60 +81,58 @@ mod tests {
73
81
use super :: * ;
74
82
75
83
use bitcoin:: amount:: Amount ;
76
- use bitcoin:: locktime:: absolute:: LockTime ;
77
- use bitcoin:: transaction:: { TxIn , OutPoint , Version } ;
78
- use bitcoin:: script:: Builder ;
79
84
use bitcoin:: hash_types:: Txid ;
80
85
use bitcoin:: hashes:: Hash ;
81
86
use bitcoin:: hex:: FromHex ;
87
+ use bitcoin:: locktime:: absolute:: LockTime ;
88
+ use bitcoin:: script:: Builder ;
89
+ use bitcoin:: transaction:: { OutPoint , TxIn , Version } ;
82
90
use bitcoin:: { PubkeyHash , Sequence , Witness } ;
83
91
84
92
use alloc:: vec;
85
93
86
94
#[ test]
87
95
fn sort_output_by_value ( ) {
88
96
let txout1 = TxOut {
89
- value : Amount :: from_sat ( 100 ) ,
90
- script_pubkey : Builder :: new ( ) . push_int ( 0 ) . into_script ( )
97
+ value : Amount :: from_sat ( 100 ) ,
98
+ script_pubkey : Builder :: new ( ) . push_int ( 0 ) . into_script ( ) ,
91
99
} ;
92
100
let txout1_ = txout1. clone ( ) ;
93
101
94
102
let txout2 = TxOut {
95
103
value : Amount :: from_sat ( 99 ) ,
96
- script_pubkey : Builder :: new ( ) . push_int ( 0 ) . into_script ( )
104
+ script_pubkey : Builder :: new ( ) . push_int ( 0 ) . into_script ( ) ,
97
105
} ;
98
106
let txout2_ = txout2. clone ( ) ;
99
107
100
108
let mut outputs = vec ! [ ( txout1, "ignore" ) , ( txout2, "ignore" ) ] ;
101
- sort_outputs ( & mut outputs, |_, _| { unreachable ! ( ) ; } ) ;
109
+ sort_outputs ( & mut outputs, |_, _| {
110
+ unreachable ! ( ) ;
111
+ } ) ;
102
112
103
- assert_eq ! (
104
- & outputs,
105
- & vec![ ( txout2_, "ignore" ) , ( txout1_, "ignore" ) ]
106
- ) ;
113
+ assert_eq ! ( & outputs, & vec![ ( txout2_, "ignore" ) , ( txout1_, "ignore" ) ] ) ;
107
114
}
108
115
109
116
#[ test]
110
117
fn sort_output_by_script_pubkey ( ) {
111
118
let txout1 = TxOut {
112
- value : Amount :: from_sat ( 100 ) ,
119
+ value : Amount :: from_sat ( 100 ) ,
113
120
script_pubkey : Builder :: new ( ) . push_int ( 3 ) . into_script ( ) ,
114
121
} ;
115
122
let txout1_ = txout1. clone ( ) ;
116
123
117
124
let txout2 = TxOut {
118
125
value : Amount :: from_sat ( 100 ) ,
119
- script_pubkey : Builder :: new ( ) . push_int ( 1 ) . push_int ( 2 ) . into_script ( )
126
+ script_pubkey : Builder :: new ( ) . push_int ( 1 ) . push_int ( 2 ) . into_script ( ) ,
120
127
} ;
121
128
let txout2_ = txout2. clone ( ) ;
122
129
123
130
let mut outputs = vec ! [ ( txout1, "ignore" ) , ( txout2, "ignore" ) ] ;
124
- sort_outputs ( & mut outputs, |_, _| { unreachable ! ( ) ; } ) ;
131
+ sort_outputs ( & mut outputs, |_, _| {
132
+ unreachable ! ( ) ;
133
+ } ) ;
125
134
126
- assert_eq ! (
127
- & outputs,
128
- & vec![ ( txout2_, "ignore" ) , ( txout1_, "ignore" ) ]
129
- ) ;
135
+ assert_eq ! ( & outputs, & vec![ ( txout2_, "ignore" ) , ( txout1_, "ignore" ) ] ) ;
130
136
}
131
137
132
138
#[ test]
@@ -145,29 +151,28 @@ mod tests {
145
151
let txout2_ = txout2. clone ( ) ;
146
152
147
153
let mut outputs = vec ! [ ( txout1, "ignore" ) , ( txout2, "ignore" ) ] ;
148
- sort_outputs ( & mut outputs, |_, _| { unreachable ! ( ) ; } ) ;
154
+ sort_outputs ( & mut outputs, |_, _| {
155
+ unreachable ! ( ) ;
156
+ } ) ;
149
157
150
158
assert_eq ! ( & outputs, & vec![ ( txout1_, "ignore" ) , ( txout2_, "ignore" ) ] ) ;
151
159
}
152
160
153
161
#[ test]
154
162
fn sort_output_tie_breaker_test ( ) {
155
163
let txout1 = TxOut {
156
- value : Amount :: from_sat ( 100 ) ,
157
- script_pubkey : Builder :: new ( ) . push_int ( 1 ) . push_int ( 2 ) . into_script ( )
164
+ value : Amount :: from_sat ( 100 ) ,
165
+ script_pubkey : Builder :: new ( ) . push_int ( 1 ) . push_int ( 2 ) . into_script ( ) ,
158
166
} ;
159
167
let txout1_ = txout1. clone ( ) ;
160
168
161
169
let txout2 = txout1. clone ( ) ;
162
170
let txout2_ = txout1. clone ( ) ;
163
171
164
172
let mut outputs = vec ! [ ( txout1, 420 ) , ( txout2, 69 ) ] ;
165
- sort_outputs ( & mut outputs, |a, b| { a. cmp ( b) } ) ;
173
+ sort_outputs ( & mut outputs, |a, b| a. cmp ( b) ) ;
166
174
167
- assert_eq ! (
168
- & outputs,
169
- & vec![ ( txout2_, 69 ) , ( txout1_, 420 ) ]
170
- ) ;
175
+ assert_eq ! ( & outputs, & vec![ ( txout2_, 69 ) , ( txout1_, 420 ) ] ) ;
171
176
}
172
177
173
178
fn script_from_hex ( hex_str : & str ) -> ScriptBuf {
@@ -215,18 +220,35 @@ mod tests {
215
220
#[ test]
216
221
fn test_tx_value_overrun ( ) {
217
222
// 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( ) ) ;
223
+ let mut tx = Transaction {
224
+ version : Version :: TWO ,
225
+ lock_time : LockTime :: ZERO ,
226
+ input : Vec :: new ( ) ,
227
+ output : vec ! [ TxOut { script_pubkey: ScriptBuf :: new( ) , value: Amount :: from_sat( 1000 ) } ] ,
228
+ } ;
229
+ assert ! ( maybe_add_change_output(
230
+ & mut tx,
231
+ Amount :: from_sat( 21_000_000_0000_0001 ) ,
232
+ 0 ,
233
+ 253 ,
234
+ ScriptBuf :: new( )
235
+ )
236
+ . is_err( ) ) ;
237
+ assert ! ( maybe_add_change_output( & mut tx, Amount :: from_sat( 400 ) , 0 , 253 , ScriptBuf :: new( ) )
238
+ . is_err( ) ) ;
239
+ assert ! ( maybe_add_change_output( & mut tx, Amount :: from_sat( 4000 ) , 0 , 253 , ScriptBuf :: new( ) )
240
+ . is_ok( ) ) ;
224
241
}
225
242
226
243
#[ test]
227
244
fn test_tx_change_edge ( ) {
228
245
// 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 ( ) } ;
246
+ let mut tx = Transaction {
247
+ version : Version :: TWO ,
248
+ lock_time : LockTime :: ZERO ,
249
+ input : Vec :: new ( ) ,
250
+ output : Vec :: new ( ) ,
251
+ } ;
230
252
let orig_wtxid = tx. compute_wtxid ( ) ;
231
253
let output_spk = ScriptBuf :: new_p2pkh ( & PubkeyHash :: hash ( & [ 0 ; 0 ] ) ) ;
232
254
assert_eq ! ( output_spk. minimal_non_dust( ) . to_sat( ) , 546 ) ;
@@ -235,25 +257,48 @@ mod tests {
235
257
// 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
258
assert_eq ! ( tx. weight( ) . to_wu( ) , 42 ) ;
237
259
// 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( ) ) ;
260
+ assert ! ( maybe_add_change_output( & mut tx, Amount :: from_sat( 10 ) , 0 , 250 , output_spk. clone( ) )
261
+ . is_err( ) ) ;
239
262
assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ; // Failure doesn't change the transaction
240
- // 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( ) ) ;
263
+ // but 11 (= ceil(42 * 250 / 1000)) is, just not enough to add a change output...
264
+ assert ! ( maybe_add_change_output( & mut tx, Amount :: from_sat( 11 ) , 0 , 250 , output_spk. clone( ) )
265
+ . is_ok( ) ) ;
242
266
assert_eq ! ( tx. output. len( ) , 0 ) ;
243
267
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( ) ) ;
268
+ assert ! ( maybe_add_change_output(
269
+ & mut tx,
270
+ Amount :: from_sat( 549 ) ,
271
+ 0 ,
272
+ 250 ,
273
+ output_spk. clone( )
274
+ )
275
+ . is_ok( ) ) ;
245
276
assert_eq ! ( tx. output. len( ) , 0 ) ;
246
277
assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ; // If we don't add an output, we don't change the transaction
247
- // 590 is also not enough
248
- assert ! ( maybe_add_change_output( & mut tx, Amount :: from_sat( 590 ) , 0 , 250 , output_spk. clone( ) ) . is_ok( ) ) ;
278
+ // 590 is also not enough
279
+ assert ! ( maybe_add_change_output(
280
+ & mut tx,
281
+ Amount :: from_sat( 590 ) ,
282
+ 0 ,
283
+ 250 ,
284
+ output_spk. clone( )
285
+ )
286
+ . is_ok( ) ) ;
249
287
assert_eq ! ( tx. output. len( ) , 0 ) ;
250
288
assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ; // If we don't add an output, we don't change the transaction
251
- // 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( ) ) ;
289
+ // at 591 we can afford the change output at the dust limit (546)
290
+ assert ! ( maybe_add_change_output(
291
+ & mut tx,
292
+ Amount :: from_sat( 591 ) ,
293
+ 0 ,
294
+ 250 ,
295
+ output_spk. clone( )
296
+ )
297
+ . is_ok( ) ) ;
253
298
assert_eq ! ( tx. output. len( ) , 1 ) ;
254
299
assert_eq ! ( tx. output[ 0 ] . value. to_sat( ) , 546 ) ;
255
300
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.
301
+ assert_eq ! ( tx. weight( ) . to_wu( ) / 4 , 590 - 546 ) ; // New weight is exactly the fee we wanted.
257
302
258
303
tx. output . pop ( ) ;
259
304
assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ; // The only change is the addition of one output.
@@ -262,28 +307,65 @@ mod tests {
262
307
#[ test]
263
308
fn test_tx_extra_outputs ( ) {
264
309
// 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
- } ] } ;
310
+ let mut tx = Transaction {
311
+ version : Version :: TWO ,
312
+ lock_time : LockTime :: ZERO ,
313
+ input : vec ! [ TxIn {
314
+ previous_output: OutPoint :: new( Txid :: all_zeros( ) , 0 ) ,
315
+ script_sig: ScriptBuf :: new( ) ,
316
+ witness: Witness :: new( ) ,
317
+ sequence: Sequence :: ZERO ,
318
+ } ] ,
319
+ output : vec ! [ TxOut {
320
+ script_pubkey: Builder :: new( ) . push_int( 1 ) . into_script( ) ,
321
+ value: Amount :: from_sat( 1000 ) ,
322
+ } ] ,
323
+ } ;
270
324
let orig_wtxid = tx. compute_wtxid ( ) ;
271
325
let orig_weight = tx. weight ( ) . to_wu ( ) ;
272
326
assert_eq ! ( orig_weight / 4 , 61 ) ;
273
327
274
328
assert_eq ! ( Builder :: new( ) . push_int( 2 ) . into_script( ) . minimal_non_dust( ) . to_sat( ) , 474 ) ;
275
329
276
330
// 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( ) ) ;
331
+ assert ! ( maybe_add_change_output(
332
+ & mut tx,
333
+ Amount :: from_sat( 1000 + 61 + 100 - 1 ) ,
334
+ 400 ,
335
+ 250 ,
336
+ Builder :: new( ) . push_int( 2 ) . into_script( )
337
+ )
338
+ . is_err( ) ) ;
278
339
assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ; // Failure doesn't change the transaction
279
- // 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( ) ) ;
340
+ // but one more input sat should succeed, without changing the transaction
341
+ assert ! ( maybe_add_change_output(
342
+ & mut tx,
343
+ Amount :: from_sat( 1000 + 61 + 100 ) ,
344
+ 400 ,
345
+ 250 ,
346
+ Builder :: new( ) . push_int( 2 ) . into_script( )
347
+ )
348
+ . is_ok( ) ) ;
281
349
assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ; // If we don't add an output, we don't change the transaction
282
- // 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( ) ) ;
350
+ // In order to get a change output, we need to add 474 plus the output's weight / 4 (10)...
351
+ assert ! ( maybe_add_change_output(
352
+ & mut tx,
353
+ Amount :: from_sat( 1000 + 61 + 100 + 474 + 9 ) ,
354
+ 400 ,
355
+ 250 ,
356
+ Builder :: new( ) . push_int( 2 ) . into_script( )
357
+ )
358
+ . is_ok( ) ) ;
284
359
assert_eq ! ( tx. compute_wtxid( ) , orig_wtxid) ; // If we don't add an output, we don't change the transaction
285
360
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( ) ) ;
361
+ assert ! ( maybe_add_change_output(
362
+ & mut tx,
363
+ Amount :: from_sat( 1000 + 61 + 100 + 474 + 10 ) ,
364
+ 400 ,
365
+ 250 ,
366
+ Builder :: new( ) . push_int( 2 ) . into_script( )
367
+ )
368
+ . is_ok( ) ) ;
287
369
assert_eq ! ( tx. output. len( ) , 2 ) ;
288
370
assert_eq ! ( tx. output[ 1 ] . value. to_sat( ) , 474 ) ;
289
371
assert_eq ! ( tx. output[ 1 ] . script_pubkey, Builder :: new( ) . push_int( 2 ) . into_script( ) ) ;
0 commit comments