@@ -69,7 +69,8 @@ impl TrackedSpendableOutput {
69
69
}
70
70
}
71
71
72
- fn is_spent_in ( & self , tx : & Transaction ) -> bool {
72
+ /// Returns whether the output is spent in the given transaction.
73
+ pub fn is_spent_in ( & self , tx : & Transaction ) -> bool {
73
74
let prev_outpoint = match & self . descriptor {
74
75
SpendableOutputDescriptor :: StaticOutput { outpoint, .. } => * outpoint,
75
76
SpendableOutputDescriptor :: DelayedPaymentOutput ( output) => output. outpoint ,
@@ -92,7 +93,10 @@ impl_writeable_tlv_based!(TrackedSpendableOutput, {
92
93
pub enum OutputSpendStatus {
93
94
/// The output is tracked but an initial spending transaction hasn't been generated and
94
95
/// broadcasted yet.
95
- PendingInitialBroadcast ,
96
+ PendingInitialBroadcast {
97
+ /// The height at which we will first generate and broadcast a spending transaction.
98
+ delayed_until_height : Option < u32 > ,
99
+ } ,
96
100
/// A transaction spending the output has been broadcasted but is pending its first confirmation on-chain.
97
101
PendingFirstConfirmation {
98
102
/// The hash of the chain tip when we first broadcast a transaction spending this output.
@@ -121,7 +125,13 @@ pub enum OutputSpendStatus {
121
125
impl OutputSpendStatus {
122
126
fn broadcast ( & mut self , cur_hash : BlockHash , cur_height : u32 , latest_spending_tx : Transaction ) {
123
127
match self {
124
- Self :: PendingInitialBroadcast => {
128
+ Self :: PendingInitialBroadcast { delayed_until_height } => {
129
+ if let Some ( delayed_until_height) = delayed_until_height {
130
+ debug_assert ! (
131
+ cur_height >= * delayed_until_height,
132
+ "We should never broadcast before the required height is reached."
133
+ ) ;
134
+ }
125
135
* self = Self :: PendingFirstConfirmation {
126
136
first_broadcast_hash : cur_hash,
127
137
latest_broadcast_height : cur_height,
@@ -146,7 +156,7 @@ impl OutputSpendStatus {
146
156
latest_spending_tx : Transaction ,
147
157
) {
148
158
match self {
149
- Self :: PendingInitialBroadcast => {
159
+ Self :: PendingInitialBroadcast { .. } => {
150
160
// Generally we can't see any of our transactions confirmed if they haven't been
151
161
// broadcasted yet, so this should never be reachable via `transactions_confirmed`.
152
162
debug_assert ! ( false , "We should never confirm when we haven't broadcasted. This a bug and should never happen, please report." ) ;
@@ -190,7 +200,7 @@ impl OutputSpendStatus {
190
200
191
201
fn unconfirmed ( & mut self ) {
192
202
match self {
193
- Self :: PendingInitialBroadcast => {
203
+ Self :: PendingInitialBroadcast { .. } => {
194
204
debug_assert ! (
195
205
false ,
196
206
"We should only mark a spend as unconfirmed if it used to be confirmed."
@@ -217,9 +227,19 @@ impl OutputSpendStatus {
217
227
}
218
228
}
219
229
230
+ fn is_delayed ( & self , cur_height : u32 ) -> bool {
231
+ match self {
232
+ Self :: PendingInitialBroadcast { delayed_until_height } => {
233
+ delayed_until_height. map_or ( false , |req_height| cur_height < req_height)
234
+ } ,
235
+ Self :: PendingFirstConfirmation { .. } => false ,
236
+ Self :: PendingThresholdConfirmations { .. } => false ,
237
+ }
238
+ }
239
+
220
240
fn first_broadcast_hash ( & self ) -> Option < BlockHash > {
221
241
match self {
222
- Self :: PendingInitialBroadcast => None ,
242
+ Self :: PendingInitialBroadcast { .. } => None ,
223
243
Self :: PendingFirstConfirmation { first_broadcast_hash, .. } => {
224
244
Some ( * first_broadcast_hash)
225
245
} ,
@@ -231,7 +251,7 @@ impl OutputSpendStatus {
231
251
232
252
fn latest_broadcast_height ( & self ) -> Option < u32 > {
233
253
match self {
234
- Self :: PendingInitialBroadcast => None ,
254
+ Self :: PendingInitialBroadcast { .. } => None ,
235
255
Self :: PendingFirstConfirmation { latest_broadcast_height, .. } => {
236
256
Some ( * latest_broadcast_height)
237
257
} ,
@@ -243,7 +263,7 @@ impl OutputSpendStatus {
243
263
244
264
fn confirmation_height ( & self ) -> Option < u32 > {
245
265
match self {
246
- Self :: PendingInitialBroadcast => None ,
266
+ Self :: PendingInitialBroadcast { .. } => None ,
247
267
Self :: PendingFirstConfirmation { .. } => None ,
248
268
Self :: PendingThresholdConfirmations { confirmation_height, .. } => {
249
269
Some ( * confirmation_height)
@@ -253,7 +273,7 @@ impl OutputSpendStatus {
253
273
254
274
fn confirmation_hash ( & self ) -> Option < BlockHash > {
255
275
match self {
256
- Self :: PendingInitialBroadcast => None ,
276
+ Self :: PendingInitialBroadcast { .. } => None ,
257
277
Self :: PendingFirstConfirmation { .. } => None ,
258
278
Self :: PendingThresholdConfirmations { confirmation_hash, .. } => {
259
279
Some ( * confirmation_hash)
@@ -263,7 +283,7 @@ impl OutputSpendStatus {
263
283
264
284
fn latest_spending_tx ( & self ) -> Option < & Transaction > {
265
285
match self {
266
- Self :: PendingInitialBroadcast => None ,
286
+ Self :: PendingInitialBroadcast { .. } => None ,
267
287
Self :: PendingFirstConfirmation { latest_spending_tx, .. } => Some ( latest_spending_tx) ,
268
288
Self :: PendingThresholdConfirmations { latest_spending_tx, .. } => {
269
289
Some ( latest_spending_tx)
@@ -273,15 +293,17 @@ impl OutputSpendStatus {
273
293
274
294
fn is_confirmed ( & self ) -> bool {
275
295
match self {
276
- Self :: PendingInitialBroadcast => false ,
296
+ Self :: PendingInitialBroadcast { .. } => false ,
277
297
Self :: PendingFirstConfirmation { .. } => false ,
278
298
Self :: PendingThresholdConfirmations { .. } => true ,
279
299
}
280
300
}
281
301
}
282
302
283
303
impl_writeable_tlv_based_enum ! ( OutputSpendStatus ,
284
- ( 0 , PendingInitialBroadcast ) => { } ,
304
+ ( 0 , PendingInitialBroadcast ) => {
305
+ ( 0 , delayed_until_height, option) ,
306
+ } ,
285
307
( 2 , PendingFirstConfirmation ) => {
286
308
( 0 , first_broadcast_hash, required) ,
287
309
( 2 , latest_broadcast_height, required) ,
@@ -372,10 +394,13 @@ where
372
394
/// [`SpendableOutputDescriptor::StaticOutput`]s, which may be handled directly by the on-chain
373
395
/// wallet implementation.
374
396
///
397
+ /// If `delay_until_height` is set, we will delay the spending until the respective block
398
+ /// height is reached. This can be used to batch spends, e.g., to reduce on-chain fees.
399
+ ///
375
400
/// [`Event::SpendableOutputs`]: crate::events::Event::SpendableOutputs
376
401
pub fn track_spendable_outputs (
377
402
& self , output_descriptors : Vec < SpendableOutputDescriptor > , channel_id : Option < ChannelId > ,
378
- exclude_static_ouputs : bool ,
403
+ exclude_static_ouputs : bool , delay_until_height : Option < u32 > ,
379
404
) {
380
405
let mut relevant_descriptors = output_descriptors
381
406
. into_iter ( )
@@ -396,7 +421,9 @@ where
396
421
let output_info = TrackedSpendableOutput {
397
422
descriptor,
398
423
channel_id,
399
- status : OutputSpendStatus :: PendingInitialBroadcast ,
424
+ status : OutputSpendStatus :: PendingInitialBroadcast {
425
+ delayed_until_height : delay_until_height,
426
+ } ,
400
427
} ;
401
428
402
429
if state_lock
@@ -445,6 +472,11 @@ where
445
472
return false ;
446
473
}
447
474
475
+ if o. status . is_delayed ( cur_height) {
476
+ // Don't generate and broadcast if still delayed
477
+ return false ;
478
+ }
479
+
448
480
if o. status . latest_broadcast_height ( ) >= Some ( cur_height) {
449
481
// Only broadcast once per block height.
450
482
return false ;
@@ -726,6 +758,23 @@ impl_writeable_tlv_based!(SweeperState, {
726
758
( 2 , best_block, required) ,
727
759
} ) ;
728
760
761
+ /// A `enum` signalling to the [`OutputSweeper`] that it should delay spending an output until a
762
+ /// future block height is reached.
763
+ #[ derive( Debug , Clone ) ]
764
+ pub enum SpendingDelay {
765
+ /// A relative delay indicating we shouldn't spend the output before `cur_height + num_blocks`
766
+ /// is reached.
767
+ Relative {
768
+ /// The number of blocks until we'll generate and broadcast the spending transaction.
769
+ num_blocks : u32 ,
770
+ } ,
771
+ /// An absolute delay indicating we shouldn't spend the output before `height` is reached.
772
+ Absolute {
773
+ /// The height at which we'll generate and broadcast the spending transaction.
774
+ height : u32 ,
775
+ } ,
776
+ }
777
+
729
778
impl < B : Deref , D : Deref , E : Deref , F : Deref , K : Deref , L : Deref , O : Deref >
730
779
ReadableArgs < ( B , E , Option < F > , O , D , K , L ) > for OutputSweeper < B , D , E , F , K , L , O >
731
780
where
0 commit comments