@@ -308,6 +308,34 @@ impl Default for ChannelHandshakeLimits {
308308 }
309309}
310310
311+ /// Options for how to set the max dust HTLC exposure allowed on a channel. See
312+ /// [`ChannelConfig::max_dust_htlc_exposure_msat`] for details.
313+ #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
314+ pub enum MaxDustHTLCExposure {
315+ /// This sets a fixed limit on the total dust exposure in millisatoshis. Setting this too low
316+ /// may prevent the sending or receipt of low-value HTLCs on high-traffic nodes, and this limit
317+ /// is very important to prevent stealing of dust HTLCs by miners.
318+ ///
319+ /// Note that if the feerate increases significantly, without a manually increase
320+ /// to this maximum the channel may be unable to send/receive HTLCs between the maximum dust
321+ /// exposure and the new minimum value for HTLCs to be economically viable to claim.
322+ FixedLimitMsat ( u64 ) ,
323+ /// This sets a multiplier on the estimated high priority feerate (sats/KW, as obtained from
324+ /// [`FeeEstimator`]) to determine the maximum allowed dust exposure. If this variant is used
325+ /// then the maximum dust exposure in millisatoshis is calculated as:
326+ /// `high_priority_feerate_per_kw * value`.
327+ ///
328+ /// This allows the maximum dust exposure to automatically scale with fee rate changes.
329+ ///
330+ /// [`FeeEstimator`]: crate::chain::chaininterface::FeeEstimator
331+ FeeRateMultiplier ( u64 ) ,
332+ }
333+
334+ impl_writeable_tlv_based_enum ! ( MaxDustHTLCExposure , ;
335+ ( 1 , FixedLimitMsat ) ,
336+ ( 3 , FeeRateMultiplier ) ,
337+ ) ;
338+
311339/// Options which apply on a per-channel basis and may change at runtime or based on negotiation
312340/// with our counterparty.
313341#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
@@ -367,13 +395,11 @@ pub struct ChannelConfig {
367395 /// (specifically the zero fee HTLC transaction variant), this threshold no longer takes into
368396 /// account the HTLC transaction fee as it is zero.
369397 ///
370- /// This limit is applied for sent, forwarded, and received HTLCs and limits the total
371- /// exposure across all three types per-channel. Setting this too low may prevent the
372- /// sending or receipt of low-value HTLCs on high-traffic nodes, and this limit is very
373- /// important to prevent stealing of dust HTLCs by miners.
398+ /// The selected limit is applied for sent, forwarded, and received HTLCs and limits the total
399+ /// exposure across all three types per-channel.
374400 ///
375- /// Default value: 5_000_000 msat .
376- pub max_dust_htlc_exposure_msat : u64 ,
401+ /// Default value: [`MaxDustHTLCExposure::FeeRateMultiplier`] with a multiplier of 5000 .
402+ pub max_dust_htlc_exposure_msat : MaxDustHTLCExposure ,
377403 /// The additional fee we're willing to pay to avoid waiting for the counterparty's
378404 /// `to_self_delay` to reclaim funds.
379405 ///
@@ -429,21 +455,6 @@ pub struct ChannelConfig {
429455 /// [`PaymentClaimable::counterparty_skimmed_fee_msat`]: crate::events::Event::PaymentClaimable::counterparty_skimmed_fee_msat
430456 // TODO: link to bLIP when it's merged
431457 pub accept_underpaying_htlcs : bool ,
432- /// Similar to [`Self::max_dust_htlc_exposure_msat`], but instead of setting a fixed maximum,
433- /// this sets a multiplier on the estimated high priority feerate (sats/KW, as obtained from
434- /// [`FeeEstimator`]) to determine the maximum allowed dust exposure. If this field is set to
435- /// `Some(value)`, then the maximum dust exposure in sats is calculated as:
436- /// `high_priority_feerate_per_kw * value / 1000`.
437- ///
438- /// This allows the maximum dust exposure to automatically scale with fee rate changes. With
439- /// a fixed maximum, if the feerate increases significantly, then without a manually increase
440- /// to this maximum, the channel may be unable to send/receive HTLCs between the maximum dust
441- /// exposure and the new minimum value for HTLCs to be economically viable to claim.
442- ///
443- /// Default value: `Some(5000)`.
444- ///
445- /// [`FeeEstimator`]: crate::chain::chaininterface::FeeEstimator
446- pub max_dust_htlc_exposure_multiplier_thousandths : Option < u64 > ,
447458}
448459
449460impl ChannelConfig {
@@ -464,9 +475,6 @@ impl ChannelConfig {
464475 if let Some ( force_close_avoidance_max_fee_satoshis) = update. force_close_avoidance_max_fee_satoshis {
465476 self . force_close_avoidance_max_fee_satoshis = force_close_avoidance_max_fee_satoshis;
466477 }
467- if let Some ( max_dust_htlc_exposure_multiplier_thousandths) = update. max_dust_htlc_exposure_multiplier_thousandths {
468- self . max_dust_htlc_exposure_multiplier_thousandths = max_dust_htlc_exposure_multiplier_thousandths;
469- }
470478 }
471479}
472480
@@ -477,36 +485,74 @@ impl Default for ChannelConfig {
477485 forwarding_fee_proportional_millionths : 0 ,
478486 forwarding_fee_base_msat : 1000 ,
479487 cltv_expiry_delta : 6 * 12 , // 6 blocks/hour * 12 hours
480- max_dust_htlc_exposure_msat : 5_000_000 ,
488+ max_dust_htlc_exposure_msat : MaxDustHTLCExposure :: FeeRateMultiplier ( 5000 ) ,
481489 force_close_avoidance_max_fee_satoshis : 1000 ,
482490 accept_underpaying_htlcs : false ,
483- max_dust_htlc_exposure_multiplier_thousandths : Some ( 5000 ) ,
484491 }
485492 }
486493}
487494
488- impl_writeable_tlv_based ! ( ChannelConfig , {
489- ( 0 , forwarding_fee_proportional_millionths, required) ,
490- ( 1 , accept_underpaying_htlcs, ( default_value, false ) ) ,
491- ( 2 , forwarding_fee_base_msat, required) ,
492- ( 3 , max_dust_htlc_exposure_multiplier_thousandths, option) ,
493- ( 4 , cltv_expiry_delta, required) ,
494- ( 6 , max_dust_htlc_exposure_msat, required) ,
495- // ChannelConfig serialized this field with a required type of 8 prior to the introduction of
496- // LegacyChannelConfig. To make sure that serialization is not compatible with this one, we use
497- // the next required type of 10, which if seen by the old serialization will always fail.
498- ( 10 , force_close_avoidance_max_fee_satoshis, required) ,
499- } ) ;
495+ impl crate :: util:: ser:: Writeable for ChannelConfig {
496+ fn write < W : crate :: util:: ser:: Writer > ( & self , writer : & mut W ) -> Result < ( ) , crate :: io:: Error > {
497+ let max_dust_htlc_exposure_msat_fixed_limit = match self . max_dust_htlc_exposure_msat {
498+ MaxDustHTLCExposure :: FixedLimitMsat ( limit) => limit,
499+ MaxDustHTLCExposure :: FeeRateMultiplier ( _) => 5_000_000 ,
500+ } ;
501+ write_tlv_fields ! ( writer, {
502+ ( 0 , self . forwarding_fee_proportional_millionths, required) ,
503+ ( 1 , self . accept_underpaying_htlcs, ( default_value, false ) ) ,
504+ ( 2 , self . forwarding_fee_base_msat, required) ,
505+ ( 3 , Some ( self . max_dust_htlc_exposure_msat) , option) ,
506+ ( 4 , self . cltv_expiry_delta, required) ,
507+ ( 6 , max_dust_htlc_exposure_msat_fixed_limit, required) ,
508+ // ChannelConfig serialized this field with a required type of 8 prior to the introduction of
509+ // LegacyChannelConfig. To make sure that serialization is not compatible with this one, we use
510+ // the next required type of 10, which if seen by the old serialization will always fail.
511+ ( 10 , self . force_close_avoidance_max_fee_satoshis, required) ,
512+ } ) ;
513+ Ok ( ( ) )
514+ }
515+ }
516+
517+ impl crate :: util:: ser:: Readable for ChannelConfig {
518+ fn read < R : crate :: io:: Read > ( reader : & mut R ) -> Result < Self , crate :: ln:: msgs:: DecodeError > {
519+ let mut forwarding_fee_proportional_millionths = 0 ;
520+ let mut accept_underpaying_htlcs = false ;
521+ let mut forwarding_fee_base_msat = 1000 ;
522+ let mut cltv_expiry_delta = 6 * 12 ;
523+ let mut max_dust_htlc_exposure_msat = 5_000_000 ;
524+ let mut max_dust_htlc_exposure_enum = None ;
525+ let mut force_close_avoidance_max_fee_satoshis = 1000 ;
526+ read_tlv_fields ! ( reader, {
527+ ( 0 , forwarding_fee_proportional_millionths, required) ,
528+ ( 1 , accept_underpaying_htlcs, ( default_value, false ) ) ,
529+ ( 2 , forwarding_fee_base_msat, required) ,
530+ ( 3 , max_dust_htlc_exposure_enum, option) ,
531+ ( 4 , cltv_expiry_delta, required) ,
532+ ( 6 , max_dust_htlc_exposure_msat, required) ,
533+ ( 10 , force_close_avoidance_max_fee_satoshis, required) ,
534+ } ) ;
535+ let max_dust_htlc_exposure_msat = max_dust_htlc_exposure_enum
536+ . unwrap_or ( MaxDustHTLCExposure :: FixedLimitMsat ( max_dust_htlc_exposure_msat) ) ;
537+ Ok ( Self {
538+ forwarding_fee_proportional_millionths,
539+ accept_underpaying_htlcs,
540+ forwarding_fee_base_msat,
541+ cltv_expiry_delta,
542+ max_dust_htlc_exposure_msat,
543+ force_close_avoidance_max_fee_satoshis,
544+ } )
545+ }
546+ }
500547
501548/// A parallel struct to [`ChannelConfig`] to define partial updates.
502549#[ allow( missing_docs) ]
503550pub struct ChannelConfigUpdate {
504551 pub forwarding_fee_proportional_millionths : Option < u32 > ,
505552 pub forwarding_fee_base_msat : Option < u32 > ,
506553 pub cltv_expiry_delta : Option < u16 > ,
507- pub max_dust_htlc_exposure_msat : Option < u64 > ,
554+ pub max_dust_htlc_exposure_msat : Option < MaxDustHTLCExposure > ,
508555 pub force_close_avoidance_max_fee_satoshis : Option < u64 > ,
509- pub max_dust_htlc_exposure_multiplier_thousandths : Option < Option < u64 > > ,
510556}
511557
512558impl Default for ChannelConfigUpdate {
@@ -517,7 +563,6 @@ impl Default for ChannelConfigUpdate {
517563 cltv_expiry_delta : None ,
518564 max_dust_htlc_exposure_msat : None ,
519565 force_close_avoidance_max_fee_satoshis : None ,
520- max_dust_htlc_exposure_multiplier_thousandths : None ,
521566 }
522567 }
523568}
@@ -530,7 +575,6 @@ impl From<ChannelConfig> for ChannelConfigUpdate {
530575 cltv_expiry_delta : Some ( config. cltv_expiry_delta ) ,
531576 max_dust_htlc_exposure_msat : Some ( config. max_dust_htlc_exposure_msat ) ,
532577 force_close_avoidance_max_fee_satoshis : Some ( config. force_close_avoidance_max_fee_satoshis ) ,
533- max_dust_htlc_exposure_multiplier_thousandths : Some ( config. max_dust_htlc_exposure_multiplier_thousandths ) ,
534578 }
535579 }
536580}
@@ -562,13 +606,17 @@ impl Default for LegacyChannelConfig {
562606
563607impl crate :: util:: ser:: Writeable for LegacyChannelConfig {
564608 fn write < W : crate :: util:: ser:: Writer > ( & self , writer : & mut W ) -> Result < ( ) , crate :: io:: Error > {
609+ let max_dust_htlc_exposure_msat_fixed_limit = match self . options . max_dust_htlc_exposure_msat {
610+ MaxDustHTLCExposure :: FixedLimitMsat ( limit) => limit,
611+ MaxDustHTLCExposure :: FeeRateMultiplier ( _) => 5_000_000 ,
612+ } ;
565613 write_tlv_fields ! ( writer, {
566614 ( 0 , self . options. forwarding_fee_proportional_millionths, required) ,
567- ( 1 , self . options . max_dust_htlc_exposure_msat , ( default_value, 5_000_000 ) ) ,
615+ ( 1 , max_dust_htlc_exposure_msat_fixed_limit , ( default_value, 5_000_000 ) ) ,
568616 ( 2 , self . options. cltv_expiry_delta, required) ,
569617 ( 3 , self . options. force_close_avoidance_max_fee_satoshis, ( default_value, 1000 ) ) ,
570618 ( 4 , self . announced_channel, required) ,
571- ( 5 , self . options. max_dust_htlc_exposure_multiplier_thousandths , option) ,
619+ ( 5 , Some ( self . options. max_dust_htlc_exposure_msat ) , option) ,
572620 ( 6 , self . commit_upfront_shutdown_pubkey, required) ,
573621 ( 8 , self . options. forwarding_fee_base_msat, required) ,
574622 } ) ;
@@ -579,23 +627,25 @@ impl crate::util::ser::Writeable for LegacyChannelConfig {
579627impl crate :: util:: ser:: Readable for LegacyChannelConfig {
580628 fn read < R : crate :: io:: Read > ( reader : & mut R ) -> Result < Self , crate :: ln:: msgs:: DecodeError > {
581629 let mut forwarding_fee_proportional_millionths = 0 ;
582- let mut max_dust_htlc_exposure_msat = 5_000_000 ;
630+ let mut max_dust_htlc_exposure_msat_fixed_limit = 5_000_000 ;
583631 let mut cltv_expiry_delta = 0 ;
584632 let mut force_close_avoidance_max_fee_satoshis = 1000 ;
585633 let mut announced_channel = false ;
586634 let mut commit_upfront_shutdown_pubkey = false ;
587635 let mut forwarding_fee_base_msat = 0 ;
588- let mut max_dust_htlc_exposure_multiplier_thousandths = None ;
636+ let mut max_dust_htlc_exposure_enum = None ;
589637 read_tlv_fields ! ( reader, {
590638 ( 0 , forwarding_fee_proportional_millionths, required) ,
591- ( 1 , max_dust_htlc_exposure_msat , ( default_value, 5_000_000u64 ) ) ,
639+ ( 1 , max_dust_htlc_exposure_msat_fixed_limit , ( default_value, 5_000_000u64 ) ) ,
592640 ( 2 , cltv_expiry_delta, required) ,
593641 ( 3 , force_close_avoidance_max_fee_satoshis, ( default_value, 1000u64 ) ) ,
594642 ( 4 , announced_channel, required) ,
595- ( 5 , max_dust_htlc_exposure_multiplier_thousandths , option) ,
643+ ( 5 , max_dust_htlc_exposure_enum , option) ,
596644 ( 6 , commit_upfront_shutdown_pubkey, required) ,
597645 ( 8 , forwarding_fee_base_msat, required) ,
598646 } ) ;
647+ let max_dust_htlc_exposure_msat = max_dust_htlc_exposure_enum
648+ . unwrap_or ( MaxDustHTLCExposure :: FixedLimitMsat ( max_dust_htlc_exposure_msat_fixed_limit) ) ;
599649 Ok ( Self {
600650 options : ChannelConfig {
601651 forwarding_fee_proportional_millionths,
@@ -604,7 +654,6 @@ impl crate::util::ser::Readable for LegacyChannelConfig {
604654 force_close_avoidance_max_fee_satoshis,
605655 forwarding_fee_base_msat,
606656 accept_underpaying_htlcs : false ,
607- max_dust_htlc_exposure_multiplier_thousandths,
608657 } ,
609658 announced_channel,
610659 commit_upfront_shutdown_pubkey,
0 commit comments