Skip to content

Commit 3e0280e

Browse files
committed
Use multiplier in dust exposure threshold calculation
This commit makes use of the added enum to calculate the dust exposure threshold based on the current fee rate. This also updates tests to ensure it works as intended.
1 parent 1271cc9 commit 3e0280e

7 files changed

+77
-27
lines changed

fuzz/src/full_stack.rs

+17-2
Large diffs are not rendered by default.

lightning/src/ln/channel.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -1073,12 +1073,16 @@ impl<Signer: ChannelSigner> ChannelContext<Signer> {
10731073
}
10741074

10751075
pub fn get_max_dust_htlc_exposure_msat<F: Deref>(&self,
1076-
_fee_estimator: &LowerBoundedFeeEstimator<F>) -> u64
1076+
fee_estimator: &LowerBoundedFeeEstimator<F>) -> u64
10771077
where F::Target: FeeEstimator
10781078
{
10791079
match self.config.options.max_dust_htlc_exposure_msat {
1080+
MaxDustHTLCExposure::FeeRateMultiplier(multiplier) => {
1081+
let feerate_per_kw = fee_estimator.bounded_sat_per_1000_weight(
1082+
ConfirmationTarget::HighPriority);
1083+
feerate_per_kw as u64 * multiplier
1084+
},
10801085
MaxDustHTLCExposure::FixedLimitMsat(limit) => limit,
1081-
MaxDustHTLCExposure::FeeRateMultiplier(_) => 5_000_000,
10821086
}
10831087
}
10841088

lightning/src/ln/channelmanager.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -9842,7 +9842,7 @@ pub mod bench {
98429842
use crate::routing::gossip::NetworkGraph;
98439843
use crate::routing::router::{PaymentParameters, RouteParameters};
98449844
use crate::util::test_utils;
9845-
use crate::util::config::UserConfig;
9845+
use crate::util::config::{UserConfig, MaxDustHTLCExposure};
98469846

98479847
use bitcoin::hashes::Hash;
98489848
use bitcoin::hashes::sha256::Hash as Sha256;
@@ -9888,6 +9888,7 @@ pub mod bench {
98889888
let router = test_utils::TestRouter::new(Arc::new(NetworkGraph::new(network, &logger_a)), &scorer);
98899889

98909890
let mut config: UserConfig = Default::default();
9891+
config.channel_config.max_dust_htlc_exposure_msat = MaxDustHTLCExposure::FeeRateMultiplier(5_000_000 / 253);
98919892
config.channel_handshake_config.minimum_depth = 1;
98929893

98939894
let chain_monitor_a = ChainMonitor::new(None, &tx_broadcaster, &logger_a, &fee_estimator, &persister_a);

lightning/src/ln/functional_test_utils.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -2563,8 +2563,10 @@ pub fn test_default_channel_config() -> UserConfig {
25632563
// It now defaults to 1, so we simply set it to the expected value here.
25642564
default_config.channel_handshake_config.our_htlc_minimum_msat = 1000;
25652565
// When most of our tests were written, we didn't have the notion of a `max_dust_htlc_exposure_msat`,
2566-
// It now defaults to 5_000_000 msat; to avoid interfering with tests we bump it to 50_000_000 msat.
2567-
default_config.channel_config.max_dust_htlc_exposure_msat = MaxDustHTLCExposure::FixedLimitMsat(50_000_000);
2566+
// to avoid interfering with tests we bump it to 50_000_000 msat (assuming the default test
2567+
// feerate of 253).
2568+
default_config.channel_config.max_dust_htlc_exposure_msat =
2569+
MaxDustHTLCExposure::FeeRateMultiplier(50_000_000 / 253);
25682570
default_config
25692571
}
25702572

lightning/src/ln/functional_tests.rs

+35-18
Original file line numberDiff line numberDiff line change
@@ -9515,7 +9515,7 @@ enum ExposureEvent {
95159515
AtUpdateFeeOutbound,
95169516
}
95179517

9518-
fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_event: ExposureEvent, on_holder_tx: bool) {
9518+
fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_event: ExposureEvent, on_holder_tx: bool, multiplier_dust_limit: bool) {
95199519
// Test that we properly reject dust HTLC violating our `max_dust_htlc_exposure_msat`
95209520
// policy.
95219521
//
@@ -9530,7 +9530,12 @@ fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_e
95309530

95319531
let chanmon_cfgs = create_chanmon_cfgs(2);
95329532
let mut config = test_default_channel_config();
9533-
config.channel_config.max_dust_htlc_exposure_msat = MaxDustHTLCExposure::FixedLimitMsat(5_000_000); // default setting value
9533+
config.channel_config.max_dust_htlc_exposure_msat = if multiplier_dust_limit {
9534+
// Default test fee estimator rate is 253 sat/kw, so we set the multiplier to 5_000_000 / 253
9535+
// to get roughly the same initial value as the default setting when this test was
9536+
// originally written.
9537+
MaxDustHTLCExposure::FeeRateMultiplier(5_000_000 / 253)
9538+
} else { MaxDustHTLCExposure::FixedLimitMsat(5_000_000) }; // initial default setting value
95349539
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
95359540
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config), None]);
95369541
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
@@ -9581,6 +9586,7 @@ fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_e
95819586
(chan.context.get_dust_buffer_feerate(None) as u64,
95829587
chan.context.get_max_dust_htlc_exposure_msat(&LowerBoundedFeeEstimator(nodes[0].fee_estimator)))
95839588
};
9589+
95849590
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;
95859591
let dust_outbound_htlc_on_holder_tx: u64 = max_dust_htlc_exposure_msat / dust_outbound_htlc_on_holder_tx_msat;
95869592

