@@ -16,15 +16,22 @@ use crate::{
16
16
} ,
17
17
wit:: Wit ,
18
18
} ;
19
+ use itertools:: Itertools ;
20
+
21
+ #[ derive( Clone , Debug ) ]
22
+ pub struct CollectedOutputs {
23
+ pub pointers : Vec < OutputPointer > ,
24
+ pub resolved : Vec < ValueTransferOutput > ,
25
+ pub total_value : u64 ,
26
+ }
19
27
20
28
/// Structure that resumes the information needed to create a Transaction
21
29
#[ derive( Clone , Debug ) ]
22
30
pub struct TransactionInfo {
23
- pub inputs : Vec < Input > ,
24
- pub outputs : Vec < ValueTransferOutput > ,
25
- pub input_value : u64 ,
26
- pub output_value : u64 ,
27
31
pub fee : AbsoluteFee ,
32
+ pub inputs : CollectedOutputs ,
33
+ pub output_value : u64 ,
34
+ pub outputs : Vec < ValueTransferOutput > ,
28
35
}
29
36
30
37
// Structure that the includes the confirmed and pending balance of a node
@@ -41,10 +48,11 @@ pub struct NodeBalance {
41
48
/// can be applied on many heterogeneous data structures that may implement it.
42
49
pub trait OutputsCollection {
43
50
fn sort_by ( & self , strategy : & UtxoSelectionStrategy ) -> Vec < OutputPointer > ;
44
- fn get_time_lock ( & self , outptr : & OutputPointer ) -> Option < u64 > ;
51
+ fn get ( & self , outptr : & OutputPointer ) -> Option < ValueTransferOutput > ;
52
+ fn get_usage_timeout ( & self , outptr : & OutputPointer ) -> Option < u64 > ;
45
53
fn get_value ( & self , outptr : & OutputPointer ) -> Option < u64 > ;
46
54
fn get_included_block_number ( & self , outptr : & OutputPointer ) -> Option < Epoch > ;
47
- fn set_used_output_pointer ( & mut self , outptrs : & [ Input ] , ts : u64 ) ;
55
+ fn set_used_output_pointer ( & mut self , outptrs : impl Iterator < Item = Input > , ts : u64 ) ;
48
56
49
57
/// Select enough UTXOs to sum up to `amount`.
50
58
///
@@ -57,27 +65,28 @@ pub trait OutputsCollection {
57
65
// The block number must be lower than this limit
58
66
block_number_limit : Option < u32 > ,
59
67
utxo_strategy : & UtxoSelectionStrategy ,
60
- ) -> Result < ( Vec < OutputPointer > , u64 ) , TransactionError > {
68
+ ) -> Result < CollectedOutputs , TransactionError > {
61
69
// FIXME: this is a very naive utxo selection algorithm
62
70
if amount == 0 {
63
71
// Transactions with no inputs make no sense
64
72
return Err ( TransactionError :: ZeroAmount ) ;
65
73
}
66
74
67
- let mut acc = 0 ;
75
+ let mut total_value = 0 ;
68
76
let mut total: u64 = 0 ;
69
- let mut list = vec ! [ ] ;
77
+ let mut outputs = vec ! [ ] ;
78
+ let mut pointers = vec ! [ ] ;
70
79
71
80
let utxo_iter = self . sort_by ( utxo_strategy) ;
72
81
73
- for op in utxo_iter. iter ( ) {
74
- let value = self . get_value ( op ) . unwrap ( ) ;
82
+ for pointer in utxo_iter. iter ( ) {
83
+ let output : ValueTransferOutput = self . get ( pointer ) . unwrap ( ) ;
75
84
total = total
76
- . checked_add ( value)
85
+ . checked_add ( output . value )
77
86
. ok_or ( TransactionError :: OutputValueOverflow ) ?;
78
87
79
- if let Some ( time_lock ) = self . get_time_lock ( op ) {
80
- if time_lock > timestamp {
88
+ if let Some ( usage_timeout ) = self . get_usage_timeout ( pointer ) {
89
+ if usage_timeout > timestamp {
81
90
continue ;
82
91
}
83
92
}
@@ -86,7 +95,7 @@ pub trait OutputsCollection {
86
95
// Ignore all outputs created after `block_number_limit`.
87
96
// Outputs from the genesis block will never be ignored because `block_number_limit`
88
97
// can't go lower than `0`.
89
- if let Some ( limit) = self . get_included_block_number ( op ) {
98
+ if let Some ( limit) = self . get_included_block_number ( pointer ) {
90
99
if limit > block_number_limit {
91
100
continue ;
92
101
}
@@ -95,20 +104,25 @@ pub trait OutputsCollection {
95
104
}
96
105
}
97
106
98
- acc += value;
99
- list. push ( * op) ;
107
+ total_value += output. value ;
108
+ pointers. push ( * pointer) ;
109
+ outputs. push ( output) ;
100
110
101
- if acc >= amount {
111
+ if total_value >= amount {
102
112
break ;
103
113
}
104
114
}
105
115
106
- if acc >= amount {
107
- Ok ( ( list, acc) )
116
+ if total_value >= amount {
117
+ Ok ( CollectedOutputs {
118
+ resolved : outputs,
119
+ pointers,
120
+ total_value,
121
+ } )
108
122
} else {
109
123
Err ( TransactionError :: NoMoney {
110
124
total_balance : total,
111
- available_balance : acc ,
125
+ available_balance : total_value ,
112
126
transaction_value : amount,
113
127
} )
114
128
}
@@ -148,16 +162,14 @@ pub trait OutputsCollection {
148
162
. checked_add ( absolute_fee. as_nanowits ( ) )
149
163
. ok_or ( TransactionError :: FeeOverflow ) ?;
150
164
151
- let ( output_pointers , input_value ) =
165
+ let inputs =
152
166
self . take_enough_utxos ( amount, timestamp, block_number_limit, utxo_strategy) ?;
153
- let inputs: Vec < Input > = output_pointers. into_iter ( ) . map ( Input :: new) . collect ( ) ;
154
167
155
168
Ok ( TransactionInfo {
169
+ fee : absolute_fee,
156
170
inputs,
157
- outputs,
158
- input_value,
159
171
output_value,
160
- fee : absolute_fee ,
172
+ outputs ,
161
173
} )
162
174
}
163
175
Fee :: Relative ( priority) => {
@@ -168,21 +180,23 @@ pub trait OutputsCollection {
168
180
. checked_add ( absolute_fee. as_nanowits ( ) )
169
181
. ok_or ( TransactionError :: FeeOverflow ) ?;
170
182
171
- let ( output_pointers , input_value ) = self . take_enough_utxos (
183
+ let inputs = self . take_enough_utxos (
172
184
amount,
173
185
timestamp,
174
186
block_number_limit,
175
187
utxo_strategy,
176
188
) ?;
177
- let inputs: Vec < Input > = output_pointers. into_iter ( ) . map ( Input :: new) . collect ( ) ;
178
189
179
- let new_weight =
180
- calculate_weight ( inputs. len ( ) , outputs. len ( ) + 1 , dr_output, max_weight) ?;
190
+ let new_weight = calculate_weight (
191
+ inputs. pointers . len ( ) ,
192
+ outputs. len ( ) + 1 ,
193
+ dr_output,
194
+ max_weight,
195
+ ) ?;
181
196
if new_weight == current_weight {
182
197
return Ok ( TransactionInfo {
183
198
inputs,
184
199
outputs,
185
- input_value,
186
200
output_value,
187
201
fee : absolute_fee,
188
202
} ) ;
@@ -316,21 +330,23 @@ pub fn build_vtt(
316
330
max_weight,
317
331
) ?;
318
332
333
+ let used_pointers = tx_info. inputs . pointers . iter ( ) . cloned ( ) . map ( Input :: new) ;
334
+
319
335
// Mark UTXOs as used so we don't double spend
320
336
// Save the timestamp after which the transaction will be considered timed out
321
337
// and the output will become available for spending it again
322
338
if !dry_run {
323
- utxos. set_used_output_pointer ( & tx_info . inputs , timestamp + tx_pending_timeout) ;
339
+ utxos. set_used_output_pointer ( used_pointers . clone ( ) , timestamp + tx_pending_timeout) ;
324
340
}
325
341
326
342
let mut outputs = tx_info. outputs ;
327
343
insert_change_output (
328
344
& mut outputs,
329
345
own_pkh,
330
- tx_info. input_value - tx_info. output_value - tx_info. fee . as_nanowits ( ) ,
346
+ tx_info. inputs . total_value - tx_info. output_value - tx_info. fee . as_nanowits ( ) ,
331
347
) ;
332
348
333
- Ok ( VTTransactionBody :: new ( tx_info . inputs , outputs) )
349
+ Ok ( VTTransactionBody :: new ( used_pointers . collect_vec ( ) , outputs) )
334
350
}
335
351
336
352
/// Build data request transaction with the given outputs and fee.
@@ -362,21 +378,27 @@ pub fn build_drt(
362
378
max_weight,
363
379
) ?;
364
380
381
+ let used_pointers = tx_info. inputs . pointers . iter ( ) . cloned ( ) . map ( Input :: new) ;
382
+
365
383
// Mark UTXOs as used so we don't double spend
366
384
// Save the timestamp after which the transaction will be considered timed out
367
385
// and the output will become available for spending it again
368
386
if !dry_run {
369
- utxos. set_used_output_pointer ( & tx_info . inputs , timestamp + tx_pending_timeout) ;
387
+ utxos. set_used_output_pointer ( used_pointers . clone ( ) , timestamp + tx_pending_timeout) ;
370
388
}
371
389
372
390
let mut outputs = tx_info. outputs ;
373
391
insert_change_output (
374
392
& mut outputs,
375
393
own_pkh,
376
- tx_info. input_value - tx_info. output_value - tx_info. fee . as_nanowits ( ) ,
394
+ tx_info. inputs . total_value - tx_info. output_value - tx_info. fee . as_nanowits ( ) ,
377
395
) ;
378
396
379
- Ok ( DRTransactionBody :: new ( tx_info. inputs , outputs, dr_output) )
397
+ Ok ( DRTransactionBody :: new (
398
+ used_pointers. collect_vec ( ) ,
399
+ outputs,
400
+ dr_output,
401
+ ) )
380
402
}
381
403
382
404
/// Check if there are enough collateral for a CommitTransaction
@@ -438,19 +460,21 @@ pub fn build_commit_collateral(
438
460
u32:: MAX ,
439
461
) ?;
440
462
463
+ let used_pointers = tx_info. inputs . pointers . iter ( ) . cloned ( ) . map ( Input :: new) ;
464
+
441
465
// Mark UTXOs as used so we don't double spend
442
466
// Save the timestamp after which the transaction will be considered timed out
443
467
// and the output will become available for spending it again
444
- utxos. set_used_output_pointer ( & tx_info . inputs , timestamp + tx_pending_timeout) ;
468
+ utxos. set_used_output_pointer ( used_pointers . clone ( ) , timestamp + tx_pending_timeout) ;
445
469
446
470
let mut outputs = tx_info. outputs ;
447
471
insert_change_output (
448
472
& mut outputs,
449
473
own_pkh,
450
- tx_info. input_value - tx_info. output_value - tx_info. fee . as_nanowits ( ) ,
474
+ tx_info. inputs . total_value - tx_info. output_value - tx_info. fee . as_nanowits ( ) ,
451
475
) ;
452
476
453
- Ok ( ( tx_info . inputs , outputs) )
477
+ Ok ( ( used_pointers . collect_vec ( ) , outputs) )
454
478
}
455
479
456
480
/// Calculate the sum of the values of the outputs pointed by the
0 commit comments