@@ -315,6 +315,55 @@ 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`] 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 manual 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.
349
+ ///
350
+ /// This variant is primarily meant to serve pre-anchor channels, as HTLC fees being included
351
+ /// on HTLC outputs means your channel may be subject to more dust exposure in the event of
352
+ /// increases in fee rate.
353
+ ///
354
+ /// # Backwards Compatibility
355
+ /// This variant only became available in LDK 0.0.116, so if you downgrade to a prior version
356
+ /// by default this will be set to a [`Self::FixedLimitMsat`] of 5,000,000 msat.
357
+ ///
358
+ /// [`FeeEstimator`]: crate::chain::chaininterface::FeeEstimator
359
+ FeeRateMultiplier ( u64 ) ,
360
+ }
361
+
362
+ impl_writeable_tlv_based_enum ! ( MaxDustHTLCExposure , ;
363
+ ( 1 , FixedLimitMsat ) ,
364
+ ( 3 , FeeRateMultiplier ) ,
365
+ ) ;
366
+
318
367
/// Options which apply on a per-channel basis and may change at runtime or based on negotiation
319
368
/// with our counterparty.
320
369
#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
@@ -372,15 +421,15 @@ pub struct ChannelConfig {
372
421
/// channel negotiated throughout the channel open process, along with the fees required to have
373
422
/// a broadcastable HTLC spending transaction. When a channel supports anchor outputs
374
423
/// (specifically the zero fee HTLC transaction variant), this threshold no longer takes into
375
- /// account the HTLC transaction fee as it is zero.
424
+ /// account the HTLC transaction fee as it is zero. Because of this, you may want to set this
425
+ /// value to a fixed limit for channels using anchor outputs, while the fee rate multiplier
426
+ /// variant is primarily intended for use with pre-anchor channels.
376
427
///
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.
428
+ /// The selected limit is applied for sent, forwarded, and received HTLCs and limits the total
429
+ /// exposure across all three types per-channel.
381
430
///
382
- /// Default value: 5_000_000 msat .
383
- pub max_dust_htlc_exposure_msat : u64 ,
431
+ /// Default value: [`MaxDustHTLCExposure::FeeRateMultiplier`] with a multiplier of 5000 .
432
+ pub max_dust_htlc_exposure : MaxDustHTLCExposure ,
384
433
/// The additional fee we're willing to pay to avoid waiting for the counterparty's
385
434
/// `to_self_delay` to reclaim funds.
386
435
///
@@ -451,7 +500,7 @@ impl ChannelConfig {
451
500
self . cltv_expiry_delta = cltv_expiry_delta;
452
501
}
453
502
if let Some ( max_dust_htlc_exposure_msat) = update. max_dust_htlc_exposure_msat {
454
- self . max_dust_htlc_exposure_msat = max_dust_htlc_exposure_msat;
503
+ self . max_dust_htlc_exposure = max_dust_htlc_exposure_msat;
455
504
}
456
505
if let Some ( force_close_avoidance_max_fee_satoshis) = update. force_close_avoidance_max_fee_satoshis {
457
506
self . force_close_avoidance_max_fee_satoshis = force_close_avoidance_max_fee_satoshis;
@@ -466,32 +515,75 @@ impl Default for ChannelConfig {
466
515
forwarding_fee_proportional_millionths : 0 ,
467
516
forwarding_fee_base_msat : 1000 ,
468
517
cltv_expiry_delta : 6 * 12 , // 6 blocks/hour * 12 hours
469
- max_dust_htlc_exposure_msat : 5_000_000 ,
518
+ max_dust_htlc_exposure : MaxDustHTLCExposure :: FeeRateMultiplier ( 5000 ) ,
470
519
force_close_avoidance_max_fee_satoshis : 1000 ,
471
520
accept_underpaying_htlcs : false ,
472
521
}
473
522
}
474
523
}
475
524
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
- } ) ;
525
+ impl crate :: util:: ser:: Writeable for ChannelConfig {
526
+ fn write < W : crate :: util:: ser:: Writer > ( & self , writer : & mut W ) -> Result < ( ) , crate :: io:: Error > {
527
+ let max_dust_htlc_exposure_msat_fixed_limit = match self . max_dust_htlc_exposure {
528
+ MaxDustHTLCExposure :: FixedLimitMsat ( limit) => limit,
529
+ MaxDustHTLCExposure :: FeeRateMultiplier ( _) => 5_000_000 ,
530
+ } ;
531
+ write_tlv_fields ! ( writer, {
532
+ ( 0 , self . forwarding_fee_proportional_millionths, required) ,
533
+ ( 1 , self . accept_underpaying_htlcs, ( default_value, false ) ) ,
534
+ ( 2 , self . forwarding_fee_base_msat, required) ,
535
+ ( 3 , self . max_dust_htlc_exposure, required) ,
536
+ ( 4 , self . cltv_expiry_delta, required) ,
537
+ ( 6 , max_dust_htlc_exposure_msat_fixed_limit, required) ,
538
+ // ChannelConfig serialized this field with a required type of 8 prior to the introduction of
539
+ // LegacyChannelConfig. To make sure that serialization is not compatible with this one, we use
540
+ // the next required type of 10, which if seen by the old serialization will always fail.
541
+ ( 10 , self . force_close_avoidance_max_fee_satoshis, required) ,
542
+ } ) ;
543
+ Ok ( ( ) )
544
+ }
545
+ }
546
+
547
+ impl crate :: util:: ser:: Readable for ChannelConfig {
548
+ fn read < R : crate :: io:: Read > ( reader : & mut R ) -> Result < Self , crate :: ln:: msgs:: DecodeError > {
549
+ let mut forwarding_fee_proportional_millionths = 0 ;
550
+ let mut accept_underpaying_htlcs = false ;
551
+ let mut forwarding_fee_base_msat = 1000 ;
552
+ let mut cltv_expiry_delta = 6 * 12 ;
553
+ let mut max_dust_htlc_exposure_msat = None ;
554
+ let mut max_dust_htlc_exposure_enum = None ;
555
+ let mut force_close_avoidance_max_fee_satoshis = 1000 ;
556
+ read_tlv_fields ! ( reader, {
557
+ ( 0 , forwarding_fee_proportional_millionths, required) ,
558
+ ( 1 , accept_underpaying_htlcs, ( default_value, false ) ) ,
559
+ ( 2 , forwarding_fee_base_msat, required) ,
560
+ ( 3 , max_dust_htlc_exposure_enum, option) ,
561
+ ( 4 , cltv_expiry_delta, required) ,
562
+ // Has always been written, but became optionally read in 0.0.116
563
+ ( 6 , max_dust_htlc_exposure_msat, option) ,
564
+ ( 10 , force_close_avoidance_max_fee_satoshis, required) ,
565
+ } ) ;
566
+ let max_dust_htlc_fixed_limit = max_dust_htlc_exposure_msat. unwrap_or ( 5_000_000 ) ;
567
+ let max_dust_htlc_exposure_msat = max_dust_htlc_exposure_enum
568
+ . unwrap_or ( MaxDustHTLCExposure :: FixedLimitMsat ( max_dust_htlc_fixed_limit) ) ;
569
+ Ok ( Self {
570
+ forwarding_fee_proportional_millionths,
571
+ accept_underpaying_htlcs,
572
+ forwarding_fee_base_msat,
573
+ cltv_expiry_delta,
574
+ max_dust_htlc_exposure : max_dust_htlc_exposure_msat,
575
+ force_close_avoidance_max_fee_satoshis,
576
+ } )
577
+ }
578
+ }
487
579
488
580
/// A parallel struct to [`ChannelConfig`] to define partial updates.
489
581
#[ allow( missing_docs) ]
490
582
pub struct ChannelConfigUpdate {
491
583
pub forwarding_fee_proportional_millionths : Option < u32 > ,
492
584
pub forwarding_fee_base_msat : Option < u32 > ,
493
585
pub cltv_expiry_delta : Option < u16 > ,
494
- pub max_dust_htlc_exposure_msat : Option < u64 > ,
586
+ pub max_dust_htlc_exposure_msat : Option < MaxDustHTLCExposure > ,
495
587
pub force_close_avoidance_max_fee_satoshis : Option < u64 > ,
496
588
}
497
589
@@ -513,7 +605,7 @@ impl From<ChannelConfig> for ChannelConfigUpdate {
513
605
forwarding_fee_proportional_millionths : Some ( config. forwarding_fee_proportional_millionths ) ,
514
606
forwarding_fee_base_msat : Some ( config. forwarding_fee_base_msat ) ,
515
607
cltv_expiry_delta : Some ( config. cltv_expiry_delta ) ,
516
- max_dust_htlc_exposure_msat : Some ( config. max_dust_htlc_exposure_msat ) ,
608
+ max_dust_htlc_exposure_msat : Some ( config. max_dust_htlc_exposure ) ,
517
609
force_close_avoidance_max_fee_satoshis : Some ( config. force_close_avoidance_max_fee_satoshis ) ,
518
610
}
519
611
}
@@ -546,12 +638,17 @@ impl Default for LegacyChannelConfig {
546
638
547
639
impl crate :: util:: ser:: Writeable for LegacyChannelConfig {
548
640
fn write < W : crate :: util:: ser:: Writer > ( & self , writer : & mut W ) -> Result < ( ) , crate :: io:: Error > {
641
+ let max_dust_htlc_exposure_msat_fixed_limit = match self . options . max_dust_htlc_exposure {
642
+ MaxDustHTLCExposure :: FixedLimitMsat ( limit) => limit,
643
+ MaxDustHTLCExposure :: FeeRateMultiplier ( _) => 5_000_000 ,
644
+ } ;
549
645
write_tlv_fields ! ( writer, {
550
646
( 0 , self . options. forwarding_fee_proportional_millionths, required) ,
551
- ( 1 , self . options . max_dust_htlc_exposure_msat , ( default_value , 5_000_000 ) ) ,
647
+ ( 1 , max_dust_htlc_exposure_msat_fixed_limit , required ) ,
552
648
( 2 , self . options. cltv_expiry_delta, required) ,
553
649
( 3 , self . options. force_close_avoidance_max_fee_satoshis, ( default_value, 1000 ) ) ,
554
650
( 4 , self . announced_channel, required) ,
651
+ ( 5 , self . options. max_dust_htlc_exposure, required) ,
555
652
( 6 , self . commit_upfront_shutdown_pubkey, required) ,
556
653
( 8 , self . options. forwarding_fee_base_msat, required) ,
557
654
} ) ;
@@ -562,25 +659,32 @@ impl crate::util::ser::Writeable for LegacyChannelConfig {
562
659
impl crate :: util:: ser:: Readable for LegacyChannelConfig {
563
660
fn read < R : crate :: io:: Read > ( reader : & mut R ) -> Result < Self , crate :: ln:: msgs:: DecodeError > {
564
661
let mut forwarding_fee_proportional_millionths = 0 ;
565
- let mut max_dust_htlc_exposure_msat = 5_000_000 ;
662
+ let mut max_dust_htlc_exposure_msat_fixed_limit = None ;
566
663
let mut cltv_expiry_delta = 0 ;
567
664
let mut force_close_avoidance_max_fee_satoshis = 1000 ;
568
665
let mut announced_channel = false ;
569
666
let mut commit_upfront_shutdown_pubkey = false ;
570
667
let mut forwarding_fee_base_msat = 0 ;
668
+ let mut max_dust_htlc_exposure_enum = None ;
571
669
read_tlv_fields ! ( reader, {
572
670
( 0 , forwarding_fee_proportional_millionths, required) ,
573
- ( 1 , max_dust_htlc_exposure_msat, ( default_value, 5_000_000u64 ) ) ,
671
+ // Has always been written, but became optionally read in 0.0.116
672
+ ( 1 , max_dust_htlc_exposure_msat_fixed_limit, option) ,
574
673
( 2 , cltv_expiry_delta, required) ,
575
674
( 3 , force_close_avoidance_max_fee_satoshis, ( default_value, 1000u64 ) ) ,
576
675
( 4 , announced_channel, required) ,
676
+ ( 5 , max_dust_htlc_exposure_enum, option) ,
577
677
( 6 , commit_upfront_shutdown_pubkey, required) ,
578
678
( 8 , forwarding_fee_base_msat, required) ,
579
679
} ) ;
680
+ let max_dust_htlc_exposure_msat_fixed_limit =
681
+ max_dust_htlc_exposure_msat_fixed_limit. unwrap_or ( 5_000_000 ) ;
682
+ let max_dust_htlc_exposure_msat = max_dust_htlc_exposure_enum
683
+ . unwrap_or ( MaxDustHTLCExposure :: FixedLimitMsat ( max_dust_htlc_exposure_msat_fixed_limit) ) ;
580
684
Ok ( Self {
581
685
options : ChannelConfig {
582
686
forwarding_fee_proportional_millionths,
583
- max_dust_htlc_exposure_msat,
687
+ max_dust_htlc_exposure : max_dust_htlc_exposure_msat,
584
688
cltv_expiry_delta,
585
689
force_close_avoidance_max_fee_satoshis,
586
690
forwarding_fee_base_msat,
0 commit comments