Skip to content

Commit ad4b09a

Browse files
committed
Use multiplier in dust exposure threshold calculation
This commit makes use of the added config knob to calculate the dust exposure threshold based on the current fee rate. This also updates tests to ensure it works as intended.
1 parent b481c17 commit ad4b09a

File tree

5 files changed

+70
-25
lines changed

5 files changed

+70
-25
lines changed

lightning/src/ln/channel.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -1070,10 +1070,17 @@ impl<Signer: ChannelSigner> ChannelContext<Signer> {
10701070
}
10711071

10721072
pub fn get_max_dust_htlc_exposure_msat<F: Deref>(&self,
1073-
_fee_estimator: &LowerBoundedFeeEstimator<F>) -> u64
1073+
fee_estimator: &LowerBoundedFeeEstimator<F>) -> u64
10741074
where F::Target: FeeEstimator
10751075
{
1076-
self.config.options.max_dust_htlc_exposure_msat
1076+
match self.config.options.max_dust_htlc_exposure_multiplier_thousandths {
1077+
Some(multiplier_thousandths) => {
1078+
let feerate_per_kw = fee_estimator.bounded_sat_per_1000_weight(
1079+
ConfirmationTarget::HighPriority);
1080+
feerate_per_kw as u64 * multiplier_thousandths
1081+
},
1082+
None => self.config.options.max_dust_htlc_exposure_msat,
1083+
}
10771084
}
10781085

10791086
/// Returns the previous [`ChannelConfig`] applied to this channel, if any.

lightning/src/ln/functional_test_utils.rs

+5
Original file line numberDiff line numberDiff line change
@@ -2550,7 +2550,12 @@ pub fn test_default_channel_config() -> UserConfig {
25502550
default_config.channel_handshake_config.our_htlc_minimum_msat = 1000;
25512551
// When most of our tests were written, we didn't have the notion of a `max_dust_htlc_exposure_msat`,
25522552
// It now defaults to 5_000_000 msat; to avoid interfering with tests we bump it to 50_000_000 msat.
2553+
// This is less relevant now as it's overriden by max_dust_htlc_exposure_multiplier_thousandths
2554+
// by default.
25532555
default_config.channel_config.max_dust_htlc_exposure_msat = 50_000_000;
2556+
// Similarly to the previous threshold, this was added after most of our tests were written, so
2557+
// we bump the default value to avoid interference.
2558+
default_config.channel_config.max_dust_htlc_exposure_multiplier_thousandths = Some(50_000_000 / 253);
25542559
default_config
25552560
}
25562561

lightning/src/ln/functional_tests.rs

+44-23
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
1414
use crate::chain;
1515
use crate::chain::{ChannelMonitorUpdateStatus, Confirm, Listen, Watch};
16-
use crate::chain::chaininterface::LowerBoundedFeeEstimator;
16+
use crate::chain::chaininterface::{LowerBoundedFeeEstimator, FeeEstimator, ConfirmationTarget};
1717
use crate::chain::channelmonitor;
1818
use crate::chain::channelmonitor::{CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY};
1919
use crate::chain::transaction::OutPoint;
@@ -9510,7 +9510,7 @@ enum ExposureEvent {
95109510
AtUpdateFeeOutbound,
95119511
}
95129512

9513-
fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_event: ExposureEvent, on_holder_tx: bool) {
9513+
fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_event: ExposureEvent, on_holder_tx: bool, multiplier_dust_limit: bool) {
95149514
// Test that we properly reject dust HTLC violating our `max_dust_htlc_exposure_msat`
95159515
// policy.
95169516
//
@@ -9526,6 +9526,9 @@ fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_e
95269526
let chanmon_cfgs = create_chanmon_cfgs(2);
95279527
let mut config = test_default_channel_config();
95289528
config.channel_config.max_dust_htlc_exposure_msat = 5_000_000; // default setting value
9529+
// Default test fee estimator rate is 253 sat/kw, so we set the multiplier to 5_000_000 / 253
9530+
// to get roughly the same initial value as the default setting.
9531+
config.channel_config.max_dust_htlc_exposure_multiplier_thousandths = if multiplier_dust_limit { Some(5_000_000 / 253) } else { None };
95299532
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
95309533
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config), None]);
95319534
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
@@ -9575,14 +9578,21 @@ fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_e
95759578
let chan = chan_lock.channel_by_id.get(&channel_id).unwrap();
95769579
chan.context.get_dust_buffer_feerate(None) as u64
95779580
};
9581+
let max_dust_htlc_exposure_msat = if multiplier_dust_limit {
9582+
nodes[0].fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) as u64
9583+
* config.channel_config.max_dust_htlc_exposure_multiplier_thousandths.unwrap()
9584+
} else {
9585+
config.channel_config.max_dust_htlc_exposure_msat
9586+
};
9587+
95789588
let dust_outbound_htlc_on_holder_tx_msat: u64 = (dust_buffer_feerate * htlc_timeout_tx_weight(opt_anchors) / 1000 + open_channel.dust_limit_satoshis - 1) * 1000;
9579-
let dust_outbound_htlc_on_holder_tx: u64 = config.channel_config.max_dust_htlc_exposure_msat / dust_outbound_htlc_on_holder_tx_msat;
9589+
let dust_outbound_htlc_on_holder_tx: u64 = max_dust_htlc_exposure_msat / dust_outbound_htlc_on_holder_tx_msat;
95809590

