pending_monitor_updates: Vec<PendingChannelMonitorUpdate>,
}
+impl<Signer: ChannelSigner> ChannelContext<Signer> {
+ pub(crate) fn opt_anchors(&self) -> bool {
+ self.channel_transaction_parameters.opt_anchors.is_some()
+ }
+
+ /// Allowed in any state (including after shutdown)
+ pub fn get_update_time_counter(&self) -> u32 {
+ self.update_time_counter
+ }
+
+ pub fn get_latest_monitor_update_id(&self) -> u64 {
+ self.latest_monitor_update_id
+ }
+
+ pub fn should_announce(&self) -> bool {
+ self.config.announced_channel
+ }
+
+ pub fn is_outbound(&self) -> bool {
+ self.channel_transaction_parameters.is_outbound_from_holder
+ }
+
+ /// Gets the fee we'd want to charge for adding an HTLC output to this Channel
+ /// Allowed in any state (including after shutdown)
+ pub fn get_outbound_forwarding_fee_base_msat(&self) -> u32 {
+ self.config.options.forwarding_fee_base_msat
+ }
+
+ /// Returns true if we've ever received a message from the remote end for this Channel
+ pub fn have_received_message(&self) -> bool {
+ self.channel_state > (ChannelState::OurInitSent as u32)
+ }
+
+ /// Returns true if this channel is fully established and not known to be closing.
+ /// Allowed in any state (including after shutdown)
+ pub fn is_usable(&self) -> bool {
+ let mask = ChannelState::ChannelReady as u32 | BOTH_SIDES_SHUTDOWN_MASK;
+ (self.channel_state & mask) == (ChannelState::ChannelReady as u32) && !self.monitor_pending_channel_ready
+ }
+
+ /// Returns true if this channel is currently available for use. This is a superset of
+ /// is_usable() and considers things like the channel being temporarily disabled.
+ /// Allowed in any state (including after shutdown)
+ pub fn is_live(&self) -> bool {
+ self.is_usable() && (self.channel_state & (ChannelState::PeerDisconnected as u32) == 0)
+ }
+}
+
+// Internal utility functions for channels
+
+/// Returns the value to use for `holder_max_htlc_value_in_flight_msat` as a percentage of the
+/// `channel_value_satoshis` in msat, set through
+/// [`ChannelHandshakeConfig::max_inbound_htlc_value_in_flight_percent_of_channel`]
+///
+/// The effective percentage is lower bounded by 1% and upper bounded by 100%.
+///
+/// [`ChannelHandshakeConfig::max_inbound_htlc_value_in_flight_percent_of_channel`]: crate::util::config::ChannelHandshakeConfig::max_inbound_htlc_value_in_flight_percent_of_channel
+fn get_holder_max_htlc_value_in_flight_msat(channel_value_satoshis: u64, config: &ChannelHandshakeConfig) -> u64 {
+ let configured_percent = if config.max_inbound_htlc_value_in_flight_percent_of_channel < 1 {
+ 1
+ } else if config.max_inbound_htlc_value_in_flight_percent_of_channel > 100 {
+ 100
+ } else {
+ config.max_inbound_htlc_value_in_flight_percent_of_channel as u64
+ };
+ channel_value_satoshis * 10 * configured_percent
+}
+
+/// Returns a minimum channel reserve value the remote needs to maintain,
+/// required by us according to the configured or default
+/// [`ChannelHandshakeConfig::their_channel_reserve_proportional_millionths`]
+///
+/// Guaranteed to return a value no larger than channel_value_satoshis
+///
+/// This is used both for outbound and inbound channels and has lower bound
+/// of `MIN_THEIR_CHAN_RESERVE_SATOSHIS`.
+pub(crate) fn get_holder_selected_channel_reserve_satoshis(channel_value_satoshis: u64, config: &UserConfig) -> u64 {
+ let calculated_reserve = channel_value_satoshis.saturating_mul(config.channel_handshake_config.their_channel_reserve_proportional_millionths as u64) / 1_000_000;
+ cmp::min(channel_value_satoshis, cmp::max(calculated_reserve, MIN_THEIR_CHAN_RESERVE_SATOSHIS))
+}
+
+/// This is for legacy reasons, present for forward-compatibility.
+/// LDK versions older than 0.0.104 don't know how read/handle values other than default
+/// from storage. Hence, we use this function to not persist default values of
+/// `holder_selected_channel_reserve_satoshis` for channels into storage.
+pub(crate) fn get_legacy_default_holder_selected_channel_reserve_satoshis(channel_value_satoshis: u64) -> u64 {
+ let (q, _) = channel_value_satoshis.overflowing_div(100);
+ cmp::min(channel_value_satoshis, cmp::max(q, 1000))
+}
+
// TODO: We should refactor this to be an Inbound/OutboundChannel until initial setup handshaking
// has been completed, and then turn into a Channel to get compiler-time enforcement of things like
// calling channel_id() before we're set up or things like get_outbound_funding_signed on an
}
impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
- /// Returns the value to use for `holder_max_htlc_value_in_flight_msat` as a percentage of the
- /// `channel_value_satoshis` in msat, set through
- /// [`ChannelHandshakeConfig::max_inbound_htlc_value_in_flight_percent_of_channel`]
- ///
- /// The effective percentage is lower bounded by 1% and upper bounded by 100%.
- ///
- /// [`ChannelHandshakeConfig::max_inbound_htlc_value_in_flight_percent_of_channel`]: crate::util::config::ChannelHandshakeConfig::max_inbound_htlc_value_in_flight_percent_of_channel
- fn get_holder_max_htlc_value_in_flight_msat(channel_value_satoshis: u64, config: &ChannelHandshakeConfig) -> u64 {
- let configured_percent = if config.max_inbound_htlc_value_in_flight_percent_of_channel < 1 {
- 1
- } else if config.max_inbound_htlc_value_in_flight_percent_of_channel > 100 {
- 100
- } else {
- config.max_inbound_htlc_value_in_flight_percent_of_channel as u64
- };
- channel_value_satoshis * 10 * configured_percent
- }
-
- /// Returns a minimum channel reserve value the remote needs to maintain,
- /// required by us according to the configured or default
- /// [`ChannelHandshakeConfig::their_channel_reserve_proportional_millionths`]
- ///
- /// Guaranteed to return a value no larger than channel_value_satoshis
- ///
- /// This is used both for outbound and inbound channels and has lower bound
- /// of `MIN_THEIR_CHAN_RESERVE_SATOSHIS`.
- pub(crate) fn get_holder_selected_channel_reserve_satoshis(channel_value_satoshis: u64, config: &UserConfig) -> u64 {
- let calculated_reserve = channel_value_satoshis.saturating_mul(config.channel_handshake_config.their_channel_reserve_proportional_millionths as u64) / 1_000_000;
- cmp::min(channel_value_satoshis, cmp::max(calculated_reserve, MIN_THEIR_CHAN_RESERVE_SATOSHIS))
- }
-
- /// This is for legacy reasons, present for forward-compatibility.
- /// LDK versions older than 0.0.104 don't know how read/handle values other than default
- /// from storage. Hence, we use this function to not persist default values of
- /// `holder_selected_channel_reserve_satoshis` for channels into storage.
- pub(crate) fn get_legacy_default_holder_selected_channel_reserve_satoshis(channel_value_satoshis: u64) -> u64 {
- let (q, _) = channel_value_satoshis.overflowing_div(100);
- cmp::min(channel_value_satoshis, cmp::max(q, 1000))
- }
-
- pub(crate) fn opt_anchors(&self) -> bool {
- self.context.channel_transaction_parameters.opt_anchors.is_some()
- }
-
fn get_initial_channel_type(config: &UserConfig, their_features: &InitFeatures) -> ChannelTypeFeatures {
// The default channel type (ie the first one we try) depends on whether the channel is
// public - if it is, we just go with `only_static_remotekey` as it's the only option
/// not of our ability to open any channel at all. Thus, on error, we should first call this
/// and see if we get a new `OpenChannel` message, otherwise the channel is failed.
pub(crate) fn maybe_handle_error_without_close(&mut self, chain_hash: BlockHash) -> Result<msgs::OpenChannel, ()> {
- if !self.is_outbound() || self.context.channel_state != ChannelState::OurInitSent as u32 { return Err(()); }
+ if !self.context.is_outbound() || self.context.channel_state != ChannelState::OurInitSent as u32 { return Err(()); }
if self.context.channel_type == ChannelTypeFeatures::only_static_remote_key() {
// We've exhausted our options
return Err(());
if holder_selected_contest_delay < BREAKDOWN_TIMEOUT {
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, config);
+ let holder_selected_channel_reserve_satoshis = get_holder_selected_channel_reserve_satoshis(channel_value_satoshis, config);
if holder_selected_channel_reserve_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
// Protocol level safety check in place, although it should never happen because
// of `MIN_THEIR_CHAN_RESERVE_SATOSHIS`
counterparty_dust_limit_satoshis: 0,
holder_dust_limit_satoshis: MIN_CHAN_DUST_LIMIT_SATOSHIS,
counterparty_max_htlc_value_in_flight_msat: 0,
- holder_max_htlc_value_in_flight_msat: Self::get_holder_max_htlc_value_in_flight_msat(channel_value_satoshis, &config.channel_handshake_config),
+ holder_max_htlc_value_in_flight_msat: get_holder_max_htlc_value_in_flight_msat(channel_value_satoshis, &config.channel_handshake_config),
counterparty_selected_channel_reserve_satoshis: None, // Filled in in accept_channel
holder_selected_channel_reserve_satoshis,
counterparty_htlc_minimum_msat: 0,
}
}
- let holder_selected_channel_reserve_satoshis = Channel::<Signer>::get_holder_selected_channel_reserve_satoshis(msg.funding_satoshis, config);
+ let holder_selected_channel_reserve_satoshis = get_holder_selected_channel_reserve_satoshis(msg.funding_satoshis, config);
if holder_selected_channel_reserve_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
// Protocol level safety check in place, although it should never happen because
// of `MIN_THEIR_CHAN_RESERVE_SATOSHIS`
counterparty_dust_limit_satoshis: msg.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),
- holder_max_htlc_value_in_flight_msat: Self::get_holder_max_htlc_value_in_flight_msat(msg.funding_satoshis, &config.channel_handshake_config),
+ holder_max_htlc_value_in_flight_msat: get_holder_max_htlc_value_in_flight_msat(msg.funding_satoshis, &config.channel_handshake_config),
counterparty_selected_channel_reserve_satoshis: Some(msg.channel_reserve_satoshis),
holder_selected_channel_reserve_satoshis,
counterparty_htlc_minimum_msat: msg.htlc_minimum_msat,
if match update_state {
// Note that these match the inclusion criteria when scanning
// pending_inbound_htlcs below.
- FeeUpdateState::RemoteAnnounced => { debug_assert!(!self.is_outbound()); !generated_by_local },
- FeeUpdateState::AwaitingRemoteRevokeToAnnounce => { debug_assert!(!self.is_outbound()); !generated_by_local },
- FeeUpdateState::Outbound => { assert!(self.is_outbound()); generated_by_local },
+ FeeUpdateState::RemoteAnnounced => { debug_assert!(!self.context.is_outbound()); !generated_by_local },
+ FeeUpdateState::AwaitingRemoteRevokeToAnnounce => { debug_assert!(!self.context.is_outbound()); !generated_by_local },
+ FeeUpdateState::Outbound => { assert!(self.context.is_outbound()); generated_by_local },
} {
feerate_per_kw = feerate;
}
log_trace!(logger, "Building commitment transaction number {} (really {} xor {}) for channel {} for {}, generated by {} with fee {}...",
commitment_number, (INITIAL_COMMITMENT_NUMBER - commitment_number),
- get_commitment_transaction_number_obscure_factor(&self.get_holder_pubkeys().payment_point, &self.get_counterparty_pubkeys().payment_point, self.is_outbound()),
+ get_commitment_transaction_number_obscure_factor(&self.get_holder_pubkeys().payment_point, &self.get_counterparty_pubkeys().payment_point, self.context.is_outbound()),
log_bytes!(self.context.channel_id), if local { "us" } else { "remote" }, if generated_by_local { "us" } else { "remote" }, feerate_per_kw);
macro_rules! get_htlc_in_commitment {
($htlc: expr, $outbound: expr, $source: expr, $state_name: expr) => {
if $outbound == local { // "offered HTLC output"
let htlc_in_tx = get_htlc_in_commitment!($htlc, true);
- let htlc_tx_fee = if self.opt_anchors() {
+ let htlc_tx_fee = if self.context.opt_anchors() {
0
} else {
feerate_per_kw as u64 * htlc_timeout_tx_weight(false) / 1000
}
} else {
let htlc_in_tx = get_htlc_in_commitment!($htlc, false);
- let htlc_tx_fee = if self.opt_anchors() {
+ let htlc_tx_fee = if self.context.opt_anchors() {
0
} else {
feerate_per_kw as u64 * htlc_success_tx_weight(false) / 1000
let total_fee_sat = Channel::<Signer>::commit_tx_fee_sat(feerate_per_kw, included_non_dust_htlcs.len(), self.context.channel_transaction_parameters.opt_anchors.is_some());
let anchors_val = if self.context.channel_transaction_parameters.opt_anchors.is_some() { ANCHOR_OUTPUT_VALUE_SATOSHI * 2 } else { 0 } as i64;
- let (value_to_self, value_to_remote) = if self.is_outbound() {
+ let (value_to_self, value_to_remote) = if self.context.is_outbound() {
(value_to_self_msat / 1000 - anchors_val - total_fee_sat as i64, value_to_remote_msat / 1000)
} else {
(value_to_self_msat / 1000, value_to_remote_msat / 1000 - anchors_val - total_fee_sat as i64)
assert!(self.context.pending_update_fee.is_none());
let mut total_fee_satoshis = proposed_total_fee_satoshis;
- let mut value_to_holder: i64 = (self.context.value_to_self_msat as i64) / 1000 - if self.is_outbound() { total_fee_satoshis as i64 } else { 0 };
- let mut value_to_counterparty: i64 = ((self.context.channel_value_satoshis * 1000 - self.context.value_to_self_msat) as i64 / 1000) - if self.is_outbound() { 0 } else { total_fee_satoshis as i64 };
+ let mut value_to_holder: i64 = (self.context.value_to_self_msat as i64) / 1000 - if self.context.is_outbound() { total_fee_satoshis as i64 } else { 0 };
+ let mut value_to_counterparty: i64 = ((self.context.channel_value_satoshis * 1000 - self.context.value_to_self_msat) as i64 / 1000) - if self.context.is_outbound() { 0 } else { total_fee_satoshis as i64 };
if value_to_holder < 0 {
- assert!(self.is_outbound());
+ assert!(self.context.is_outbound());
total_fee_satoshis += (-value_to_holder) as u64;
} else if value_to_counterparty < 0 {
- assert!(!self.is_outbound());
+ assert!(!self.context.is_outbound());
total_fee_satoshis += (-value_to_counterparty) as u64;
}
let peer_limits = if let Some(ref limits) = self.context.inbound_handshake_limits_override { limits } else { default_limits };
// Check sanity of message fields:
- if !self.is_outbound() {
+ if !self.context.is_outbound() {
return Err(ChannelError::Close("Got an accept_channel message from an inbound peer".to_owned()));
}
if self.context.channel_state != ChannelState::OurInitSent as u32 {
SP::Target: SignerProvider<Signer = Signer>,
L::Target: Logger
{
- if self.is_outbound() {
+ if self.context.is_outbound() {
return Err(ChannelError::Close("Received funding_created for an outbound channel?".to_owned()));
}
if self.context.channel_state != (ChannelState::OurInitSent as u32 | ChannelState::TheirInitSent as u32) {
let funding_redeemscript = self.get_funding_redeemscript();
let funding_txo_script = funding_redeemscript.to_v0_p2wsh();
- let obscure_factor = get_commitment_transaction_number_obscure_factor(&self.get_holder_pubkeys().payment_point, &self.get_counterparty_pubkeys().payment_point, self.is_outbound());
+ let obscure_factor = get_commitment_transaction_number_obscure_factor(&self.get_holder_pubkeys().payment_point, &self.get_counterparty_pubkeys().payment_point, self.context.is_outbound());
let shutdown_script = self.context.shutdown_scriptpubkey.clone().map(|script| script.into_inner());
let mut monitor_signer = signer_provider.derive_channel_signer(self.context.channel_value_satoshis, self.context.channel_keys_id);
monitor_signer.provide_channel_parameters(&self.context.channel_transaction_parameters);
SP::Target: SignerProvider<Signer = Signer>,
L::Target: Logger
{
- if !self.is_outbound() {
+ if !self.context.is_outbound() {
return Err(ChannelError::Close("Received funding_signed for an inbound channel?".to_owned()));
}
if self.context.channel_state & !(ChannelState::MonitorUpdateInProgress as u32) != ChannelState::FundingCreated as u32 {
let funding_redeemscript = self.get_funding_redeemscript();
let funding_txo = self.get_funding_txo().unwrap();
let funding_txo_script = funding_redeemscript.to_v0_p2wsh();
- let obscure_factor = get_commitment_transaction_number_obscure_factor(&self.get_holder_pubkeys().payment_point, &self.get_counterparty_pubkeys().payment_point, self.is_outbound());
+ let obscure_factor = get_commitment_transaction_number_obscure_factor(&self.get_holder_pubkeys().payment_point, &self.get_counterparty_pubkeys().payment_point, self.context.is_outbound());
let shutdown_script = self.context.shutdown_scriptpubkey.clone().map(|script| script.into_inner());
let mut monitor_signer = signer_provider.derive_channel_signer(self.context.channel_value_satoshis, self.context.channel_keys_id);
monitor_signer.provide_channel_parameters(&self.context.channel_transaction_parameters);
on_holder_tx_holding_cell_htlcs_count: 0,
};
- let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if self.opt_anchors() {
+ let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if self.context.opt_anchors() {
(0, 0)
} else {
let dust_buffer_feerate = self.get_dust_buffer_feerate(outbound_feerate_update) as u64;
on_holder_tx_holding_cell_htlcs_count: 0,
};
- let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if self.opt_anchors() {
+ let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if self.context.opt_anchors() {
(0, 0)
} else {
let dust_buffer_feerate = self.get_dust_buffer_feerate(outbound_feerate_update) as u64;
let mut available_capacity_msat = outbound_capacity_msat;
- if self.is_outbound() {
+ if self.context.is_outbound() {
// We should mind channel commit tx fee when computing how much of the available capacity
// can be used in the next htlc. Mirrors the logic in send_htlc.
//
// dependency.
// This complicates the computation around dust-values, up to the one-htlc-value.
let mut real_dust_limit_timeout_sat = self.context.holder_dust_limit_satoshis;
- if !self.opt_anchors() {
+ if !self.context.opt_anchors() {
real_dust_limit_timeout_sat += self.context.feerate_per_kw as u64 * htlc_timeout_tx_weight(false) / 1000;
}
// If the channel is inbound (i.e. counterparty pays the fee), we need to make sure
// sending a new HTLC won't reduce their balance below our reserve threshold.
let mut real_dust_limit_success_sat = self.context.counterparty_dust_limit_satoshis;
- if !self.opt_anchors() {
+ if !self.context.opt_anchors() {
real_dust_limit_success_sat += self.context.feerate_per_kw as u64 * htlc_success_tx_weight(false) / 1000;
}
let mut remaining_msat_below_dust_exposure_limit = None;
let mut dust_exposure_dust_limit_msat = 0;
- let (htlc_success_dust_limit, htlc_timeout_dust_limit) = if self.opt_anchors() {
+ let (htlc_success_dust_limit, htlc_timeout_dust_limit) = if self.context.opt_anchors() {
(self.context.counterparty_dust_limit_satoshis, self.context.holder_dust_limit_satoshis)
} else {
let dust_buffer_feerate = self.get_dust_buffer_feerate(None) as u64;
///
/// Dust HTLCs are excluded.
fn next_local_commit_tx_fee_msat(&self, htlc: HTLCCandidate, fee_spike_buffer_htlc: Option<()>) -> u64 {
- assert!(self.is_outbound());
+ assert!(self.context.is_outbound());
- let (htlc_success_dust_limit, htlc_timeout_dust_limit) = if self.opt_anchors() {
+ let (htlc_success_dust_limit, htlc_timeout_dust_limit) = if self.context.opt_anchors() {
(0, 0)
} else {
(self.context.feerate_per_kw as u64 * htlc_success_tx_weight(false) / 1000,
}
let num_htlcs = included_htlcs + addl_htlcs;
- let res = Self::commit_tx_fee_msat(self.context.feerate_per_kw, num_htlcs, self.opt_anchors());
+ let res = Self::commit_tx_fee_msat(self.context.feerate_per_kw, num_htlcs, self.context.opt_anchors());
#[cfg(any(test, fuzzing))]
{
let mut fee = res;
if fee_spike_buffer_htlc.is_some() {
- fee = Self::commit_tx_fee_msat(self.context.feerate_per_kw, num_htlcs - 1, self.opt_anchors());
+ fee = Self::commit_tx_fee_msat(self.context.feerate_per_kw, num_htlcs - 1, self.context.opt_anchors());
}
let total_pending_htlcs = self.context.pending_inbound_htlcs.len() + self.context.pending_outbound_htlcs.len()
+ self.context.holding_cell_htlc_updates.len();
///
/// Dust HTLCs are excluded.
fn next_remote_commit_tx_fee_msat(&self, htlc: HTLCCandidate, fee_spike_buffer_htlc: Option<()>) -> u64 {
- assert!(!self.is_outbound());
+ assert!(!self.context.is_outbound());
- let (htlc_success_dust_limit, htlc_timeout_dust_limit) = if self.opt_anchors() {
+ let (htlc_success_dust_limit, htlc_timeout_dust_limit) = if self.context.opt_anchors() {
(0, 0)
} else {
(self.context.feerate_per_kw as u64 * htlc_success_tx_weight(false) / 1000,
}
let num_htlcs = included_htlcs + addl_htlcs;
- let res = Self::commit_tx_fee_msat(self.context.feerate_per_kw, num_htlcs, self.opt_anchors());
+ let res = Self::commit_tx_fee_msat(self.context.feerate_per_kw, num_htlcs, self.context.opt_anchors());
#[cfg(any(test, fuzzing))]
{
let mut fee = res;
if fee_spike_buffer_htlc.is_some() {
- fee = Self::commit_tx_fee_msat(self.context.feerate_per_kw, num_htlcs - 1, self.opt_anchors());
+ fee = Self::commit_tx_fee_msat(self.context.feerate_per_kw, num_htlcs - 1, self.context.opt_anchors());
}
let total_pending_htlcs = self.context.pending_inbound_htlcs.len() + self.context.pending_outbound_htlcs.len();
let commitment_tx_info = CommitmentTxInfoCached {
}
}
- let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if self.opt_anchors() {
+ let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if self.context.opt_anchors() {
(0, 0)
} else {
let dust_buffer_feerate = self.get_dust_buffer_feerate(None) as u64;
// Check that the remote can afford to pay for this HTLC on-chain at the current
// feerate_per_kw, while maintaining their channel reserve (as required by the spec).
- let remote_commit_tx_fee_msat = if self.is_outbound() { 0 } else {
+ let remote_commit_tx_fee_msat = if self.context.is_outbound() { 0 } else {
let htlc_candidate = HTLCCandidate::new(msg.amount_msat, HTLCInitiator::RemoteOffered);
self.next_remote_commit_tx_fee_msat(htlc_candidate, None) // Don't include the extra fee spike buffer HTLC in calculations
};
return Err(ChannelError::Close("Remote HTLC add would put them under remote reserve value".to_owned()));
}
- if !self.is_outbound() {
+ if !self.context.is_outbound() {
// `2 *` and `Some(())` is for the fee spike buffer we keep for the remote. This deviates from
// the spec because in the spec, the fee spike buffer requirement doesn't exist on the
// receiver's side, only on the sender's.
update_state == FeeUpdateState::RemoteAnnounced
} else { false };
if update_fee {
- debug_assert!(!self.is_outbound());
+ debug_assert!(!self.context.is_outbound());
let counterparty_reserve_we_require_msat = self.context.holder_selected_channel_reserve_satoshis * 1000;
if commitment_stats.remote_balance_msat < commitment_stats.total_fee_sat * 1000 + counterparty_reserve_we_require_msat {
return Err(ChannelError::Close("Funding remote cannot afford proposed new fee".to_owned()));
}
#[cfg(any(test, fuzzing))]
{
- if self.is_outbound() {
+ if self.context.is_outbound() {
let projected_commit_tx_info = self.context.next_local_commitment_tx_fee_info_cached.lock().unwrap().take();
*self.context.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = None;
if let Some(info) = projected_commit_tx_info {
for (idx, (htlc, mut source_opt)) in htlcs_cloned.drain(..).enumerate() {
if let Some(_) = htlc.transaction_output_index {
let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, commitment_stats.feerate_per_kw,
- self.get_counterparty_selected_contest_delay().unwrap(), &htlc, self.opt_anchors(),
+ self.get_counterparty_selected_contest_delay().unwrap(), &htlc, self.context.opt_anchors(),
false, &keys.broadcaster_delayed_payment_key, &keys.revocation_key);
- let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, self.opt_anchors(), &keys);
- let htlc_sighashtype = if self.opt_anchors() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All };
+ let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, self.context.opt_anchors(), &keys);
+ let htlc_sighashtype = if self.context.opt_anchors() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All };
let htlc_sighash = hash_to_message!(&sighash::SighashCache::new(&htlc_tx).segwit_signature_hash(0, &htlc_redeemscript, htlc.amount_msat / 1000, htlc_sighashtype).unwrap()[..]);
log_trace!(logger, "Checking HTLC tx signature {} by key {} against tx {} (sighash {}) with redeemscript {} in channel {}.",
log_bytes!(msg.htlc_signatures[idx].serialize_compact()[..]), log_bytes!(keys.countersignatory_htlc_key.serialize()),
if let Some((feerate, update_state)) = self.context.pending_update_fee {
match update_state {
FeeUpdateState::Outbound => {
- debug_assert!(self.is_outbound());
+ debug_assert!(self.context.is_outbound());
log_trace!(logger, " ...promoting outbound fee update {} to Committed", feerate);
self.context.feerate_per_kw = feerate;
self.context.pending_update_fee = None;
},
- FeeUpdateState::RemoteAnnounced => { debug_assert!(!self.is_outbound()); },
+ FeeUpdateState::RemoteAnnounced => { debug_assert!(!self.context.is_outbound()); },
FeeUpdateState::AwaitingRemoteRevokeToAnnounce => {
- debug_assert!(!self.is_outbound());
+ debug_assert!(!self.context.is_outbound());
log_trace!(logger, " ...promoting inbound AwaitingRemoteRevokeToAnnounce fee update {} to Committed", feerate);
require_commitment = true;
self.context.feerate_per_kw = feerate;
/// You MUST call [`Self::send_commitment_no_state_update`] prior to any other calls on this
/// [`Channel`] if `force_holding_cell` is false.
fn send_update_fee<L: Deref>(&mut self, feerate_per_kw: u32, mut force_holding_cell: bool, logger: &L) -> Option<msgs::UpdateFee> where L::Target: Logger {
- if !self.is_outbound() {
+ if !self.context.is_outbound() {
panic!("Cannot send fee from inbound channel");
}
- if !self.is_usable() {
+ if !self.context.is_usable() {
panic!("Cannot update fee until channel is fully established and we haven't started shutting down");
}
- if !self.is_live() {
+ if !self.context.is_live() {
panic!("Cannot update fee while peer is disconnected/we're awaiting a monitor update (ChannelManager should have caught this)");
}
let outbound_stats = self.get_outbound_pending_htlc_stats(Some(feerate_per_kw));
let keys = self.build_holder_transaction_keys(self.context.cur_holder_commitment_transaction_number);
let commitment_stats = self.build_commitment_transaction(self.context.cur_holder_commitment_transaction_number, &keys, true, true, logger);
- let buffer_fee_msat = Channel::<Signer>::commit_tx_fee_sat(feerate_per_kw, commitment_stats.num_nondust_htlcs + outbound_stats.on_holder_tx_holding_cell_htlcs_count as usize + CONCURRENT_INBOUND_HTLC_FEE_BUFFER as usize, self.opt_anchors()) * 1000;
+ let buffer_fee_msat = Channel::<Signer>::commit_tx_fee_sat(feerate_per_kw, commitment_stats.num_nondust_htlcs + outbound_stats.on_holder_tx_holding_cell_htlcs_count as usize + CONCURRENT_INBOUND_HTLC_FEE_BUFFER as usize, self.context.opt_anchors()) * 1000;
let holder_balance_msat = commitment_stats.local_balance_msat - outbound_stats.holding_cell_msat;
if holder_balance_msat < buffer_fee_msat + self.context.counterparty_selected_channel_reserve_satoshis.unwrap() * 1000 {
//TODO: auto-close after a number of failures?
if let Some((_, update_state)) = self.context.pending_update_fee {
if update_state == FeeUpdateState::RemoteAnnounced {
- debug_assert!(!self.is_outbound());
+ debug_assert!(!self.context.is_outbound());
self.context.pending_update_fee = None;
}
}
// (re-)broadcast the funding transaction as we may have declined to broadcast it when we
// first received the funding_signed.
let mut funding_broadcastable =
- if self.is_outbound() && self.context.channel_state & !MULTI_STATE_FLAGS >= ChannelState::FundingSent as u32 {
+ if self.context.is_outbound() && self.context.channel_state & !MULTI_STATE_FLAGS >= ChannelState::FundingSent as u32 {
self.context.funding_transaction.take()
} else { None };
// That said, if the funding transaction is already confirmed (ie we're active with a
// the funding transaction confirmed before the monitor was persisted, or
// * a 0-conf channel and intended to send the channel_ready before any broadcast at all.
let channel_ready = if self.context.monitor_pending_channel_ready {
- assert!(!self.is_outbound() || self.context.minimum_depth == Some(0),
+ assert!(!self.context.is_outbound() || self.context.minimum_depth == Some(0),
"Funding transaction broadcast by the local client before it should have - LDK didn't do it!");
self.context.monitor_pending_channel_ready = false;
let next_per_commitment_point = self.context.holder_signer.get_per_commitment_point(self.context.cur_holder_commitment_transaction_number, &self.context.secp_ctx);
pub fn update_fee<F: Deref, L: Deref>(&mut self, fee_estimator: &LowerBoundedFeeEstimator<F>, msg: &msgs::UpdateFee, logger: &L) -> Result<(), ChannelError>
where F::Target: FeeEstimator, L::Target: Logger
{
- if self.is_outbound() {
+ if self.context.is_outbound() {
return Err(ChannelError::Close("Non-funding remote tried to update channel fee".to_owned()));
}
if self.context.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 {
}
}
- let update_fee = if self.is_outbound() && self.context.pending_update_fee.is_some() {
+ let update_fee = if self.context.is_outbound() && self.context.pending_update_fee.is_some() {
Some(msgs::UpdateFee {
channel_id: self.channel_id(),
feerate_per_kw: self.context.pending_update_fee.unwrap().0,
// If we fail to come to consensus, we'll have to force-close.
let mut proposed_feerate = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::Background);
let normal_feerate = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::Normal);
- let mut proposed_max_feerate = if self.is_outbound() { normal_feerate } else { u32::max_value() };
+ let mut proposed_max_feerate = if self.context.is_outbound() { normal_feerate } else { u32::max_value() };
// The spec requires that (when the channel does not have anchors) we only send absolute
// channel fees no greater than the absolute channel fee on the current commitment
// some force-closure by old nodes, but we wanted to close the channel anyway.
if let Some(target_feerate) = self.context.target_closing_feerate_sats_per_kw {
- let min_feerate = if self.is_outbound() { target_feerate } else { cmp::min(self.context.feerate_per_kw, target_feerate) };
+ let min_feerate = if self.context.is_outbound() { target_feerate } else { cmp::min(self.context.feerate_per_kw, target_feerate) };
proposed_feerate = cmp::max(proposed_feerate, min_feerate);
proposed_max_feerate = cmp::max(proposed_max_feerate, min_feerate);
}
// if the funders' output is dust we have to know the absolute fee we're going to use.
let tx_weight = self.get_closing_transaction_weight(Some(&self.get_closing_scriptpubkey()), Some(self.context.counterparty_shutdown_scriptpubkey.as_ref().unwrap()));
let proposed_total_fee_satoshis = proposed_feerate as u64 * tx_weight / 1000;
- let proposed_max_total_fee_satoshis = if self.is_outbound() {
+ let proposed_max_total_fee_satoshis = if self.context.is_outbound() {
// We always add force_close_avoidance_max_fee_satoshis to our normal
// feerate-calculated fee, but allow the max to be overridden if we're using a
// target feerate-calculated fee.
return Ok((None, None));
}
- if !self.is_outbound() {
+ if !self.context.is_outbound() {
if let Some(msg) = &self.context.pending_counterparty_closing_signed.take() {
return self.closing_signed(fee_estimator, &msg);
}
return Err(ChannelError::Close("Remote tried to send us a closing tx with > 21 million BTC fee".to_owned()));
}
- if self.is_outbound() && self.context.last_sent_closing_fee.is_none() {
+ if self.context.is_outbound() && self.context.last_sent_closing_fee.is_none() {
return Err(ChannelError::Close("Remote tried to send a closing_signed when we were supposed to propose the first one".to_owned()));
}
return Err(ChannelError::Warn(format!("Unable to come to consensus about closing feerate, remote's min fee ({} sat) was greater than our max fee ({} sat)", min_fee_satoshis, our_max_fee)));
}
- if !self.is_outbound() {
+ if !self.context.is_outbound() {
// They have to pay, so pick the highest fee in the overlapping range.
// We should never set an upper bound aside from their full balance
debug_assert_eq!(our_max_fee, self.context.channel_value_satoshis - (self.context.value_to_self_msat + 999) / 1000);
// Checks whether we should emit a `ChannelReady` event.
pub(crate) fn should_emit_channel_ready_event(&mut self) -> bool {
- self.is_usable() && !self.context.channel_ready_event_emitted
+ self.context.is_usable() && !self.context.channel_ready_event_emitted
}
// Remembers that we already emitted a `ChannelReady` event.
}
}
- /// Allowed in any state (including after shutdown)
- pub fn get_update_time_counter(&self) -> u32 {
- self.context.update_time_counter
- }
-
- pub fn get_latest_monitor_update_id(&self) -> u64 {
- self.context.latest_monitor_update_id
- }
-
- pub fn should_announce(&self) -> bool {
- self.context.config.announced_channel
- }
-
- pub fn is_outbound(&self) -> bool {
- self.context.channel_transaction_parameters.is_outbound_from_holder
- }
-
- /// Gets the fee we'd want to charge for adding an HTLC output to this Channel
- /// Allowed in any state (including after shutdown)
- pub fn get_outbound_forwarding_fee_base_msat(&self) -> u32 {
- self.context.config.options.forwarding_fee_base_msat
- }
-
- /// Returns true if we've ever received a message from the remote end for this Channel
- pub fn have_received_message(&self) -> bool {
- self.context.channel_state > (ChannelState::OurInitSent as u32)
- }
-
- /// Returns true if this channel is fully established and not known to be closing.
- /// Allowed in any state (including after shutdown)
- pub fn is_usable(&self) -> bool {
- let mask = ChannelState::ChannelReady as u32 | BOTH_SIDES_SHUTDOWN_MASK;
- (self.context.channel_state & mask) == (ChannelState::ChannelReady as u32) && !self.context.monitor_pending_channel_ready
- }
-
- /// Returns true if this channel is currently available for use. This is a superset of
- /// is_usable() and considers things like the channel being temporarily disabled.
- /// Allowed in any state (including after shutdown)
- pub fn is_live(&self) -> bool {
- self.is_usable() && (self.context.channel_state & (ChannelState::PeerDisconnected as u32) == 0)
- }
-
/// Returns true if this channel has been marked as awaiting a monitor update to move forward.
/// Allowed in any state (including after shutdown)
pub fn is_awaiting_monitor_update(&self) -> bool {
}
pub fn get_latest_complete_monitor_update_id(&self) -> u64 {
- if self.context.pending_monitor_updates.is_empty() { return self.get_latest_monitor_update_id(); }
+ if self.context.pending_monitor_updates.is_empty() { return self.context.get_latest_monitor_update_id(); }
self.context.pending_monitor_updates[0].update.update_id - 1
}
// Because deciding we're awaiting initial broadcast spuriously could result in
// funds-loss (as we don't have a monitor, but have the funding transaction confirmed),
// we hard-assert here, even in production builds.
- if self.is_outbound() { assert!(self.context.funding_transaction.is_some()); }
+ if self.context.is_outbound() { assert!(self.context.funding_transaction.is_some()); }
assert!(self.context.monitor_pending_channel_ready);
assert_eq!(self.context.latest_monitor_update_id, 0);
return true;
let txo_idx = funding_txo.index as usize;
if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != self.get_funding_redeemscript().to_v0_p2wsh() ||
tx.output[txo_idx].value != self.context.channel_value_satoshis {
- if self.is_outbound() {
+ if self.context.is_outbound() {
// If we generated the funding transaction and it doesn't match what it
// should, the client is really broken and we should just panic and
// tell them off. That said, because hash collisions happen with high
let err_reason = "funding tx had wrong script/value or output index";
return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() });
} else {
- if self.is_outbound() {
+ if self.context.is_outbound() {
for input in tx.input.iter() {
if input.witness.is_empty() {
// We generated a malleable funding transaction, implying we've
self.context.minimum_depth.unwrap(), funding_tx_confirmations);
return Err(ClosureReason::ProcessingError { err: err_reason });
}
- } else if !self.is_outbound() && self.context.funding_tx_confirmed_in.is_none() &&
+ } else if !self.context.is_outbound() && self.context.funding_tx_confirmed_in.is_none() &&
height >= self.context.channel_creation_height + FUNDING_CONF_DEADLINE_BLOCKS {
log_info!(logger, "Closing channel {} due to funding timeout", log_bytes!(self.context.channel_id));
// If funding_tx_confirmed_in is unset, the channel must not be active
// something in the handler for the message that prompted this message):
pub fn get_open_channel(&self, chain_hash: BlockHash) -> msgs::OpenChannel {
- if !self.is_outbound() {
+ if !self.context.is_outbound() {
panic!("Tried to open a channel for an inbound channel?");
}
if self.context.channel_state != ChannelState::OurInitSent as u32 {
///
/// [`msgs::AcceptChannel`]: crate::ln::msgs::AcceptChannel
pub fn accept_inbound_channel(&mut self, user_id: u128) -> msgs::AcceptChannel {
- if self.is_outbound() {
+ if self.context.is_outbound() {
panic!("Tried to send accept_channel for an outbound channel?");
}
if self.context.channel_state != (ChannelState::OurInitSent as u32) | (ChannelState::TheirInitSent as u32) {
/// Do NOT broadcast the funding transaction until after a successful funding_signed call!
/// If an Err is returned, it is a ChannelError::Close.
pub fn get_outbound_funding_created<L: Deref>(&mut self, funding_transaction: Transaction, funding_txo: OutPoint, logger: &L) -> Result<msgs::FundingCreated, ChannelError> where L::Target: Logger {
- if !self.is_outbound() {
+ if !self.context.is_outbound() {
panic!("Tried to create outbound funding_created message on an inbound channel!");
}
if self.context.channel_state != (ChannelState::OurInitSent as u32 | ChannelState::TheirInitSent as u32) {
if !self.context.config.announced_channel {
return Err(ChannelError::Ignore("Channel is not available for public announcements".to_owned()));
}
- if !self.is_usable() {
+ if !self.context.is_usable() {
return Err(ChannelError::Ignore("Cannot get a ChannelAnnouncement if the channel is not currently usable".to_owned()));
}
return None;
}
- if !self.is_usable() {
+ if !self.context.is_usable() {
return None;
}
}
if let Some((feerate, update_state)) = self.context.pending_update_fee {
if update_state == FeeUpdateState::AwaitingRemoteRevokeToAnnounce {
- debug_assert!(!self.is_outbound());
+ debug_assert!(!self.context.is_outbound());
log_trace!(logger, " ...promoting inbound AwaitingRemoteRevokeToAnnounce fee update {} to Committed", feerate);
self.context.feerate_per_kw = feerate;
self.context.pending_update_fee = None;
#[cfg(any(test, fuzzing))]
{
- if !self.is_outbound() {
+ if !self.context.is_outbound() {
let projected_commit_tx_info = self.context.next_remote_commitment_tx_fee_info_cached.lock().unwrap().take();
*self.context.next_local_commitment_tx_fee_info_cached.lock().unwrap() = None;
if let Some(info) = projected_commit_tx_info {
&& info.next_holder_htlc_id == self.context.next_holder_htlc_id
&& info.next_counterparty_htlc_id == self.context.next_counterparty_htlc_id
&& info.feerate == self.context.feerate_per_kw {
- let actual_fee = Self::commit_tx_fee_msat(self.context.feerate_per_kw, commitment_stats.num_nondust_htlcs, self.opt_anchors());
+ let actual_fee = Self::commit_tx_fee_msat(self.context.feerate_per_kw, commitment_stats.num_nondust_htlcs, self.context.opt_anchors());
assert_eq!(actual_fee, info.fee);
}
}
for (ref htlc_sig, ref htlc) in htlc_signatures.iter().zip(htlcs) {
log_trace!(logger, "Signed remote HTLC tx {} with redeemscript {} with pubkey {} -> {} in channel {}",
- encode::serialize_hex(&chan_utils::build_htlc_transaction(&counterparty_commitment_txid, commitment_stats.feerate_per_kw, self.get_holder_selected_contest_delay(), htlc, self.opt_anchors(), false, &counterparty_keys.broadcaster_delayed_payment_key, &counterparty_keys.revocation_key)),
- encode::serialize_hex(&chan_utils::get_htlc_redeemscript(&htlc, self.opt_anchors(), &counterparty_keys)),
+ encode::serialize_hex(&chan_utils::build_htlc_transaction(&counterparty_commitment_txid, commitment_stats.feerate_per_kw, self.get_holder_selected_contest_delay(), htlc, self.context.opt_anchors(), false, &counterparty_keys.broadcaster_delayed_payment_key, &counterparty_keys.revocation_key)),
+ encode::serialize_hex(&chan_utils::get_htlc_redeemscript(&htlc, self.context.opt_anchors(), &counterparty_keys)),
log_bytes!(counterparty_keys.broadcaster_htlc_key.serialize()),
log_bytes!(htlc_sig.serialize_compact()[..]), log_bytes!(self.channel_id()));
}
fail_reason.write(writer)?;
}
- if self.is_outbound() {
+ if self.context.is_outbound() {
self.context.pending_update_fee.map(|(a, _)| a).write(writer)?;
} else if let Some((feerate, FeeUpdateState::AwaitingRemoteRevokeToAnnounce)) = self.context.pending_update_fee {
Some(feerate).write(writer)?;
// a different percentage of the channel value then 10%, which older versions of LDK used
// to set it to before the percentage was made configurable.
let serialized_holder_selected_reserve =
- if self.context.holder_selected_channel_reserve_satoshis != Self::get_legacy_default_holder_selected_channel_reserve_satoshis(self.context.channel_value_satoshis)
+ if self.context.holder_selected_channel_reserve_satoshis != get_legacy_default_holder_selected_channel_reserve_satoshis(self.context.channel_value_satoshis)
{ Some(self.context.holder_selected_channel_reserve_satoshis) } else { None };
let mut old_max_in_flight_percent_config = UserConfig::default().channel_handshake_config;
old_max_in_flight_percent_config.max_inbound_htlc_value_in_flight_percent_of_channel = MAX_IN_FLIGHT_PERCENT_LEGACY;
let serialized_holder_htlc_max_in_flight =
- if self.context.holder_max_htlc_value_in_flight_msat != Self::get_holder_max_htlc_value_in_flight_msat(self.context.channel_value_satoshis, &old_max_in_flight_percent_config)
+ if self.context.holder_max_htlc_value_in_flight_msat != get_holder_max_htlc_value_in_flight_msat(self.context.channel_value_satoshis, &old_max_in_flight_percent_config)
{ Some(self.context.holder_max_htlc_value_in_flight_msat) } else { None };
let channel_pending_event_emitted = Some(self.context.channel_pending_event_emitted);
let mut announcement_sigs = None;
let mut target_closing_feerate_sats_per_kw = None;
let mut monitor_pending_finalized_fulfills = Some(Vec::new());
- let mut holder_selected_channel_reserve_satoshis = Some(Self::get_legacy_default_holder_selected_channel_reserve_satoshis(channel_value_satoshis));
- let mut holder_max_htlc_value_in_flight_msat = Some(Self::get_holder_max_htlc_value_in_flight_msat(channel_value_satoshis, &UserConfig::default().channel_handshake_config));
+ let mut holder_selected_channel_reserve_satoshis = Some(get_legacy_default_holder_selected_channel_reserve_satoshis(channel_value_satoshis));
+ let mut holder_max_htlc_value_in_flight_msat = Some(get_holder_max_htlc_value_in_flight_msat(channel_value_satoshis, &UserConfig::default().channel_handshake_config));
// Prior to supporting channel type negotiation, all of our channels were static_remotekey
// only, so we default to that if none was written.
let mut channel_type = Some(ChannelTypeFeatures::only_static_remote_key());
// the dust limit check.
let htlc_candidate = HTLCCandidate::new(htlc_amount_msat, HTLCInitiator::LocalOffered);
let local_commit_tx_fee = node_a_chan.next_local_commit_tx_fee_msat(htlc_candidate, None);
- let local_commit_fee_0_htlcs = Channel::<EnforcingSigner>::commit_tx_fee_msat(node_a_chan.context.feerate_per_kw, 0, node_a_chan.opt_anchors());
+ let local_commit_fee_0_htlcs = Channel::<EnforcingSigner>::commit_tx_fee_msat(node_a_chan.context.feerate_per_kw, 0, node_a_chan.context.opt_anchors());
assert_eq!(local_commit_tx_fee, local_commit_fee_0_htlcs);
// Finally, make sure that when Node A calculates the remote's commitment transaction fees, all
// of the HTLCs are seen to be above the dust limit.
node_a_chan.context.channel_transaction_parameters.is_outbound_from_holder = false;
- let remote_commit_fee_3_htlcs = Channel::<EnforcingSigner>::commit_tx_fee_msat(node_a_chan.context.feerate_per_kw, 3, node_a_chan.opt_anchors());
+ let remote_commit_fee_3_htlcs = Channel::<EnforcingSigner>::commit_tx_fee_msat(node_a_chan.context.feerate_per_kw, 3, node_a_chan.context.opt_anchors());
let htlc_candidate = HTLCCandidate::new(htlc_amount_msat, HTLCInitiator::LocalOffered);
let remote_commit_tx_fee = node_a_chan.next_remote_commit_tx_fee_msat(htlc_candidate, None);
assert_eq!(remote_commit_tx_fee, remote_commit_fee_3_htlcs);
let config = UserConfig::default();
let mut chan = Channel::<EnforcingSigner>::new_outbound(&fee_est, &&keys_provider, &&keys_provider, node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
- let commitment_tx_fee_0_htlcs = Channel::<EnforcingSigner>::commit_tx_fee_msat(chan.context.feerate_per_kw, 0, chan.opt_anchors());
- let commitment_tx_fee_1_htlc = Channel::<EnforcingSigner>::commit_tx_fee_msat(chan.context.feerate_per_kw, 1, chan.opt_anchors());
+ let commitment_tx_fee_0_htlcs = Channel::<EnforcingSigner>::commit_tx_fee_msat(chan.context.feerate_per_kw, 0, chan.context.opt_anchors());
+ let commitment_tx_fee_1_htlc = Channel::<EnforcingSigner>::commit_tx_fee_msat(chan.context.feerate_per_kw, 1, chan.context.opt_anchors());
// If HTLC_SUCCESS_TX_WEIGHT and HTLC_TIMEOUT_TX_WEIGHT were swapped: then this HTLC would be
// counted as dust when it shouldn't be.
- let htlc_amt_above_timeout = ((253 * htlc_timeout_tx_weight(chan.opt_anchors()) / 1000) + chan.context.holder_dust_limit_satoshis + 1) * 1000;
+ let htlc_amt_above_timeout = ((253 * htlc_timeout_tx_weight(chan.context.opt_anchors()) / 1000) + chan.context.holder_dust_limit_satoshis + 1) * 1000;
let htlc_candidate = HTLCCandidate::new(htlc_amt_above_timeout, HTLCInitiator::LocalOffered);
let commitment_tx_fee = chan.next_local_commit_tx_fee_msat(htlc_candidate, None);
assert_eq!(commitment_tx_fee, commitment_tx_fee_1_htlc);
// If swapped: this HTLC would be counted as non-dust when it shouldn't be.
- let dust_htlc_amt_below_success = ((253 * htlc_success_tx_weight(chan.opt_anchors()) / 1000) + chan.context.holder_dust_limit_satoshis - 1) * 1000;
+ let dust_htlc_amt_below_success = ((253 * htlc_success_tx_weight(chan.context.opt_anchors()) / 1000) + chan.context.holder_dust_limit_satoshis - 1) * 1000;
let htlc_candidate = HTLCCandidate::new(dust_htlc_amt_below_success, HTLCInitiator::RemoteOffered);
let commitment_tx_fee = chan.next_local_commit_tx_fee_msat(htlc_candidate, None);
assert_eq!(commitment_tx_fee, commitment_tx_fee_0_htlcs);
chan.context.channel_transaction_parameters.is_outbound_from_holder = false;
// If swapped: this HTLC would be counted as non-dust when it shouldn't be.
- let dust_htlc_amt_above_timeout = ((253 * htlc_timeout_tx_weight(chan.opt_anchors()) / 1000) + chan.context.counterparty_dust_limit_satoshis + 1) * 1000;
+ let dust_htlc_amt_above_timeout = ((253 * htlc_timeout_tx_weight(chan.context.opt_anchors()) / 1000) + chan.context.counterparty_dust_limit_satoshis + 1) * 1000;
let htlc_candidate = HTLCCandidate::new(dust_htlc_amt_above_timeout, HTLCInitiator::LocalOffered);
let commitment_tx_fee = chan.next_remote_commit_tx_fee_msat(htlc_candidate, None);
assert_eq!(commitment_tx_fee, commitment_tx_fee_0_htlcs);
// If swapped: this HTLC would be counted as dust when it shouldn't be.
- let htlc_amt_below_success = ((253 * htlc_success_tx_weight(chan.opt_anchors()) / 1000) + chan.context.counterparty_dust_limit_satoshis - 1) * 1000;
+ let htlc_amt_below_success = ((253 * htlc_success_tx_weight(chan.context.opt_anchors()) / 1000) + chan.context.counterparty_dust_limit_satoshis - 1) * 1000;
let htlc_candidate = HTLCCandidate::new(htlc_amt_below_success, HTLCInitiator::RemoteOffered);
let commitment_tx_fee = chan.next_remote_commit_tx_fee_msat(htlc_candidate, None);
assert_eq!(commitment_tx_fee, commitment_tx_fee_1_htlc);