@@ -315,6 +315,50 @@ 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, however this
324
+ /// limit is very important to prevent stealing of large amounts of dust HTLCs by miners
325
+ /// through [fee griefing
326
+ /// attacks](https://lists.linuxfoundation.org/pipermail/lightning-dev/2020-May/002714.html).
327
+ ///
328
+ /// Note that if the feerate increases significantly, without a manually increase
329
+ /// to this maximum the channel may be unable to send/receive HTLCs between the maximum dust
330
+ /// exposure and the new minimum value for HTLCs to be economically viable to claim.
331
+ FixedLimitMsat ( u64 ) ,
332
+ /// This sets a multiplier on the estimated high priority feerate (sats/KW, as obtained from
333
+ /// [`FeeEstimator`]) to determine the maximum allowed dust exposure. If this variant is used
334
+ /// then the maximum dust exposure in millisatoshis is calculated as:
335
+ /// `high_priority_feerate_per_kw * value`. For example, with our default value
336
+ /// `FeeRateMultiplier(5000)`:
337
+ ///
338
+ /// - For the minimum fee rate of 1 sat/vByte (250 sat/KW, although the minimum
339
+ /// defaults to 253 sats/KW for rounding, see [`FeeEstimator`]), the max dust exposure would
340
+ /// be 253 * 5000 = 1,265,000 msats.
341
+ /// - For a fee rate of 30 sat/vByte (7500 sat/KW), the max dust exposure would be
342
+ /// 7500 * 5000 = 37,500,000 msats.
343
+ ///
344
+ /// This allows the maximum dust exposure to automatically scale with fee rate changes.
345
+ ///
346
+ /// Note, if you're using a third-party fee estimator, this may leave you more exposed to a
347
+ /// fee griefing attack, where your fee estimator may purposely overestimate the fee rate,
348
+ /// causing you to accept more dust HTLCs than you would otherwise. Similarly, you may
349
+ /// accept more dust than you would like if you're using a fee estimator that
350
+ /// is evaluated at each block tick, such as Bitcoin Core, as the fee rate may change
351
+ /// significantly between blocks.
352
+ ///
353
+ /// [`FeeEstimator`]: crate::chain::chaininterface::FeeEstimator
354
+ FeeRateMultiplier ( u64 ) ,
355
+ }
356
+
357
+ impl_writeable_tlv_based_enum ! ( MaxDustHTLCExposure , ;
358
+ ( 1 , FixedLimitMsat ) ,
359
+ ( 3 , FeeRateMultiplier ) ,
360
+ ) ;
361
+
318
362
/// Options which apply on a per-channel basis and may change at runtime or based on negotiation
319
363
/// with our counterparty.
320
364
#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
@@ -374,13 +418,11 @@ pub struct ChannelConfig {
374
418
/// (specifically the zero fee HTLC transaction variant), this threshold no longer takes into
375
419
/// account the HTLC transaction fee as it is zero.
376
420
///
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.
421
+ /// The selected limit is applied for sent, forwarded, and received HTLCs and limits the total
422
+ /// exposure across all three types per-channel.
381
423
///
382
- /// Default value: 5_000_000 msat .
383
- pub max_dust_htlc_exposure_msat : u64 ,
424
+ /// Default value: [`MaxDustHTLCExposure::FeeRateMultiplier`] with a multiplier of 5000 .
425
+ pub max_dust_htlc_exposure_msat : MaxDustHTLCExposure ,
384
426
/// The additional fee we're willing to pay to avoid waiting for the counterparty's
385
427
/// `to_self_delay` to reclaim funds.
386
428
///
@@ -466,32 +508,73 @@ impl Default for ChannelConfig {
466
508
forwarding_fee_proportional_millionths : 0 ,
467
509
forwarding_fee_base_msat : 1000 ,
468
510
cltv_expiry_delta : 6 * 12 , // 6 blocks/hour * 12 hours
469
- max_dust_htlc_exposure_msat : 5_000_000 ,
511
+ max_dust_htlc_exposure_msat : MaxDustHTLCExposure :: FeeRateMultiplier ( 5000 ) ,
470
512
force_close_avoidance_max_fee_satoshis : 1000 ,
471
513
accept_underpaying_htlcs : false ,
472
514
}
473
515
}
474
516
}
475
517
476
- impl_writeable_tlv_based ! ( ChannelConfig , {
477
- ( 0 , forwarding_fee_proportional_millionths, required) ,
478
- ( 1 , accept_underpaying_htlcs, ( default_value, false ) ) ,
479
- ( 2 , forwarding_fee_base_msat, required) ,
480
- ( 4 , cltv_expiry_delta, required) ,
481
- ( 6 , max_dust_htlc_exposure_msat, required) ,
482
- // ChannelConfig serialized this field with a required type of 8 prior to the introduction of
483
- // LegacyChannelConfig. To make sure that serialization is not compatible with this one, we use
484
- // the next required type of 10, which if seen by the old serialization will always fail.
485
- ( 10 , force_close_avoidance_max_fee_satoshis, required) ,
486
- } ) ;
518
+ impl crate :: util:: ser:: Writeable for ChannelConfig {
519
+ fn write < W : crate :: util:: ser:: Writer > ( & self , writer : & mut W ) -> Result < ( ) , crate :: io:: Error > {
520
+ let max_dust_htlc_exposure_msat_fixed_limit = match self . max_dust_htlc_exposure_msat {
521
+ MaxDustHTLCExposure :: FixedLimitMsat ( limit) => limit,
522
+ MaxDustHTLCExposure :: FeeRateMultiplier ( _) => 5_000_000 ,
523
+ } ;
524
+ write_tlv_fields ! ( writer, {
525
+ ( 0 , self . forwarding_fee_proportional_millionths, required) ,
526
+ ( 1 , self . accept_underpaying_htlcs, ( default_value, false ) ) ,
527
+ ( 2 , self . forwarding_fee_base_msat, required) ,
528
+ ( 3 , Some ( self . max_dust_htlc_exposure_msat) , option) ,
529
+ ( 4 , self . cltv_expiry_delta, required) ,
530
+ ( 6 , max_dust_htlc_exposure_msat_fixed_limit, required) ,
531
+ // ChannelConfig serialized this field with a required type of 8 prior to the introduction of
532
+ // LegacyChannelConfig. To make sure that serialization is not compatible with this one, we use
533
+ // the next required type of 10, which if seen by the old serialization will always fail.
534
+ ( 10 , self . force_close_avoidance_max_fee_satoshis, required) ,
535
+ } ) ;
536
+ Ok ( ( ) )
537
+ }
538
+ }
539
+
540
+ impl crate :: util:: ser:: Readable for ChannelConfig {
541
+ fn read < R : crate :: io:: Read > ( reader : & mut R ) -> Result < Self , crate :: ln:: msgs:: DecodeError > {
542
+ let mut forwarding_fee_proportional_millionths = 0 ;
543
+ let mut accept_underpaying_htlcs = false ;
544
+ let mut forwarding_fee_base_msat = 1000 ;
545
+ let mut cltv_expiry_delta = 6 * 12 ;
546
+ let mut max_dust_htlc_exposure_msat = 5_000_000 ;
547
+ let mut max_dust_htlc_exposure_enum = None ;
548
+ let mut force_close_avoidance_max_fee_satoshis = 1000 ;
549
+ read_tlv_fields ! ( reader, {
550
+ ( 0 , forwarding_fee_proportional_millionths, required) ,
551
+ ( 1 , accept_underpaying_htlcs, ( default_value, false ) ) ,
552
+ ( 2 , forwarding_fee_base_msat, required) ,
553
+ ( 3 , max_dust_htlc_exposure_enum, option) ,
554
+ ( 4 , cltv_expiry_delta, required) ,
555
+ ( 6 , max_dust_htlc_exposure_msat, required) ,
556
+ ( 10 , force_close_avoidance_max_fee_satoshis, required) ,
557
+ } ) ;
558
+ let max_dust_htlc_exposure_msat = max_dust_htlc_exposure_enum
559
+ . unwrap_or ( MaxDustHTLCExposure :: FixedLimitMsat ( max_dust_htlc_exposure_msat) ) ;
560
+ Ok ( Self {
561
+ forwarding_fee_proportional_millionths,
562
+ accept_underpaying_htlcs,
563
+ forwarding_fee_base_msat,
564
+ cltv_expiry_delta,
565
+ max_dust_htlc_exposure_msat,
566
+ force_close_avoidance_max_fee_satoshis,
567
+ } )
568
+ }
569
+ }
487
570
488
571
/// A parallel struct to [`ChannelConfig`] to define partial updates.
489
572
#[ allow( missing_docs) ]
490
573
pub struct ChannelConfigUpdate {
491
574
pub forwarding_fee_proportional_millionths : Option < u32 > ,
492
575
pub forwarding_fee_base_msat : Option < u32 > ,
493
576
pub cltv_expiry_delta : Option < u16 > ,
494
- pub max_dust_htlc_exposure_msat : Option < u64 > ,
577
+ pub max_dust_htlc_exposure_msat : Option < MaxDustHTLCExposure > ,
495
578
pub force_close_avoidance_max_fee_satoshis : Option < u64 > ,
496
579
}
497
580
@@ -546,12 +629,17 @@ impl Default for LegacyChannelConfig {
546
629
547
630
impl crate :: util:: ser:: Writeable for LegacyChannelConfig {
548
631
fn write < W : crate :: util:: ser:: Writer > ( & self , writer : & mut W ) -> Result < ( ) , crate :: io:: Error > {
632
+ let max_dust_htlc_exposure_msat_fixed_limit = match self . options . max_dust_htlc_exposure_msat {
633
+ MaxDustHTLCExposure :: FixedLimitMsat ( limit) => limit,
634
+ MaxDustHTLCExposure :: FeeRateMultiplier ( _) => 5_000_000 ,
635
+ } ;
549
636
write_tlv_fields ! ( writer, {
550
637
( 0 , self . options. forwarding_fee_proportional_millionths, required) ,
551
- ( 1 , self . options . max_dust_htlc_exposure_msat , ( default_value, 5_000_000 ) ) ,
638
+ ( 1 , max_dust_htlc_exposure_msat_fixed_limit , ( default_value, 5_000_000 ) ) ,
552
639
( 2 , self . options. cltv_expiry_delta, required) ,
553
640
( 3 , self . options. force_close_avoidance_max_fee_satoshis, ( default_value, 1000 ) ) ,
554
641
( 4 , self . announced_channel, required) ,
642
+ ( 5 , Some ( self . options. max_dust_htlc_exposure_msat) , option) ,
555
643
( 6 , self . commit_upfront_shutdown_pubkey, required) ,
556
644
( 8 , self . options. forwarding_fee_base_msat, required) ,
557
645
} ) ;
@@ -562,21 +650,25 @@ impl crate::util::ser::Writeable for LegacyChannelConfig {
562
650
impl crate :: util:: ser:: Readable for LegacyChannelConfig {
563
651
fn read < R : crate :: io:: Read > ( reader : & mut R ) -> Result < Self , crate :: ln:: msgs:: DecodeError > {
564
652
let mut forwarding_fee_proportional_millionths = 0 ;
565
- let mut max_dust_htlc_exposure_msat = 5_000_000 ;
653
+ let mut max_dust_htlc_exposure_msat_fixed_limit = 5_000_000 ;
566
654
let mut cltv_expiry_delta = 0 ;
567
655
let mut force_close_avoidance_max_fee_satoshis = 1000 ;
568
656
let mut announced_channel = false ;
569
657
let mut commit_upfront_shutdown_pubkey = false ;
570
658
let mut forwarding_fee_base_msat = 0 ;
659
+ let mut max_dust_htlc_exposure_enum = None ;
571
660
read_tlv_fields ! ( reader, {
572
661
( 0 , forwarding_fee_proportional_millionths, required) ,
573
- ( 1 , max_dust_htlc_exposure_msat , ( default_value, 5_000_000u64 ) ) ,
662
+ ( 1 , max_dust_htlc_exposure_msat_fixed_limit , ( default_value, 5_000_000u64 ) ) ,
574
663
( 2 , cltv_expiry_delta, required) ,
575
664
( 3 , force_close_avoidance_max_fee_satoshis, ( default_value, 1000u64 ) ) ,
576
665
( 4 , announced_channel, required) ,
666
+ ( 5 , max_dust_htlc_exposure_enum, option) ,
577
667
( 6 , commit_upfront_shutdown_pubkey, required) ,
578
668
( 8 , forwarding_fee_base_msat, required) ,
579
669
} ) ;
670
+ let max_dust_htlc_exposure_msat = max_dust_htlc_exposure_enum
671
+ . unwrap_or ( MaxDustHTLCExposure :: FixedLimitMsat ( max_dust_htlc_exposure_msat_fixed_limit) ) ;
580
672
Ok ( Self {
581
673
options : ChannelConfig {
582
674
forwarding_fee_proportional_millionths,
0 commit comments