Skip to content

Commit 58e4ce2

Browse files
authored
Merge pull request #845 from ariard/2021-03-hardcode-dust
Switch to a max counterparty's `dust_limit_satoshis` constant
2 parents d4d3225 + b307c1f commit 58e4ce2

File tree

5 files changed

+69
-55
lines changed

5 files changed

+69
-55
lines changed

fuzz/src/chanmon_consistency.rs

-2
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,6 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
338338
let mut config = UserConfig::default();
339339
config.channel_options.fee_proportional_millionths = 0;
340340
config.channel_options.announced_channel = true;
341-
config.peer_channel_config_limits.min_dust_limit_satoshis = 0;
342341
let network = Network::Bitcoin;
343342
let params = ChainParameters {
344343
network,
@@ -358,7 +357,6 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
358357
let mut config = UserConfig::default();
359358
config.channel_options.fee_proportional_millionths = 0;
360359
config.channel_options.announced_channel = true;
361-
config.peer_channel_config_limits.min_dust_limit_satoshis = 0;
362360

363361
let mut monitors = HashMap::new();
364362
let mut old_monitors = $old_monitors.latest_monitors.lock().unwrap();

fuzz/src/full_stack.rs

+9-10
Large diffs are not rendered by default.

lightning/src/ln/channel.rs

+34-24
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,6 @@ struct CommitmentTxInfoCached {
439439

440440
pub const OUR_MAX_HTLCS: u16 = 50; //TODO
441441
const SPENDING_INPUT_FOR_A_OUTPUT_WEIGHT: u64 = 79; // prevout: 36, nSequence: 4, script len: 1, witness lengths: (3+1)/4, sig: 73/4, if-selector: 1, redeemScript: (6 ops + 2*33 pubkeys + 1*2 delay)/4
442-
const B_OUTPUT_PLUS_SPENDING_INPUT_WEIGHT: u64 = 104; // prevout: 40, nSequence: 4, script len: 1, witness lengths: 3/4, sig: 73/4, pubkey: 33/4, output: 31 (TODO: Wrong? Useless?)
443442

444443
#[cfg(not(test))]
445444
const COMMITMENT_TX_BASE_WEIGHT: u64 = 724;
@@ -454,6 +453,22 @@ pub const COMMITMENT_TX_WEIGHT_PER_HTLC: u64 = 172;
454453
/// it's 2^24.
455454
pub const MAX_FUNDING_SATOSHIS: u64 = 1 << 24;
456455

456+
/// Maximum counterparty `dust_limit_satoshis` allowed. 2 * standard dust threshold on p2wsh output
457+
/// Scales up on Bitcoin Core's proceeding policy with dust outputs. A typical p2wsh output is 43
458+
/// bytes to which Core's `GetDustThreshold()` sums up a minimal spend of 67 bytes (even if
459+
/// a p2wsh witnessScript might be *effectively* smaller), `dustRelayFee` is set to 3000sat/kb, thus
460+
/// 110 * 3000 / 1000 = 330. Per-protocol rules, all time-sensitive outputs are p2wsh, a value of
461+
/// 330 sats is the lower bound desired to ensure good propagation of transactions. We give a bit
462+
/// of margin to our counterparty and pick up 660 satoshis as an accepted `dust_limit_satoshis`
463+
/// upper bound to avoid negotiation conflicts with other implementations.
464+
pub const MAX_DUST_LIMIT_SATOSHIS: u64 = 2 * 330;
465+
466+
/// A typical p2wsh output is 43 bytes to which Core's `GetDustThreshold()` sums up a minimal
467+
/// spend of 67 bytes (even if a p2wsh witnessScript might be *effectively* smaller), `dustRelayFee`
468+
/// is set to 3000sat/kb, thus 110 * 3000 / 1000 = 330. Per-protocol rules, all time-sensitive outputs
469+
/// are p2wsh, a value of 330 sats is the lower bound desired to ensure good propagation of transactions.
470+
pub const MIN_DUST_LIMIT_SATOSHIS: u64 = 330;
471+
457472
/// Used to return a simple Error back to ChannelManager. Will get converted to a
458473
/// msgs::ErrorAction::SendErrorMessage or msgs::ErrorAction::IgnoreError as appropriate with our
459474
/// channel_id in ChannelManager.
@@ -497,10 +512,6 @@ impl<Signer: Sign> Channel<Signer> {
497512
cmp::min(channel_value_satoshis, cmp::max(q, 1000)) //TODO
498513
}
499514

500-
fn derive_holder_dust_limit_satoshis(at_open_background_feerate: u32) -> u64 {
501-
cmp::max(at_open_background_feerate as u64 * B_OUTPUT_PLUS_SPENDING_INPUT_WEIGHT / 1000, 546) //TODO
502-
}
503-
504515
// Constructors:
505516
pub fn new_outbound<K: Deref, F: Deref>(fee_estimator: &F, keys_provider: &K, counterparty_node_id: PublicKey, channel_value_satoshis: u64, push_msat: u64, user_id: u64, config: &UserConfig) -> Result<Channel<Signer>, APIError>
506517
where K::Target: KeysInterface<Signer = Signer>,
@@ -520,9 +531,9 @@ impl<Signer: Sign> Channel<Signer> {
520531
if holder_selected_contest_delay < BREAKDOWN_TIMEOUT {
521532
return Err(APIError::APIMisuseError {err: format!("Configured with an unreasonable our_to_self_delay ({}) putting user funds at risks", holder_selected_contest_delay)});
522533
}
523-
let background_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background);
524-
if Channel::<Signer>::get_holder_selected_channel_reserve_satoshis(channel_value_satoshis) < Channel::<Signer>::derive_holder_dust_limit_satoshis(background_feerate) {
525-
return Err(APIError::FeeRateTooHigh{err: format!("Not enough reserve above dust limit can be found at current fee rate({})", background_feerate), feerate: background_feerate});
534+
let holder_selected_channel_reserve_satoshis = Channel::<Signer>::get_holder_selected_channel_reserve_satoshis(channel_value_satoshis);
535+
if holder_selected_channel_reserve_satoshis < MIN_DUST_LIMIT_SATOSHIS {
536+
return Err(APIError::APIMisuseError { err: format!("Holder selected channel reserve below implemention limit dust_limit_satoshis {}", holder_selected_channel_reserve_satoshis) });
526537
}
527538

528539
let feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Normal);
@@ -579,7 +590,7 @@ impl<Signer: Sign> Channel<Signer> {
579590

580591
feerate_per_kw: feerate,
581592
counterparty_dust_limit_satoshis: 0,
582-
holder_dust_limit_satoshis: Channel::<Signer>::derive_holder_dust_limit_satoshis(background_feerate),
593+
holder_dust_limit_satoshis: MIN_DUST_LIMIT_SATOSHIS,
583594
counterparty_max_htlc_value_in_flight_msat: 0,
584595
counterparty_selected_channel_reserve_satoshis: 0,
585596
counterparty_htlc_minimum_msat: 0,
@@ -700,11 +711,11 @@ impl<Signer: Sign> Channel<Signer> {
700711
if msg.max_accepted_htlcs < config.peer_channel_config_limits.min_max_accepted_htlcs {
701712
return Err(ChannelError::Close(format!("max_accepted_htlcs ({}) is less than the user specified limit ({})", msg.max_accepted_htlcs, config.peer_channel_config_limits.min_max_accepted_htlcs)));
702713
}
703-
if msg.dust_limit_satoshis < config.peer_channel_config_limits.min_dust_limit_satoshis {
704-
return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is less than the user specified limit ({})", msg.dust_limit_satoshis, config.peer_channel_config_limits.min_dust_limit_satoshis)));
714+
if msg.dust_limit_satoshis < MIN_DUST_LIMIT_SATOSHIS {
715+
return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is less than the implementation limit ({})", msg.dust_limit_satoshis, MIN_DUST_LIMIT_SATOSHIS)));
705716
}
706-
if msg.dust_limit_satoshis > config.peer_channel_config_limits.max_dust_limit_satoshis {
707-
return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is greater than the user specified limit ({})", msg.dust_limit_satoshis, config.peer_channel_config_limits.max_dust_limit_satoshis)));
717+
if msg.dust_limit_satoshis > MAX_DUST_LIMIT_SATOSHIS {
718+
return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is greater than the implementation limit ({})", msg.dust_limit_satoshis, MAX_DUST_LIMIT_SATOSHIS)));
708719
}
709720

710721
// Convert things into internal flags and prep our state:
@@ -720,13 +731,12 @@ impl<Signer: Sign> Channel<Signer> {
720731

721732
let background_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background);
722733

723-
let holder_dust_limit_satoshis = Channel::<Signer>::derive_holder_dust_limit_satoshis(background_feerate);
724734
let holder_selected_channel_reserve_satoshis = Channel::<Signer>::get_holder_selected_channel_reserve_satoshis(msg.funding_satoshis);
725-
if holder_selected_channel_reserve_satoshis < holder_dust_limit_satoshis {
726-
return Err(ChannelError::Close(format!("Suitable channel reserve not found. remote_channel_reserve was ({}). dust_limit_satoshis is ({}).", holder_selected_channel_reserve_satoshis, holder_dust_limit_satoshis)));
735+
if holder_selected_channel_reserve_satoshis < MIN_DUST_LIMIT_SATOSHIS {
736+
return Err(ChannelError::Close(format!("Suitable channel reserve not found. remote_channel_reserve was ({}). dust_limit_satoshis is ({}).", holder_selected_channel_reserve_satoshis, MIN_DUST_LIMIT_SATOSHIS)));
727737
}
728-
if msg.channel_reserve_satoshis < holder_dust_limit_satoshis {
729-
return Err(ChannelError::Close(format!("channel_reserve_satoshis ({}) is smaller than our dust limit ({})", msg.channel_reserve_satoshis, holder_dust_limit_satoshis)));
738+
if msg.channel_reserve_satoshis < MIN_DUST_LIMIT_SATOSHIS {
739+
return Err(ChannelError::Close(format!("channel_reserve_satoshis ({}) is smaller than our dust limit ({})", msg.channel_reserve_satoshis, MIN_DUST_LIMIT_SATOSHIS)));
730740
}
731741
if holder_selected_channel_reserve_satoshis < msg.dust_limit_satoshis {
732742
return Err(ChannelError::Close(format!("Dust limit ({}) too high for the channel reserve we require the remote to keep ({})", msg.dust_limit_satoshis, holder_selected_channel_reserve_satoshis)));
@@ -818,7 +828,7 @@ impl<Signer: Sign> Channel<Signer> {
818828
feerate_per_kw: msg.feerate_per_kw,
819829
channel_value_satoshis: msg.funding_satoshis,
820830
counterparty_dust_limit_satoshis: msg.dust_limit_satoshis,
821-
holder_dust_limit_satoshis,
831+
holder_dust_limit_satoshis: MIN_DUST_LIMIT_SATOSHIS,
822832
counterparty_max_htlc_value_in_flight_msat: cmp::min(msg.max_htlc_value_in_flight_msat, msg.funding_satoshis * 1000),
823833
counterparty_selected_channel_reserve_satoshis: msg.channel_reserve_satoshis,
824834
counterparty_htlc_minimum_msat: msg.htlc_minimum_msat,
@@ -1442,11 +1452,11 @@ impl<Signer: Sign> Channel<Signer> {
14421452
if msg.max_accepted_htlcs < config.peer_channel_config_limits.min_max_accepted_htlcs {
14431453
return Err(ChannelError::Close(format!("max_accepted_htlcs ({}) is less than the user specified limit ({})", msg.max_accepted_htlcs, config.peer_channel_config_limits.min_max_accepted_htlcs)));
14441454
}
1445-
if msg.dust_limit_satoshis < config.peer_channel_config_limits.min_dust_limit_satoshis {
1446-
return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is less than the user specified limit ({})", msg.dust_limit_satoshis, config.peer_channel_config_limits.min_dust_limit_satoshis)));
1455+
if msg.dust_limit_satoshis < MIN_DUST_LIMIT_SATOSHIS {
1456+
return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is less than the implementation limit ({})", msg.dust_limit_satoshis, MIN_DUST_LIMIT_SATOSHIS)));
14471457
}
1448-
if msg.dust_limit_satoshis > config.peer_channel_config_limits.max_dust_limit_satoshis {
1449-
return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is greater than the user specified limit ({})", msg.dust_limit_satoshis, config.peer_channel_config_limits.max_dust_limit_satoshis)));
1458+
if msg.dust_limit_satoshis > MAX_DUST_LIMIT_SATOSHIS {
1459+
return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is greater than the implementation limit ({})", msg.dust_limit_satoshis, MAX_DUST_LIMIT_SATOSHIS)));
14501460
}
14511461
if msg.minimum_depth > config.peer_channel_config_limits.max_minimum_depth {
14521462
return Err(ChannelError::Close(format!("We consider the minimum depth to be unreasonably large. Expected minimum: ({}). Actual: ({})", config.peer_channel_config_limits.max_minimum_depth, msg.minimum_depth)));
@@ -4939,14 +4949,14 @@ mod tests {
49394949
// Create Node B's channel by receiving Node A's open_channel message
49404950
// Make sure A's dust limit is as we expect.
49414951
let open_channel_msg = node_a_chan.get_open_channel(genesis_block(network).header.block_hash());
4942-
assert_eq!(open_channel_msg.dust_limit_satoshis, 1560);
49434952
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap());
49444953
let node_b_chan = Channel::<EnforcingSigner>::new_from_req(&&feeest, &&keys_provider, node_b_node_id, InitFeatures::known(), &open_channel_msg, 7, &config).unwrap();
49454954

49464955
// Node B --> Node A: accept channel, explicitly setting B's dust limit.
49474956
let mut accept_channel_msg = node_b_chan.get_accept_channel();
49484957
accept_channel_msg.dust_limit_satoshis = 546;
49494958
node_a_chan.accept_channel(&accept_channel_msg, &config, InitFeatures::known()).unwrap();
4959+
node_a_chan.holder_dust_limit_satoshis = 1560;
49504960

49514961
// Put some inbound and outbound HTLCs in A's channel.
49524962
let htlc_amount_msat = 11_092_000; // put an amount below A's effective dust limit but above B's.

lightning/src/ln/functional_tests.rs

+26-1
Original file line numberDiff line numberDiff line change
@@ -1764,7 +1764,7 @@ fn test_chan_reserve_dust_inbound_htlcs_outbound_chan() {
17641764
// transaction fee with 0 HTLCs (183 sats)).
17651765
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 98817000, InitFeatures::known(), InitFeatures::known());
17661766

1767-
let dust_amt = 546000; // Dust amount
1767+
let dust_amt = 329000; // Dust amount
17681768
// In the previous code, routing this dust payment would cause nodes[0] to perceive a channel
17691769
// reserve violation even though it's a dust HTLC and therefore shouldn't count towards the
17701770
// commitment transaction fee.
@@ -5888,6 +5888,31 @@ fn bolt2_open_channel_sending_node_checks_part2() {
58885888
assert!(PublicKey::from_slice(&node0_to_1_send_open_channel.delayed_payment_basepoint.serialize()).is_ok());
58895889
}
58905890

5891+
#[test]
5892+
fn bolt2_open_channel_sane_dust_limit() {
5893+
let chanmon_cfgs = create_chanmon_cfgs(2);
5894+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
5895+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
5896+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
5897+
5898+
let channel_value_satoshis=1000000;
5899+
let push_msat=10001;
5900+
nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), channel_value_satoshis, push_msat, 42, None).unwrap();
5901+
let mut node0_to_1_send_open_channel = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
5902+
node0_to_1_send_open_channel.dust_limit_satoshis = 661;
5903+
node0_to_1_send_open_channel.channel_reserve_satoshis = 100001;
5904+
5905+
nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), InitFeatures::known(), &node0_to_1_send_open_channel);
5906+
let events = nodes[1].node.get_and_clear_pending_msg_events();
5907+
let err_msg = match events[0] {
5908+
MessageSendEvent::HandleError { action: ErrorAction::SendErrorMessage { ref msg }, node_id: _ } => {
5909+
msg.clone()
5910+
},
5911+
_ => panic!("Unexpected event"),
5912+
};
5913+
assert_eq!(err_msg.data, "dust_limit_satoshis (661) is greater than the implementation limit (660)");
5914+
}
5915+
58915916
// Test that if we fail to send an HTLC that is being freed from the holding cell, and the HTLC
58925917
// originated from our node, its failure is surfaced to the user. We trigger this failure to
58935918
// free the HTLC by increasing our fee while the HTLC is in the holding cell such that the HTLC

lightning/src/util/config.rs

-18
Original file line numberDiff line numberDiff line change
@@ -98,22 +98,6 @@ pub struct ChannelHandshakeLimits {
9898
///
9999
/// Default value: 0.
100100
pub min_max_accepted_htlcs: u16,
101-
/// Outputs below a certain value will not be added to on-chain transactions. The dust value is
102-
/// required to always be higher than this value so this only applies to HTLC outputs (and
103-
/// potentially to-self outputs before any payments have been made).
104-
/// Thus, HTLCs below this amount plus HTLC transaction fees are not enforceable on-chain.
105-
/// This setting allows you to set a minimum dust limit for their commitment transactions,
106-
/// reflecting the reality that tiny outputs are not considered standard transactions and will
107-
/// not propagate through the Bitcoin network.
108-
///
109-
/// Default value: 546, the current dust limit on the Bitcoin network.
110-
pub min_dust_limit_satoshis: u64,
111-
/// Maximum allowed threshold above which outputs will not be generated in their commitment
112-
/// transactions.
113-
/// HTLCs below this amount plus HTLC transaction fees are not enforceable on-chain.
114-
///
115-
/// Default value: u64::max_value.
116-
pub max_dust_limit_satoshis: u64,
117101
/// Before a channel is usable the funding transaction will need to be confirmed by at least a
118102
/// certain number of blocks, specified by the node which is not the funder (as the funder can
119103
/// assume they aren't going to double-spend themselves).
@@ -145,8 +129,6 @@ impl Default for ChannelHandshakeLimits {
145129
min_max_htlc_value_in_flight_msat: 0,
146130
max_channel_reserve_satoshis: <u64>::max_value(),
147131
min_max_accepted_htlcs: 0,
148-
min_dust_limit_satoshis: 546,
149-
max_dust_limit_satoshis: <u64>::max_value(),
150132
max_minimum_depth: 144,
151133
force_announced_channel_preference: true,
152134
their_to_self_delay: MAX_LOCAL_BREAKDOWN_TIMEOUT,

0 commit comments

Comments
 (0)