@@ -9640,7 +9646,7 @@ fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_e
96409646
), true, APIError::ChannelUnavailable { .. }, {});
96419647
}
96429648
} else if exposure_breach_event == ExposureEvent::AtHTLCReception {
9643-
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 });
9649+
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 });
96449650
nodes[1].node.send_payment_with_route(&route, payment_hash,
96459651
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap();
96469652
check_added_monitors!(nodes[1], 1);
@@ -9658,13 +9664,19 @@ fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_e
96589664
// Outbound dust balance: 5200 sats
96599665
nodes[0].logger.assert_log("lightning::ln::channel".to_string(),
96609666
format!("Cannot accept value that would put our exposure to dust HTLCs at {} over the limit {} on counterparty commitment tx",
9661-
dust_htlc_on_counterparty_tx_msat * (dust_htlc_on_counterparty_tx - 1) + dust_htlc_on_counterparty_tx_msat + 1,
9667+
dust_htlc_on_counterparty_tx_msat * (dust_htlc_on_counterparty_tx - 1) + dust_htlc_on_counterparty_tx_msat + 4,
96629668
max_dust_htlc_exposure_msat), 1);
96639669
}
96649670
} else if exposure_breach_event == ExposureEvent::AtUpdateFeeOutbound {
96659671
route.paths[0].hops.last_mut().unwrap().fee_msat = 2_500_000;
9666-
nodes[0].node.send_payment_with_route(&route, payment_hash,
9667-
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap();
9672+
// For the multiplier dust exposure limit, since it scales with feerate,
9673+
// we need to add a lot of HTLCs that will become dust at the new feerate
9674+
// to cross the threshold.
9675+
for _ in 0..20 {
9676+
let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[1], Some(1_000), None);
9677+
nodes[0].node.send_payment_with_route(&route, payment_hash,
9678+
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap();
9679+
}
96689680
{
96699681
let mut feerate_lock = chanmon_cfgs[0].fee_estimator.sat_per_kw.lock().unwrap();
96709682
*feerate_lock = *feerate_lock * 10;
@@ -9679,20 +9691,25 @@ fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_e
96799691
added_monitors.clear();
96809692
}
96819693

9694+
fn do_test_max_dust_htlc_exposure_by_threshold_type(multiplier_dust_limit: bool) {
9695+
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCForward, true, multiplier_dust_limit);
9696+
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCForward, true, multiplier_dust_limit);
9697+
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCReception, true, multiplier_dust_limit);
9698+
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCReception, false, multiplier_dust_limit);
9699+
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCForward, false, multiplier_dust_limit);
9700+
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCReception, false, multiplier_dust_limit);
9701+
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCReception, true, multiplier_dust_limit);
9702+
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCForward, false, multiplier_dust_limit);
9703+
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtUpdateFeeOutbound, true, multiplier_dust_limit);
9704+
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtUpdateFeeOutbound, false, multiplier_dust_limit);
9705+
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, false, multiplier_dust_limit);
9706+
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, true, multiplier_dust_limit);
9707+
}
9708+
96829709
#[test]
96839710
fn test_max_dust_htlc_exposure() {
9684-
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCForward, true);
9685-
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCForward, true);
9686-
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCReception, true);
9687-
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCReception, false);
9688-
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCForward, false);
9689-
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCReception, false);
9690-
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCReception, true);
9691-
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCForward, false);
9692-
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtUpdateFeeOutbound, true);
9693-
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtUpdateFeeOutbound, false);
9694-
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, false);
9695-
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, true);
9711+
do_test_max_dust_htlc_exposure_by_threshold_type(false);
9712+
do_test_max_dust_htlc_exposure_by_threshold_type(true);
96969713
}
96979714

96989715
#[test]

lightning/src/ln/onion_route_tests.rs

+10-1
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_msat = MaxDustHTLCExposure::FeeRateMultiplier(5_000_000 / 253);
674675
let chanmon_cfgs = create_chanmon_cfgs(3);
675676
let persister;
676677
let chain_monitor;
@@ -1371,11 +1372,19 @@ 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();
1383+
// Default test fee estimator rate is 253, so to set the max dust exposure to the dust limit,
1384+
// we need to set the multiplier to 2.
13771385
receiver_config.channel_config.max_dust_htlc_exposure_msat =
1378-
MaxDustHTLCExposure::FixedLimitMsat(max_dust_exposure);
1386+
if multiplier_dust_limit { MaxDustHTLCExposure::FeeRateMultiplier(2) }
1387+
else { MaxDustHTLCExposure::FixedLimitMsat(max_dust_exposure) };
13791388
receiver_config.channel_handshake_config.announced_channel = true;
13801389

13811390
let chanmon_cfgs = create_chanmon_cfgs(2);

lightning/src/ln/priv_short_conf_tests.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use crate::ln::features::ChannelTypeFeatures;
2121
use crate::ln::msgs;
2222
use crate::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, ChannelUpdate, ErrorAction};
2323
use crate::ln::wire::Encode;
24-
use crate::util::config::UserConfig;
24+
use crate::util::config::{UserConfig, MaxDustHTLCExposure};
2525
use crate::util::ser::Writeable;
2626
use crate::util::test_utils;
2727

@@ -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_msat = MaxDustHTLCExposure::FeeRateMultiplier(5_000_000 / 253);
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_msat = MaxDustHTLCExposure::FeeRateMultiplier(5_000_000 / 253);
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)