mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-03-10 21:36:17 +01:00
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.
This commit is contained in:
parent
c2992fd94b
commit
b040335712
7 changed files with 76 additions and 28 deletions
File diff suppressed because one or more lines are too long
|
@ -1060,12 +1060,16 @@ impl<Signer: ChannelSigner> ChannelContext<Signer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_max_dust_htlc_exposure_msat<F: Deref>(&self,
|
pub fn get_max_dust_htlc_exposure_msat<F: Deref>(&self,
|
||||||
_fee_estimator: &LowerBoundedFeeEstimator<F>) -> u64
|
fee_estimator: &LowerBoundedFeeEstimator<F>) -> u64
|
||||||
where F::Target: FeeEstimator
|
where F::Target: FeeEstimator
|
||||||
{
|
{
|
||||||
match self.config.options.max_dust_htlc_exposure {
|
match self.config.options.max_dust_htlc_exposure {
|
||||||
|
MaxDustHTLCExposure::FeeRateMultiplier(multiplier) => {
|
||||||
|
let feerate_per_kw = fee_estimator.bounded_sat_per_1000_weight(
|
||||||
|
ConfirmationTarget::HighPriority);
|
||||||
|
feerate_per_kw as u64 * multiplier
|
||||||
|
},
|
||||||
MaxDustHTLCExposure::FixedLimitMsat(limit) => limit,
|
MaxDustHTLCExposure::FixedLimitMsat(limit) => limit,
|
||||||
MaxDustHTLCExposure::FeeRateMultiplier(_) => 5_000_000,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1509,7 +1509,6 @@ impl ChannelDetails {
|
||||||
) -> Self
|
) -> Self
|
||||||
where F::Target: FeeEstimator
|
where F::Target: FeeEstimator
|
||||||
{
|
{
|
||||||
|
|
||||||
let balance = context.get_available_balances(fee_estimator);
|
let balance = context.get_available_balances(fee_estimator);
|
||||||
let (to_remote_reserve_satoshis, to_self_reserve_satoshis) =
|
let (to_remote_reserve_satoshis, to_self_reserve_satoshis) =
|
||||||
context.get_holder_counterparty_selected_channel_reserve_satoshis();
|
context.get_holder_counterparty_selected_channel_reserve_satoshis();
|
||||||
|
@ -10007,7 +10006,7 @@ pub mod bench {
|
||||||
use crate::routing::gossip::NetworkGraph;
|
use crate::routing::gossip::NetworkGraph;
|
||||||
use crate::routing::router::{PaymentParameters, RouteParameters};
|
use crate::routing::router::{PaymentParameters, RouteParameters};
|
||||||
use crate::util::test_utils;
|
use crate::util::test_utils;
|
||||||
use crate::util::config::UserConfig;
|
use crate::util::config::{UserConfig, MaxDustHTLCExposure};
|
||||||
|
|
||||||
use bitcoin::hashes::Hash;
|
use bitcoin::hashes::Hash;
|
||||||
use bitcoin::hashes::sha256::Hash as Sha256;
|
use bitcoin::hashes::sha256::Hash as Sha256;
|
||||||
|
@ -10053,6 +10052,7 @@ pub mod bench {
|
||||||
let router = test_utils::TestRouter::new(Arc::new(NetworkGraph::new(network, &logger_a)), &scorer);
|
let router = test_utils::TestRouter::new(Arc::new(NetworkGraph::new(network, &logger_a)), &scorer);
|
||||||
|
|
||||||
let mut config: UserConfig = Default::default();
|
let mut config: UserConfig = Default::default();
|
||||||
|
config.channel_config.max_dust_htlc_exposure = MaxDustHTLCExposure::FeeRateMultiplier(5_000_000 / 253);
|
||||||
config.channel_handshake_config.minimum_depth = 1;
|
config.channel_handshake_config.minimum_depth = 1;
|
||||||
|
|
||||||
let chain_monitor_a = ChainMonitor::new(None, &tx_broadcaster, &logger_a, &fee_estimator, &persister_a);
|
let chain_monitor_a = ChainMonitor::new(None, &tx_broadcaster, &logger_a, &fee_estimator, &persister_a);
|
||||||
|
|
|
@ -2572,8 +2572,10 @@ pub fn test_default_channel_config() -> UserConfig {
|
||||||
// It now defaults to 1, so we simply set it to the expected value here.
|
// It now defaults to 1, so we simply set it to the expected value here.
|
||||||
default_config.channel_handshake_config.our_htlc_minimum_msat = 1000;
|
default_config.channel_handshake_config.our_htlc_minimum_msat = 1000;
|
||||||
// When most of our tests were written, we didn't have the notion of a `max_dust_htlc_exposure_msat`,
|
// When most of our tests were written, we didn't have the notion of a `max_dust_htlc_exposure_msat`,
|
||||||
// It now defaults to 5_000_000 msat; to avoid interfering with tests we bump it to 50_000_000 msat.
|
// to avoid interfering with tests we bump it to 50_000_000 msat (assuming the default test
|
||||||
default_config.channel_config.max_dust_htlc_exposure = MaxDustHTLCExposure::FixedLimitMsat(50_000_000);
|
// feerate of 253).
|
||||||
|
default_config.channel_config.max_dust_htlc_exposure =
|
||||||
|
MaxDustHTLCExposure::FeeRateMultiplier(50_000_000 / 253);
|
||||||
default_config
|
default_config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9515,7 +9515,7 @@ enum ExposureEvent {
|
||||||
AtUpdateFeeOutbound,
|
AtUpdateFeeOutbound,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_event: ExposureEvent, on_holder_tx: bool) {
|
fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_event: ExposureEvent, on_holder_tx: bool, multiplier_dust_limit: bool) {
|
||||||
// Test that we properly reject dust HTLC violating our `max_dust_htlc_exposure_msat`
|
// Test that we properly reject dust HTLC violating our `max_dust_htlc_exposure_msat`
|
||||||
// policy.
|
// policy.
|
||||||
//
|
//
|
||||||
|
@ -9530,7 +9530,12 @@ fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_e
|
||||||
|
|
||||||
let chanmon_cfgs = create_chanmon_cfgs(2);
|
let chanmon_cfgs = create_chanmon_cfgs(2);
|
||||||
let mut config = test_default_channel_config();
|
let mut config = test_default_channel_config();
|
||||||
config.channel_config.max_dust_htlc_exposure = MaxDustHTLCExposure::FixedLimitMsat(5_000_000); // default setting value
|
config.channel_config.max_dust_htlc_exposure = if multiplier_dust_limit {
|
||||||
|
// Default test fee estimator rate is 253 sat/kw, so we set the multiplier to 5_000_000 / 253
|
||||||
|
// to get roughly the same initial value as the default setting when this test was
|
||||||
|
// originally written.
|
||||||
|
MaxDustHTLCExposure::FeeRateMultiplier(5_000_000 / 253)
|
||||||
|
} else { MaxDustHTLCExposure::FixedLimitMsat(5_000_000) }; // initial default setting value
|
||||||
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
|
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
|
||||||
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config), None]);
|
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config), None]);
|
||||||
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
|
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
|
||||||
|
@ -9640,7 +9645,7 @@ fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_e
|
||||||
), true, APIError::ChannelUnavailable { .. }, {});
|
), true, APIError::ChannelUnavailable { .. }, {});
|
||||||
}
|
}
|
||||||
} else if exposure_breach_event == ExposureEvent::AtHTLCReception {
|
} else if exposure_breach_event == ExposureEvent::AtHTLCReception {
|
||||||
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 });
|
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 });
|
||||||
nodes[1].node.send_payment_with_route(&route, payment_hash,
|
nodes[1].node.send_payment_with_route(&route, payment_hash,
|
||||||
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap();
|
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap();
|
||||||
check_added_monitors!(nodes[1], 1);
|
check_added_monitors!(nodes[1], 1);
|
||||||
|
@ -9658,13 +9663,19 @@ fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_e
|
||||||
// Outbound dust balance: 5200 sats
|
// Outbound dust balance: 5200 sats
|
||||||
nodes[0].logger.assert_log("lightning::ln::channel".to_string(),
|
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 counterparty commitment tx",
|
format!("Cannot accept value that would put our exposure to dust HTLCs at {} over the limit {} on counterparty commitment tx",
|
||||||
dust_htlc_on_counterparty_tx_msat * (dust_htlc_on_counterparty_tx - 1) + dust_htlc_on_counterparty_tx_msat + 1,
|
dust_htlc_on_counterparty_tx_msat * (dust_htlc_on_counterparty_tx - 1) + dust_htlc_on_counterparty_tx_msat + 4,
|
||||||
max_dust_htlc_exposure_msat), 1);
|
max_dust_htlc_exposure_msat), 1);
|
||||||
}
|
}
|
||||||
} else if exposure_breach_event == ExposureEvent::AtUpdateFeeOutbound {
|
} else if exposure_breach_event == ExposureEvent::AtUpdateFeeOutbound {
|
||||||
route.paths[0].hops.last_mut().unwrap().fee_msat = 2_500_000;
|
route.paths[0].hops.last_mut().unwrap().fee_msat = 2_500_000;
|
||||||
|
// For the multiplier dust exposure limit, since it scales with feerate,
|
||||||
|
// we need to add a lot of HTLCs that will become dust at the new feerate
|
||||||
|
// to cross the threshold.
|
||||||
|
for _ in 0..20 {
|
||||||
|
let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[1], Some(1_000), None);
|
||||||
nodes[0].node.send_payment_with_route(&route, payment_hash,
|
nodes[0].node.send_payment_with_route(&route, payment_hash,
|
||||||
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap();
|
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap();
|
||||||
|
}
|
||||||
{
|
{
|
||||||
let mut feerate_lock = chanmon_cfgs[0].fee_estimator.sat_per_kw.lock().unwrap();
|
let mut feerate_lock = chanmon_cfgs[0].fee_estimator.sat_per_kw.lock().unwrap();
|
||||||
*feerate_lock = *feerate_lock * 10;
|
*feerate_lock = *feerate_lock * 10;
|
||||||
|
@ -9679,20 +9690,25 @@ fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_e
|
||||||
added_monitors.clear();
|
added_monitors.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn do_test_max_dust_htlc_exposure_by_threshold_type(multiplier_dust_limit: bool) {
|
||||||
|
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCForward, true, multiplier_dust_limit);
|
||||||
|
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCForward, true, multiplier_dust_limit);
|
||||||
|
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCReception, true, multiplier_dust_limit);
|
||||||
|
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCReception, false, multiplier_dust_limit);
|
||||||
|
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCForward, false, multiplier_dust_limit);
|
||||||
|
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCReception, false, multiplier_dust_limit);
|
||||||
|
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCReception, true, multiplier_dust_limit);
|
||||||
|
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCForward, false, multiplier_dust_limit);
|
||||||
|
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtUpdateFeeOutbound, true, multiplier_dust_limit);
|
||||||
|
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtUpdateFeeOutbound, false, multiplier_dust_limit);
|
||||||
|
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, false, multiplier_dust_limit);
|
||||||
|
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, true, multiplier_dust_limit);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_max_dust_htlc_exposure() {
|
fn test_max_dust_htlc_exposure() {
|
||||||
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCForward, true);
|
do_test_max_dust_htlc_exposure_by_threshold_type(false);
|
||||||
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCForward, true);
|
do_test_max_dust_htlc_exposure_by_threshold_type(true);
|
||||||
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCReception, true);
|
|
||||||
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCReception, false);
|
|
||||||
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCForward, false);
|
|
||||||
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCReception, false);
|
|
||||||
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCReception, true);
|
|
||||||
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCForward, false);
|
|
||||||
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtUpdateFeeOutbound, true);
|
|
||||||
do_test_max_dust_htlc_exposure(true, ExposureEvent::AtUpdateFeeOutbound, false);
|
|
||||||
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, false);
|
|
||||||
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -671,6 +671,7 @@ fn do_test_onion_failure_stale_channel_update(announced_channel: bool) {
|
||||||
config.channel_handshake_config.announced_channel = announced_channel;
|
config.channel_handshake_config.announced_channel = announced_channel;
|
||||||
config.channel_handshake_limits.force_announced_channel_preference = false;
|
config.channel_handshake_limits.force_announced_channel_preference = false;
|
||||||
config.accept_forwards_to_priv_channels = !announced_channel;
|
config.accept_forwards_to_priv_channels = !announced_channel;
|
||||||
|
config.channel_config.max_dust_htlc_exposure = MaxDustHTLCExposure::FeeRateMultiplier(5_000_000 / 253);
|
||||||
let chanmon_cfgs = create_chanmon_cfgs(3);
|
let chanmon_cfgs = create_chanmon_cfgs(3);
|
||||||
let persister;
|
let persister;
|
||||||
let chain_monitor;
|
let chain_monitor;
|
||||||
|
@ -1371,11 +1372,19 @@ fn test_phantom_failure_too_low_recv_amt() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_phantom_dust_exposure_failure() {
|
fn test_phantom_dust_exposure_failure() {
|
||||||
|
do_test_phantom_dust_exposure_failure(false);
|
||||||
|
do_test_phantom_dust_exposure_failure(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_test_phantom_dust_exposure_failure(multiplier_dust_limit: bool) {
|
||||||
// Set the max dust exposure to the dust limit.
|
// Set the max dust exposure to the dust limit.
|
||||||
let max_dust_exposure = 546;
|
let max_dust_exposure = 546;
|
||||||
let mut receiver_config = UserConfig::default();
|
let mut receiver_config = UserConfig::default();
|
||||||
|
// Default test fee estimator rate is 253, so to set the max dust exposure to the dust limit,
|
||||||
|
// we need to set the multiplier to 2.
|
||||||
receiver_config.channel_config.max_dust_htlc_exposure =
|
receiver_config.channel_config.max_dust_htlc_exposure =
|
||||||
MaxDustHTLCExposure::FixedLimitMsat(max_dust_exposure);
|
if multiplier_dust_limit { MaxDustHTLCExposure::FeeRateMultiplier(2) }
|
||||||
|
else { MaxDustHTLCExposure::FixedLimitMsat(max_dust_exposure) };
|
||||||
receiver_config.channel_handshake_config.announced_channel = true;
|
receiver_config.channel_handshake_config.announced_channel = true;
|
||||||
|
|
||||||
let chanmon_cfgs = create_chanmon_cfgs(2);
|
let chanmon_cfgs = create_chanmon_cfgs(2);
|
||||||
|
|
|
@ -21,7 +21,7 @@ use crate::ln::features::ChannelTypeFeatures;
|
||||||
use crate::ln::msgs;
|
use crate::ln::msgs;
|
||||||
use crate::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, ChannelUpdate, ErrorAction};
|
use crate::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, ChannelUpdate, ErrorAction};
|
||||||
use crate::ln::wire::Encode;
|
use crate::ln::wire::Encode;
|
||||||
use crate::util::config::UserConfig;
|
use crate::util::config::{UserConfig, MaxDustHTLCExposure};
|
||||||
use crate::util::ser::Writeable;
|
use crate::util::ser::Writeable;
|
||||||
use crate::util::test_utils;
|
use crate::util::test_utils;
|
||||||
|
|
||||||
|
@ -141,10 +141,12 @@ fn do_test_1_conf_open(connect_style: ConnectStyle) {
|
||||||
alice_config.channel_handshake_config.minimum_depth = 1;
|
alice_config.channel_handshake_config.minimum_depth = 1;
|
||||||
alice_config.channel_handshake_config.announced_channel = true;
|
alice_config.channel_handshake_config.announced_channel = true;
|
||||||
alice_config.channel_handshake_limits.force_announced_channel_preference = false;
|
alice_config.channel_handshake_limits.force_announced_channel_preference = false;
|
||||||
|
alice_config.channel_config.max_dust_htlc_exposure = MaxDustHTLCExposure::FeeRateMultiplier(5_000_000 / 253);
|
||||||
let mut bob_config = UserConfig::default();
|
let mut bob_config = UserConfig::default();
|
||||||
bob_config.channel_handshake_config.minimum_depth = 1;
|
bob_config.channel_handshake_config.minimum_depth = 1;
|
||||||
bob_config.channel_handshake_config.announced_channel = true;
|
bob_config.channel_handshake_config.announced_channel = true;
|
||||||
bob_config.channel_handshake_limits.force_announced_channel_preference = false;
|
bob_config.channel_handshake_limits.force_announced_channel_preference = false;
|
||||||
|
bob_config.channel_config.max_dust_htlc_exposure = MaxDustHTLCExposure::FeeRateMultiplier(5_000_000 / 253);
|
||||||
let chanmon_cfgs = create_chanmon_cfgs(2);
|
let chanmon_cfgs = create_chanmon_cfgs(2);
|
||||||
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
|
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
|
||||||
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(alice_config), Some(bob_config)]);
|
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(alice_config), Some(bob_config)]);
|
||||||
|
|
Loading…
Add table
Reference in a new issue