pub const INITIAL_COMMITMENT_NUMBER: u64 = (1 << 48) - 1;
+pub const DEFAULT_MAX_HTLCS: u16 = 50;
+
+pub(crate) fn commitment_tx_base_weight(opt_anchors: bool) -> u64 {
+ const COMMITMENT_TX_BASE_WEIGHT: u64 = 724;
+ const COMMITMENT_TX_BASE_ANCHOR_WEIGHT: u64 = 1124;
+ if opt_anchors { COMMITMENT_TX_BASE_ANCHOR_WEIGHT } else { COMMITMENT_TX_BASE_WEIGHT }
+}
+
+#[cfg(not(test))]
+const COMMITMENT_TX_WEIGHT_PER_HTLC: u64 = 172;
+#[cfg(test)]
+pub const COMMITMENT_TX_WEIGHT_PER_HTLC: u64 = 172;
+
+pub const ANCHOR_OUTPUT_VALUE_SATOSHI: u64 = 330;
+
+/// The percentage of the channel value `holder_max_htlc_value_in_flight_msat` used to be set to,
+/// before this was made configurable. The percentage was made configurable in LDK 0.0.107,
+/// although LDK 0.0.104+ enabled serialization of channels with a different value set for
+/// `holder_max_htlc_value_in_flight_msat`.
+pub const MAX_IN_FLIGHT_PERCENT_LEGACY: u8 = 10;
+
+/// Maximum `funding_satoshis` value according to the BOLT #2 specification, if
+/// `option_support_large_channel` (aka wumbo channels) is not supported.
+/// It's 2^24 - 1.
+pub const MAX_FUNDING_SATOSHIS_NO_WUMBO: u64 = (1 << 24) - 1;
+
+/// Total bitcoin supply in satoshis.
+pub const TOTAL_BITCOIN_SUPPLY_SATOSHIS: u64 = 21_000_000 * 1_0000_0000;
+
+/// 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;
+
+/// 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/lightning/bolts/issues/905> for more details.
+pub const MIN_CHAN_DUST_LIMIT_SATOSHIS: u64 = 354;
+
+// Just a reasonable implementation-specific safe lower bound, higher than the dust limit.
+pub const MIN_THEIR_CHAN_RESERVE_SATOSHIS: u64 = 1000;
+
+/// 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
+/// channel_id in ChannelManager.
+pub(super) enum ChannelError {
+ Ignore(String),
+ Warn(String),
+ Close(String),
+}
+
+impl fmt::Debug for ChannelError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ &ChannelError::Ignore(ref e) => write!(f, "Ignore : {}", e),
+ &ChannelError::Warn(ref e) => write!(f, "Warn : {}", e),
+ &ChannelError::Close(ref e) => write!(f, "Close : {}", e),
+ }
+ }
+}
+
+macro_rules! secp_check {
+ ($res: expr, $err: expr) => {
+ match $res {
+ Ok(thing) => thing,
+ Err(_) => return Err(ChannelError::Close($err)),
+ }
+ };
+}
+
/// The "channel disabled" bit in channel_update must be set based on whether we are connected to
/// our counterparty or not. However, we don't want to announce updates right away to avoid
/// spamming the network with updates if the connection is flapping. Instead, we "stage" updates to
pub fn counterparty_forwarding_info(&self) -> Option<CounterpartyForwardingInfo> {
self.counterparty_forwarding_info.clone()
}
-}
-
-// 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))
-}
+ /// Returns a HTLCStats about inbound pending htlcs
+ fn get_inbound_pending_htlc_stats(&self, outbound_feerate_update: Option<u32>) -> HTLCStats {
+ let context = self;
+ let mut stats = HTLCStats {
+ pending_htlcs: context.pending_inbound_htlcs.len() as u32,
+ pending_htlcs_value_msat: 0,
+ on_counterparty_tx_dust_exposure_msat: 0,
+ on_holder_tx_dust_exposure_msat: 0,
+ holding_cell_msat: 0,
+ on_holder_tx_holding_cell_htlcs_count: 0,
+ };
-// Get the fee cost in SATS of a commitment tx with a given number of HTLC outputs.
-// Note that num_htlcs should not include dust HTLCs.
-#[inline]
-fn commit_tx_fee_sat(feerate_per_kw: u32, num_htlcs: usize, opt_anchors: bool) -> u64 {
- feerate_per_kw as u64 * (commitment_tx_base_weight(opt_anchors) + num_htlcs as u64 * COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000
-}
+ let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if context.opt_anchors() {
+ (0, 0)
+ } else {
+ let dust_buffer_feerate = context.get_dust_buffer_feerate(outbound_feerate_update) as u64;
+ (dust_buffer_feerate * htlc_timeout_tx_weight(false) / 1000,
+ dust_buffer_feerate * htlc_success_tx_weight(false) / 1000)
+ };
+ let counterparty_dust_limit_timeout_sat = htlc_timeout_dust_limit + context.counterparty_dust_limit_satoshis;
+ let holder_dust_limit_success_sat = htlc_success_dust_limit + context.holder_dust_limit_satoshis;
+ for ref htlc in context.pending_inbound_htlcs.iter() {
+ stats.pending_htlcs_value_msat += htlc.amount_msat;
+ if htlc.amount_msat / 1000 < counterparty_dust_limit_timeout_sat {
+ stats.on_counterparty_tx_dust_exposure_msat += htlc.amount_msat;
+ }
+ if htlc.amount_msat / 1000 < holder_dust_limit_success_sat {
+ stats.on_holder_tx_dust_exposure_msat += htlc.amount_msat;
+ }
+ }
+ stats
+ }
-// 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
-// inbound channel.
-//
-// Holder designates channel data owned for the benefit of the user client.
-// Counterparty designates channel data owned by the another channel participant entity.
-pub(super) struct Channel<Signer: ChannelSigner> {
- pub context: ChannelContext<Signer>,
-}
+ /// Returns a HTLCStats about pending outbound htlcs, *including* pending adds in our holding cell.
+ fn get_outbound_pending_htlc_stats(&self, outbound_feerate_update: Option<u32>) -> HTLCStats {
+ let context = self;
+ let mut stats = HTLCStats {
+ pending_htlcs: context.pending_outbound_htlcs.len() as u32,
+ pending_htlcs_value_msat: 0,
+ on_counterparty_tx_dust_exposure_msat: 0,
+ on_holder_tx_dust_exposure_msat: 0,
+ holding_cell_msat: 0,
+ on_holder_tx_holding_cell_htlcs_count: 0,
+ };
-#[cfg(any(test, fuzzing))]
-struct CommitmentTxInfoCached {
- fee: u64,
- total_pending_htlcs: usize,
- next_holder_htlc_id: u64,
- next_counterparty_htlc_id: u64,
- feerate: u32,
-}
+ let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if context.opt_anchors() {
+ (0, 0)
+ } else {
+ let dust_buffer_feerate = context.get_dust_buffer_feerate(outbound_feerate_update) as u64;
+ (dust_buffer_feerate * htlc_timeout_tx_weight(false) / 1000,
+ dust_buffer_feerate * htlc_success_tx_weight(false) / 1000)
+ };
+ let counterparty_dust_limit_success_sat = htlc_success_dust_limit + context.counterparty_dust_limit_satoshis;
+ let holder_dust_limit_timeout_sat = htlc_timeout_dust_limit + context.holder_dust_limit_satoshis;
+ for ref htlc in context.pending_outbound_htlcs.iter() {
+ stats.pending_htlcs_value_msat += htlc.amount_msat;
+ if htlc.amount_msat / 1000 < counterparty_dust_limit_success_sat {
+ stats.on_counterparty_tx_dust_exposure_msat += htlc.amount_msat;
+ }
+ if htlc.amount_msat / 1000 < holder_dust_limit_timeout_sat {
+ stats.on_holder_tx_dust_exposure_msat += htlc.amount_msat;
+ }
+ }
-pub const DEFAULT_MAX_HTLCS: u16 = 50;
+ for update in context.holding_cell_htlc_updates.iter() {
+ if let &HTLCUpdateAwaitingACK::AddHTLC { ref amount_msat, .. } = update {
+ stats.pending_htlcs += 1;
+ stats.pending_htlcs_value_msat += amount_msat;
+ stats.holding_cell_msat += amount_msat;
+ if *amount_msat / 1000 < counterparty_dust_limit_success_sat {
+ stats.on_counterparty_tx_dust_exposure_msat += amount_msat;
+ }
+ if *amount_msat / 1000 < holder_dust_limit_timeout_sat {
+ stats.on_holder_tx_dust_exposure_msat += amount_msat;
+ } else {
+ stats.on_holder_tx_holding_cell_htlcs_count += 1;
+ }
+ }
+ }
+ stats
+ }
-pub(crate) fn commitment_tx_base_weight(opt_anchors: bool) -> u64 {
- const COMMITMENT_TX_BASE_WEIGHT: u64 = 724;
- const COMMITMENT_TX_BASE_ANCHOR_WEIGHT: u64 = 1124;
- if opt_anchors { COMMITMENT_TX_BASE_ANCHOR_WEIGHT } else { COMMITMENT_TX_BASE_WEIGHT }
-}
+ /// Get the available balances, see [`AvailableBalances`]'s fields for more info.
+ /// Doesn't bother handling the
+ /// if-we-removed-it-already-but-haven't-fully-resolved-they-can-still-send-an-inbound-HTLC
+ /// corner case properly.
+ pub fn get_available_balances(&self) -> AvailableBalances {
+ let context = &self;
+ // Note that we have to handle overflow due to the above case.
+ let inbound_stats = context.get_inbound_pending_htlc_stats(None);
+ let outbound_stats = context.get_outbound_pending_htlc_stats(None);
-#[cfg(not(test))]
-const COMMITMENT_TX_WEIGHT_PER_HTLC: u64 = 172;
-#[cfg(test)]
-pub const COMMITMENT_TX_WEIGHT_PER_HTLC: u64 = 172;
+ let mut balance_msat = context.value_to_self_msat;
+ for ref htlc in context.pending_inbound_htlcs.iter() {
+ if let InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::Fulfill(_)) = htlc.state {
+ balance_msat += htlc.amount_msat;
+ }
+ }
+ balance_msat -= outbound_stats.pending_htlcs_value_msat;
-pub const ANCHOR_OUTPUT_VALUE_SATOSHI: u64 = 330;
+ let outbound_capacity_msat = context.value_to_self_msat
+ .saturating_sub(outbound_stats.pending_htlcs_value_msat)
+ .saturating_sub(
+ context.counterparty_selected_channel_reserve_satoshis.unwrap_or(0) * 1000);
-/// The percentage of the channel value `holder_max_htlc_value_in_flight_msat` used to be set to,
-/// before this was made configurable. The percentage was made configurable in LDK 0.0.107,
-/// although LDK 0.0.104+ enabled serialization of channels with a different value set for
-/// `holder_max_htlc_value_in_flight_msat`.
-pub const MAX_IN_FLIGHT_PERCENT_LEGACY: u8 = 10;
+ let mut available_capacity_msat = outbound_capacity_msat;
-/// Maximum `funding_satoshis` value according to the BOLT #2 specification, if
-/// `option_support_large_channel` (aka wumbo channels) is not supported.
-/// It's 2^24 - 1.
-pub const MAX_FUNDING_SATOSHIS_NO_WUMBO: u64 = (1 << 24) - 1;
+ if 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.
+ //
+ // The fee depends on whether the amount we will be sending is above dust or not,
+ // and the answer will in turn change the amount itself — making it a circular
+ // dependency.
+ // This complicates the computation around dust-values, up to the one-htlc-value.
+ let mut real_dust_limit_timeout_sat = context.holder_dust_limit_satoshis;
+ if !context.opt_anchors() {
+ real_dust_limit_timeout_sat += context.feerate_per_kw as u64 * htlc_timeout_tx_weight(false) / 1000;
+ }
-/// Total bitcoin supply in satoshis.
-pub const TOTAL_BITCOIN_SUPPLY_SATOSHIS: u64 = 21_000_000 * 1_0000_0000;
+ let htlc_above_dust = HTLCCandidate::new(real_dust_limit_timeout_sat * 1000, HTLCInitiator::LocalOffered);
+ let max_reserved_commit_tx_fee_msat = FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE * context.next_local_commit_tx_fee_msat(htlc_above_dust, Some(()));
+ let htlc_dust = HTLCCandidate::new(real_dust_limit_timeout_sat * 1000 - 1, HTLCInitiator::LocalOffered);
+ let min_reserved_commit_tx_fee_msat = FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE * context.next_local_commit_tx_fee_msat(htlc_dust, Some(()));
-/// 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;
+ // We will first subtract the fee as if we were above-dust. Then, if the resulting
+ // value ends up being below dust, we have this fee available again. In that case,
+ // match the value to right-below-dust.
+ let mut capacity_minus_commitment_fee_msat: i64 = (available_capacity_msat as i64) - (max_reserved_commit_tx_fee_msat as i64);
+ if capacity_minus_commitment_fee_msat < (real_dust_limit_timeout_sat as i64) * 1000 {
+ let one_htlc_difference_msat = max_reserved_commit_tx_fee_msat - min_reserved_commit_tx_fee_msat;
+ debug_assert!(one_htlc_difference_msat != 0);
+ capacity_minus_commitment_fee_msat += one_htlc_difference_msat as i64;
+ capacity_minus_commitment_fee_msat = cmp::min(real_dust_limit_timeout_sat as i64 * 1000 - 1, capacity_minus_commitment_fee_msat);
+ available_capacity_msat = cmp::max(0, cmp::min(capacity_minus_commitment_fee_msat, available_capacity_msat as i64)) as u64;
+ } else {
+ available_capacity_msat = capacity_minus_commitment_fee_msat as u64;
+ }
+ } else {
+ // 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 = context.counterparty_dust_limit_satoshis;
+ if !context.opt_anchors() {
+ real_dust_limit_success_sat += context.feerate_per_kw as u64 * htlc_success_tx_weight(false) / 1000;
+ }
-/// 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;
+ let htlc_above_dust = HTLCCandidate::new(real_dust_limit_success_sat * 1000, HTLCInitiator::LocalOffered);
+ let max_reserved_commit_tx_fee_msat = context.next_remote_commit_tx_fee_msat(htlc_above_dust, None);
-/// 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/lightning/bolts/issues/905> for more details.
-pub const MIN_CHAN_DUST_LIMIT_SATOSHIS: u64 = 354;
+ let holder_selected_chan_reserve_msat = context.holder_selected_channel_reserve_satoshis * 1000;
+ let remote_balance_msat = (context.channel_value_satoshis * 1000 - context.value_to_self_msat)
+ .saturating_sub(inbound_stats.pending_htlcs_value_msat);
-// Just a reasonable implementation-specific safe lower bound, higher than the dust limit.
-pub const MIN_THEIR_CHAN_RESERVE_SATOSHIS: u64 = 1000;
+ if remote_balance_msat < max_reserved_commit_tx_fee_msat + holder_selected_chan_reserve_msat {
+ // If another HTLC's fee would reduce the remote's balance below the reserve limit
+ // we've selected for them, we can only send dust HTLCs.
+ available_capacity_msat = cmp::min(available_capacity_msat, real_dust_limit_success_sat * 1000 - 1);
+ }
+ }
-/// 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
-/// channel_id in ChannelManager.
-pub(super) enum ChannelError {
- Ignore(String),
- Warn(String),
- Close(String),
-}
+ let mut next_outbound_htlc_minimum_msat = context.counterparty_htlc_minimum_msat;
-impl fmt::Debug for ChannelError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- &ChannelError::Ignore(ref e) => write!(f, "Ignore : {}", e),
- &ChannelError::Warn(ref e) => write!(f, "Warn : {}", e),
- &ChannelError::Close(ref e) => write!(f, "Close : {}", e),
- }
- }
-}
+ // If we get close to our maximum dust exposure, we end up in a situation where we can send
+ // between zero and the remaining dust exposure limit remaining OR above the dust limit.
+ // Because we cannot express this as a simple min/max, we prefer to tell the user they can
+ // send above the dust limit (as the router can always overpay to meet the dust limit).
+ let mut remaining_msat_below_dust_exposure_limit = None;
+ let mut dust_exposure_dust_limit_msat = 0;
-macro_rules! secp_check {
- ($res: expr, $err: expr) => {
- match $res {
- Ok(thing) => thing,
- Err(_) => return Err(ChannelError::Close($err)),
+ let (htlc_success_dust_limit, htlc_timeout_dust_limit) = if context.opt_anchors() {
+ (context.counterparty_dust_limit_satoshis, context.holder_dust_limit_satoshis)
+ } else {
+ let dust_buffer_feerate = context.get_dust_buffer_feerate(None) as u64;
+ (context.counterparty_dust_limit_satoshis + dust_buffer_feerate * htlc_success_tx_weight(false) / 1000,
+ context.holder_dust_limit_satoshis + dust_buffer_feerate * htlc_timeout_tx_weight(false) / 1000)
+ };
+ let on_counterparty_dust_htlc_exposure_msat = inbound_stats.on_counterparty_tx_dust_exposure_msat + outbound_stats.on_counterparty_tx_dust_exposure_msat;
+ if on_counterparty_dust_htlc_exposure_msat as i64 + htlc_success_dust_limit as i64 * 1000 - 1 > context.get_max_dust_htlc_exposure_msat() as i64 {
+ remaining_msat_below_dust_exposure_limit =
+ Some(context.get_max_dust_htlc_exposure_msat().saturating_sub(on_counterparty_dust_htlc_exposure_msat));
+ dust_exposure_dust_limit_msat = cmp::max(dust_exposure_dust_limit_msat, htlc_success_dust_limit * 1000);
}
- };
-}
-impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
- 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
- // available. If it's private, we first try `scid_privacy` as it provides better privacy
- // with no other changes, and fall back to `only_static_remotekey`.
- let mut ret = ChannelTypeFeatures::only_static_remote_key();
- if !config.channel_handshake_config.announced_channel &&
- config.channel_handshake_config.negotiate_scid_privacy &&
- their_features.supports_scid_privacy() {
- ret.set_scid_privacy_required();
+ let on_holder_dust_htlc_exposure_msat = inbound_stats.on_holder_tx_dust_exposure_msat + outbound_stats.on_holder_tx_dust_exposure_msat;
+ if on_holder_dust_htlc_exposure_msat as i64 + htlc_timeout_dust_limit as i64 * 1000 - 1 > context.get_max_dust_htlc_exposure_msat() as i64 {
+ remaining_msat_below_dust_exposure_limit = Some(cmp::min(
+ remaining_msat_below_dust_exposure_limit.unwrap_or(u64::max_value()),
+ context.get_max_dust_htlc_exposure_msat().saturating_sub(on_holder_dust_htlc_exposure_msat)));
+ dust_exposure_dust_limit_msat = cmp::max(dust_exposure_dust_limit_msat, htlc_timeout_dust_limit * 1000);
}
- // Optionally, if the user would like to negotiate the `anchors_zero_fee_htlc_tx` option, we
- // set it now. If they don't understand it, we'll fall back to our default of
- // `only_static_remotekey`.
- #[cfg(anchors)]
- { // Attributes are not allowed on if expressions on our current MSRV of 1.41.
- if config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx &&
- their_features.supports_anchors_zero_fee_htlc_tx() {
- ret.set_anchors_zero_fee_htlc_tx_required();
+ if let Some(remaining_limit_msat) = remaining_msat_below_dust_exposure_limit {
+ if available_capacity_msat < dust_exposure_dust_limit_msat {
+ available_capacity_msat = cmp::min(available_capacity_msat, remaining_limit_msat);
+ } else {
+ next_outbound_htlc_minimum_msat = cmp::max(next_outbound_htlc_minimum_msat, dust_exposure_dust_limit_msat);
}
}
- ret
- }
+ available_capacity_msat = cmp::min(available_capacity_msat,
+ context.counterparty_max_htlc_value_in_flight_msat - outbound_stats.pending_htlcs_value_msat);
- /// If we receive an error message, it may only be a rejection of the channel type we tried,
- /// 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.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 outbound_stats.pending_htlcs + 1 > context.counterparty_max_accepted_htlcs as u32 {
+ available_capacity_msat = 0;
}
- // We support opening a few different types of channels. Try removing our additional
- // features one by one until we've either arrived at our default or the counterparty has
- // accepted one.
- //
- // Due to the order below, we may not negotiate `option_anchors_zero_fee_htlc_tx` if the
- // counterparty doesn't support `option_scid_privacy`. Since `get_initial_channel_type`
- // checks whether the counterparty supports every feature, this would only happen if the
- // counterparty is advertising the feature, but rejecting channels proposing the feature for
- // whatever reason.
- if self.context.channel_type.supports_anchors_zero_fee_htlc_tx() {
- self.context.channel_type.clear_anchors_zero_fee_htlc_tx();
- assert!(self.context.channel_transaction_parameters.opt_non_zero_fee_anchors.is_none());
- self.context.channel_transaction_parameters.opt_anchors = None;
- } else if self.context.channel_type.supports_scid_privacy() {
- self.context.channel_type.clear_scid_privacy();
- } else {
- self.context.channel_type = ChannelTypeFeatures::only_static_remote_key();
+
+ AvailableBalances {
+ inbound_capacity_msat: cmp::max(context.channel_value_satoshis as i64 * 1000
+ - context.value_to_self_msat as i64
+ - context.get_inbound_pending_htlc_stats(None).pending_htlcs_value_msat as i64
+ - context.holder_selected_channel_reserve_satoshis as i64 * 1000,
+ 0) as u64,
+ outbound_capacity_msat,
+ next_outbound_htlc_limit_msat: available_capacity_msat,
+ next_outbound_htlc_minimum_msat,
+ balance_msat,
}
- Ok(self.get_open_channel(chain_hash))
}
- // Constructors:
- pub fn new_outbound<ES: Deref, SP: Deref, F: Deref>(
- fee_estimator: &LowerBoundedFeeEstimator<F>, entropy_source: &ES, signer_provider: &SP, counterparty_node_id: PublicKey, their_features: &InitFeatures,
- channel_value_satoshis: u64, push_msat: u64, user_id: u128, config: &UserConfig, current_chain_height: u32,
- outbound_scid_alias: u64
- ) -> Result<Channel<Signer>, APIError>
- where ES::Target: EntropySource,
- SP::Target: SignerProvider<Signer = Signer>,
- F::Target: FeeEstimator,
- {
- let holder_selected_contest_delay = config.channel_handshake_config.our_to_self_delay;
- let channel_keys_id = signer_provider.generate_channel_keys_id(false, channel_value_satoshis, user_id);
- let holder_signer = signer_provider.derive_channel_signer(channel_value_satoshis, channel_keys_id);
- let pubkeys = holder_signer.pubkeys().clone();
-
- if !their_features.supports_wumbo() && channel_value_satoshis > MAX_FUNDING_SATOSHIS_NO_WUMBO {
- return Err(APIError::APIMisuseError{err: format!("funding_value must not exceed {}, it was {}", MAX_FUNDING_SATOSHIS_NO_WUMBO, channel_value_satoshis)});
- }
- if channel_value_satoshis >= TOTAL_BITCOIN_SUPPLY_SATOSHIS {
- return Err(APIError::APIMisuseError{err: format!("funding_value must be smaller than the total bitcoin supply, it was {}", channel_value_satoshis)});
- }
- let channel_value_msat = channel_value_satoshis * 1000;
- if push_msat > channel_value_msat {
- return Err(APIError::APIMisuseError { err: format!("Push value ({}) was larger than channel_value ({})", push_msat, channel_value_msat) });
- }
- 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 = 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`
- return Err(APIError::APIMisuseError { err: format!("Holder selected channel reserve below implemention limit dust_limit_satoshis {}", holder_selected_channel_reserve_satoshis) });
- }
+ pub fn get_holder_counterparty_selected_channel_reserve_satoshis(&self) -> (u64, Option<u64>) {
+ let context = &self;
+ (context.holder_selected_channel_reserve_satoshis, context.counterparty_selected_channel_reserve_satoshis)
+ }
- let channel_type = Self::get_initial_channel_type(&config, their_features);
- debug_assert!(channel_type.is_subset(&channelmanager::provided_channel_type_features(&config)));
+ /// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
+ /// number of pending HTLCs that are on track to be in our next commitment tx.
+ ///
+ /// Optionally includes the `HTLCCandidate` given by `htlc` and an additional non-dust HTLC if
+ /// `fee_spike_buffer_htlc` is `Some`.
+ ///
+ /// The first extra HTLC is useful for determining whether we can accept a further HTLC, the
+ /// second allows for creating a buffer to ensure a further HTLC can always be accepted/added.
+ ///
+ /// Dust HTLCs are excluded.
+ fn next_local_commit_tx_fee_msat(&self, htlc: HTLCCandidate, fee_spike_buffer_htlc: Option<()>) -> u64 {
+ let context = &self;
+ assert!(context.is_outbound());
- let feerate = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::Normal);
+ let (htlc_success_dust_limit, htlc_timeout_dust_limit) = if context.opt_anchors() {
+ (0, 0)
+ } else {
+ (context.feerate_per_kw as u64 * htlc_success_tx_weight(false) / 1000,
+ context.feerate_per_kw as u64 * htlc_timeout_tx_weight(false) / 1000)
+ };
+ let real_dust_limit_success_sat = htlc_success_dust_limit + context.holder_dust_limit_satoshis;
+ let real_dust_limit_timeout_sat = htlc_timeout_dust_limit + context.holder_dust_limit_satoshis;
- let value_to_self_msat = channel_value_satoshis * 1000 - push_msat;
- let commitment_tx_fee = Self::commit_tx_fee_msat(feerate, MIN_AFFORDABLE_HTLC_COUNT, channel_type.requires_anchors_zero_fee_htlc_tx());
- if value_to_self_msat < commitment_tx_fee {
- return Err(APIError::APIMisuseError{ err: format!("Funding amount ({}) can't even pay fee for initial commitment transaction fee of {}.", value_to_self_msat / 1000, commitment_tx_fee / 1000) });
+ let mut addl_htlcs = 0;
+ if fee_spike_buffer_htlc.is_some() { addl_htlcs += 1; }
+ match htlc.origin {
+ HTLCInitiator::LocalOffered => {
+ if htlc.amount_msat / 1000 >= real_dust_limit_timeout_sat {
+ addl_htlcs += 1;
+ }
+ },
+ HTLCInitiator::RemoteOffered => {
+ if htlc.amount_msat / 1000 >= real_dust_limit_success_sat {
+ addl_htlcs += 1;
+ }
+ }
}
- let mut secp_ctx = Secp256k1::new();
- secp_ctx.seeded_randomize(&entropy_source.get_secure_random_bytes());
-
- let shutdown_scriptpubkey = if config.channel_handshake_config.commit_upfront_shutdown_pubkey {
- match signer_provider.get_shutdown_scriptpubkey() {
- Ok(scriptpubkey) => Some(scriptpubkey),
- Err(_) => return Err(APIError::ChannelUnavailable { err: "Failed to get shutdown scriptpubkey".to_owned()}),
+ let mut included_htlcs = 0;
+ for ref htlc in context.pending_inbound_htlcs.iter() {
+ if htlc.amount_msat / 1000 < real_dust_limit_success_sat {
+ continue
}
- } else { None };
+ // We include LocalRemoved HTLCs here because we may still need to broadcast a commitment
+ // transaction including this HTLC if it times out before they RAA.
+ included_htlcs += 1;
+ }
- if let Some(shutdown_scriptpubkey) = &shutdown_scriptpubkey {
- if !shutdown_scriptpubkey.is_compatible(&their_features) {
- return Err(APIError::IncompatibleShutdownScript { script: shutdown_scriptpubkey.clone() });
+ for ref htlc in context.pending_outbound_htlcs.iter() {
+ if htlc.amount_msat / 1000 < real_dust_limit_timeout_sat {
+ continue
+ }
+ match htlc.state {
+ OutboundHTLCState::LocalAnnounced {..} => included_htlcs += 1,
+ OutboundHTLCState::Committed => included_htlcs += 1,
+ OutboundHTLCState::RemoteRemoved {..} => included_htlcs += 1,
+ // We don't include AwaitingRemoteRevokeToRemove HTLCs because our next commitment
+ // transaction won't be generated until they send us their next RAA, which will mean
+ // dropping any HTLCs in this state.
+ _ => {},
}
}
- let destination_script = match signer_provider.get_destination_script() {
- Ok(script) => script,
- Err(_) => return Err(APIError::ChannelUnavailable { err: "Failed to get destination script".to_owned()}),
- };
-
- let temporary_channel_id = entropy_source.get_secure_random_bytes();
+ for htlc in context.holding_cell_htlc_updates.iter() {
+ match htlc {
+ &HTLCUpdateAwaitingACK::AddHTLC { amount_msat, .. } => {
+ if amount_msat / 1000 < real_dust_limit_timeout_sat {
+ continue
+ }
+ included_htlcs += 1
+ },
+ _ => {}, // Don't include claims/fails that are awaiting ack, because once we get the
+ // ack we're guaranteed to never include them in commitment txs anymore.
+ }
+ }
- Ok(Channel {
- context: ChannelContext {
- user_id,
-
- config: LegacyChannelConfig {
- options: config.channel_config.clone(),
- announced_channel: config.channel_handshake_config.announced_channel,
- commit_upfront_shutdown_pubkey: config.channel_handshake_config.commit_upfront_shutdown_pubkey,
+ let num_htlcs = included_htlcs + addl_htlcs;
+ let res = commit_tx_fee_msat(context.feerate_per_kw, num_htlcs, context.opt_anchors());
+ #[cfg(any(test, fuzzing))]
+ {
+ let mut fee = res;
+ if fee_spike_buffer_htlc.is_some() {
+ fee = commit_tx_fee_msat(context.feerate_per_kw, num_htlcs - 1, context.opt_anchors());
+ }
+ let total_pending_htlcs = context.pending_inbound_htlcs.len() + context.pending_outbound_htlcs.len()
+ + context.holding_cell_htlc_updates.len();
+ let commitment_tx_info = CommitmentTxInfoCached {
+ fee,
+ total_pending_htlcs,
+ next_holder_htlc_id: match htlc.origin {
+ HTLCInitiator::LocalOffered => context.next_holder_htlc_id + 1,
+ HTLCInitiator::RemoteOffered => context.next_holder_htlc_id,
},
+ next_counterparty_htlc_id: match htlc.origin {
+ HTLCInitiator::LocalOffered => context.next_counterparty_htlc_id,
+ HTLCInitiator::RemoteOffered => context.next_counterparty_htlc_id + 1,
+ },
+ feerate: context.feerate_per_kw,
+ };
+ *context.next_local_commitment_tx_fee_info_cached.lock().unwrap() = Some(commitment_tx_info);
+ }
+ res
+ }
- prev_config: None,
-
- inbound_handshake_limits_override: Some(config.channel_handshake_limits.clone()),
-
- channel_id: temporary_channel_id,
- temporary_channel_id: Some(temporary_channel_id),
- channel_state: ChannelState::OurInitSent as u32,
- announcement_sigs_state: AnnouncementSigsState::NotSent,
- secp_ctx,
- channel_value_satoshis,
-
- latest_monitor_update_id: 0,
-
- holder_signer,
- shutdown_scriptpubkey,
- destination_script,
-
- cur_holder_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER,
- cur_counterparty_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER,
- value_to_self_msat,
-
- pending_inbound_htlcs: Vec::new(),
- pending_outbound_htlcs: Vec::new(),
- holding_cell_htlc_updates: Vec::new(),
- pending_update_fee: None,
- holding_cell_update_fee: None,
- next_holder_htlc_id: 0,
- next_counterparty_htlc_id: 0,
- update_time_counter: 1,
-
- resend_order: RAACommitmentOrder::CommitmentFirst,
-
- monitor_pending_channel_ready: false,
- monitor_pending_revoke_and_ack: false,
- monitor_pending_commitment_signed: false,
- monitor_pending_forwards: Vec::new(),
- monitor_pending_failures: Vec::new(),
- monitor_pending_finalized_fulfills: Vec::new(),
-
- #[cfg(debug_assertions)]
- holder_max_commitment_tx_output: Mutex::new((channel_value_satoshis * 1000 - push_msat, push_msat)),
- #[cfg(debug_assertions)]
- counterparty_max_commitment_tx_output: Mutex::new((channel_value_satoshis * 1000 - push_msat, push_msat)),
-
- last_sent_closing_fee: None,
- pending_counterparty_closing_signed: None,
- closing_fee_limits: None,
- target_closing_feerate_sats_per_kw: None,
+ /// Get the commitment tx fee for the remote's next commitment transaction based on the number of
+ /// pending HTLCs that are on track to be in their next commitment tx
+ ///
+ /// Optionally includes the `HTLCCandidate` given by `htlc` and an additional non-dust HTLC if
+ /// `fee_spike_buffer_htlc` is `Some`.
+ ///
+ /// The first extra HTLC is useful for determining whether we can accept a further HTLC, the
+ /// second allows for creating a buffer to ensure a further HTLC can always be accepted/added.
+ ///
+ /// Dust HTLCs are excluded.
+ fn next_remote_commit_tx_fee_msat(&self, htlc: HTLCCandidate, fee_spike_buffer_htlc: Option<()>) -> u64 {
+ let context = &self;
+ assert!(!context.is_outbound());
- inbound_awaiting_accept: false,
+ let (htlc_success_dust_limit, htlc_timeout_dust_limit) = if context.opt_anchors() {
+ (0, 0)
+ } else {
+ (context.feerate_per_kw as u64 * htlc_success_tx_weight(false) / 1000,
+ context.feerate_per_kw as u64 * htlc_timeout_tx_weight(false) / 1000)
+ };
+ let real_dust_limit_success_sat = htlc_success_dust_limit + context.counterparty_dust_limit_satoshis;
+ let real_dust_limit_timeout_sat = htlc_timeout_dust_limit + context.counterparty_dust_limit_satoshis;
- funding_tx_confirmed_in: None,
- funding_tx_confirmation_height: 0,
- short_channel_id: None,
- channel_creation_height: current_chain_height,
+ let mut addl_htlcs = 0;
+ if fee_spike_buffer_htlc.is_some() { addl_htlcs += 1; }
+ match htlc.origin {
+ HTLCInitiator::LocalOffered => {
+ if htlc.amount_msat / 1000 >= real_dust_limit_success_sat {
+ addl_htlcs += 1;
+ }
+ },
+ HTLCInitiator::RemoteOffered => {
+ if htlc.amount_msat / 1000 >= real_dust_limit_timeout_sat {
+ addl_htlcs += 1;
+ }
+ }
+ }
- feerate_per_kw: feerate,
- 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: 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,
- holder_htlc_minimum_msat: if config.channel_handshake_config.our_htlc_minimum_msat == 0 { 1 } else { config.channel_handshake_config.our_htlc_minimum_msat },
- counterparty_max_accepted_htlcs: 0,
- holder_max_accepted_htlcs: cmp::min(config.channel_handshake_config.our_max_accepted_htlcs, MAX_HTLCS),
- minimum_depth: None, // Filled in in accept_channel
+ // When calculating the set of HTLCs which will be included in their next commitment_signed, all
+ // non-dust inbound HTLCs are included (as all states imply it will be included) and only
+ // committed outbound HTLCs, see below.
+ let mut included_htlcs = 0;
+ for ref htlc in context.pending_inbound_htlcs.iter() {
+ if htlc.amount_msat / 1000 <= real_dust_limit_timeout_sat {
+ continue
+ }
+ included_htlcs += 1;
+ }
- counterparty_forwarding_info: None,
+ for ref htlc in context.pending_outbound_htlcs.iter() {
+ if htlc.amount_msat / 1000 <= real_dust_limit_success_sat {
+ continue
+ }
+ // We only include outbound HTLCs if it will not be included in their next commitment_signed,
+ // i.e. if they've responded to us with an RAA after announcement.
+ match htlc.state {
+ OutboundHTLCState::Committed => included_htlcs += 1,
+ OutboundHTLCState::RemoteRemoved {..} => included_htlcs += 1,
+ OutboundHTLCState::LocalAnnounced { .. } => included_htlcs += 1,
+ _ => {},
+ }
+ }
- channel_transaction_parameters: ChannelTransactionParameters {
- holder_pubkeys: pubkeys,
- holder_selected_contest_delay: config.channel_handshake_config.our_to_self_delay,
- is_outbound_from_holder: true,
- counterparty_parameters: None,
- funding_outpoint: None,
- opt_anchors: if channel_type.requires_anchors_zero_fee_htlc_tx() { Some(()) } else { None },
- opt_non_zero_fee_anchors: None
+ let num_htlcs = included_htlcs + addl_htlcs;
+ let res = commit_tx_fee_msat(context.feerate_per_kw, num_htlcs, context.opt_anchors());
+ #[cfg(any(test, fuzzing))]
+ {
+ let mut fee = res;
+ if fee_spike_buffer_htlc.is_some() {
+ fee = commit_tx_fee_msat(context.feerate_per_kw, num_htlcs - 1, context.opt_anchors());
+ }
+ let total_pending_htlcs = context.pending_inbound_htlcs.len() + context.pending_outbound_htlcs.len();
+ let commitment_tx_info = CommitmentTxInfoCached {
+ fee,
+ total_pending_htlcs,
+ next_holder_htlc_id: match htlc.origin {
+ HTLCInitiator::LocalOffered => context.next_holder_htlc_id + 1,
+ HTLCInitiator::RemoteOffered => context.next_holder_htlc_id,
},
- funding_transaction: None,
-
- counterparty_cur_commitment_point: None,
- counterparty_prev_commitment_point: None,
- counterparty_node_id,
-
- counterparty_shutdown_scriptpubkey: None,
+ next_counterparty_htlc_id: match htlc.origin {
+ HTLCInitiator::LocalOffered => context.next_counterparty_htlc_id,
+ HTLCInitiator::RemoteOffered => context.next_counterparty_htlc_id + 1,
+ },
+ feerate: context.feerate_per_kw,
+ };
+ *context.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = Some(commitment_tx_info);
+ }
+ res
+ }
- commitment_secrets: CounterpartyCommitmentSecrets::new(),
+ /// Returns transaction if there is pending funding transaction that is yet to broadcast
+ pub fn unbroadcasted_funding(&self) -> Option<Transaction> {
+ if self.channel_state & (ChannelState::FundingCreated as u32) != 0 {
+ self.funding_transaction.clone()
+ } else {
+ None
+ }
+ }
+}
- channel_update_status: ChannelUpdateStatus::Enabled,
- closing_signed_in_flight: false,
+// Internal utility functions for channels
- announcement_sigs: None,
+/// 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
+}
- #[cfg(any(test, fuzzing))]
- next_local_commitment_tx_fee_info_cached: Mutex::new(None),
- #[cfg(any(test, fuzzing))]
- next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
+/// 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))
+}
- workaround_lnd_bug_4006: None,
- sent_message_awaiting_response: None,
+/// 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))
+}
- latest_inbound_scid_alias: None,
- outbound_scid_alias,
+// Get the fee cost in SATS of a commitment tx with a given number of HTLC outputs.
+// Note that num_htlcs should not include dust HTLCs.
+#[inline]
+fn commit_tx_fee_sat(feerate_per_kw: u32, num_htlcs: usize, opt_anchors: bool) -> u64 {
+ feerate_per_kw as u64 * (commitment_tx_base_weight(opt_anchors) + num_htlcs as u64 * COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000
+}
- channel_pending_event_emitted: false,
- channel_ready_event_emitted: false,
+// Get the fee cost in MSATS of a commitment tx with a given number of HTLC outputs.
+// Note that num_htlcs should not include dust HTLCs.
+fn commit_tx_fee_msat(feerate_per_kw: u32, num_htlcs: usize, opt_anchors: bool) -> u64 {
+ // Note that we need to divide before multiplying to round properly,
+ // since the lowest denomination of bitcoin on-chain is the satoshi.
+ (commitment_tx_base_weight(opt_anchors) + num_htlcs as u64 * COMMITMENT_TX_WEIGHT_PER_HTLC) * feerate_per_kw as u64 / 1000 * 1000
+}
- #[cfg(any(test, fuzzing))]
- historical_inbound_htlc_fulfills: HashSet::new(),
+// 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
+// inbound channel.
+//
+// Holder designates channel data owned for the benefit of the user client.
+// Counterparty designates channel data owned by the another channel participant entity.
+pub(super) struct Channel<Signer: ChannelSigner> {
+ pub context: ChannelContext<Signer>,
+}
- channel_type,
- channel_keys_id,
+#[cfg(any(test, fuzzing))]
+struct CommitmentTxInfoCached {
+ fee: u64,
+ total_pending_htlcs: usize,
+ next_holder_htlc_id: u64,
+ next_counterparty_htlc_id: u64,
+ feerate: u32,
+}
- pending_monitor_updates: Vec::new(),
- }
- })
+impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
+ /// If we receive an error message, it may only be a rejection of the channel type we tried,
+ /// 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.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(());
+ }
+ // We support opening a few different types of channels. Try removing our additional
+ // features one by one until we've either arrived at our default or the counterparty has
+ // accepted one.
+ //
+ // Due to the order below, we may not negotiate `option_anchors_zero_fee_htlc_tx` if the
+ // counterparty doesn't support `option_scid_privacy`. Since `get_initial_channel_type`
+ // checks whether the counterparty supports every feature, this would only happen if the
+ // counterparty is advertising the feature, but rejecting channels proposing the feature for
+ // whatever reason.
+ if self.context.channel_type.supports_anchors_zero_fee_htlc_tx() {
+ self.context.channel_type.clear_anchors_zero_fee_htlc_tx();
+ assert!(self.context.channel_transaction_parameters.opt_non_zero_fee_anchors.is_none());
+ self.context.channel_transaction_parameters.opt_anchors = None;
+ } else if self.context.channel_type.supports_scid_privacy() {
+ self.context.channel_type.clear_scid_privacy();
+ } else {
+ self.context.channel_type = ChannelTypeFeatures::only_static_remote_key();
+ }
+ Ok(self.get_open_channel(chain_hash))
}
+ // Constructors:
+
fn check_remote_fee<F: Deref, L: Deref>(fee_estimator: &LowerBoundedFeeEstimator<F>,
feerate_per_kw: u32, cur_feerate_per_kw: Option<u32>, logger: &L)
-> Result<(), ChannelError> where F::Target: FeeEstimator, L::Target: Logger,
// check if the funder's amount for the initial commitment tx is sufficient
// for full fee payment plus a few HTLCs to ensure the channel will be useful.
let funders_amount_msat = msg.funding_satoshis * 1000 - msg.push_msat;
- let commitment_tx_fee = Self::commit_tx_fee_msat(msg.feerate_per_kw, MIN_AFFORDABLE_HTLC_COUNT, opt_anchors) / 1000;
+ let commitment_tx_fee = commit_tx_fee_msat(msg.feerate_per_kw, MIN_AFFORDABLE_HTLC_COUNT, opt_anchors) / 1000;
if funders_amount_msat / 1000 < commitment_tx_fee {
return Err(ChannelError::Close(format!("Funding amount ({} sats) can't even pay fee for initial commitment transaction fee of {} sats.", funders_amount_msat / 1000, commitment_tx_fee)));
}
self.context.channel_transaction_parameters.funding_outpoint = None;
return Err(ChannelError::Close(e));
},
- Err(e) => {
- // The only error we know how to handle is ChannelError::Close, so we fall over here
- // to make sure we don't continue with an inconsistent state.
- panic!("unexpected error type from funding_created_signature {:?}", e);
- }
- };
-
- let holder_commitment_tx = HolderCommitmentTransaction::new(
- initial_commitment_tx,
- msg.signature,
- Vec::new(),
- &self.context.get_holder_pubkeys().funding_pubkey,
- self.context.counterparty_funding_pubkey()
- );
-
- self.context.holder_signer.validate_holder_commitment(&holder_commitment_tx, Vec::new())
- .map_err(|_| ChannelError::Close("Failed to validate our commitment".to_owned()))?;
-
- // Now that we're past error-generating stuff, update our local state:
-
- let funding_redeemscript = self.context.get_funding_redeemscript();
- let funding_txo_script = funding_redeemscript.to_v0_p2wsh();
- let obscure_factor = get_commitment_transaction_number_obscure_factor(&self.context.get_holder_pubkeys().payment_point, &self.context.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);
- let channel_monitor = ChannelMonitor::new(self.context.secp_ctx.clone(), monitor_signer,
- shutdown_script, self.context.get_holder_selected_contest_delay(),
- &self.context.destination_script, (funding_txo, funding_txo_script.clone()),
- &self.context.channel_transaction_parameters,
- funding_redeemscript.clone(), self.context.channel_value_satoshis,
- obscure_factor,
- holder_commitment_tx, best_block, self.context.counterparty_node_id);
-
- channel_monitor.provide_latest_counterparty_commitment_tx(counterparty_initial_commitment_txid, Vec::new(), self.context.cur_counterparty_commitment_transaction_number, self.context.counterparty_cur_commitment_point.unwrap(), logger);
-
- self.context.channel_state = ChannelState::FundingSent as u32;
- self.context.channel_id = funding_txo.to_channel_id();
- self.context.cur_counterparty_commitment_transaction_number -= 1;
- self.context.cur_holder_commitment_transaction_number -= 1;
-
- log_info!(logger, "Generated funding_signed for peer for channel {}", log_bytes!(self.context.channel_id()));
-
- let need_channel_ready = self.check_get_channel_ready(0).is_some();
- self.monitor_updating_paused(false, false, need_channel_ready, Vec::new(), Vec::new(), Vec::new());
-
- Ok((msgs::FundingSigned {
- channel_id: self.context.channel_id,
- signature,
- #[cfg(taproot)]
- partial_signature_with_nonce: None,
- }, channel_monitor))
- }
-
- /// Handles a funding_signed message from the remote end.
- /// If this call is successful, broadcast the funding transaction (and not before!)
- pub fn funding_signed<SP: Deref, L: Deref>(
- &mut self, msg: &msgs::FundingSigned, best_block: BestBlock, signer_provider: &SP, logger: &L
- ) -> Result<ChannelMonitor<Signer>, ChannelError>
- where
- SP::Target: SignerProvider<Signer = Signer>,
- L::Target: Logger
- {
- 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 {
- return Err(ChannelError::Close("Received funding_signed in strange state!".to_owned()));
- }
- if self.context.commitment_secrets.get_min_seen_secret() != (1 << 48) ||
- self.context.cur_counterparty_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER ||
- self.context.cur_holder_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER {
- panic!("Should not have advanced channel commitment tx numbers prior to funding_created");
- }
-
- let funding_script = self.context.get_funding_redeemscript();
-
- let counterparty_keys = self.context.build_remote_transaction_keys();
- let counterparty_initial_commitment_tx = self.context.build_commitment_transaction(self.context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx;
- let counterparty_trusted_tx = counterparty_initial_commitment_tx.trust();
- let counterparty_initial_bitcoin_tx = counterparty_trusted_tx.built_transaction();
-
- log_trace!(logger, "Initial counterparty tx for channel {} is: txid {} tx {}",
- log_bytes!(self.context.channel_id()), counterparty_initial_bitcoin_tx.txid, encode::serialize_hex(&counterparty_initial_bitcoin_tx.transaction));
-
- let holder_signer = self.context.build_holder_transaction_keys(self.context.cur_holder_commitment_transaction_number);
- let initial_commitment_tx = self.context.build_commitment_transaction(self.context.cur_holder_commitment_transaction_number, &holder_signer, true, false, logger).tx;
- {
- let trusted_tx = initial_commitment_tx.trust();
- let initial_commitment_bitcoin_tx = trusted_tx.built_transaction();
- let sighash = initial_commitment_bitcoin_tx.get_sighash_all(&funding_script, self.context.channel_value_satoshis);
- // They sign our commitment transaction, allowing us to broadcast the tx if we wish.
- if let Err(_) = self.context.secp_ctx.verify_ecdsa(&sighash, &msg.signature, &self.context.get_counterparty_pubkeys().funding_pubkey) {
- return Err(ChannelError::Close("Invalid funding_signed signature from peer".to_owned()));
+ Err(e) => {
+ // The only error we know how to handle is ChannelError::Close, so we fall over here
+ // to make sure we don't continue with an inconsistent state.
+ panic!("unexpected error type from funding_created_signature {:?}", e);
}
- }
+ };
let holder_commitment_tx = HolderCommitmentTransaction::new(
initial_commitment_tx,
self.context.holder_signer.validate_holder_commitment(&holder_commitment_tx, Vec::new())
.map_err(|_| ChannelError::Close("Failed to validate our commitment".to_owned()))?;
+ // Now that we're past error-generating stuff, update our local state:
let funding_redeemscript = self.context.get_funding_redeemscript();
- let funding_txo = self.context.get_funding_txo().unwrap();
let funding_txo_script = funding_redeemscript.to_v0_p2wsh();
let obscure_factor = get_commitment_transaction_number_obscure_factor(&self.context.get_holder_pubkeys().payment_point, &self.context.get_counterparty_pubkeys().payment_point, self.context.is_outbound());
let shutdown_script = self.context.shutdown_scriptpubkey.clone().map(|script| script.into_inner());
monitor_signer.provide_channel_parameters(&self.context.channel_transaction_parameters);
let channel_monitor = ChannelMonitor::new(self.context.secp_ctx.clone(), monitor_signer,
shutdown_script, self.context.get_holder_selected_contest_delay(),
- &self.context.destination_script, (funding_txo, funding_txo_script),
+ &self.context.destination_script, (funding_txo, funding_txo_script.clone()),
&self.context.channel_transaction_parameters,
funding_redeemscript.clone(), self.context.channel_value_satoshis,
obscure_factor,
holder_commitment_tx, best_block, self.context.counterparty_node_id);
- channel_monitor.provide_latest_counterparty_commitment_tx(counterparty_initial_bitcoin_tx.txid, Vec::new(), self.context.cur_counterparty_commitment_transaction_number, self.context.counterparty_cur_commitment_point.unwrap(), logger);
+ channel_monitor.provide_latest_counterparty_commitment_tx(counterparty_initial_commitment_txid, Vec::new(), self.context.cur_counterparty_commitment_transaction_number, self.context.counterparty_cur_commitment_point.unwrap(), logger);
- assert_eq!(self.context.channel_state & (ChannelState::MonitorUpdateInProgress as u32), 0); // We have no had any monitor(s) yet to fail update!
self.context.channel_state = ChannelState::FundingSent as u32;
- self.context.cur_holder_commitment_transaction_number -= 1;
+ self.context.channel_id = funding_txo.to_channel_id();
self.context.cur_counterparty_commitment_transaction_number -= 1;
+ self.context.cur_holder_commitment_transaction_number -= 1;
- log_info!(logger, "Received funding_signed from peer for channel {}", log_bytes!(self.context.channel_id()));
+ log_info!(logger, "Generated funding_signed for peer for channel {}", log_bytes!(self.context.channel_id()));
let need_channel_ready = self.check_get_channel_ready(0).is_some();
self.monitor_updating_paused(false, false, need_channel_ready, Vec::new(), Vec::new(), Vec::new());
- Ok(channel_monitor)
+
+ Ok((msgs::FundingSigned {
+ channel_id: self.context.channel_id,
+ signature,
+ #[cfg(taproot)]
+ partial_signature_with_nonce: None,
+ }, channel_monitor))
}
- /// Handles a channel_ready message from our peer. If we've already sent our channel_ready
- /// and the channel is now usable (and public), this may generate an announcement_signatures to
- /// reply with.
- pub fn channel_ready<NS: Deref, L: Deref>(
- &mut self, msg: &msgs::ChannelReady, node_signer: &NS, genesis_block_hash: BlockHash,
- user_config: &UserConfig, best_block: &BestBlock, logger: &L
- ) -> Result<Option<msgs::AnnouncementSignatures>, ChannelError>
+ /// Handles a funding_signed message from the remote end.
+ /// If this call is successful, broadcast the funding transaction (and not before!)
+ pub fn funding_signed<SP: Deref, L: Deref>(
+ &mut self, msg: &msgs::FundingSigned, best_block: BestBlock, signer_provider: &SP, logger: &L
+ ) -> Result<ChannelMonitor<Signer>, ChannelError>
where
- NS::Target: NodeSigner,
+ SP::Target: SignerProvider<Signer = Signer>,
L::Target: Logger
{
- if self.context.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 {
- self.context.workaround_lnd_bug_4006 = Some(msg.clone());
- return Err(ChannelError::Ignore("Peer sent channel_ready when we needed a channel_reestablish. The peer is likely lnd, see https://github.com/lightningnetwork/lnd/issues/4006".to_owned()));
- }
-
- if let Some(scid_alias) = msg.short_channel_id_alias {
- if Some(scid_alias) != self.context.short_channel_id {
- // The scid alias provided can be used to route payments *from* our counterparty,
- // i.e. can be used for inbound payments and provided in invoices, but is not used
- // when routing outbound payments.
- self.context.latest_inbound_scid_alias = Some(scid_alias);
- }
- }
-
- let non_shutdown_state = self.context.channel_state & (!MULTI_STATE_FLAGS);
-
- if non_shutdown_state == ChannelState::FundingSent as u32 {
- self.context.channel_state |= ChannelState::TheirChannelReady as u32;
- } else if non_shutdown_state == (ChannelState::FundingSent as u32 | ChannelState::OurChannelReady as u32) {
- self.context.channel_state = ChannelState::ChannelReady as u32 | (self.context.channel_state & MULTI_STATE_FLAGS);
- self.context.update_time_counter += 1;
- } else if self.context.channel_state & (ChannelState::ChannelReady as u32) != 0 ||
- // If we reconnected before sending our `channel_ready` they may still resend theirs:
- (self.context.channel_state & (ChannelState::FundingSent as u32 | ChannelState::TheirChannelReady as u32) ==
- (ChannelState::FundingSent as u32 | ChannelState::TheirChannelReady as u32))
- {
- // They probably disconnected/reconnected and re-sent the channel_ready, which is
- // required, or they're sending a fresh SCID alias.
- let expected_point =
- if self.context.cur_counterparty_commitment_transaction_number == INITIAL_COMMITMENT_NUMBER - 1 {
- // If they haven't ever sent an updated point, the point they send should match
- // the current one.
- self.context.counterparty_cur_commitment_point
- } else if self.context.cur_counterparty_commitment_transaction_number == INITIAL_COMMITMENT_NUMBER - 2 {
- // If we've advanced the commitment number once, the second commitment point is
- // at `counterparty_prev_commitment_point`, which is not yet revoked.
- debug_assert!(self.context.counterparty_prev_commitment_point.is_some());
- self.context.counterparty_prev_commitment_point
- } else {
- // If they have sent updated points, channel_ready is always supposed to match
- // their "first" point, which we re-derive here.
- Some(PublicKey::from_secret_key(&self.context.secp_ctx, &SecretKey::from_slice(
- &self.context.commitment_secrets.get_secret(INITIAL_COMMITMENT_NUMBER - 1).expect("We should have all prev secrets available")
- ).expect("We already advanced, so previous secret keys should have been validated already")))
- };
- if expected_point != Some(msg.next_per_commitment_point) {
- return Err(ChannelError::Close("Peer sent a reconnect channel_ready with a different point".to_owned()));
- }
- return Ok(None);
- } else {
- return Err(ChannelError::Close("Peer sent a channel_ready at a strange time".to_owned()));
- }
-
- self.context.counterparty_prev_commitment_point = self.context.counterparty_cur_commitment_point;
- self.context.counterparty_cur_commitment_point = Some(msg.next_per_commitment_point);
-
- log_info!(logger, "Received channel_ready from peer for channel {}", log_bytes!(self.context.channel_id()));
-
- Ok(self.get_announcement_sigs(node_signer, genesis_block_hash, user_config, best_block.height(), logger))
- }
-
- /// Returns transaction if there is pending funding transaction that is yet to broadcast
- pub fn unbroadcasted_funding(&self) -> Option<Transaction> {
- if self.context.channel_state & (ChannelState::FundingCreated as u32) != 0 {
- self.context.funding_transaction.clone()
- } else {
- None
- }
- }
-
- /// Returns a HTLCStats about inbound pending htlcs
- fn get_inbound_pending_htlc_stats(&self, outbound_feerate_update: Option<u32>) -> HTLCStats {
- let mut stats = HTLCStats {
- pending_htlcs: self.context.pending_inbound_htlcs.len() as u32,
- pending_htlcs_value_msat: 0,
- on_counterparty_tx_dust_exposure_msat: 0,
- on_holder_tx_dust_exposure_msat: 0,
- holding_cell_msat: 0,
- on_holder_tx_holding_cell_htlcs_count: 0,
- };
-
- let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if self.context.opt_anchors() {
- (0, 0)
- } else {
- let dust_buffer_feerate = self.context.get_dust_buffer_feerate(outbound_feerate_update) as u64;
- (dust_buffer_feerate * htlc_timeout_tx_weight(false) / 1000,
- dust_buffer_feerate * htlc_success_tx_weight(false) / 1000)
- };
- let counterparty_dust_limit_timeout_sat = htlc_timeout_dust_limit + self.context.counterparty_dust_limit_satoshis;
- let holder_dust_limit_success_sat = htlc_success_dust_limit + self.context.holder_dust_limit_satoshis;
- for ref htlc in self.context.pending_inbound_htlcs.iter() {
- stats.pending_htlcs_value_msat += htlc.amount_msat;
- if htlc.amount_msat / 1000 < counterparty_dust_limit_timeout_sat {
- stats.on_counterparty_tx_dust_exposure_msat += htlc.amount_msat;
- }
- if htlc.amount_msat / 1000 < holder_dust_limit_success_sat {
- stats.on_holder_tx_dust_exposure_msat += htlc.amount_msat;
- }
- }
- stats
- }
-
- /// Returns a HTLCStats about pending outbound htlcs, *including* pending adds in our holding cell.
- fn get_outbound_pending_htlc_stats(&self, outbound_feerate_update: Option<u32>) -> HTLCStats {
- let mut stats = HTLCStats {
- pending_htlcs: self.context.pending_outbound_htlcs.len() as u32,
- pending_htlcs_value_msat: 0,
- on_counterparty_tx_dust_exposure_msat: 0,
- on_holder_tx_dust_exposure_msat: 0,
- holding_cell_msat: 0,
- on_holder_tx_holding_cell_htlcs_count: 0,
- };
-
- let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if self.context.opt_anchors() {
- (0, 0)
- } else {
- let dust_buffer_feerate = self.context.get_dust_buffer_feerate(outbound_feerate_update) as u64;
- (dust_buffer_feerate * htlc_timeout_tx_weight(false) / 1000,
- dust_buffer_feerate * htlc_success_tx_weight(false) / 1000)
- };
- let counterparty_dust_limit_success_sat = htlc_success_dust_limit + self.context.counterparty_dust_limit_satoshis;
- let holder_dust_limit_timeout_sat = htlc_timeout_dust_limit + self.context.holder_dust_limit_satoshis;
- for ref htlc in self.context.pending_outbound_htlcs.iter() {
- stats.pending_htlcs_value_msat += htlc.amount_msat;
- if htlc.amount_msat / 1000 < counterparty_dust_limit_success_sat {
- stats.on_counterparty_tx_dust_exposure_msat += htlc.amount_msat;
- }
- if htlc.amount_msat / 1000 < holder_dust_limit_timeout_sat {
- stats.on_holder_tx_dust_exposure_msat += htlc.amount_msat;
- }
- }
-
- for update in self.context.holding_cell_htlc_updates.iter() {
- if let &HTLCUpdateAwaitingACK::AddHTLC { ref amount_msat, .. } = update {
- stats.pending_htlcs += 1;
- stats.pending_htlcs_value_msat += amount_msat;
- stats.holding_cell_msat += amount_msat;
- if *amount_msat / 1000 < counterparty_dust_limit_success_sat {
- stats.on_counterparty_tx_dust_exposure_msat += amount_msat;
- }
- if *amount_msat / 1000 < holder_dust_limit_timeout_sat {
- stats.on_holder_tx_dust_exposure_msat += amount_msat;
- } else {
- stats.on_holder_tx_holding_cell_htlcs_count += 1;
- }
- }
- }
- stats
- }
-
- /// Get the available balances, see [`AvailableBalances`]'s fields for more info.
- /// Doesn't bother handling the
- /// if-we-removed-it-already-but-haven't-fully-resolved-they-can-still-send-an-inbound-HTLC
- /// corner case properly.
- pub fn get_available_balances(&self) -> AvailableBalances {
- // Note that we have to handle overflow due to the above case.
- let inbound_stats = self.get_inbound_pending_htlc_stats(None);
- let outbound_stats = self.get_outbound_pending_htlc_stats(None);
-
- let mut balance_msat = self.context.value_to_self_msat;
- for ref htlc in self.context.pending_inbound_htlcs.iter() {
- if let InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::Fulfill(_)) = htlc.state {
- balance_msat += htlc.amount_msat;
- }
- }
- balance_msat -= outbound_stats.pending_htlcs_value_msat;
-
- let outbound_capacity_msat = self.context.value_to_self_msat
- .saturating_sub(outbound_stats.pending_htlcs_value_msat)
- .saturating_sub(
- self.context.counterparty_selected_channel_reserve_satoshis.unwrap_or(0) * 1000);
-
- let mut available_capacity_msat = outbound_capacity_msat;
-
- 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.
- //
- // The fee depends on whether the amount we will be sending is above dust or not,
- // and the answer will in turn change the amount itself — making it a circular
- // 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.context.opt_anchors() {
- real_dust_limit_timeout_sat += self.context.feerate_per_kw as u64 * htlc_timeout_tx_weight(false) / 1000;
- }
-
- let htlc_above_dust = HTLCCandidate::new(real_dust_limit_timeout_sat * 1000, HTLCInitiator::LocalOffered);
- let max_reserved_commit_tx_fee_msat = FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE * self.next_local_commit_tx_fee_msat(htlc_above_dust, Some(()));
- let htlc_dust = HTLCCandidate::new(real_dust_limit_timeout_sat * 1000 - 1, HTLCInitiator::LocalOffered);
- let min_reserved_commit_tx_fee_msat = FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE * self.next_local_commit_tx_fee_msat(htlc_dust, Some(()));
-
- // We will first subtract the fee as if we were above-dust. Then, if the resulting
- // value ends up being below dust, we have this fee available again. In that case,
- // match the value to right-below-dust.
- let mut capacity_minus_commitment_fee_msat: i64 = (available_capacity_msat as i64) - (max_reserved_commit_tx_fee_msat as i64);
- if capacity_minus_commitment_fee_msat < (real_dust_limit_timeout_sat as i64) * 1000 {
- let one_htlc_difference_msat = max_reserved_commit_tx_fee_msat - min_reserved_commit_tx_fee_msat;
- debug_assert!(one_htlc_difference_msat != 0);
- capacity_minus_commitment_fee_msat += one_htlc_difference_msat as i64;
- capacity_minus_commitment_fee_msat = cmp::min(real_dust_limit_timeout_sat as i64 * 1000 - 1, capacity_minus_commitment_fee_msat);
- available_capacity_msat = cmp::max(0, cmp::min(capacity_minus_commitment_fee_msat, available_capacity_msat as i64)) as u64;
- } else {
- available_capacity_msat = capacity_minus_commitment_fee_msat as u64;
- }
- } else {
- // 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.context.opt_anchors() {
- real_dust_limit_success_sat += self.context.feerate_per_kw as u64 * htlc_success_tx_weight(false) / 1000;
- }
-
- let htlc_above_dust = HTLCCandidate::new(real_dust_limit_success_sat * 1000, HTLCInitiator::LocalOffered);
- let max_reserved_commit_tx_fee_msat = self.next_remote_commit_tx_fee_msat(htlc_above_dust, None);
-
- let holder_selected_chan_reserve_msat = self.context.holder_selected_channel_reserve_satoshis * 1000;
- let remote_balance_msat = (self.context.channel_value_satoshis * 1000 - self.context.value_to_self_msat)
- .saturating_sub(inbound_stats.pending_htlcs_value_msat);
-
- if remote_balance_msat < max_reserved_commit_tx_fee_msat + holder_selected_chan_reserve_msat {
- // If another HTLC's fee would reduce the remote's balance below the reserve limit
- // we've selected for them, we can only send dust HTLCs.
- available_capacity_msat = cmp::min(available_capacity_msat, real_dust_limit_success_sat * 1000 - 1);
- }
+ if !self.context.is_outbound() {
+ return Err(ChannelError::Close("Received funding_signed for an inbound channel?".to_owned()));
}
-
- let mut next_outbound_htlc_minimum_msat = self.context.counterparty_htlc_minimum_msat;
-
- // If we get close to our maximum dust exposure, we end up in a situation where we can send
- // between zero and the remaining dust exposure limit remaining OR above the dust limit.
- // Because we cannot express this as a simple min/max, we prefer to tell the user they can
- // send above the dust limit (as the router can always overpay to meet the dust limit).
- 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.context.opt_anchors() {
- (self.context.counterparty_dust_limit_satoshis, self.context.holder_dust_limit_satoshis)
- } else {
- let dust_buffer_feerate = self.context.get_dust_buffer_feerate(None) as u64;
- (self.context.counterparty_dust_limit_satoshis + dust_buffer_feerate * htlc_success_tx_weight(false) / 1000,
- self.context.holder_dust_limit_satoshis + dust_buffer_feerate * htlc_timeout_tx_weight(false) / 1000)
- };
- let on_counterparty_dust_htlc_exposure_msat = inbound_stats.on_counterparty_tx_dust_exposure_msat + outbound_stats.on_counterparty_tx_dust_exposure_msat;
- if on_counterparty_dust_htlc_exposure_msat as i64 + htlc_success_dust_limit as i64 * 1000 - 1 > self.context.get_max_dust_htlc_exposure_msat() as i64 {
- remaining_msat_below_dust_exposure_limit =
- Some(self.context.get_max_dust_htlc_exposure_msat().saturating_sub(on_counterparty_dust_htlc_exposure_msat));
- dust_exposure_dust_limit_msat = cmp::max(dust_exposure_dust_limit_msat, htlc_success_dust_limit * 1000);
+ if self.context.channel_state & !(ChannelState::MonitorUpdateInProgress as u32) != ChannelState::FundingCreated as u32 {
+ return Err(ChannelError::Close("Received funding_signed in strange state!".to_owned()));
}
-
- let on_holder_dust_htlc_exposure_msat = inbound_stats.on_holder_tx_dust_exposure_msat + outbound_stats.on_holder_tx_dust_exposure_msat;
- if on_holder_dust_htlc_exposure_msat as i64 + htlc_timeout_dust_limit as i64 * 1000 - 1 > self.context.get_max_dust_htlc_exposure_msat() as i64 {
- remaining_msat_below_dust_exposure_limit = Some(cmp::min(
- remaining_msat_below_dust_exposure_limit.unwrap_or(u64::max_value()),
- self.context.get_max_dust_htlc_exposure_msat().saturating_sub(on_holder_dust_htlc_exposure_msat)));
- dust_exposure_dust_limit_msat = cmp::max(dust_exposure_dust_limit_msat, htlc_timeout_dust_limit * 1000);
+ if self.context.commitment_secrets.get_min_seen_secret() != (1 << 48) ||
+ self.context.cur_counterparty_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER ||
+ self.context.cur_holder_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER {
+ panic!("Should not have advanced channel commitment tx numbers prior to funding_created");
}
- if let Some(remaining_limit_msat) = remaining_msat_below_dust_exposure_limit {
- if available_capacity_msat < dust_exposure_dust_limit_msat {
- available_capacity_msat = cmp::min(available_capacity_msat, remaining_limit_msat);
- } else {
- next_outbound_htlc_minimum_msat = cmp::max(next_outbound_htlc_minimum_msat, dust_exposure_dust_limit_msat);
- }
- }
+ let funding_script = self.context.get_funding_redeemscript();
- available_capacity_msat = cmp::min(available_capacity_msat,
- self.context.counterparty_max_htlc_value_in_flight_msat - outbound_stats.pending_htlcs_value_msat);
+ let counterparty_keys = self.context.build_remote_transaction_keys();
+ let counterparty_initial_commitment_tx = self.context.build_commitment_transaction(self.context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx;
+ let counterparty_trusted_tx = counterparty_initial_commitment_tx.trust();
+ let counterparty_initial_bitcoin_tx = counterparty_trusted_tx.built_transaction();
- if outbound_stats.pending_htlcs + 1 > self.context.counterparty_max_accepted_htlcs as u32 {
- available_capacity_msat = 0;
- }
+ log_trace!(logger, "Initial counterparty tx for channel {} is: txid {} tx {}",
+ log_bytes!(self.context.channel_id()), counterparty_initial_bitcoin_tx.txid, encode::serialize_hex(&counterparty_initial_bitcoin_tx.transaction));
- AvailableBalances {
- inbound_capacity_msat: cmp::max(self.context.channel_value_satoshis as i64 * 1000
- - self.context.value_to_self_msat as i64
- - self.get_inbound_pending_htlc_stats(None).pending_htlcs_value_msat as i64
- - self.context.holder_selected_channel_reserve_satoshis as i64 * 1000,
- 0) as u64,
- outbound_capacity_msat,
- next_outbound_htlc_limit_msat: available_capacity_msat,
- next_outbound_htlc_minimum_msat,
- balance_msat,
+ let holder_signer = self.context.build_holder_transaction_keys(self.context.cur_holder_commitment_transaction_number);
+ let initial_commitment_tx = self.context.build_commitment_transaction(self.context.cur_holder_commitment_transaction_number, &holder_signer, true, false, logger).tx;
+ {
+ let trusted_tx = initial_commitment_tx.trust();
+ let initial_commitment_bitcoin_tx = trusted_tx.built_transaction();
+ let sighash = initial_commitment_bitcoin_tx.get_sighash_all(&funding_script, self.context.channel_value_satoshis);
+ // They sign our commitment transaction, allowing us to broadcast the tx if we wish.
+ if let Err(_) = self.context.secp_ctx.verify_ecdsa(&sighash, &msg.signature, &self.context.get_counterparty_pubkeys().funding_pubkey) {
+ return Err(ChannelError::Close("Invalid funding_signed signature from peer".to_owned()));
+ }
}
- }
-
- pub fn get_holder_counterparty_selected_channel_reserve_satoshis(&self) -> (u64, Option<u64>) {
- (self.context.holder_selected_channel_reserve_satoshis, self.context.counterparty_selected_channel_reserve_satoshis)
- }
- // Get the fee cost in MSATS of a commitment tx with a given number of HTLC outputs.
- // Note that num_htlcs should not include dust HTLCs.
- fn commit_tx_fee_msat(feerate_per_kw: u32, num_htlcs: usize, opt_anchors: bool) -> u64 {
- // Note that we need to divide before multiplying to round properly,
- // since the lowest denomination of bitcoin on-chain is the satoshi.
- (commitment_tx_base_weight(opt_anchors) + num_htlcs as u64 * COMMITMENT_TX_WEIGHT_PER_HTLC) * feerate_per_kw as u64 / 1000 * 1000
- }
+ let holder_commitment_tx = HolderCommitmentTransaction::new(
+ initial_commitment_tx,
+ msg.signature,
+ Vec::new(),
+ &self.context.get_holder_pubkeys().funding_pubkey,
+ self.context.counterparty_funding_pubkey()
+ );
- /// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
- /// number of pending HTLCs that are on track to be in our next commitment tx.
- ///
- /// Optionally includes the `HTLCCandidate` given by `htlc` and an additional non-dust HTLC if
- /// `fee_spike_buffer_htlc` is `Some`.
- ///
- /// The first extra HTLC is useful for determining whether we can accept a further HTLC, the
- /// second allows for creating a buffer to ensure a further HTLC can always be accepted/added.
- ///
- /// Dust HTLCs are excluded.
- fn next_local_commit_tx_fee_msat(&self, htlc: HTLCCandidate, fee_spike_buffer_htlc: Option<()>) -> u64 {
- assert!(self.context.is_outbound());
+ self.context.holder_signer.validate_holder_commitment(&holder_commitment_tx, Vec::new())
+ .map_err(|_| ChannelError::Close("Failed to validate our commitment".to_owned()))?;
- 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,
- self.context.feerate_per_kw as u64 * htlc_timeout_tx_weight(false) / 1000)
- };
- let real_dust_limit_success_sat = htlc_success_dust_limit + self.context.holder_dust_limit_satoshis;
- let real_dust_limit_timeout_sat = htlc_timeout_dust_limit + self.context.holder_dust_limit_satoshis;
- let mut addl_htlcs = 0;
- if fee_spike_buffer_htlc.is_some() { addl_htlcs += 1; }
- match htlc.origin {
- HTLCInitiator::LocalOffered => {
- if htlc.amount_msat / 1000 >= real_dust_limit_timeout_sat {
- addl_htlcs += 1;
- }
- },
- HTLCInitiator::RemoteOffered => {
- if htlc.amount_msat / 1000 >= real_dust_limit_success_sat {
- addl_htlcs += 1;
- }
- }
- }
+ let funding_redeemscript = self.context.get_funding_redeemscript();
+ let funding_txo = self.context.get_funding_txo().unwrap();
+ let funding_txo_script = funding_redeemscript.to_v0_p2wsh();
+ let obscure_factor = get_commitment_transaction_number_obscure_factor(&self.context.get_holder_pubkeys().payment_point, &self.context.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);
+ let channel_monitor = ChannelMonitor::new(self.context.secp_ctx.clone(), monitor_signer,
+ shutdown_script, self.context.get_holder_selected_contest_delay(),
+ &self.context.destination_script, (funding_txo, funding_txo_script),
+ &self.context.channel_transaction_parameters,
+ funding_redeemscript.clone(), self.context.channel_value_satoshis,
+ obscure_factor,
+ holder_commitment_tx, best_block, self.context.counterparty_node_id);
- let mut included_htlcs = 0;
- for ref htlc in self.context.pending_inbound_htlcs.iter() {
- if htlc.amount_msat / 1000 < real_dust_limit_success_sat {
- continue
- }
- // We include LocalRemoved HTLCs here because we may still need to broadcast a commitment
- // transaction including this HTLC if it times out before they RAA.
- included_htlcs += 1;
- }
+ channel_monitor.provide_latest_counterparty_commitment_tx(counterparty_initial_bitcoin_tx.txid, Vec::new(), self.context.cur_counterparty_commitment_transaction_number, self.context.counterparty_cur_commitment_point.unwrap(), logger);
- for ref htlc in self.context.pending_outbound_htlcs.iter() {
- if htlc.amount_msat / 1000 < real_dust_limit_timeout_sat {
- continue
- }
- match htlc.state {
- OutboundHTLCState::LocalAnnounced {..} => included_htlcs += 1,
- OutboundHTLCState::Committed => included_htlcs += 1,
- OutboundHTLCState::RemoteRemoved {..} => included_htlcs += 1,
- // We don't include AwaitingRemoteRevokeToRemove HTLCs because our next commitment
- // transaction won't be generated until they send us their next RAA, which will mean
- // dropping any HTLCs in this state.
- _ => {},
- }
- }
+ assert_eq!(self.context.channel_state & (ChannelState::MonitorUpdateInProgress as u32), 0); // We have no had any monitor(s) yet to fail update!
+ self.context.channel_state = ChannelState::FundingSent as u32;
+ self.context.cur_holder_commitment_transaction_number -= 1;
+ self.context.cur_counterparty_commitment_transaction_number -= 1;
- for htlc in self.context.holding_cell_htlc_updates.iter() {
- match htlc {
- &HTLCUpdateAwaitingACK::AddHTLC { amount_msat, .. } => {
- if amount_msat / 1000 < real_dust_limit_timeout_sat {
- continue
- }
- included_htlcs += 1
- },
- _ => {}, // Don't include claims/fails that are awaiting ack, because once we get the
- // ack we're guaranteed to never include them in commitment txs anymore.
- }
- }
+ log_info!(logger, "Received funding_signed from peer for channel {}", log_bytes!(self.context.channel_id()));
- let num_htlcs = included_htlcs + addl_htlcs;
- 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.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();
- let commitment_tx_info = CommitmentTxInfoCached {
- fee,
- total_pending_htlcs,
- next_holder_htlc_id: match htlc.origin {
- HTLCInitiator::LocalOffered => self.context.next_holder_htlc_id + 1,
- HTLCInitiator::RemoteOffered => self.context.next_holder_htlc_id,
- },
- next_counterparty_htlc_id: match htlc.origin {
- HTLCInitiator::LocalOffered => self.context.next_counterparty_htlc_id,
- HTLCInitiator::RemoteOffered => self.context.next_counterparty_htlc_id + 1,
- },
- feerate: self.context.feerate_per_kw,
- };
- *self.context.next_local_commitment_tx_fee_info_cached.lock().unwrap() = Some(commitment_tx_info);
- }
- res
+ let need_channel_ready = self.check_get_channel_ready(0).is_some();
+ self.monitor_updating_paused(false, false, need_channel_ready, Vec::new(), Vec::new(), Vec::new());
+ Ok(channel_monitor)
}
- /// Get the commitment tx fee for the remote's next commitment transaction based on the number of
- /// pending HTLCs that are on track to be in their next commitment tx
- ///
- /// Optionally includes the `HTLCCandidate` given by `htlc` and an additional non-dust HTLC if
- /// `fee_spike_buffer_htlc` is `Some`.
- ///
- /// The first extra HTLC is useful for determining whether we can accept a further HTLC, the
- /// second allows for creating a buffer to ensure a further HTLC can always be accepted/added.
- ///
- /// Dust HTLCs are excluded.
- fn next_remote_commit_tx_fee_msat(&self, htlc: HTLCCandidate, fee_spike_buffer_htlc: Option<()>) -> u64 {
- assert!(!self.context.is_outbound());
-
- 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,
- self.context.feerate_per_kw as u64 * htlc_timeout_tx_weight(false) / 1000)
- };
- let real_dust_limit_success_sat = htlc_success_dust_limit + self.context.counterparty_dust_limit_satoshis;
- let real_dust_limit_timeout_sat = htlc_timeout_dust_limit + self.context.counterparty_dust_limit_satoshis;
-
- let mut addl_htlcs = 0;
- if fee_spike_buffer_htlc.is_some() { addl_htlcs += 1; }
- match htlc.origin {
- HTLCInitiator::LocalOffered => {
- if htlc.amount_msat / 1000 >= real_dust_limit_success_sat {
- addl_htlcs += 1;
- }
- },
- HTLCInitiator::RemoteOffered => {
- if htlc.amount_msat / 1000 >= real_dust_limit_timeout_sat {
- addl_htlcs += 1;
- }
- }
+ /// Handles a channel_ready message from our peer. If we've already sent our channel_ready
+ /// and the channel is now usable (and public), this may generate an announcement_signatures to
+ /// reply with.
+ pub fn channel_ready<NS: Deref, L: Deref>(
+ &mut self, msg: &msgs::ChannelReady, node_signer: &NS, genesis_block_hash: BlockHash,
+ user_config: &UserConfig, best_block: &BestBlock, logger: &L
+ ) -> Result<Option<msgs::AnnouncementSignatures>, ChannelError>
+ where
+ NS::Target: NodeSigner,
+ L::Target: Logger
+ {
+ if self.context.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 {
+ self.context.workaround_lnd_bug_4006 = Some(msg.clone());
+ return Err(ChannelError::Ignore("Peer sent channel_ready when we needed a channel_reestablish. The peer is likely lnd, see https://github.com/lightningnetwork/lnd/issues/4006".to_owned()));
}
- // When calculating the set of HTLCs which will be included in their next commitment_signed, all
- // non-dust inbound HTLCs are included (as all states imply it will be included) and only
- // committed outbound HTLCs, see below.
- let mut included_htlcs = 0;
- for ref htlc in self.context.pending_inbound_htlcs.iter() {
- if htlc.amount_msat / 1000 <= real_dust_limit_timeout_sat {
- continue
+ if let Some(scid_alias) = msg.short_channel_id_alias {
+ if Some(scid_alias) != self.context.short_channel_id {
+ // The scid alias provided can be used to route payments *from* our counterparty,
+ // i.e. can be used for inbound payments and provided in invoices, but is not used
+ // when routing outbound payments.
+ self.context.latest_inbound_scid_alias = Some(scid_alias);
}
- included_htlcs += 1;
}
- for ref htlc in self.context.pending_outbound_htlcs.iter() {
- if htlc.amount_msat / 1000 <= real_dust_limit_success_sat {
- continue
- }
- // We only include outbound HTLCs if it will not be included in their next commitment_signed,
- // i.e. if they've responded to us with an RAA after announcement.
- match htlc.state {
- OutboundHTLCState::Committed => included_htlcs += 1,
- OutboundHTLCState::RemoteRemoved {..} => included_htlcs += 1,
- OutboundHTLCState::LocalAnnounced { .. } => included_htlcs += 1,
- _ => {},
+ let non_shutdown_state = self.context.channel_state & (!MULTI_STATE_FLAGS);
+
+ if non_shutdown_state == ChannelState::FundingSent as u32 {
+ self.context.channel_state |= ChannelState::TheirChannelReady as u32;
+ } else if non_shutdown_state == (ChannelState::FundingSent as u32 | ChannelState::OurChannelReady as u32) {
+ self.context.channel_state = ChannelState::ChannelReady as u32 | (self.context.channel_state & MULTI_STATE_FLAGS);
+ self.context.update_time_counter += 1;
+ } else if self.context.channel_state & (ChannelState::ChannelReady as u32) != 0 ||
+ // If we reconnected before sending our `channel_ready` they may still resend theirs:
+ (self.context.channel_state & (ChannelState::FundingSent as u32 | ChannelState::TheirChannelReady as u32) ==
+ (ChannelState::FundingSent as u32 | ChannelState::TheirChannelReady as u32))
+ {
+ // They probably disconnected/reconnected and re-sent the channel_ready, which is
+ // required, or they're sending a fresh SCID alias.
+ let expected_point =
+ if self.context.cur_counterparty_commitment_transaction_number == INITIAL_COMMITMENT_NUMBER - 1 {
+ // If they haven't ever sent an updated point, the point they send should match
+ // the current one.
+ self.context.counterparty_cur_commitment_point
+ } else if self.context.cur_counterparty_commitment_transaction_number == INITIAL_COMMITMENT_NUMBER - 2 {
+ // If we've advanced the commitment number once, the second commitment point is
+ // at `counterparty_prev_commitment_point`, which is not yet revoked.
+ debug_assert!(self.context.counterparty_prev_commitment_point.is_some());
+ self.context.counterparty_prev_commitment_point
+ } else {
+ // If they have sent updated points, channel_ready is always supposed to match
+ // their "first" point, which we re-derive here.
+ Some(PublicKey::from_secret_key(&self.context.secp_ctx, &SecretKey::from_slice(
+ &self.context.commitment_secrets.get_secret(INITIAL_COMMITMENT_NUMBER - 1).expect("We should have all prev secrets available")
+ ).expect("We already advanced, so previous secret keys should have been validated already")))
+ };
+ if expected_point != Some(msg.next_per_commitment_point) {
+ return Err(ChannelError::Close("Peer sent a reconnect channel_ready with a different point".to_owned()));
}
+ return Ok(None);
+ } else {
+ return Err(ChannelError::Close("Peer sent a channel_ready at a strange time".to_owned()));
}
- let num_htlcs = included_htlcs + addl_htlcs;
- 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.context.opt_anchors());
- }
- let total_pending_htlcs = self.context.pending_inbound_htlcs.len() + self.context.pending_outbound_htlcs.len();
- let commitment_tx_info = CommitmentTxInfoCached {
- fee,
- total_pending_htlcs,
- next_holder_htlc_id: match htlc.origin {
- HTLCInitiator::LocalOffered => self.context.next_holder_htlc_id + 1,
- HTLCInitiator::RemoteOffered => self.context.next_holder_htlc_id,
- },
- next_counterparty_htlc_id: match htlc.origin {
- HTLCInitiator::LocalOffered => self.context.next_counterparty_htlc_id,
- HTLCInitiator::RemoteOffered => self.context.next_counterparty_htlc_id + 1,
- },
- feerate: self.context.feerate_per_kw,
- };
- *self.context.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = Some(commitment_tx_info);
- }
- res
+ self.context.counterparty_prev_commitment_point = self.context.counterparty_cur_commitment_point;
+ self.context.counterparty_cur_commitment_point = Some(msg.next_per_commitment_point);
+
+ log_info!(logger, "Received channel_ready from peer for channel {}", log_bytes!(self.context.channel_id()));
+
+ Ok(self.get_announcement_sigs(node_signer, genesis_block_hash, user_config, best_block.height(), logger))
}
pub fn update_add_htlc<F, L: Deref>(&mut self, msg: &msgs::UpdateAddHTLC, mut pending_forward_status: PendingHTLCStatus, create_pending_htlc_status: F, logger: &L) -> Result<(), ChannelError>
return Err(ChannelError::Close(format!("Remote side tried to send less than our minimum HTLC value. Lower limit: ({}). Actual: ({})", self.context.holder_htlc_minimum_msat, msg.amount_msat)));
}
- let inbound_stats = self.get_inbound_pending_htlc_stats(None);
- let outbound_stats = self.get_outbound_pending_htlc_stats(None);
+ let inbound_stats = self.context.get_inbound_pending_htlc_stats(None);
+ let outbound_stats = self.context.get_outbound_pending_htlc_stats(None);
if inbound_stats.pending_htlcs + 1 > self.context.holder_max_accepted_htlcs as u32 {
return Err(ChannelError::Close(format!("Remote tried to push more than our max accepted HTLCs ({})", self.context.holder_max_accepted_htlcs)));
}
// feerate_per_kw, while maintaining their channel reserve (as required by the spec).
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
+ self.context.next_remote_commit_tx_fee_msat(htlc_candidate, None) // Don't include the extra fee spike buffer HTLC in calculations
};
if pending_remote_value_msat - msg.amount_msat < remote_commit_tx_fee_msat {
return Err(ChannelError::Close("Remote HTLC add would not leave enough to pay for fees".to_owned()));
// still be able to afford adding this HTLC plus one more future HTLC, regardless of being
// sensitive to fee spikes.
let htlc_candidate = HTLCCandidate::new(msg.amount_msat, HTLCInitiator::RemoteOffered);
- let remote_fee_cost_incl_stuck_buffer_msat = 2 * self.next_remote_commit_tx_fee_msat(htlc_candidate, Some(()));
+ let remote_fee_cost_incl_stuck_buffer_msat = 2 * self.context.next_remote_commit_tx_fee_msat(htlc_candidate, Some(()));
if pending_remote_value_msat - msg.amount_msat - self.context.holder_selected_channel_reserve_satoshis * 1000 < remote_fee_cost_incl_stuck_buffer_msat {
// Note that if the pending_forward_status is not updated here, then it's because we're already failing
// the HTLC, i.e. its status is already set to failing.
} else {
// Check that they won't violate our local required channel reserve by adding this HTLC.
let htlc_candidate = HTLCCandidate::new(msg.amount_msat, HTLCInitiator::RemoteOffered);
- let local_commit_tx_fee_msat = self.next_local_commit_tx_fee_msat(htlc_candidate, None);
+ let local_commit_tx_fee_msat = self.context.next_local_commit_tx_fee_msat(htlc_candidate, None);
if self.context.value_to_self_msat < self.context.counterparty_selected_channel_reserve_satoshis.unwrap() * 1000 + local_commit_tx_fee_msat {
return Err(ChannelError::Close("Cannot accept HTLC that would put our balance under counterparty-announced channel reserve value".to_owned()));
}
}
// Before proposing a feerate update, check that we can actually afford the new fee.
- let inbound_stats = self.get_inbound_pending_htlc_stats(Some(feerate_per_kw));
- let outbound_stats = self.get_outbound_pending_htlc_stats(Some(feerate_per_kw));
+ let inbound_stats = self.context.get_inbound_pending_htlc_stats(Some(feerate_per_kw));
+ let outbound_stats = self.context.get_outbound_pending_htlc_stats(Some(feerate_per_kw));
let keys = self.context.build_holder_transaction_keys(self.context.cur_holder_commitment_transaction_number);
let commitment_stats = self.context.build_commitment_transaction(self.context.cur_holder_commitment_transaction_number, &keys, true, true, logger);
let buffer_fee_msat = 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;
// `get_dust_buffer_feerate` considers the `pending_update_fee` status), check that we
// won't be pushed over our dust exposure limit by the feerate increase.
if feerate_over_dust_buffer {
- let inbound_stats = self.get_inbound_pending_htlc_stats(None);
- let outbound_stats = self.get_outbound_pending_htlc_stats(None);
+ let inbound_stats = self.context.get_inbound_pending_htlc_stats(None);
+ let outbound_stats = self.context.get_outbound_pending_htlc_stats(None);
let holder_tx_dust_exposure = inbound_stats.on_holder_tx_dust_exposure_msat + outbound_stats.on_holder_tx_dust_exposure_msat;
let counterparty_tx_dust_exposure = inbound_stats.on_counterparty_tx_dust_exposure_msat + outbound_stats.on_counterparty_tx_dust_exposure_msat;
if holder_tx_dust_exposure > self.context.get_max_dust_htlc_exposure_msat() {
return Err(ChannelError::Ignore("Cannot send 0-msat HTLC".to_owned()));
}
- let available_balances = self.get_available_balances();
+ let available_balances = self.context.get_available_balances();
if amount_msat < available_balances.next_outbound_htlc_minimum_msat {
return Err(ChannelError::Ignore(format!("Cannot send less than our next-HTLC minimum - {} msat",
available_balances.next_outbound_htlc_minimum_msat)));
&& 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.context.opt_anchors());
+ let actual_fee = commit_tx_fee_msat(self.context.feerate_per_kw, commitment_stats.num_nondust_htlcs, self.context.opt_anchors());
assert_eq!(actual_fee, info.fee);
}
}
}
}
+/// A not-yet-funded outbound (from holder) channel using V1 channel establishment.
+pub(super) struct OutboundV1Channel<Signer: ChannelSigner> {
+ pub context: ChannelContext<Signer>,
+}
+
+impl<Signer: WriteableEcdsaChannelSigner> OutboundV1Channel<Signer> {
+ 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
+ // available. If it's private, we first try `scid_privacy` as it provides better privacy
+ // with no other changes, and fall back to `only_static_remotekey`.
+ let mut ret = ChannelTypeFeatures::only_static_remote_key();
+ if !config.channel_handshake_config.announced_channel &&
+ config.channel_handshake_config.negotiate_scid_privacy &&
+ their_features.supports_scid_privacy() {
+ ret.set_scid_privacy_required();
+ }
+
+ // Optionally, if the user would like to negotiate the `anchors_zero_fee_htlc_tx` option, we
+ // set it now. If they don't understand it, we'll fall back to our default of
+ // `only_static_remotekey`.
+ #[cfg(anchors)]
+ { // Attributes are not allowed on if expressions on our current MSRV of 1.41.
+ if config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx &&
+ their_features.supports_anchors_zero_fee_htlc_tx() {
+ ret.set_anchors_zero_fee_htlc_tx_required();
+ }
+ }
+
+ ret
+ }
+
+ pub fn new_outbound<ES: Deref, SP: Deref, F: Deref>(
+ fee_estimator: &LowerBoundedFeeEstimator<F>, entropy_source: &ES, signer_provider: &SP, counterparty_node_id: PublicKey, their_features: &InitFeatures,
+ channel_value_satoshis: u64, push_msat: u64, user_id: u128, config: &UserConfig, current_chain_height: u32,
+ outbound_scid_alias: u64
+ ) -> Result<Channel<Signer>, APIError>
+ where ES::Target: EntropySource,
+ SP::Target: SignerProvider<Signer = Signer>,
+ F::Target: FeeEstimator,
+ {
+ let holder_selected_contest_delay = config.channel_handshake_config.our_to_self_delay;
+ let channel_keys_id = signer_provider.generate_channel_keys_id(false, channel_value_satoshis, user_id);
+ let holder_signer = signer_provider.derive_channel_signer(channel_value_satoshis, channel_keys_id);
+ let pubkeys = holder_signer.pubkeys().clone();
+
+ if !their_features.supports_wumbo() && channel_value_satoshis > MAX_FUNDING_SATOSHIS_NO_WUMBO {
+ return Err(APIError::APIMisuseError{err: format!("funding_value must not exceed {}, it was {}", MAX_FUNDING_SATOSHIS_NO_WUMBO, channel_value_satoshis)});
+ }
+ if channel_value_satoshis >= TOTAL_BITCOIN_SUPPLY_SATOSHIS {
+ return Err(APIError::APIMisuseError{err: format!("funding_value must be smaller than the total bitcoin supply, it was {}", channel_value_satoshis)});
+ }
+ let channel_value_msat = channel_value_satoshis * 1000;
+ if push_msat > channel_value_msat {
+ return Err(APIError::APIMisuseError { err: format!("Push value ({}) was larger than channel_value ({})", push_msat, channel_value_msat) });
+ }
+ 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 = 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`
+ return Err(APIError::APIMisuseError { err: format!("Holder selected channel reserve below implemention limit dust_limit_satoshis {}", holder_selected_channel_reserve_satoshis) });
+ }
+
+ let channel_type = Self::get_initial_channel_type(&config, their_features);
+ debug_assert!(channel_type.is_subset(&channelmanager::provided_channel_type_features(&config)));
+
+ let feerate = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::Normal);
+
+ let value_to_self_msat = channel_value_satoshis * 1000 - push_msat;
+ let commitment_tx_fee = commit_tx_fee_msat(feerate, MIN_AFFORDABLE_HTLC_COUNT, channel_type.requires_anchors_zero_fee_htlc_tx());
+ if value_to_self_msat < commitment_tx_fee {
+ return Err(APIError::APIMisuseError{ err: format!("Funding amount ({}) can't even pay fee for initial commitment transaction fee of {}.", value_to_self_msat / 1000, commitment_tx_fee / 1000) });
+ }
+
+ let mut secp_ctx = Secp256k1::new();
+ secp_ctx.seeded_randomize(&entropy_source.get_secure_random_bytes());
+
+ let shutdown_scriptpubkey = if config.channel_handshake_config.commit_upfront_shutdown_pubkey {
+ match signer_provider.get_shutdown_scriptpubkey() {
+ Ok(scriptpubkey) => Some(scriptpubkey),
+ Err(_) => return Err(APIError::ChannelUnavailable { err: "Failed to get shutdown scriptpubkey".to_owned()}),
+ }
+ } else { None };
+
+ if let Some(shutdown_scriptpubkey) = &shutdown_scriptpubkey {
+ if !shutdown_scriptpubkey.is_compatible(&their_features) {
+ return Err(APIError::IncompatibleShutdownScript { script: shutdown_scriptpubkey.clone() });
+ }
+ }
+
+ let destination_script = match signer_provider.get_destination_script() {
+ Ok(script) => script,
+ Err(_) => return Err(APIError::ChannelUnavailable { err: "Failed to get destination script".to_owned()}),
+ };
+
+ let temporary_channel_id = entropy_source.get_secure_random_bytes();
+
+ Ok(Channel {
+ context: ChannelContext {
+ user_id,
+
+ config: LegacyChannelConfig {
+ options: config.channel_config.clone(),
+ announced_channel: config.channel_handshake_config.announced_channel,
+ commit_upfront_shutdown_pubkey: config.channel_handshake_config.commit_upfront_shutdown_pubkey,
+ },
+
+ prev_config: None,
+
+ inbound_handshake_limits_override: Some(config.channel_handshake_limits.clone()),
+
+ channel_id: temporary_channel_id,
+ temporary_channel_id: Some(temporary_channel_id),
+ channel_state: ChannelState::OurInitSent as u32,
+ announcement_sigs_state: AnnouncementSigsState::NotSent,
+ secp_ctx,
+ channel_value_satoshis,
+
+ latest_monitor_update_id: 0,
+
+ holder_signer,
+ shutdown_scriptpubkey,
+ destination_script,
+
+ cur_holder_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER,
+ cur_counterparty_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER,
+ value_to_self_msat,
+
+ pending_inbound_htlcs: Vec::new(),
+ pending_outbound_htlcs: Vec::new(),
+ holding_cell_htlc_updates: Vec::new(),
+ pending_update_fee: None,
+ holding_cell_update_fee: None,
+ next_holder_htlc_id: 0,
+ next_counterparty_htlc_id: 0,
+ update_time_counter: 1,
+
+ resend_order: RAACommitmentOrder::CommitmentFirst,
+
+ monitor_pending_channel_ready: false,
+ monitor_pending_revoke_and_ack: false,
+ monitor_pending_commitment_signed: false,
+ monitor_pending_forwards: Vec::new(),
+ monitor_pending_failures: Vec::new(),
+ monitor_pending_finalized_fulfills: Vec::new(),
+
+ #[cfg(debug_assertions)]
+ holder_max_commitment_tx_output: Mutex::new((channel_value_satoshis * 1000 - push_msat, push_msat)),
+ #[cfg(debug_assertions)]
+ counterparty_max_commitment_tx_output: Mutex::new((channel_value_satoshis * 1000 - push_msat, push_msat)),
+
+ last_sent_closing_fee: None,
+ pending_counterparty_closing_signed: None,
+ closing_fee_limits: None,
+ target_closing_feerate_sats_per_kw: None,
+
+ inbound_awaiting_accept: false,
+
+ funding_tx_confirmed_in: None,
+ funding_tx_confirmation_height: 0,
+ short_channel_id: None,
+ channel_creation_height: current_chain_height,
+
+ feerate_per_kw: feerate,
+ 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: 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,
+ holder_htlc_minimum_msat: if config.channel_handshake_config.our_htlc_minimum_msat == 0 { 1 } else { config.channel_handshake_config.our_htlc_minimum_msat },
+ counterparty_max_accepted_htlcs: 0,
+ holder_max_accepted_htlcs: cmp::min(config.channel_handshake_config.our_max_accepted_htlcs, MAX_HTLCS),
+ minimum_depth: None, // Filled in in accept_channel
+
+ counterparty_forwarding_info: None,
+
+ channel_transaction_parameters: ChannelTransactionParameters {
+ holder_pubkeys: pubkeys,
+ holder_selected_contest_delay: config.channel_handshake_config.our_to_self_delay,
+ is_outbound_from_holder: true,
+ counterparty_parameters: None,
+ funding_outpoint: None,
+ opt_anchors: if channel_type.requires_anchors_zero_fee_htlc_tx() { Some(()) } else { None },
+ opt_non_zero_fee_anchors: None
+ },
+ funding_transaction: None,
+
+ counterparty_cur_commitment_point: None,
+ counterparty_prev_commitment_point: None,
+ counterparty_node_id,
+
+ counterparty_shutdown_scriptpubkey: None,
+
+ commitment_secrets: CounterpartyCommitmentSecrets::new(),
+
+ channel_update_status: ChannelUpdateStatus::Enabled,
+ closing_signed_in_flight: false,
+
+ announcement_sigs: None,
+
+ #[cfg(any(test, fuzzing))]
+ next_local_commitment_tx_fee_info_cached: Mutex::new(None),
+ #[cfg(any(test, fuzzing))]
+ next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
+
+ workaround_lnd_bug_4006: None,
+ sent_message_awaiting_response: None,
+
+ latest_inbound_scid_alias: None,
+ outbound_scid_alias,
+
+ channel_pending_event_emitted: false,
+ channel_ready_event_emitted: false,
+
+ #[cfg(any(test, fuzzing))]
+ historical_inbound_htlc_fulfills: HashSet::new(),
+
+ channel_type,
+ channel_keys_id,
+
+ pending_monitor_updates: Vec::new(),
+ }
+ })
+ }
+}
+
+/// A not-yet-funded inbound (from counterparty) channel using V1 channel establishment.
+pub(super) struct InboundV1Channel<Signer: ChannelSigner> {
+ pub context: ChannelContext<Signer>,
+}
+
+impl<Signer: WriteableEcdsaChannelSigner> InboundV1Channel<Signer> {}
+
const SERIALIZATION_VERSION: u8 = 3;
const MIN_SERIALIZATION_VERSION: u8 = 2;
use crate::ln::channelmanager::{self, HTLCSource, PaymentId};
#[cfg(anchors)]
use crate::ln::channel::InitFeatures;
- use crate::ln::channel::{Channel, InboundHTLCOutput, OutboundHTLCOutput, InboundHTLCState, OutboundHTLCState, HTLCCandidate, HTLCInitiator};
+ use crate::ln::channel::{Channel, InboundHTLCOutput, OutboundV1Channel, OutboundHTLCOutput, InboundHTLCState, OutboundHTLCState, HTLCCandidate, HTLCInitiator, commit_tx_fee_msat};
use crate::ln::channel::{MAX_FUNDING_SATOSHIS_NO_WUMBO, TOTAL_BITCOIN_SUPPLY_SATOSHIS, MIN_THEIR_CHAN_RESERVE_SATOSHIS};
use crate::ln::features::ChannelTypeFeatures;
use crate::ln::msgs::{ChannelUpdate, DecodeError, UnsignedChannelUpdate, MAX_VALUE_MSAT};
let secp_ctx = Secp256k1::new();
let node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- match Channel::<EnforcingSigner>::new_outbound(&LowerBoundedFeeEstimator::new(&TestFeeEstimator { fee_est: 253 }), &&keys_provider, &&keys_provider, node_id, &features, 10000000, 100000, 42, &config, 0, 42) {
+ match OutboundV1Channel::<EnforcingSigner>::new_outbound(&LowerBoundedFeeEstimator::new(&TestFeeEstimator { fee_est: 253 }), &&keys_provider, &&keys_provider, node_id, &features, 10000000, 100000, 42, &config, 0, 42) {
Err(APIError::IncompatibleShutdownScript { script }) => {
assert_eq!(script.into_inner(), non_v0_segwit_shutdown_script.into_inner());
},
let node_a_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- let node_a_chan = Channel::<EnforcingSigner>::new_outbound(&bounded_fee_estimator, &&keys_provider, &&keys_provider, node_a_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
+ let node_a_chan = OutboundV1Channel::<EnforcingSigner>::new_outbound(&bounded_fee_estimator, &&keys_provider, &&keys_provider, node_a_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
// Now change the fee so we can check that the fee in the open_channel message is the
// same as the old fee.
// Create Node A's channel pointing to Node B's pubkey
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- let mut node_a_chan = Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
+ let mut node_a_chan = OutboundV1Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
// Create Node B's channel by receiving Node A's open_channel message
// Make sure A's dust limit is as we expect.
// Make sure when Node A calculates their local commitment transaction, none of the HTLCs pass
// 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.context.opt_anchors());
+ let local_commit_tx_fee = node_a_chan.context.next_local_commit_tx_fee_msat(htlc_candidate, None);
+ let local_commit_fee_0_htlcs = 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.context.opt_anchors());
+ let remote_commit_fee_3_htlcs = 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);
+ let remote_commit_tx_fee = node_a_chan.context.next_remote_commit_tx_fee_msat(htlc_candidate, None);
assert_eq!(remote_commit_tx_fee, remote_commit_fee_3_htlcs);
}
let node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
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 mut chan = OutboundV1Channel::<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.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());
+ let commitment_tx_fee_0_htlcs = commit_tx_fee_msat(chan.context.feerate_per_kw, 0, chan.context.opt_anchors());
+ let commitment_tx_fee_1_htlc = 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.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);
+ let commitment_tx_fee = chan.context.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.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);
+ let commitment_tx_fee = chan.context.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.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);
+ let commitment_tx_fee = chan.context.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.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);
+ let commitment_tx_fee = chan.context.next_remote_commit_tx_fee_msat(htlc_candidate, None);
assert_eq!(commitment_tx_fee, commitment_tx_fee_1_htlc);
}
// Create Node A's channel pointing to Node B's pubkey
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- let mut node_a_chan = Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
+ let mut node_a_chan = OutboundV1Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
// Create Node B's channel by receiving Node A's open_channel message
let open_channel_msg = node_a_chan.get_open_channel(chain_hash);
// Test that `new_outbound` creates a channel with the correct value for
// `holder_max_htlc_value_in_flight_msat`, when configured with a valid percentage value,
// which is set to the lower bound + 1 (2%) of the `channel_value`.
- let chan_1 = Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_2_percent), 10000000, 100000, 42, &config_2_percent, 0, 42).unwrap();
+ let chan_1 = OutboundV1Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_2_percent), 10000000, 100000, 42, &config_2_percent, 0, 42).unwrap();
let chan_1_value_msat = chan_1.context.channel_value_satoshis * 1000;
assert_eq!(chan_1.context.holder_max_htlc_value_in_flight_msat, (chan_1_value_msat as f64 * 0.02) as u64);
// Test with the upper bound - 1 of valid values (99%).
- let chan_2 = Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_99_percent), 10000000, 100000, 42, &config_99_percent, 0, 42).unwrap();
+ let chan_2 = OutboundV1Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_99_percent), 10000000, 100000, 42, &config_99_percent, 0, 42).unwrap();
let chan_2_value_msat = chan_2.context.channel_value_satoshis * 1000;
assert_eq!(chan_2.context.holder_max_htlc_value_in_flight_msat, (chan_2_value_msat as f64 * 0.99) as u64);
// Test that `new_outbound` uses the lower bound of the configurable percentage values (1%)
// if `max_inbound_htlc_value_in_flight_percent_of_channel` is set to a value less than 1.
- let chan_5 = Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_0_percent), 10000000, 100000, 42, &config_0_percent, 0, 42).unwrap();
+ let chan_5 = OutboundV1Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_0_percent), 10000000, 100000, 42, &config_0_percent, 0, 42).unwrap();
let chan_5_value_msat = chan_5.context.channel_value_satoshis * 1000;
assert_eq!(chan_5.context.holder_max_htlc_value_in_flight_msat, (chan_5_value_msat as f64 * 0.01) as u64);
// Test that `new_outbound` uses the upper bound of the configurable percentage values
// (100%) if `max_inbound_htlc_value_in_flight_percent_of_channel` is set to a larger value
// than 100.
- let chan_6 = Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_101_percent), 10000000, 100000, 42, &config_101_percent, 0, 42).unwrap();
+ let chan_6 = OutboundV1Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_101_percent), 10000000, 100000, 42, &config_101_percent, 0, 42).unwrap();
let chan_6_value_msat = chan_6.context.channel_value_satoshis * 1000;
assert_eq!(chan_6.context.holder_max_htlc_value_in_flight_msat, chan_6_value_msat);
let mut outbound_node_config = UserConfig::default();
outbound_node_config.channel_handshake_config.their_channel_reserve_proportional_millionths = (outbound_selected_channel_reserve_perc * 1_000_000.0) as u32;
- let chan = Channel::<EnforcingSigner>::new_outbound(&&fee_est, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&outbound_node_config), channel_value_satoshis, 100_000, 42, &outbound_node_config, 0, 42).unwrap();
+ let chan = OutboundV1Channel::<EnforcingSigner>::new_outbound(&&fee_est, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&outbound_node_config), channel_value_satoshis, 100_000, 42, &outbound_node_config, 0, 42).unwrap();
let expected_outbound_selected_chan_reserve = cmp::max(MIN_THEIR_CHAN_RESERVE_SATOSHIS, (chan.context.channel_value_satoshis as f64 * outbound_selected_channel_reserve_perc) as u64);
assert_eq!(chan.context.holder_selected_channel_reserve_satoshis, expected_outbound_selected_chan_reserve);
// Create a channel.
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- let mut node_a_chan = Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
+ let mut node_a_chan = OutboundV1Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
assert!(node_a_chan.context.counterparty_forwarding_info.is_none());
assert_eq!(node_a_chan.context.holder_htlc_minimum_msat, 1); // the default
assert!(node_a_chan.context.counterparty_forwarding_info().is_none());
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- let node_a_chan = Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider,
+ let node_a_chan = OutboundV1Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider,
node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
let mut channel_type_features = ChannelTypeFeatures::only_static_remote_key();
// It is not enough for just the initiator to signal `option_anchors_zero_fee_htlc_tx`, both
// need to signal it.
- let channel_a = Channel::<EnforcingSigner>::new_outbound(
+ let channel_a = OutboundV1Channel::<EnforcingSigner>::new_outbound(
&fee_estimator, &&keys_provider, &&keys_provider, node_id_b,
&channelmanager::provided_init_features(&UserConfig::default()), 10000000, 100000, 42,
&config, 0, 42
expected_channel_type.set_static_remote_key_required();
expected_channel_type.set_anchors_zero_fee_htlc_tx_required();
- let channel_a = Channel::<EnforcingSigner>::new_outbound(
+ let channel_a = OutboundV1Channel::<EnforcingSigner>::new_outbound(
&fee_estimator, &&keys_provider, &&keys_provider, node_id_b,
&channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42
).unwrap();
let raw_init_features = static_remote_key_required | simple_anchors_required;
let init_features_with_simple_anchors = InitFeatures::from_le_bytes(raw_init_features.to_le_bytes().to_vec());
- let channel_a = Channel::<EnforcingSigner>::new_outbound(
+ let channel_a = OutboundV1Channel::<EnforcingSigner>::new_outbound(
&fee_estimator, &&keys_provider, &&keys_provider, node_id_b,
&channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42
).unwrap();
// First, we'll try to open a channel between A and B where A requests a channel type for
// the original `option_anchors` feature (non zero fee htlc tx). This should be rejected by
// B as it's not supported by LDK.
- let channel_a = Channel::<EnforcingSigner>::new_outbound(
+ let channel_a = OutboundV1Channel::<EnforcingSigner>::new_outbound(
&fee_estimator, &&keys_provider, &&keys_provider, node_id_b,
&channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42
).unwrap();
// `anchors_zero_fee_htlc_tx`. B is malicious and tries to downgrade the channel type to the
// original `option_anchors` feature, which should be rejected by A as it's not supported by
// LDK.
- let mut channel_a = Channel::<EnforcingSigner>::new_outbound(
+ let mut channel_a = OutboundV1Channel::<EnforcingSigner>::new_outbound(
&fee_estimator, &&keys_provider, &&keys_provider, node_id_b, &simple_anchors_init,
10000000, 100000, 42, &config, 0, 42
).unwrap();