95819591
let dust_inbound_htlc_on_holder_tx_msat: u64 = (dust_buffer_feerate * htlc_success_tx_weight(opt_anchors) / 1000 + open_channel.dust_limit_satoshis - 1) * 1000;
9582-
let dust_inbound_htlc_on_holder_tx: u64 = config.channel_config.max_dust_htlc_exposure_msat / dust_inbound_htlc_on_holder_tx_msat;
9592+
let dust_inbound_htlc_on_holder_tx: u64 = max_dust_htlc_exposure_msat / dust_inbound_htlc_on_holder_tx_msat;
95839593

95849594
let dust_htlc_on_counterparty_tx: u64 = 4;
9585-
let dust_htlc_on_counterparty_tx_msat: u64 = config.channel_config.max_dust_htlc_exposure_msat / dust_htlc_on_counterparty_tx;
9595+
let dust_htlc_on_counterparty_tx_msat: u64 = max_dust_htlc_exposure_msat / dust_htlc_on_counterparty_tx;
95869596

95879597
if on_holder_tx {
95889598
if dust_outbound_balance {
@@ -9634,7 +9644,7 @@ fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_e
96349644
), true, APIError::ChannelUnavailable { .. }, {});
96359645
}
96369646
} else if exposure_breach_event == ExposureEvent::AtHTLCReception {
9637-
let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], if on_holder_tx { dust_inbound_htlc_on_holder_tx_msat } else { dust_htlc_on_counterparty_tx_msat + 1 });
9647+
let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], if on_holder_tx { dust_inbound_htlc_on_holder_tx_msat } else { dust_htlc_on_counterparty_tx_msat + 4 });
96389648
nodes[1].node.send_payment_with_route(&route, payment_hash,
96399649
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap();
96409650
check_added_monitors!(nodes[1], 1);
@@ -9647,18 +9657,24 @@ fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_e
96479657
// Outbound dust balance: 6399 sats
96489658
let dust_inbound_overflow = dust_inbound_htlc_on_holder_tx_msat * (dust_inbound_htlc_on_holder_tx + 1);
96499659
let dust_outbound_overflow = dust_outbound_htlc_on_holder_tx_msat * dust_outbound_htlc_on_holder_tx + dust_inbound_htlc_on_holder_tx_msat;
9650-
nodes[0].logger.assert_log("lightning::ln::channel".to_string(), format!("Cannot accept value that would put our exposure to dust HTLCs at {} over the limit {} on holder commitment tx", if dust_outbound_balance { dust_outbound_overflow } else { dust_inbound_overflow }, config.channel_config.max_dust_htlc_exposure_msat), 1);
9660+
nodes[0].logger.assert_log("lightning::ln::channel".to_string(), format!("Cannot accept value that would put our exposure to dust HTLCs at {} over the limit {} on holder commitment tx", if dust_outbound_balance { dust_outbound_overflow } else { dust_inbound_overflow }, max_dust_htlc_exposure_msat), 1);
96519661
} else {
96529662
// Outbound dust balance: 5200 sats
96539663
nodes[0].logger.assert_log("lightning::ln::channel".to_string(),
96549664
format!("Cannot accept value that would put our exposure to dust HTLCs at {} over the limit {} on counterparty commitment tx",
9655-
dust_htlc_on_counterparty_tx_msat * (dust_htlc_on_counterparty_tx - 1) + dust_htlc_on_counterparty_tx_msat + 1,
9656-
config.channel_config.max_dust_htlc_exposure_msat), 1);
9665+
dust_htlc_on_counterparty_tx_msat * (dust_htlc_on_counterparty_tx - 1) + dust_htlc_on_counterparty_tx_msat + 4,
9666+
max_dust_htlc_exposure_msat), 1);
96579667
}
96589668
} else if exposure_breach_event == ExposureEvent::AtUpdateFeeOutbound {
96599669
route.paths[0].hops.last_mut().unwrap().fee_msat = 2_500_000;
9660-
nodes[0].node.send_payment_with_route(&route, payment_hash,
9661-
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap();
9670+
// For the multiplier dust exposure limit, since it scales with feerate,
9671+
// we need to add a lot of HTLCs that will become dust at the new feerate
9672+
// to cross the threshold.
9673+
for _ in 0..20 {
9674+
let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[1], Some(1_000), None);
9675+
nodes[0].node.send_payment_with_route(&route, payment_hash,
9676+
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap();
9677+
}
96629678
{
96639679
let mut feerate_lock = chanmon_cfgs[0].fee_estimator.sat_per_kw.lock().unwrap();
96649680
*feerate_lock = *feerate_lock * 10;
@@ -9673,20 +9689,25 @@ fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_e
96739689
added_monitors.clear();
96749690
}
96759691

9692+
fn do_test_max_dust_htlc_exposure_by_threshold_type(multiplier_dust_limit: bool) {
9693+
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCForward, true, multiplier_dust_limit);
9694+
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCForward, true, multiplier_dust_limit);
9695+
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCReception, true, multiplier_dust_limit);
9696+
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCReception, false, multiplier_dust_limit);
9697+
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCForward, false, multiplier_dust_limit);
9698+
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCReception, false, multiplier_dust_limit);
9699+
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCReception, true, multiplier_dust_limit);
9700+
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCForward, false, multiplier_dust_limit);
9701+
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtUpdateFeeOutbound, true, multiplier_dust_limit);
9702+
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtUpdateFeeOutbound, false, multiplier_dust_limit);
9703+
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, false, multiplier_dust_limit);
9704+
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, true, multiplier_dust_limit);
9705+
}
9706+
96769707
#[test]
96779708
fn test_max_dust_htlc_exposure() {
9678-
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCForward, true);
9679-
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCForward, true);
9680-
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCReception, true);
9681-
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCReception, false);
9682-
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCForward, false);
9683-
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCReception, false);
9684-
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCReception, true);
9685-
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCForward, false);
9686-
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtUpdateFeeOutbound, true);
9687-
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtUpdateFeeOutbound, false);
9688-
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, false);
9689-
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, true);
9709+
do_test_max_dust_htlc_exposure_by_threshold_type(false);
9710+
do_test_max_dust_htlc_exposure_by_threshold_type(true);
96909711
}
96919712

