mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-02-24 15:02:20 +01:00
Merge pull request #1065 from TheBlueMatt/2021-08-bump-dust
Increase our default/minimum dust limit and decrease our max
This commit is contained in:
commit
ad819ea705
4 changed files with 66 additions and 200 deletions
File diff suppressed because one or more lines are too long
|
@ -26,7 +26,7 @@ use ln::{PaymentPreimage, PaymentHash};
|
|||
use ln::features::{ChannelFeatures, InitFeatures};
|
||||
use ln::msgs;
|
||||
use ln::msgs::{DecodeError, OptionalField, DataLossProtect};
|
||||
use ln::script::ShutdownScript;
|
||||
use ln::script::{self, ShutdownScript};
|
||||
use ln::channelmanager::{CounterpartyForwardingInfo, PendingHTLCStatus, HTLCSource, HTLCFailReason, HTLCFailureMsg, PendingHTLCInfo, RAACommitmentOrder, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA, MAX_LOCAL_BREAKDOWN_TIMEOUT};
|
||||
use ln::chan_utils::{CounterpartyCommitmentSecrets, TxCreationKeys, HTLCOutputInCommitment, HTLC_SUCCESS_TX_WEIGHT, HTLC_TIMEOUT_TX_WEIGHT, make_funding_redeemscript, ChannelPublicKeys, CommitmentTransaction, HolderCommitmentTransaction, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, MAX_HTLCS, get_commitment_transaction_number_obscure_factor, ClosingTransaction};
|
||||
use ln::chan_utils;
|
||||
|
@ -44,7 +44,6 @@ use util::scid_utils::scid_from_parts;
|
|||
use io;
|
||||
use prelude::*;
|
||||
use core::{cmp,mem,fmt};
|
||||
use core::convert::TryFrom;
|
||||
use core::ops::Deref;
|
||||
#[cfg(any(test, feature = "fuzztarget", debug_assertions))]
|
||||
use sync::Mutex;
|
||||
|
@ -555,21 +554,24 @@ pub const ANCHOR_OUTPUT_VALUE_SATOSHI: u64 = 330;
|
|||
/// it's 2^24.
|
||||
pub const MAX_FUNDING_SATOSHIS: u64 = 1 << 24;
|
||||
|
||||
/// Maximum counterparty `dust_limit_satoshis` allowed. 2 * standard dust threshold on p2wsh output
|
||||
/// Scales up on Bitcoin Core's proceeding policy with dust outputs. A typical p2wsh output is 43
|
||||
/// bytes to which Core's `GetDustThreshold()` sums up a minimal spend of 67 bytes (even if
|
||||
/// a p2wsh witnessScript might be *effectively* smaller), `dustRelayFee` is set to 3000sat/kb, thus
|
||||
/// 110 * 3000 / 1000 = 330. Per-protocol rules, all time-sensitive outputs are p2wsh, a value of
|
||||
/// 330 sats is the lower bound desired to ensure good propagation of transactions. We give a bit
|
||||
/// of margin to our counterparty and pick up 660 satoshis as an accepted `dust_limit_satoshis`
|
||||
/// upper bound to avoid negotiation conflicts with other implementations.
|
||||
pub const MAX_DUST_LIMIT_SATOSHIS: u64 = 2 * 330;
|
||||
/// The maximum network dust limit for standard script formats. This currently represents the
|
||||
/// minimum output value for a P2SH output before Bitcoin Core 22 considers the entire
|
||||
/// transaction non-standard and thus refuses to relay it.
|
||||
/// We also use this as the maximum counterparty `dust_limit_satoshis` allowed, given many
|
||||
/// implementations use this value for their dust limit today.
|
||||
pub const MAX_STD_OUTPUT_DUST_LIMIT_SATOSHIS: u64 = 546;
|
||||
|
||||
/// A typical p2wsh output is 43 bytes to which Core's `GetDustThreshold()` sums up a minimal
|
||||
/// spend of 67 bytes (even if a p2wsh witnessScript might be *effectively* smaller), `dustRelayFee`
|
||||
/// is set to 3000sat/kb, thus 110 * 3000 / 1000 = 330. Per-protocol rules, all time-sensitive outputs
|
||||
/// are p2wsh, a value of 330 sats is the lower bound desired to ensure good propagation of transactions.
|
||||
pub const MIN_DUST_LIMIT_SATOSHIS: u64 = 330;
|
||||
/// The maximum channel dust limit we will accept from our counterparty.
|
||||
pub const MAX_CHAN_DUST_LIMIT_SATOSHIS: u64 = MAX_STD_OUTPUT_DUST_LIMIT_SATOSHIS;
|
||||
|
||||
/// The dust limit is used for both the commitment transaction outputs as well as the closing
|
||||
/// transactions. For cooperative closing transactions, we require segwit outputs, though accept
|
||||
/// *any* segwit scripts, which are allowed to be up to 42 bytes in length.
|
||||
/// In order to avoid having to concern ourselves with standardness during the closing process, we
|
||||
/// simply require our counterparty to use a dust limit which will leave any segwit output
|
||||
/// standard.
|
||||
/// See https://github.com/lightningnetwork/lightning-rfc/issues/905 for more details.
|
||||
pub const MIN_CHAN_DUST_LIMIT_SATOSHIS: u64 = 354;
|
||||
|
||||
/// Used to return a simple Error back to ChannelManager. Will get converted to a
|
||||
/// msgs::ErrorAction::SendErrorMessage or msgs::ErrorAction::IgnoreError as appropriate with our
|
||||
|
@ -636,7 +638,7 @@ impl<Signer: Sign> Channel<Signer> {
|
|||
return Err(APIError::APIMisuseError {err: format!("Configured with an unreasonable our_to_self_delay ({}) putting user funds at risks", holder_selected_contest_delay)});
|
||||
}
|
||||
let holder_selected_channel_reserve_satoshis = Channel::<Signer>::get_holder_selected_channel_reserve_satoshis(channel_value_satoshis);
|
||||
if holder_selected_channel_reserve_satoshis < MIN_DUST_LIMIT_SATOSHIS {
|
||||
if holder_selected_channel_reserve_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
|
||||
return Err(APIError::APIMisuseError { err: format!("Holder selected channel reserve below implemention limit dust_limit_satoshis {}", holder_selected_channel_reserve_satoshis) });
|
||||
}
|
||||
|
||||
|
@ -707,7 +709,7 @@ impl<Signer: Sign> Channel<Signer> {
|
|||
|
||||
feerate_per_kw: feerate,
|
||||
counterparty_dust_limit_satoshis: 0,
|
||||
holder_dust_limit_satoshis: MIN_DUST_LIMIT_SATOSHIS,
|
||||
holder_dust_limit_satoshis: MIN_CHAN_DUST_LIMIT_SATOSHIS,
|
||||
counterparty_max_htlc_value_in_flight_msat: 0,
|
||||
counterparty_selected_channel_reserve_satoshis: None, // Filled in in accept_channel
|
||||
counterparty_htlc_minimum_msat: 0,
|
||||
|
@ -841,11 +843,11 @@ impl<Signer: Sign> Channel<Signer> {
|
|||
if msg.max_accepted_htlcs < config.peer_channel_config_limits.min_max_accepted_htlcs {
|
||||
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)));
|
||||
}
|
||||
if msg.dust_limit_satoshis < MIN_DUST_LIMIT_SATOSHIS {
|
||||
return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is less than the implementation limit ({})", msg.dust_limit_satoshis, MIN_DUST_LIMIT_SATOSHIS)));
|
||||
if msg.dust_limit_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
|
||||
return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is less than the implementation limit ({})", msg.dust_limit_satoshis, MIN_CHAN_DUST_LIMIT_SATOSHIS)));
|
||||
}
|
||||
if msg.dust_limit_satoshis > MAX_DUST_LIMIT_SATOSHIS {
|
||||
return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is greater than the implementation limit ({})", msg.dust_limit_satoshis, MAX_DUST_LIMIT_SATOSHIS)));
|
||||
if msg.dust_limit_satoshis > MAX_CHAN_DUST_LIMIT_SATOSHIS {
|
||||
return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is greater than the implementation limit ({})", msg.dust_limit_satoshis, MAX_CHAN_DUST_LIMIT_SATOSHIS)));
|
||||
}
|
||||
|
||||
// Convert things into internal flags and prep our state:
|
||||
|
@ -862,11 +864,11 @@ impl<Signer: Sign> Channel<Signer> {
|
|||
let background_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background);
|
||||
|
||||
let holder_selected_channel_reserve_satoshis = Channel::<Signer>::get_holder_selected_channel_reserve_satoshis(msg.funding_satoshis);
|
||||
if holder_selected_channel_reserve_satoshis < MIN_DUST_LIMIT_SATOSHIS {
|
||||
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)));
|
||||
if holder_selected_channel_reserve_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
|
||||
return Err(ChannelError::Close(format!("Suitable channel reserve not found. remote_channel_reserve was ({}). dust_limit_satoshis is ({}).", holder_selected_channel_reserve_satoshis, MIN_CHAN_DUST_LIMIT_SATOSHIS)));
|
||||
}
|
||||
if msg.channel_reserve_satoshis < MIN_DUST_LIMIT_SATOSHIS {
|
||||
return Err(ChannelError::Close(format!("channel_reserve_satoshis ({}) is smaller than our dust limit ({})", msg.channel_reserve_satoshis, MIN_DUST_LIMIT_SATOSHIS)));
|
||||
if msg.channel_reserve_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
|
||||
return Err(ChannelError::Close(format!("channel_reserve_satoshis ({}) is smaller than our dust limit ({})", msg.channel_reserve_satoshis, MIN_CHAN_DUST_LIMIT_SATOSHIS)));
|
||||
}
|
||||
if holder_selected_channel_reserve_satoshis < msg.dust_limit_satoshis {
|
||||
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)));
|
||||
|
@ -893,10 +895,10 @@ impl<Signer: Sign> Channel<Signer> {
|
|||
if script.len() == 0 {
|
||||
None
|
||||
} else {
|
||||
match ShutdownScript::try_from((script.clone(), their_features)) {
|
||||
Ok(shutdown_script) => Some(shutdown_script.into_inner()),
|
||||
Err(_) => return Err(ChannelError::Close(format!("Peer is signaling upfront_shutdown but has provided an unacceptable scriptpubkey format: {}", script))),
|
||||
if !script::is_bolt2_compliant(&script, their_features) {
|
||||
return Err(ChannelError::Close(format!("Peer is signaling upfront_shutdown but has provided an unacceptable scriptpubkey format: {}", script)))
|
||||
}
|
||||
Some(script.clone())
|
||||
}
|
||||
},
|
||||
// Peer is signaling upfront shutdown but don't opt-out with correct mechanism (a.k.a 0-length script). Peer looks buggy, we fail the channel
|
||||
|
@ -971,7 +973,7 @@ impl<Signer: Sign> Channel<Signer> {
|
|||
feerate_per_kw: msg.feerate_per_kw,
|
||||
channel_value_satoshis: msg.funding_satoshis,
|
||||
counterparty_dust_limit_satoshis: msg.dust_limit_satoshis,
|
||||
holder_dust_limit_satoshis: MIN_DUST_LIMIT_SATOSHIS,
|
||||
holder_dust_limit_satoshis: MIN_CHAN_DUST_LIMIT_SATOSHIS,
|
||||
counterparty_max_htlc_value_in_flight_msat: cmp::min(msg.max_htlc_value_in_flight_msat, msg.funding_satoshis * 1000),
|
||||
counterparty_selected_channel_reserve_satoshis: Some(msg.channel_reserve_satoshis),
|
||||
counterparty_htlc_minimum_msat: msg.htlc_minimum_msat,
|
||||
|
@ -1615,11 +1617,11 @@ impl<Signer: Sign> Channel<Signer> {
|
|||
if msg.max_accepted_htlcs < config.peer_channel_config_limits.min_max_accepted_htlcs {
|
||||
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)));
|
||||
}
|
||||
if msg.dust_limit_satoshis < MIN_DUST_LIMIT_SATOSHIS {
|
||||
return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is less than the implementation limit ({})", msg.dust_limit_satoshis, MIN_DUST_LIMIT_SATOSHIS)));
|
||||
if msg.dust_limit_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
|
||||
return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is less than the implementation limit ({})", msg.dust_limit_satoshis, MIN_CHAN_DUST_LIMIT_SATOSHIS)));
|
||||
}
|
||||
if msg.dust_limit_satoshis > MAX_DUST_LIMIT_SATOSHIS {
|
||||
return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is greater than the implementation limit ({})", msg.dust_limit_satoshis, MAX_DUST_LIMIT_SATOSHIS)));
|
||||
if msg.dust_limit_satoshis > MAX_CHAN_DUST_LIMIT_SATOSHIS {
|
||||
return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is greater than the implementation limit ({})", msg.dust_limit_satoshis, MAX_CHAN_DUST_LIMIT_SATOSHIS)));
|
||||
}
|
||||
if msg.minimum_depth > config.peer_channel_config_limits.max_minimum_depth {
|
||||
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)));
|
||||
|
@ -1638,10 +1640,10 @@ impl<Signer: Sign> Channel<Signer> {
|
|||
if script.len() == 0 {
|
||||
None
|
||||
} else {
|
||||
match ShutdownScript::try_from((script.clone(), their_features)) {
|
||||
Ok(shutdown_script) => Some(shutdown_script.into_inner()),
|
||||
Err(_) => return Err(ChannelError::Close(format!("Peer is signaling upfront_shutdown but has provided an unacceptable scriptpubkey format: {}", script))),
|
||||
if !script::is_bolt2_compliant(&script, their_features) {
|
||||
return Err(ChannelError::Close(format!("Peer is signaling upfront_shutdown but has provided an unacceptable scriptpubkey format: {}", script)));
|
||||
}
|
||||
Some(script.clone())
|
||||
}
|
||||
},
|
||||
// Peer is signaling upfront shutdown but don't opt-out with correct mechanism (a.k.a 0-length script). Peer looks buggy, we fail the channel
|
||||
|
@ -3491,17 +3493,16 @@ impl<Signer: Sign> Channel<Signer> {
|
|||
}
|
||||
assert_eq!(self.channel_state & ChannelState::ShutdownComplete as u32, 0);
|
||||
|
||||
let shutdown_scriptpubkey = match ShutdownScript::try_from((msg.scriptpubkey.clone(), their_features)) {
|
||||
Ok(script) => script.into_inner(),
|
||||
Err(_) => return Err(ChannelError::Close(format!("Got a nonstandard scriptpubkey ({}) from remote peer", msg.scriptpubkey.to_bytes().to_hex()))),
|
||||
};
|
||||
if !script::is_bolt2_compliant(&msg.scriptpubkey, their_features) {
|
||||
return Err(ChannelError::Close(format!("Got a nonstandard scriptpubkey ({}) from remote peer", msg.scriptpubkey.to_bytes().to_hex())));
|
||||
}
|
||||
|
||||
if self.counterparty_shutdown_scriptpubkey.is_some() {
|
||||
if Some(&shutdown_scriptpubkey) != self.counterparty_shutdown_scriptpubkey.as_ref() {
|
||||
return Err(ChannelError::Close(format!("Got shutdown request with a scriptpubkey ({}) which did not match their previous scriptpubkey.", shutdown_scriptpubkey.to_bytes().to_hex())));
|
||||
if Some(&msg.scriptpubkey) != self.counterparty_shutdown_scriptpubkey.as_ref() {
|
||||
return Err(ChannelError::Close(format!("Got shutdown request with a scriptpubkey ({}) which did not match their previous scriptpubkey.", msg.scriptpubkey.to_bytes().to_hex())));
|
||||
}
|
||||
} else {
|
||||
self.counterparty_shutdown_scriptpubkey = Some(shutdown_scriptpubkey);
|
||||
self.counterparty_shutdown_scriptpubkey = Some(msg.scriptpubkey.clone());
|
||||
}
|
||||
|
||||
// If we have any LocalAnnounced updates we'll probably just get back an update_fail_htlc
|
||||
|
@ -3628,6 +3629,12 @@ impl<Signer: Sign> Channel<Signer> {
|
|||
},
|
||||
};
|
||||
|
||||
for outp in closing_tx.trust().built_transaction().output.iter() {
|
||||
if !outp.script_pubkey.is_witness_program() && outp.value < MAX_STD_OUTPUT_DUST_LIMIT_SATOSHIS {
|
||||
return Err(ChannelError::Close("Remote sent us a closing_signed with a dust output. Always use segwit closing scripts!".to_owned()));
|
||||
}
|
||||
}
|
||||
|
||||
assert!(self.shutdown_scriptpubkey.is_some());
|
||||
if let Some((last_fee, sig)) = self.last_sent_closing_fee {
|
||||
if last_fee == msg.fee_satoshis {
|
||||
|
|
|
@ -1497,7 +1497,7 @@ fn test_chan_reserve_dust_inbound_htlcs_outbound_chan() {
|
|||
push_amt -= Channel::<EnforcingSigner>::get_holder_selected_channel_reserve_satoshis(100_000) * 1000;
|
||||
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, push_amt, InitFeatures::known(), InitFeatures::known());
|
||||
|
||||
let dust_amt = crate::ln::channel::MIN_DUST_LIMIT_SATOSHIS * 1000
|
||||
let dust_amt = crate::ln::channel::MIN_CHAN_DUST_LIMIT_SATOSHIS * 1000
|
||||
+ feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000 * 1000 - 1;
|
||||
// In the previous code, routing this dust payment would cause nodes[0] to perceive a channel
|
||||
// reserve violation even though it's a dust HTLC and therefore shouldn't count towards the
|
||||
|
@ -5985,7 +5985,7 @@ fn bolt2_open_channel_sane_dust_limit() {
|
|||
let push_msat=10001;
|
||||
nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), channel_value_satoshis, push_msat, 42, None).unwrap();
|
||||
let mut node0_to_1_send_open_channel = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
|
||||
node0_to_1_send_open_channel.dust_limit_satoshis = 661;
|
||||
node0_to_1_send_open_channel.dust_limit_satoshis = 547;
|
||||
node0_to_1_send_open_channel.channel_reserve_satoshis = 100001;
|
||||
|
||||
nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), InitFeatures::known(), &node0_to_1_send_open_channel);
|
||||
|
@ -5996,7 +5996,7 @@ fn bolt2_open_channel_sane_dust_limit() {
|
|||
},
|
||||
_ => panic!("Unexpected event"),
|
||||
};
|
||||
assert_eq!(err_msg.data, "dust_limit_satoshis (661) is greater than the implementation limit (660)");
|
||||
assert_eq!(err_msg.data, "dust_limit_satoshis (547) is greater than the implementation limit (546)");
|
||||
}
|
||||
|
||||
// Test that if we fail to send an HTLC that is being freed from the holding cell, and the HTLC
|
||||
|
@ -9263,116 +9263,3 @@ fn test_keysend_payments_to_private_node() {
|
|||
pass_along_path(&nodes[0], &path, 10000, payment_hash, None, event, true, Some(test_preimage));
|
||||
claim_payment(&nodes[0], &path, test_preimage);
|
||||
}
|
||||
|
||||
fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, at_forward: bool, on_holder_tx: bool) {
|
||||
// Test that we properly reject dust HTLC violating our `max_dust_htlc_exposure_msat` policy.
|
||||
//
|
||||
// At HTLC forward (`send_payment()`), if the sum of the trimmed-to-dust HTLC inbound and
|
||||
// trimmed-to-dust HTLC outbound balance and this new payment as included on next counterparty
|
||||
// commitment are above our `max_dust_htlc_exposure_msat`, we'll reject the update.
|
||||
// At HTLC reception (`update_add_htlc()`), if the sum of the trimmed-to-dust HTLC inbound
|
||||
// and trimmed-to-dust HTLC outbound balance and this new received HTLC as included on next
|
||||
// counterparty commitment are above our `max_dust_htlc_exposure_msat`, we'll fail the update.
|
||||
// Note, we return a `temporary_channel_failure` (0x1000 | 7), as the channel might be
|
||||
// available again for HTLC processing once the dust bandwidth has cleared up.
|
||||
|
||||
let chanmon_cfgs = create_chanmon_cfgs(2);
|
||||
let mut config = test_default_channel_config();
|
||||
config.channel_options.max_dust_htlc_exposure_msat = 5_000_000; // default setting value
|
||||
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
|
||||
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(config)]);
|
||||
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
|
||||
|
||||
nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 1_000_000, 500_000_000, 42, None).unwrap();
|
||||
let mut open_channel = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
|
||||
open_channel.max_htlc_value_in_flight_msat = 50_000_000;
|
||||
open_channel.max_accepted_htlcs = 60;
|
||||
nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), InitFeatures::known(), &open_channel);
|
||||
let mut accept_channel = get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id());
|
||||
if on_holder_tx {
|
||||
accept_channel.dust_limit_satoshis = 660;
|
||||
}
|
||||
nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), InitFeatures::known(), &accept_channel);
|
||||
|
||||
let (temporary_channel_id, tx, _) = create_funding_transaction(&nodes[0], 1_000_000, 42);
|
||||
|
||||
if on_holder_tx {
|
||||
if let Some(mut chan) = nodes[1].node.channel_state.lock().unwrap().by_id.get_mut(&temporary_channel_id) {
|
||||
chan.holder_dust_limit_satoshis = 660;
|
||||
}
|
||||
}
|
||||
|
||||
nodes[0].node.funding_transaction_generated(&temporary_channel_id, tx.clone()).unwrap();
|
||||
nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &get_event_msg!(nodes[0], MessageSendEvent::SendFundingCreated, nodes[1].node.get_our_node_id()));
|
||||
check_added_monitors!(nodes[1], 1);
|
||||
|
||||
nodes[0].node.handle_funding_signed(&nodes[1].node.get_our_node_id(), &get_event_msg!(nodes[1], MessageSendEvent::SendFundingSigned, nodes[0].node.get_our_node_id()));
|
||||
check_added_monitors!(nodes[0], 1);
|
||||
|
||||
let (funding_locked, _) = create_chan_between_nodes_with_value_confirm(&nodes[0], &nodes[1], &tx);
|
||||
let (announcement, as_update, bs_update) = create_chan_between_nodes_with_value_b(&nodes[0], &nodes[1], &funding_locked);
|
||||
update_nodes_with_chan_announce(&nodes, 0, 1, &announcement, &as_update, &bs_update);
|
||||
|
||||
if on_holder_tx {
|
||||
if dust_outbound_balance {
|
||||
for i in 0..2 {
|
||||
let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], 2_300_000);
|
||||
if let Err(_) = nodes[1].node.send_payment(&route, payment_hash, &Some(payment_secret)) { panic!("Unexpected event at dust HTLC {}", i); }
|
||||
}
|
||||
} else {
|
||||
for _ in 0..2 {
|
||||
route_payment(&nodes[0], &[&nodes[1]], 2_300_000);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if dust_outbound_balance {
|
||||
for i in 0..25 {
|
||||
let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], 200_000); // + 177_000 msat of HTLC-success tx at 253 sats/kWU
|
||||
if let Err(_) = nodes[1].node.send_payment(&route, payment_hash, &Some(payment_secret)) { panic!("Unexpected event at dust HTLC {}", i); }
|
||||
}
|
||||
} else {
|
||||
for _ in 0..25 {
|
||||
route_payment(&nodes[0], &[&nodes[1]], 200_000); // + 167_000 msat of HTLC-timeout tx at 253 sats/kWU
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if at_forward {
|
||||
let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], if on_holder_tx { 2_300_000 } else { 200_000 });
|
||||
let mut config = UserConfig::default();
|
||||
if on_holder_tx {
|
||||
unwrap_send_err!(nodes[1].node.send_payment(&route, payment_hash, &Some(payment_secret)), true, APIError::ChannelUnavailable { ref err }, assert_eq!(err, &format!("Cannot send value that would put our exposure to dust HTLCs at {} over the limit {} on holder commitment tx", 6_900_000, config.channel_options.max_dust_htlc_exposure_msat)));
|
||||
} else {
|
||||
unwrap_send_err!(nodes[1].node.send_payment(&route, payment_hash, &Some(payment_secret)), true, APIError::ChannelUnavailable { ref err }, assert_eq!(err, &format!("Cannot send value that would put our exposure to dust HTLCs at {} over the limit {} on counterparty commitment tx", 5_200_000, config.channel_options.max_dust_htlc_exposure_msat)));
|
||||
}
|
||||
} else {
|
||||
let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1 ], if on_holder_tx { 2_300_000 } else { 200_000 });
|
||||
nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
|
||||
check_added_monitors!(nodes[0], 1);
|
||||
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
|
||||
assert_eq!(events.len(), 1);
|
||||
let payment_event = SendEvent::from_event(events.remove(0));
|
||||
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
|
||||
if on_holder_tx {
|
||||
nodes[1].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", 6_900_000, config.channel_options.max_dust_htlc_exposure_msat), 1);
|
||||
} else {
|
||||
nodes[1].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", 5_200_000, config.channel_options.max_dust_htlc_exposure_msat), 1);
|
||||
}
|
||||
}
|
||||
|
||||
let _ = nodes[1].node.get_and_clear_pending_msg_events();
|
||||
let mut added_monitors = nodes[1].chain_monitor.added_monitors.lock().unwrap();
|
||||
added_monitors.clear();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_max_dust_htlc_exposure() {
|
||||
do_test_max_dust_htlc_exposure(true, true, true);
|
||||
do_test_max_dust_htlc_exposure(false, true, true);
|
||||
do_test_max_dust_htlc_exposure(false, false, true);
|
||||
do_test_max_dust_htlc_exposure(false, false, false);
|
||||
do_test_max_dust_htlc_exposure(true, true, false);
|
||||
do_test_max_dust_htlc_exposure(true, false, false);
|
||||
do_test_max_dust_htlc_exposure(true, false, true);
|
||||
do_test_max_dust_htlc_exposure(false, true, false);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use bitcoin::blockdata::opcodes::all::OP_PUSHBYTES_0 as SEGWIT_V0;
|
||||
use bitcoin::blockdata::script::{Builder, Script};
|
||||
use bitcoin::hashes::Hash;
|
||||
use bitcoin::hash_types::{PubkeyHash, ScriptHash, WPubkeyHash, WScriptHash};
|
||||
use bitcoin::hash_types::{WPubkeyHash, WScriptHash};
|
||||
use bitcoin::secp256k1::key::PublicKey;
|
||||
|
||||
use ln::features::InitFeatures;
|
||||
|
@ -66,16 +66,6 @@ impl ShutdownScript {
|
|||
Self(ShutdownScriptImpl::Legacy(pubkey))
|
||||
}
|
||||
|
||||
/// Generates a P2PKH script pubkey from the given [`PubkeyHash`].
|
||||
pub fn new_p2pkh(pubkey_hash: &PubkeyHash) -> Self {
|
||||
Self(ShutdownScriptImpl::Bolt2(Script::new_p2pkh(pubkey_hash)))
|
||||
}
|
||||
|
||||
/// Generates a P2SH script pubkey from the given [`ScriptHash`].
|
||||
pub fn new_p2sh(script_hash: &ScriptHash) -> Self {
|
||||
Self(ShutdownScriptImpl::Bolt2(Script::new_p2sh(script_hash)))
|
||||
}
|
||||
|
||||
/// Generates a P2WPKH script pubkey from the given [`WPubkeyHash`].
|
||||
pub fn new_p2wpkh(pubkey_hash: &WPubkeyHash) -> Self {
|
||||
Self(ShutdownScriptImpl::Bolt2(Script::new_v0_wpkh(pubkey_hash)))
|
||||
|
@ -126,7 +116,9 @@ impl ShutdownScript {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_bolt2_compliant(script: &Script, features: &InitFeatures) -> bool {
|
||||
/// Check if a given script is compliant with BOLT 2's shutdown script requirements for the given
|
||||
/// counterparty features.
|
||||
pub(crate) fn is_bolt2_compliant(script: &Script, features: &InitFeatures) -> bool {
|
||||
if script.is_p2pkh() || script.is_p2sh() || script.is_v0_p2wpkh() || script.is_v0_p2wsh() {
|
||||
true
|
||||
} else if features.supports_shutdown_anysegwit() {
|
||||
|
@ -136,6 +128,8 @@ fn is_bolt2_compliant(script: &Script, features: &InitFeatures) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
// Note that this is only for our own shutdown scripts. Counterparties are still allowed to send us
|
||||
// non-witness shutdown scripts which this rejects.
|
||||
impl TryFrom<Script> for ShutdownScript {
|
||||
type Error = InvalidShutdownScript;
|
||||
|
||||
|
@ -144,11 +138,13 @@ impl TryFrom<Script> for ShutdownScript {
|
|||
}
|
||||
}
|
||||
|
||||
// Note that this is only for our own shutdown scripts. Counterparties are still allowed to send us
|
||||
// non-witness shutdown scripts which this rejects.
|
||||
impl TryFrom<(Script, &InitFeatures)> for ShutdownScript {
|
||||
type Error = InvalidShutdownScript;
|
||||
|
||||
fn try_from((script, features): (Script, &InitFeatures)) -> Result<Self, Self::Error> {
|
||||
if is_bolt2_compliant(&script, features) {
|
||||
if is_bolt2_compliant(&script, features) && script.is_witness_program() {
|
||||
Ok(Self(ShutdownScriptImpl::Bolt2(script)))
|
||||
} else {
|
||||
Err(InvalidShutdownScript { script })
|
||||
|
@ -216,30 +212,6 @@ mod shutdown_script_tests {
|
|||
assert_eq!(shutdown_script.into_inner(), p2wpkh_script);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generates_p2pkh_from_pubkey_hash() {
|
||||
let pubkey_hash = pubkey().pubkey_hash();
|
||||
let p2pkh_script = Script::new_p2pkh(&pubkey_hash);
|
||||
|
||||
let shutdown_script = ShutdownScript::new_p2pkh(&pubkey_hash);
|
||||
assert!(shutdown_script.is_compatible(&InitFeatures::known()));
|
||||
assert!(shutdown_script.is_compatible(&InitFeatures::known().clear_shutdown_anysegwit()));
|
||||
assert_eq!(shutdown_script.into_inner(), p2pkh_script);
|
||||
assert!(ShutdownScript::try_from(p2pkh_script).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generates_p2sh_from_script_hash() {
|
||||
let script_hash = redeem_script().script_hash();
|
||||
let p2sh_script = Script::new_p2sh(&script_hash);
|
||||
|
||||
let shutdown_script = ShutdownScript::new_p2sh(&script_hash);
|
||||
assert!(shutdown_script.is_compatible(&InitFeatures::known()));
|
||||
assert!(shutdown_script.is_compatible(&InitFeatures::known().clear_shutdown_anysegwit()));
|
||||
assert_eq!(shutdown_script.into_inner(), p2sh_script);
|
||||
assert!(ShutdownScript::try_from(p2sh_script).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generates_p2wpkh_from_pubkey_hash() {
|
||||
let pubkey_hash = pubkey().wpubkey_hash().unwrap();
|
||||
|
|
Loading…
Add table
Reference in a new issue