@@ -481,6 +481,59 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
481481 events. into_iter ( ) . map ( |( _, event) | event) . collect ( )
482482 }
483483
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+
484537 /// Lightning security model (i.e being able to redeem/timeout HTLC or penalize counterparty
485538 /// onchain) lays on the assumption of claim transactions getting confirmed before timelock
486539 /// 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>
489542 ///
490543 /// Panics if there are signing errors, because signing operations in reaction to on-chain
491544 /// 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 ,
495552 {
496553 let request_outpoints = cached_request. outpoints ( ) ;
497554 if request_outpoints. is_empty ( ) {
@@ -538,8 +595,9 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
538595 #[ cfg( anchors) ]
539596 { // Attributes are not allowed on if expressions on our current MSRV of 1.41.
540597 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+ ) ;
543601 if let Some ( htlcs) = cached_request. construct_malleable_package_with_external_funding ( self ) {
544602 return Some ( (
545603 new_timer,
@@ -558,7 +616,8 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
558616
559617 let predicted_weight = cached_request. package_weight ( & self . destination_script ) ;
560618 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,
562621 ) {
563622 assert ! ( new_feerate != 0 ) ;
564623
@@ -601,7 +660,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
601660 // counterparty's latest commitment don't have any HTLCs present.
602661 let conf_target = ConfirmationTarget :: HighPriority ;
603662 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 ) ;
605664 Some ( (
606665 new_timer,
607666 package_target_feerate_sat_per_1000_weight as u64 ,
@@ -700,7 +759,9 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
700759 // Generate claim transactions and track them to bump if necessary at
701760 // height timer expiration (i.e in how many blocks we're going to take action).
702761 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+ ) {
704765 req. set_timer ( new_timer) ;
705766 req. set_feerate ( new_feerate) ;
706767 let package_id = match claim {
@@ -893,7 +954,9 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
893954 // Build, bump and rebroadcast tx accordingly
894955 log_trace ! ( logger, "Bumping {} candidates" , bump_candidates. len( ) ) ;
895956 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+ ) {
897960 match bump_claim {
898961 OnchainClaim :: Tx ( bump_tx) => {
899962 log_info ! ( logger, "Broadcasting RBF-bumped onchain {}" , log_tx!( bump_tx) ) ;
@@ -973,7 +1036,9 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
9731036 }
9741037 }
9751038 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+ ) {
9771042 request. set_timer ( new_timer) ;
9781043 request. set_feerate ( new_feerate) ;
9791044 match bump_claim {
0 commit comments