@@ -481,6 +481,59 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
481
481
events. into_iter ( ) . map ( |( _, event) | event) . collect ( )
482
482
}
483
483
484
+ /// Triggers rebroadcasts/fee-bumps of pending claims from a force-closed channel. This is
485
+ /// crucial in preventing certain classes of pinning attacks, detecting substantial mempool
486
+ /// feerate changes between blocks, and ensuring reliability if broadcasting fails. We recommend
487
+ /// invoking this every 30 seconds, or lower if running in an environment with spotty
488
+ /// connections, like on mobile.
489
+ pub ( crate ) fn rebroadcast_pending_claims < B : Deref , F : Deref , L : Deref > (
490
+ & mut self , current_height : u32 , broadcaster : & B , fee_estimator : & LowerBoundedFeeEstimator < F > ,
491
+ logger : & L ,
492
+ )
493
+ where
494
+ B :: Target : BroadcasterInterface ,
495
+ F :: Target : FeeEstimator ,
496
+ L :: Target : Logger ,
497
+ {
498
+ let mut bump_requests = Vec :: with_capacity ( self . pending_claim_requests . len ( ) ) ;
499
+ for ( package_id, request) in self . pending_claim_requests . iter ( ) {
500
+ let inputs = request. outpoints ( ) ;
501
+ log_info ! ( logger, "Triggering rebroadcast/fee-bump for request with inputs {:?}" , inputs) ;
502
+ bump_requests. push ( ( * package_id, request. clone ( ) ) ) ;
503
+ }
504
+ for ( package_id, request) in bump_requests {
505
+ self . generate_claim ( current_height, & request, false /* force_feerate_bump */ , fee_estimator, logger)
506
+ . map ( |( _, new_feerate, claim) | {
507
+ let mut bumped_feerate = false ;
508
+ if let Some ( mut_request) = self . pending_claim_requests . get_mut ( & package_id) {
509
+ bumped_feerate = request. previous_feerate ( ) > new_feerate;
510
+ mut_request. set_feerate ( new_feerate) ;
511
+ }
512
+ match claim {
513
+ OnchainClaim :: Tx ( tx) => {
514
+ let log_start = if bumped_feerate { "Broadcasting RBF-bumped" } else { "Rebroadcasting" } ;
515
+ log_info ! ( logger, "{} onchain {}" , log_start, log_tx!( tx) ) ;
516
+ broadcaster. broadcast_transaction ( & tx) ;
517
+ } ,
518
+ #[ cfg( anchors) ]
519
+ OnchainClaim :: Event ( event) => {
520
+ let log_start = if bumped_feerate { "Yielding fee-bumped" } else { "Replaying" } ;
521
+ log_info ! ( logger, "{} onchain event to spend inputs {:?}" , log_start,
522
+ request. outpoints( ) ) ;
523
+ #[ cfg( debug_assertions) ] {
524
+ debug_assert ! ( request. requires_external_funding( ) ) ;
525
+ let num_existing = self . pending_claim_events . iter ( )
526
+ . filter ( |entry| entry. 0 == package_id) . count ( ) ;
527
+ assert ! ( num_existing == 0 || num_existing == 1 ) ;
528
+ }
529
+ self . pending_claim_events . retain ( |event| event. 0 != package_id) ;
530
+ self . pending_claim_events . push ( ( package_id, event) ) ;
531
+ }
532
+ }
533
+ } ) ;
534
+ }
535
+ }
536
+
484
537
/// Lightning security model (i.e being able to redeem/timeout HTLC or penalize counterparty
485
538
/// onchain) lays on the assumption of claim transactions getting confirmed before timelock
486
539
/// expiration (CSV or CLTV following cases). In case of high-fee spikes, claim tx may get stuck
@@ -489,9 +542,13 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
489
542
///
490
543
/// Panics if there are signing errors, because signing operations in reaction to on-chain
491
544
/// events are not expected to fail, and if they do, we may lose funds.
492
- fn generate_claim < F : Deref , L : Deref > ( & mut self , cur_height : u32 , cached_request : & PackageTemplate , fee_estimator : & LowerBoundedFeeEstimator < F > , logger : & L ) -> Option < ( u32 , u64 , OnchainClaim ) >
493
- where F :: Target : FeeEstimator ,
494
- L :: Target : Logger ,
545
+ fn generate_claim < F : Deref , L : Deref > (
546
+ & mut self , cur_height : u32 , cached_request : & PackageTemplate , force_feerate_bump : bool ,
547
+ fee_estimator : & LowerBoundedFeeEstimator < F > , logger : & L ,
548
+ ) -> Option < ( u32 , u64 , OnchainClaim ) >
549
+ where
550
+ F :: Target : FeeEstimator ,
551
+ L :: Target : Logger ,
495
552
{
496
553
let request_outpoints = cached_request. outpoints ( ) ;
497
554
if request_outpoints. is_empty ( ) {
@@ -538,8 +595,9 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
538
595
#[ cfg( anchors) ]
539
596
{ // Attributes are not allowed on if expressions on our current MSRV of 1.41.
540
597
if cached_request. requires_external_funding ( ) {
541
- let target_feerate_sat_per_1000_weight = cached_request
542
- . compute_package_feerate ( fee_estimator, ConfirmationTarget :: HighPriority ) ;
598
+ let target_feerate_sat_per_1000_weight = cached_request. compute_package_feerate (
599
+ fee_estimator, ConfirmationTarget :: HighPriority , force_feerate_bump
600
+ ) ;
543
601
if let Some ( htlcs) = cached_request. construct_malleable_package_with_external_funding ( self ) {
544
602
return Some ( (
545
603
new_timer,
@@ -558,7 +616,8 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
558
616
559
617
let predicted_weight = cached_request. package_weight ( & self . destination_script ) ;
560
618
if let Some ( ( output_value, new_feerate) ) = cached_request. compute_package_output (
561
- predicted_weight, self . destination_script . dust_value ( ) . to_sat ( ) , fee_estimator, logger,
619
+ predicted_weight, self . destination_script . dust_value ( ) . to_sat ( ) ,
620
+ force_feerate_bump, fee_estimator, logger,
562
621
) {
563
622
assert ! ( new_feerate != 0 ) ;
564
623
@@ -601,7 +660,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
601
660
// counterparty's latest commitment don't have any HTLCs present.
602
661
let conf_target = ConfirmationTarget :: HighPriority ;
603
662
let package_target_feerate_sat_per_1000_weight = cached_request
604
- . compute_package_feerate ( fee_estimator, conf_target) ;
663
+ . compute_package_feerate ( fee_estimator, conf_target, force_feerate_bump ) ;
605
664
Some ( (
606
665
new_timer,
607
666
package_target_feerate_sat_per_1000_weight as u64 ,
@@ -700,7 +759,9 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
700
759
// Generate claim transactions and track them to bump if necessary at
701
760
// height timer expiration (i.e in how many blocks we're going to take action).
702
761
for mut req in preprocessed_requests {
703
- if let Some ( ( new_timer, new_feerate, claim) ) = self . generate_claim ( cur_height, & req, & * fee_estimator, & * logger) {
762
+ if let Some ( ( new_timer, new_feerate, claim) ) = self . generate_claim (
763
+ cur_height, & req, true /* force_feerate_bump */ , & * fee_estimator, & * logger,
764
+ ) {
704
765
req. set_timer ( new_timer) ;
705
766
req. set_feerate ( new_feerate) ;
706
767
let package_id = match claim {
@@ -893,7 +954,9 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
893
954
// Build, bump and rebroadcast tx accordingly
894
955
log_trace ! ( logger, "Bumping {} candidates" , bump_candidates. len( ) ) ;
895
956
for ( package_id, request) in bump_candidates. iter ( ) {
896
- if let Some ( ( new_timer, new_feerate, bump_claim) ) = self . generate_claim ( cur_height, & request, & * fee_estimator, & * logger) {
957
+ if let Some ( ( new_timer, new_feerate, bump_claim) ) = self . generate_claim (
958
+ cur_height, & request, true /* force_feerate_bump */ , & * fee_estimator, & * logger,
959
+ ) {
897
960
match bump_claim {
898
961
OnchainClaim :: Tx ( bump_tx) => {
899
962
log_info ! ( logger, "Broadcasting RBF-bumped onchain {}" , log_tx!( bump_tx) ) ;
@@ -973,7 +1036,9 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
973
1036
}
974
1037
}
975
1038
for ( ( _package_id, _) , ref mut request) in bump_candidates. iter_mut ( ) {
976
- if let Some ( ( new_timer, new_feerate, bump_claim) ) = self . generate_claim ( height, & request, fee_estimator, & & * logger) {
1039
+ if let Some ( ( new_timer, new_feerate, bump_claim) ) = self . generate_claim (
1040
+ height, & request, true /* force_feerate_bump */ , fee_estimator, & & * logger
1041
+ ) {
977
1042
request. set_timer ( new_timer) ;
978
1043
request. set_feerate ( new_feerate) ;
979
1044
match bump_claim {
0 commit comments