pub fn counterparty_forwarding_info(&self) -> Option<CounterpartyForwardingInfo> {
self.counterparty_forwarding_info.clone()
}
+
+ /// 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,
+ };
+
+ 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
+ }
+
+ /// 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,
+ };
+
+ 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;
+ }
+ }
+
+ 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
+ }
+
+ /// 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);
+
+ 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;
+
+ 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);
+
+ let mut available_capacity_msat = outbound_capacity_msat;
+
+ 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;
+ }
+
+ 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(()));
+
+ // 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;
+ }
+
+ 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);
+
+ 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);
+
+ 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);
+ }
+ }
+
+ let mut next_outbound_htlc_minimum_msat = 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 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);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+ }
+
+ available_capacity_msat = cmp::min(available_capacity_msat,
+ context.counterparty_max_htlc_value_in_flight_msat - outbound_stats.pending_htlcs_value_msat);
+
+ if outbound_stats.pending_htlcs + 1 > context.counterparty_max_accepted_htlcs as u32 {
+ available_capacity_msat = 0;
+ }
+
+ 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,
+ }
+ }
+
+ 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)
+ }
+
+ /// 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 (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 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 included_htlcs = 0;
+ for ref htlc in 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;
+ }
+
+ 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.
+ _ => {},
+ }
+ }
+
+ 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.
+ }
+ }
+
+ 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
+ }
+
+ /// 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());
+
+ 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;
+
+ 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;
+ }
+ }
+ }
+
+ // 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;
+ }
+
+ 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,
+ _ => {},
+ }
+ }
+
+ 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,
+ },
+ 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
+ }
+
+ /// 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
+ }
+ }
}
// Internal utility functions for channels
feerate_per_kw as u64 * (commitment_tx_base_weight(opt_anchors) + num_htlcs as u64 * COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000
}
+// 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
+}
+
// 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
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 = Self::commit_tx_fee_msat(feerate, MIN_AFFORDABLE_HTLC_COUNT, channel_type.requires_anchors_zero_fee_htlc_tx());
+ 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) });
}
// 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)));
}
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 counterparty_signature = self.context.holder_signer.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &self.context.secp_ctx)
- .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?.0;
-
- // We sign "counterparty" commitment transaction, allowing them to broadcast the tx if they wish.
- Ok((counterparty_initial_bitcoin_tx.txid, initial_commitment_tx, counterparty_signature))
- }
-
- pub fn funding_created<SP: Deref, L: Deref>(
- &mut self, msg: &msgs::FundingCreated, best_block: BestBlock, signer_provider: &SP, logger: &L
- ) -> Result<(msgs::FundingSigned, ChannelMonitor<Signer>), ChannelError>
- where
- SP::Target: SignerProvider<Signer = Signer>,
- L::Target: Logger
- {
- if self.context.is_outbound() {
- return Err(ChannelError::Close("Received funding_created for an outbound channel?".to_owned()));
- }
- if self.context.channel_state != (ChannelState::OurInitSent as u32 | ChannelState::TheirInitSent as u32) {
- // BOLT 2 says that if we disconnect before we send funding_signed we SHOULD NOT
- // remember the channel, so it's safe to just send an error_message here and drop the
- // channel.
- return Err(ChannelError::Close("Received funding_created after we got the channel!".to_owned()));
- }
- if self.context.inbound_awaiting_accept {
- return Err(ChannelError::Close("FundingCreated message received before the channel was accepted".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_txo = OutPoint { txid: msg.funding_txid, index: msg.funding_output_index };
- self.context.channel_transaction_parameters.funding_outpoint = Some(funding_txo);
- // This is an externally observable change before we finish all our checks. In particular
- // funding_created_signature may fail.
- self.context.holder_signer.provide_channel_parameters(&self.context.channel_transaction_parameters);
-
- let (counterparty_initial_commitment_txid, initial_commitment_tx, signature) = match self.funding_created_signature(&msg.signature, logger) {
- Ok(res) => res,
- Err(ChannelError::Close(e)) => {
- 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()));
- }
- }
-
- 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()))?;
-
-
- 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);
-
- 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);
-
- 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;
-
- log_info!(logger, "Received funding_signed from 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)
- }
-
- /// 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()));
- }
-
- 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);
+ 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));
- log_info!(logger, "Received channel_ready from peer for channel {}", log_bytes!(self.context.channel_id()));
+ let counterparty_signature = self.context.holder_signer.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &self.context.secp_ctx)
+ .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?.0;
- Ok(self.get_announcement_sigs(node_signer, genesis_block_hash, user_config, best_block.height(), logger))
+ // We sign "counterparty" commitment transaction, allowing them to broadcast the tx if they wish.
+ Ok((counterparty_initial_bitcoin_tx.txid, initial_commitment_tx, counterparty_signature))
}
- /// 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
+ pub fn funding_created<SP: Deref, L: Deref>(
+ &mut self, msg: &msgs::FundingCreated, best_block: BestBlock, signer_provider: &SP, logger: &L
+ ) -> Result<(msgs::FundingSigned, ChannelMonitor<Signer>), ChannelError>
+ where
+ SP::Target: SignerProvider<Signer = Signer>,
+ L::Target: Logger
+ {
+ if self.context.is_outbound() {
+ return Err(ChannelError::Close("Received funding_created for an outbound channel?".to_owned()));
}
- }
-
- /// 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;
- }
+ if self.context.channel_state != (ChannelState::OurInitSent as u32 | ChannelState::TheirInitSent as u32) {
+ // BOLT 2 says that if we disconnect before we send funding_signed we SHOULD NOT
+ // remember the channel, so it's safe to just send an error_message here and drop the
+ // channel.
+ return Err(ChannelError::Close("Received funding_created after we got the channel!".to_owned()));
}
- 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;
- }
+ if self.context.inbound_awaiting_accept {
+ return Err(ChannelError::Close("FundingCreated message received before the channel was accepted".to_owned()));
}
-
- 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;
- }
- }
+ 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");
}
- 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 funding_txo = OutPoint { txid: msg.funding_txid, index: msg.funding_output_index };
+ self.context.channel_transaction_parameters.funding_outpoint = Some(funding_txo);
+ // This is an externally observable change before we finish all our checks. In particular
+ // funding_created_signature may fail.
+ self.context.holder_signer.provide_channel_parameters(&self.context.channel_transaction_parameters);
- 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;
+ let (counterparty_initial_commitment_txid, initial_commitment_tx, signature) = match self.funding_created_signature(&msg.signature, logger) {
+ Ok(res) => res,
+ Err(ChannelError::Close(e)) => {
+ 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);
}
- }
- 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;
+ 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()
+ );
- 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;
- }
+ 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_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(()));
+ // Now that we're past error-generating stuff, update our local state:
- // 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 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);
- 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);
+ 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);
- 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);
+ 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;
- 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);
- }
- }
+ log_info!(logger, "Generated funding_signed for peer for channel {}", log_bytes!(self.context.channel_id()));
- let mut next_outbound_htlc_minimum_msat = self.context.counterparty_htlc_minimum_msat;
+ 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());
- // 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;
+ Ok((msgs::FundingSigned {
+ channel_id: self.context.channel_id,
+ signature,
+ #[cfg(taproot)]
+ partial_signature_with_nonce: None,
+ }, channel_monitor))
+ }
- 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);
+ /// 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()));
}
-
- 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.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");
}
- 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();
+
+ 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()));
}
}
- available_capacity_msat = cmp::min(available_capacity_msat,
- self.context.counterparty_max_htlc_value_in_flight_msat - outbound_stats.pending_htlcs_value_msat);
+ 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()
+ );
- if outbound_stats.pending_htlcs + 1 > self.context.counterparty_max_accepted_htlcs as u32 {
- available_capacity_msat = 0;
- }
+ self.context.holder_signer.validate_holder_commitment(&holder_commitment_tx, Vec::new())
+ .map_err(|_| ChannelError::Close("Failed to validate our commitment".to_owned()))?;
- 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,
- }
- }
- 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)
- }
+ 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);
- // 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
- }
+ 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);
- /// 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());
+ 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;
- 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;
+ log_info!(logger, "Received funding_signed from peer for channel {}", log_bytes!(self.context.channel_id()));
- 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 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)
+ }
- 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;
+ /// 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()));
}
- 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.
- _ => {},
+ 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);
}
}
- 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.
- }
- }
+ let non_shutdown_state = self.context.channel_state & (!MULTI_STATE_FLAGS);
- 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))]
+ 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))
{
- 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());
+ // 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()));
}
- 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
- }
-
- /// 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)
+ return Ok(None);
} 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;
- }
- }
+ return Err(ChannelError::Close("Peer sent a channel_ready at a strange time".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
- }
- included_htlcs += 1;
- }
+ self.context.counterparty_prev_commitment_point = self.context.counterparty_cur_commitment_point;
+ self.context.counterparty_cur_commitment_point = Some(msg.next_per_commitment_point);
- 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,
- _ => {},
- }
- }
+ log_info!(logger, "Received channel_ready 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();
- 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
+ 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);
}
}
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, 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};
// 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 config = UserConfig::default();
let mut chan = Channel::<EnforcingSigner>::new_outbound(&fee_est, &&keys_provider, &&keys_provider, node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
- let commitment_tx_fee_0_htlcs = Channel::<EnforcingSigner>::commit_tx_fee_msat(chan.context.feerate_per_kw, 0, chan.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);
}