96929713
#[test]

lightning/src/ln/onion_route_tests.rs

+10
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,7 @@ fn do_test_onion_failure_stale_channel_update(announced_channel: bool) {
671671
config.channel_handshake_config.announced_channel = announced_channel;
672672
config.channel_handshake_limits.force_announced_channel_preference = false;
673673
config.accept_forwards_to_priv_channels = !announced_channel;
674+
config.channel_config.max_dust_htlc_exposure_multiplier_thousandths = None;
674675
let chanmon_cfgs = create_chanmon_cfgs(3);
675676
let persister;
676677
let chain_monitor;
@@ -1371,11 +1372,20 @@ fn test_phantom_failure_too_low_recv_amt() {
13711372

13721373
#[test]
13731374
fn test_phantom_dust_exposure_failure() {
1375+
do_test_phantom_dust_exposure_failure(false);
1376+
do_test_phantom_dust_exposure_failure(true);
1377+
}
1378+
1379+
fn do_test_phantom_dust_exposure_failure(multiplier_dust_limit: bool) {
13741380
// Set the max dust exposure to the dust limit.
13751381
let max_dust_exposure = 546;
13761382
let mut receiver_config = UserConfig::default();
13771383
receiver_config.channel_config.max_dust_htlc_exposure_msat = max_dust_exposure;
13781384
receiver_config.channel_handshake_config.announced_channel = true;
1385+
// Default test fee estimator rate is 253, so to set the max dust exposure to the dust limit,
1386+
// we need to set the multiplier to 2.
1387+
receiver_config.channel_config.max_dust_htlc_exposure_multiplier_thousandths =
1388+
if multiplier_dust_limit { Some(2) } else { None };
13791389

13801390
let chanmon_cfgs = create_chanmon_cfgs(2);
13811391
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);

lightning/src/ln/priv_short_conf_tests.rs

+2
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,12 @@ fn do_test_1_conf_open(connect_style: ConnectStyle) {
141141
alice_config.channel_handshake_config.minimum_depth = 1;
142142
alice_config.channel_handshake_config.announced_channel = true;
143143
alice_config.channel_handshake_limits.force_announced_channel_preference = false;
144+
alice_config.channel_config.max_dust_htlc_exposure_multiplier_thousandths = None;
144145
let mut bob_config = UserConfig::default();
145146
bob_config.channel_handshake_config.minimum_depth = 1;
146147
bob_config.channel_handshake_config.announced_channel = true;
147148
bob_config.channel_handshake_limits.force_announced_channel_preference = false;
149+
bob_config.channel_config.max_dust_htlc_exposure_multiplier_thousandths = None;
148150
let chanmon_cfgs = create_chanmon_cfgs(2);
149151
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
150152
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(alice_config), Some(bob_config)]);

0 commit comments

Comments
 (0)