@@ -315,6 +315,34 @@ impl Default for ChannelHandshakeLimits {
315
315
}
316
316
}
317
317
318
+ /// Options for how to set the max dust HTLC exposure allowed on a channel. See
319
+ /// [`ChannelConfig::max_dust_htlc_exposure_msat`] for details.
320
+ #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
321
+ pub enum MaxDustHTLCExposure {
322
+ /// This sets a fixed limit on the total dust exposure in millisatoshis. Setting this too low
323
+ /// may prevent the sending or receipt of low-value HTLCs on high-traffic nodes, and this limit
324
+ /// is very important to prevent stealing of dust HTLCs by miners.
325
+ ///
326
+ /// Note that if the feerate increases significantly, without a manually increase
327
+ /// to this maximum the channel may be unable to send/receive HTLCs between the maximum dust
328
+ /// exposure and the new minimum value for HTLCs to be economically viable to claim.
329
+ FixedLimitMsat ( u64 ) ,
330
+ /// This sets a multiplier on the estimated high priority feerate (sats/KW, as obtained from
331
+ /// [`FeeEstimator`]) to determine the maximum allowed dust exposure. If this variant is used
332
+ /// then the maximum dust exposure in millisatoshis is calculated as:
333
+ /// `high_priority_feerate_per_kw * value`.
334
+ ///
335
+ /// This allows the maximum dust exposure to automatically scale with fee rate changes.
336
+ ///
337
+ /// [`FeeEstimator`]: crate::chain::chaininterface::FeeEstimator
338
+ FeeRateMultiplier ( u64 ) ,
339
+ }
340
+
341
+ impl_writeable_tlv_based_enum ! ( MaxDustHTLCExposure , ;
342
+ ( 1 , FixedLimitMsat ) ,
343
+ ( 3 , FeeRateMultiplier ) ,
344
+ ) ;
345
+
318
346
/// Options which apply on a per-channel basis and may change at runtime or based on negotiation
319
347
/// with our counterparty.
320
348
#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
@@ -374,13 +402,11 @@ pub struct ChannelConfig {
374
402
/// (specifically the zero fee HTLC transaction variant), this threshold no longer takes into
375
403
/// account the HTLC transaction fee as it is zero.
376
404
///
377
- /// This limit is applied for sent, forwarded, and received HTLCs and limits the total
378
- /// exposure across all three types per-channel. Setting this too low may prevent the
379
- /// sending or receipt of low-value HTLCs on high-traffic nodes, and this limit is very
380
- /// important to prevent stealing of dust HTLCs by miners.
405
+ /// The selected limit is applied for sent, forwarded, and received HTLCs and limits the total
406
+ /// exposure across all three types per-channel.
381
407
///
382
- /// Default value: 5_000_000 msat .
383
- pub max_dust_htlc_exposure_msat : u64 ,
408
+ /// Default value: [`MaxDustHTLCExposure::FeeRateMultiplier`] with a multiplier of 5000 .
409
+ pub max_dust_htlc_exposure_msat : MaxDustHTLCExposure ,
384
410
/// The additional fee we're willing to pay to avoid waiting for the counterparty's
385
411
/// `to_self_delay` to reclaim funds.
386
412
///
@@ -436,21 +462,6 @@ pub struct ChannelConfig {
436
462
/// [`PaymentClaimable::counterparty_skimmed_fee_msat`]: crate::events::Event::PaymentClaimable::counterparty_skimmed_fee_msat
437
463
// TODO: link to bLIP when it's merged
438
464
pub accept_underpaying_htlcs : bool ,
439
- /// Similar to [`Self::max_dust_htlc_exposure_msat`], but instead of setting a fixed maximum,
440
- /// this sets a multiplier on the estimated high priority feerate (sats/KW, as obtained from
441
- /// [`FeeEstimator`]) to determine the maximum allowed dust exposure. If this field is set to
442
- /// `Some(value)`, then the maximum dust exposure in sats is calculated as:
443
- /// `high_priority_feerate_per_kw * value / 1000`.
444
- ///
445
- /// This allows the maximum dust exposure to automatically scale with fee rate changes. With
446
- /// a fixed maximum, if the feerate increases significantly, then without a manually increase
447
- /// to this maximum, the channel may be unable to send/receive HTLCs between the maximum dust
448
- /// exposure and the new minimum value for HTLCs to be economically viable to claim.
449
- ///
450
- /// Default value: `Some(5000)`.
451
- ///
452
- /// [`FeeEstimator`]: crate::chain::chaininterface::FeeEstimator
453
- pub max_dust_htlc_exposure_multiplier_thousandths : Option < u64 > ,
454
465
}
455
466
456
467
impl ChannelConfig {
@@ -471,9 +482,6 @@ impl ChannelConfig {
471
482
if let Some ( force_close_avoidance_max_fee_satoshis) = update. force_close_avoidance_max_fee_satoshis {
472
483
self . force_close_avoidance_max_fee_satoshis = force_close_avoidance_max_fee_satoshis;
473
484
}
474
- if let Some ( max_dust_htlc_exposure_multiplier_thousandths) = update. max_dust_htlc_exposure_multiplier_thousandths {
475
- self . max_dust_htlc_exposure_multiplier_thousandths = max_dust_htlc_exposure_multiplier_thousandths;
476
- }
477
485
}
478
486
}
479
487
@@ -484,36 +492,74 @@ impl Default for ChannelConfig {
484
492
forwarding_fee_proportional_millionths : 0 ,
485
493
forwarding_fee_base_msat : 1000 ,
486
494
cltv_expiry_delta : 6 * 12 , // 6 blocks/hour * 12 hours
487
- max_dust_htlc_exposure_msat : 5_000_000 ,
495
+ max_dust_htlc_exposure_msat : MaxDustHTLCExposure :: FeeRateMultiplier ( 5000 ) ,
488
496
force_close_avoidance_max_fee_satoshis : 1000 ,
489
497
accept_underpaying_htlcs : false ,
490
- max_dust_htlc_exposure_multiplier_thousandths : Some ( 5000 ) ,
491
498
}
492
499
}
493
500
}
494
501
495
- impl_writeable_tlv_based ! ( ChannelConfig , {
496
- ( 0 , forwarding_fee_proportional_millionths, required) ,
497
- ( 1 , accept_underpaying_htlcs, ( default_value, false ) ) ,
498
- ( 2 , forwarding_fee_base_msat, required) ,
499
- ( 3 , max_dust_htlc_exposure_multiplier_thousandths, option) ,
500
- ( 4 , cltv_expiry_delta, required) ,
501
- ( 6 , max_dust_htlc_exposure_msat, required) ,
502
- // ChannelConfig serialized this field with a required type of 8 prior to the introduction of
503
- // LegacyChannelConfig. To make sure that serialization is not compatible with this one, we use
504
- // the next required type of 10, which if seen by the old serialization will always fail.
505
- ( 10 , force_close_avoidance_max_fee_satoshis, required) ,
506
- } ) ;
502
+ impl crate :: util:: ser:: Writeable for ChannelConfig {
503
+ fn write < W : crate :: util:: ser:: Writer > ( & self , writer : & mut W ) -> Result < ( ) , crate :: io:: Error > {
504
+ let max_dust_htlc_exposure_msat_fixed_limit = match self . max_dust_htlc_exposure_msat {
505
+ MaxDustHTLCExposure :: FixedLimitMsat ( limit) => limit,
506
+ MaxDustHTLCExposure :: FeeRateMultiplier ( _) => 5_000_000 ,
507
+ } ;
508
+ write_tlv_fields ! ( writer, {
509
+ ( 0 , self . forwarding_fee_proportional_millionths, required) ,
510
+ ( 1 , self . accept_underpaying_htlcs, ( default_value, false ) ) ,
511
+ ( 2 , self . forwarding_fee_base_msat, required) ,
512
+ ( 3 , Some ( self . max_dust_htlc_exposure_msat) , option) ,
513
+ ( 4 , self . cltv_expiry_delta, required) ,
514
+ ( 6 , max_dust_htlc_exposure_msat_fixed_limit, required) ,
515
+ // ChannelConfig serialized this field with a required type of 8 prior to the introduction of
516
+ // LegacyChannelConfig. To make sure that serialization is not compatible with this one, we use
517
+ // the next required type of 10, which if seen by the old serialization will always fail.
518
+ ( 10 , self . force_close_avoidance_max_fee_satoshis, required) ,
519
+ } ) ;
520
+ Ok ( ( ) )
521
+ }
522
+ }
523
+
524
+ impl crate :: util:: ser:: Readable for ChannelConfig {
525
+ fn read < R : crate :: io:: Read > ( reader : & mut R ) -> Result < Self , crate :: ln:: msgs:: DecodeError > {
526
+ let mut forwarding_fee_proportional_millionths = 0 ;
527
+ let mut accept_underpaying_htlcs = false ;
528
+ let mut forwarding_fee_base_msat = 1000 ;
529
+ let mut cltv_expiry_delta = 6 * 12 ;
530
+ let mut max_dust_htlc_exposure_msat = 5_000_000 ;
531
+ let mut max_dust_htlc_exposure_enum = None ;
532
+ let mut force_close_avoidance_max_fee_satoshis = 1000 ;
533
+ read_tlv_fields ! ( reader, {
534
+ ( 0 , forwarding_fee_proportional_millionths, required) ,
535
+ ( 1 , accept_underpaying_htlcs, ( default_value, false ) ) ,
536
+ ( 2 , forwarding_fee_base_msat, required) ,
537
+ ( 3 , max_dust_htlc_exposure_enum, option) ,
538
+ ( 4 , cltv_expiry_delta, required) ,
539
+ ( 6 , max_dust_htlc_exposure_msat, required) ,
540
+ ( 10 , force_close_avoidance_max_fee_satoshis, required) ,
541
+ } ) ;
542
+ let max_dust_htlc_exposure_msat = max_dust_htlc_exposure_enum
543
+ . unwrap_or ( MaxDustHTLCExposure :: FixedLimitMsat ( max_dust_htlc_exposure_msat) ) ;
544
+ Ok ( Self {
545
+ forwarding_fee_proportional_millionths,
546
+ accept_underpaying_htlcs,
547
+ forwarding_fee_base_msat,
548
+ cltv_expiry_delta,
549
+ max_dust_htlc_exposure_msat,
550
+ force_close_avoidance_max_fee_satoshis,
551
+ } )
552
+ }
553
+ }
507
554
508
555
/// A parallel struct to [`ChannelConfig`] to define partial updates.
509
556
#[ allow( missing_docs) ]
510
557
pub struct ChannelConfigUpdate {
511
558
pub forwarding_fee_proportional_millionths : Option < u32 > ,
512
559
pub forwarding_fee_base_msat : Option < u32 > ,
513
560
pub cltv_expiry_delta : Option < u16 > ,
514
- pub max_dust_htlc_exposure_msat : Option < u64 > ,
561
+ pub max_dust_htlc_exposure_msat : Option < MaxDustHTLCExposure > ,
515
562
pub force_close_avoidance_max_fee_satoshis : Option < u64 > ,
516
- pub max_dust_htlc_exposure_multiplier_thousandths : Option < Option < u64 > > ,
517
563
}
518
564
519
565
impl Default for ChannelConfigUpdate {
@@ -524,7 +570,6 @@ impl Default for ChannelConfigUpdate {
524
570
cltv_expiry_delta : None ,
525
571
max_dust_htlc_exposure_msat : None ,
526
572
force_close_avoidance_max_fee_satoshis : None ,
527
- max_dust_htlc_exposure_multiplier_thousandths : None ,
528
573
}
529
574
}
530
575
}
@@ -537,7 +582,6 @@ impl From<ChannelConfig> for ChannelConfigUpdate {
537
582
cltv_expiry_delta : Some ( config. cltv_expiry_delta ) ,
538
583
max_dust_htlc_exposure_msat : Some ( config. max_dust_htlc_exposure_msat ) ,
539
584
force_close_avoidance_max_fee_satoshis : Some ( config. force_close_avoidance_max_fee_satoshis ) ,
540
- max_dust_htlc_exposure_multiplier_thousandths : Some ( config. max_dust_htlc_exposure_multiplier_thousandths ) ,
541
585
}
542
586
}
543
587
}
@@ -569,13 +613,17 @@ impl Default for LegacyChannelConfig {
569
613
570
614
impl crate :: util:: ser:: Writeable for LegacyChannelConfig {
571
615
fn write < W : crate :: util:: ser:: Writer > ( & self , writer : & mut W ) -> Result < ( ) , crate :: io:: Error > {
616
+ let max_dust_htlc_exposure_msat_fixed_limit = match self . options . max_dust_htlc_exposure_msat {
617
+ MaxDustHTLCExposure :: FixedLimitMsat ( limit) => limit,
618
+ MaxDustHTLCExposure :: FeeRateMultiplier ( _) => 5_000_000 ,
619
+ } ;
572
620
write_tlv_fields ! ( writer, {
573
621
( 0 , self . options. forwarding_fee_proportional_millionths, required) ,
574
- ( 1 , self . options . max_dust_htlc_exposure_msat , ( default_value, 5_000_000 ) ) ,
622
+ ( 1 , max_dust_htlc_exposure_msat_fixed_limit , ( default_value, 5_000_000 ) ) ,
575
623
( 2 , self . options. cltv_expiry_delta, required) ,
576
624
( 3 , self . options. force_close_avoidance_max_fee_satoshis, ( default_value, 1000 ) ) ,
577
625
( 4 , self . announced_channel, required) ,
578
- ( 5 , self . options. max_dust_htlc_exposure_multiplier_thousandths , option) ,
626
+ ( 5 , Some ( self . options. max_dust_htlc_exposure_msat ) , option) ,
579
627
( 6 , self . commit_upfront_shutdown_pubkey, required) ,
580
628
( 8 , self . options. forwarding_fee_base_msat, required) ,
581
629
} ) ;
@@ -586,23 +634,25 @@ impl crate::util::ser::Writeable for LegacyChannelConfig {
586
634
impl crate :: util:: ser:: Readable for LegacyChannelConfig {
587
635
fn read < R : crate :: io:: Read > ( reader : & mut R ) -> Result < Self , crate :: ln:: msgs:: DecodeError > {
588
636
let mut forwarding_fee_proportional_millionths = 0 ;
589
- let mut max_dust_htlc_exposure_msat = 5_000_000 ;
637
+ let mut max_dust_htlc_exposure_msat_fixed_limit = 5_000_000 ;
590
638
let mut cltv_expiry_delta = 0 ;
591
639
let mut force_close_avoidance_max_fee_satoshis = 1000 ;
592
640
let mut announced_channel = false ;
593
641
let mut commit_upfront_shutdown_pubkey = false ;
594
642
let mut forwarding_fee_base_msat = 0 ;
595
- let mut max_dust_htlc_exposure_multiplier_thousandths = None ;
643
+ let mut max_dust_htlc_exposure_enum = None ;
596
644
read_tlv_fields ! ( reader, {
597
645
( 0 , forwarding_fee_proportional_millionths, required) ,
598
- ( 1 , max_dust_htlc_exposure_msat , ( default_value, 5_000_000u64 ) ) ,
646
+ ( 1 , max_dust_htlc_exposure_msat_fixed_limit , ( default_value, 5_000_000u64 ) ) ,
599
647
( 2 , cltv_expiry_delta, required) ,
600
648
( 3 , force_close_avoidance_max_fee_satoshis, ( default_value, 1000u64 ) ) ,
601
649
( 4 , announced_channel, required) ,
602
- ( 5 , max_dust_htlc_exposure_multiplier_thousandths , option) ,
650
+ ( 5 , max_dust_htlc_exposure_enum , option) ,
603
651
( 6 , commit_upfront_shutdown_pubkey, required) ,
604
652
( 8 , forwarding_fee_base_msat, required) ,
605
653
} ) ;
654
+ let max_dust_htlc_exposure_msat = max_dust_htlc_exposure_enum
655
+ . unwrap_or ( MaxDustHTLCExposure :: FixedLimitMsat ( max_dust_htlc_exposure_msat_fixed_limit) ) ;
606
656
Ok ( Self {
607
657
options : ChannelConfig {
608
658
forwarding_fee_proportional_millionths,
@@ -611,7 +661,6 @@ impl crate::util::ser::Readable for LegacyChannelConfig {
611
661
force_close_avoidance_max_fee_satoshis,
612
662
forwarding_fee_base_msat,
613
663
accept_underpaying_htlcs : false ,
614
- max_dust_htlc_exposure_multiplier_thousandths,
615
664
} ,
616
665
announced_channel,
617
666
commit_upfront_shutdown_pubkey,
0 commit comments