diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 9a0c0a0c67f..f3abb77a849 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -1176,6 +1176,37 @@ impl Channel where } } + pub fn funding(&self) -> &FundingScope { + match &self.phase { + ChannelPhase::Undefined => unreachable!(), + ChannelPhase::Funded(chan) => &chan.funding, + ChannelPhase::UnfundedOutboundV1(chan) => &chan.funding, + ChannelPhase::UnfundedInboundV1(chan) => &chan.funding, + ChannelPhase::UnfundedV2(chan) => &chan.funding, + } + } + + #[cfg(test)] + pub fn funding_mut(&mut self) -> &mut FundingScope { + match &mut self.phase { + ChannelPhase::Undefined => unreachable!(), + ChannelPhase::Funded(chan) => &mut chan.funding, + ChannelPhase::UnfundedOutboundV1(chan) => &mut chan.funding, + ChannelPhase::UnfundedInboundV1(chan) => &mut chan.funding, + ChannelPhase::UnfundedV2(chan) => &mut chan.funding, + } + } + + pub fn funding_and_context_mut(&mut self) -> (&FundingScope, &mut ChannelContext) { + match &mut self.phase { + ChannelPhase::Undefined => unreachable!(), + ChannelPhase::Funded(chan) => (&chan.funding, &mut chan.context), + ChannelPhase::UnfundedOutboundV1(chan) => (&chan.funding, &mut chan.context), + ChannelPhase::UnfundedInboundV1(chan) => (&chan.funding, &mut chan.context), + ChannelPhase::UnfundedV2(chan) => (&chan.funding, &mut chan.context), + } + } + pub fn unfunded_context_mut(&mut self) -> Option<&mut UnfundedChannelContext> { match &mut self.phase { ChannelPhase::Undefined => unreachable!(), @@ -1443,6 +1474,11 @@ impl Channel where debug_assert!(!matches!(self.phase, ChannelPhase::Undefined)); result } + + pub fn force_shutdown(&mut self, should_broadcast: bool, closure_reason: ClosureReason) -> ShutdownResult { + let (funding, context) = self.funding_and_context_mut(); + context.force_shutdown(funding, should_broadcast, closure_reason) + } } impl From> for Channel @@ -1522,6 +1558,62 @@ impl UnfundedChannelContext { } } +/// Information pertaining to an attempt at funding the channel. This is typically constructed +/// during channel establishment and may be replaced during channel splicing or if the attempted +/// funding transaction is replaced using tx_init_rbf. +pub(super) struct FundingScope { + channel_value_satoshis: u64, + value_to_self_msat: u64, // Excluding all pending_htlcs, fees, and anchor outputs + + /// minimum channel reserve for self to maintain - set by them. + counterparty_selected_channel_reserve_satoshis: Option, + + #[cfg(test)] + pub(super) holder_selected_channel_reserve_satoshis: u64, + #[cfg(not(test))] + holder_selected_channel_reserve_satoshis: u64, + + #[cfg(debug_assertions)] + /// Max to_local and to_remote outputs in a locally-generated commitment transaction + holder_max_commitment_tx_output: Mutex<(u64, u64)>, + #[cfg(debug_assertions)] + /// Max to_local and to_remote outputs in a remote-generated commitment transaction + counterparty_max_commitment_tx_output: Mutex<(u64, u64)>, + + // We save these values so we can make sure `next_local_commit_tx_fee_msat` and + // `next_remote_commit_tx_fee_msat` properly predict what the next commitment transaction fee will + // be, by comparing the cached values to the fee of the transaction generated by + // `build_commitment_transaction`. + #[cfg(any(test, fuzzing))] + next_local_commitment_tx_fee_info_cached: Mutex>, + #[cfg(any(test, fuzzing))] + next_remote_commitment_tx_fee_info_cached: Mutex>, +} + +impl FundingScope { + pub fn get_value_satoshis(&self) -> u64 { + self.channel_value_satoshis + } + + pub(crate) fn get_value_to_self_msat(&self) -> u64 { + self.value_to_self_msat + } + + pub fn get_holder_counterparty_selected_channel_reserve_satoshis(&self) -> (u64, Option) { + (self.holder_selected_channel_reserve_satoshis, self.counterparty_selected_channel_reserve_satoshis) + } + + fn get_htlc_maximum_msat(&self, party_max_htlc_value_in_flight_msat: u64) -> Option { + self.counterparty_selected_channel_reserve_satoshis.map(|counterparty_reserve| { + let holder_reserve = self.holder_selected_channel_reserve_satoshis; + cmp::min( + (self.channel_value_satoshis - counterparty_reserve - holder_reserve) * 1000, + party_max_htlc_value_in_flight_msat + ) + }) + } +} + /// Contains everything about the channel including state, and various flags. pub(super) struct ChannelContext where SP::Target: SignerProvider { config: LegacyChannelConfig, @@ -1555,7 +1647,6 @@ pub(super) struct ChannelContext where SP::Target: SignerProvider { announcement_sigs_state: AnnouncementSigsState, secp_ctx: Secp256k1, - channel_value_satoshis: u64, latest_monitor_update_id: u64, @@ -1568,7 +1659,6 @@ pub(super) struct ChannelContext where SP::Target: SignerProvider { // cost of others, but should really just be changed. cur_counterparty_commitment_transaction_number: u64, - value_to_self_msat: u64, // Excluding all pending_htlcs, fees, and anchor outputs pending_inbound_htlcs: Vec, pending_outbound_htlcs: Vec, holding_cell_htlc_updates: Vec, @@ -1645,13 +1735,6 @@ pub(super) struct ChannelContext where SP::Target: SignerProvider { /// time. update_time_counter: u32, - #[cfg(debug_assertions)] - /// Max to_local and to_remote outputs in a locally-generated commitment transaction - holder_max_commitment_tx_output: Mutex<(u64, u64)>, - #[cfg(debug_assertions)] - /// Max to_local and to_remote outputs in a remote-generated commitment transaction - counterparty_max_commitment_tx_output: Mutex<(u64, u64)>, - // (fee_sats, skip_remote_output, fee_range, holder_sig) last_sent_closing_fee: Option<(u64, bool, ClosingSignedFeeRange, Option)>, last_received_closing_sig: Option, @@ -1708,14 +1791,6 @@ pub(super) struct ChannelContext where SP::Target: SignerProvider { #[cfg(not(test))] holder_max_htlc_value_in_flight_msat: u64, - /// minimum channel reserve for self to maintain - set by them. - counterparty_selected_channel_reserve_satoshis: Option, - - #[cfg(test)] - pub(super) holder_selected_channel_reserve_satoshis: u64, - #[cfg(not(test))] - holder_selected_channel_reserve_satoshis: u64, - counterparty_htlc_minimum_msat: u64, holder_htlc_minimum_msat: u64, #[cfg(test)] @@ -1757,15 +1832,6 @@ pub(super) struct ChannelContext where SP::Target: SignerProvider { /// This can be used to rebroadcast the channel_announcement message later. announcement_sigs: Option<(Signature, Signature)>, - // We save these values so we can make sure `next_local_commit_tx_fee_msat` and - // `next_remote_commit_tx_fee_msat` properly predict what the next commitment transaction fee will - // be, by comparing the cached values to the fee of the tranaction generated by - // `build_commitment_transaction`. - #[cfg(any(test, fuzzing))] - next_local_commitment_tx_fee_info_cached: Mutex>, - #[cfg(any(test, fuzzing))] - next_remote_commitment_tx_fee_info_cached: Mutex>, - /// lnd has a long-standing bug where, upon reconnection, if the channel is not yet confirmed /// they will not send a channel_reestablish until the channel locks in. Then, they will send a /// channel_ready *before* sending the channel_reestablish (which is clearly a violation of @@ -1855,6 +1921,8 @@ trait InitialRemoteCommitmentReceiver where SP::Target: SignerProvide fn context_mut(&mut self) -> &mut ChannelContext; + fn funding(&self) -> &FundingScope; + fn received_msg(&self) -> &'static str; fn check_counterparty_commitment_signature( @@ -1863,10 +1931,10 @@ trait InitialRemoteCommitmentReceiver where SP::Target: SignerProvide let funding_script = self.context().get_funding_redeemscript(); let keys = self.context().build_holder_transaction_keys(holder_commitment_point.current_point()); - let initial_commitment_tx = self.context().build_commitment_transaction(holder_commitment_point.transaction_number(), &keys, true, false, logger).tx; + let initial_commitment_tx = self.context().build_commitment_transaction(self.funding(), holder_commitment_point.transaction_number(), &keys, 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); + let sighash = initial_commitment_bitcoin_tx.get_sighash_all(&funding_script, self.funding().channel_value_satoshis); // They sign the holder commitment transaction... log_trace!(logger, "Checking {} tx signature {} by key {} against tx {} (sighash {}) with redeemscript {} for channel {}.", self.received_msg(), log_bytes!(sig.serialize_compact()[..]), log_bytes!(self.context().counterparty_funding_pubkey().serialize()), @@ -1899,9 +1967,9 @@ trait InitialRemoteCommitmentReceiver where SP::Target: SignerProvide panic!("unexpected error type from check_counterparty_commitment_signature {:?}", e); } }; - let context = self.context_mut(); + let context = self.context(); let counterparty_keys = context.build_remote_transaction_keys(); - let counterparty_initial_commitment_tx = context.build_commitment_transaction(context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx; + let counterparty_initial_commitment_tx = context.build_commitment_transaction(self.funding(), 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(); @@ -1922,6 +1990,7 @@ trait InitialRemoteCommitmentReceiver where SP::Target: SignerProvide // Now that we're past error-generating stuff, update our local state: + let context = self.context_mut(); context.channel_id = channel_id; assert!(!context.channel_state.is_monitor_update_in_progress()); // We have not had any monitor(s) yet to fail update! @@ -1939,12 +2008,13 @@ trait InitialRemoteCommitmentReceiver where SP::Target: SignerProvide return Err(ChannelError::close("Failed to advance holder commitment point".to_owned())); } + let context = self.context(); let funding_redeemscript = context.get_funding_redeemscript(); let funding_txo = context.get_funding_txo().unwrap(); let funding_txo_script = funding_redeemscript.to_p2wsh(); let obscure_factor = get_commitment_transaction_number_obscure_factor(&context.get_holder_pubkeys().payment_point, &context.get_counterparty_pubkeys().payment_point, context.is_outbound()); let shutdown_script = context.shutdown_scriptpubkey.clone().map(|script| script.into_inner()); - let mut monitor_signer = signer_provider.derive_channel_signer(context.channel_value_satoshis, context.channel_keys_id); + let mut monitor_signer = signer_provider.derive_channel_signer(self.funding().channel_value_satoshis, context.channel_keys_id); monitor_signer.provide_channel_parameters(&context.channel_transaction_parameters); // TODO(RBF): When implementing RBF, the funding_txo passed here must only update // ChannelMonitorImp::first_confirmed_funding_txo during channel establishment, not splicing @@ -1952,7 +2022,7 @@ trait InitialRemoteCommitmentReceiver where SP::Target: SignerProvide shutdown_script, context.get_holder_selected_contest_delay(), &context.destination_script, (funding_txo, funding_txo_script), &context.channel_transaction_parameters, context.is_outbound(), - funding_redeemscript.clone(), context.channel_value_satoshis, + funding_redeemscript.clone(), self.funding().channel_value_satoshis, obscure_factor, holder_commitment_tx, best_block, context.counterparty_node_id, context.channel_id()); channel_monitor.provide_initial_counterparty_commitment_tx( @@ -1964,7 +2034,7 @@ trait InitialRemoteCommitmentReceiver where SP::Target: SignerProvide counterparty_initial_commitment_tx.to_countersignatory_value_sat(), logger); - context.cur_counterparty_commitment_transaction_number -= 1; + self.context_mut().cur_counterparty_commitment_transaction_number -= 1; Ok((channel_monitor, counterparty_initial_commitment_tx)) } @@ -1979,6 +2049,10 @@ impl InitialRemoteCommitmentReceiver for OutboundV1Channel wh &mut self.context } + fn funding(&self) -> &FundingScope { + &self.funding + } + fn received_msg(&self) -> &'static str { "funding_signed" } @@ -1993,6 +2067,10 @@ impl InitialRemoteCommitmentReceiver for InboundV1Channel whe &mut self.context } + fn funding(&self) -> &FundingScope { + &self.funding + } + fn received_msg(&self) -> &'static str { "funding_created" } @@ -2007,6 +2085,10 @@ impl InitialRemoteCommitmentReceiver for FundedChannel where &mut self.context } + fn funding(&self) -> &FundingScope { + &self.funding + } + fn received_msg(&self) -> &'static str { "commitment_signed" } @@ -2095,7 +2177,7 @@ impl PendingV2Channel where SP::Target: SignerProvider { let mut output_index = None; let expected_spk = self.context.get_funding_redeemscript().to_p2wsh(); for (idx, outp) in signing_session.unsigned_tx.outputs().enumerate() { - if outp.script_pubkey() == &expected_spk && outp.value() == self.context.get_value_satoshis() { + if outp.script_pubkey() == &expected_spk && outp.value() == self.funding.get_value_satoshis() { if output_index.is_some() { return Err(ChannelError::Close( ( @@ -2119,7 +2201,7 @@ impl PendingV2Channel where SP::Target: SignerProvider { self.context.holder_signer.as_mut().provide_channel_parameters(&self.context.channel_transaction_parameters); self.context.assert_no_commitment_advancement(transaction_number, "initial commitment_signed"); - let commitment_signed = self.context.get_initial_commitment_signed(logger); + let commitment_signed = self.context.get_initial_commitment_signed(&self.funding, logger); let commitment_signed = match commitment_signed { Ok(commitment_signed) => { self.context.funding_transaction = Some(signing_session.unsigned_tx.build_unsigned_tx()); @@ -2166,6 +2248,7 @@ impl PendingV2Channel where SP::Target: SignerProvider { match self.unfunded_context.holder_commitment_point { Some(holder_commitment_point) => { let funded_chan = FundedChannel { + funding: self.funding, context: self.context, interactive_tx_signing_session: Some(signing_session), holder_commitment_point, @@ -2203,7 +2286,7 @@ impl ChannelContext where SP::Target: SignerProvider { msg_channel_reserve_satoshis: u64, msg_push_msat: u64, open_channel_fields: msgs::CommonOpenChannelFields, - ) -> Result, ChannelError> + ) -> Result<(FundingScope, ChannelContext), ChannelError> where ES::Target: EntropySource, F::Target: FeeEstimator, @@ -2377,6 +2460,22 @@ impl ChannelContext where SP::Target: SignerProvider { // TODO(dual_funding): Checks for `funding_feerate_sat_per_1000_weight`? + let funding = FundingScope { + channel_value_satoshis, + value_to_self_msat, + counterparty_selected_channel_reserve_satoshis: Some(msg_channel_reserve_satoshis), + holder_selected_channel_reserve_satoshis, + + #[cfg(debug_assertions)] + holder_max_commitment_tx_output: Mutex::new((value_to_self_msat, (channel_value_satoshis * 1000 - msg_push_msat).saturating_sub(value_to_self_msat))), + #[cfg(debug_assertions)] + counterparty_max_commitment_tx_output: Mutex::new((value_to_self_msat, (channel_value_satoshis * 1000 - msg_push_msat).saturating_sub(value_to_self_msat))), + + #[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), + }; let channel_context = ChannelContext { user_id, @@ -2405,7 +2504,6 @@ impl ChannelContext where SP::Target: SignerProvider { destination_script, cur_counterparty_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER, - value_to_self_msat, pending_inbound_htlcs: Vec::new(), pending_outbound_htlcs: Vec::new(), @@ -2433,12 +2531,6 @@ impl ChannelContext where SP::Target: SignerProvider { signer_pending_closing: false, signer_pending_channel_ready: false, - - #[cfg(debug_assertions)] - holder_max_commitment_tx_output: Mutex::new((value_to_self_msat, (channel_value_satoshis * 1000 - msg_push_msat).saturating_sub(value_to_self_msat))), - #[cfg(debug_assertions)] - counterparty_max_commitment_tx_output: Mutex::new((value_to_self_msat, (channel_value_satoshis * 1000 - msg_push_msat).saturating_sub(value_to_self_msat))), - last_sent_closing_fee: None, last_received_closing_sig: None, pending_counterparty_closing_signed: None, @@ -2452,13 +2544,10 @@ impl ChannelContext where SP::Target: SignerProvider { channel_creation_height: current_chain_height, feerate_per_kw: open_channel_fields.commitment_feerate_sat_per_1000_weight, - channel_value_satoshis, counterparty_dust_limit_satoshis: open_channel_fields.dust_limit_satoshis, holder_dust_limit_satoshis: MIN_CHAN_DUST_LIMIT_SATOSHIS, counterparty_max_htlc_value_in_flight_msat: cmp::min(open_channel_fields.max_htlc_value_in_flight_msat, channel_value_satoshis * 1000), 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: Some(msg_channel_reserve_satoshis), - holder_selected_channel_reserve_satoshis, counterparty_htlc_minimum_msat: open_channel_fields.htlc_minimum_msat, 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: open_channel_fields.max_accepted_htlcs, @@ -2494,11 +2583,6 @@ impl ChannelContext where SP::Target: SignerProvider { 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, @@ -2521,7 +2605,7 @@ impl ChannelContext where SP::Target: SignerProvider { next_funding_txid: None, }; - Ok(channel_context) + Ok((funding, channel_context)) } fn new_for_outbound_channel<'a, ES: Deref, F: Deref, L: Deref>( @@ -2542,7 +2626,7 @@ impl ChannelContext where SP::Target: SignerProvider { holder_signer: ::EcdsaSigner, pubkeys: ChannelPublicKeys, _logger: L, - ) -> Result, APIError> + ) -> Result<(FundingScope, ChannelContext), APIError> where ES::Target: EntropySource, F::Target: FeeEstimator, @@ -2608,7 +2692,26 @@ impl ChannelContext where SP::Target: SignerProvider { let temporary_channel_id = temporary_channel_id.unwrap_or_else(|| ChannelId::temporary_from_entropy_source(entropy_source)); - Ok(Self { + let funding = FundingScope { + // We'll add our counterparty's `funding_satoshis` when we receive `accept_channel2`. + channel_value_satoshis, + value_to_self_msat, + counterparty_selected_channel_reserve_satoshis: None, // Filled in in accept_channel + holder_selected_channel_reserve_satoshis, + + // We'll add our counterparty's `funding_satoshis` to these max commitment output assertions + // when we receive `accept_channel2`. + #[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)), + + #[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), + }; + let channel_context = Self { user_id, config: LegacyChannelConfig { @@ -2626,8 +2729,6 @@ impl ChannelContext where SP::Target: SignerProvider { channel_state: ChannelState::NegotiatingFunding(NegotiatingFundingFlags::OUR_INIT_SENT), announcement_sigs_state: AnnouncementSigsState::NotSent, secp_ctx, - // We'll add our counterparty's `funding_satoshis` when we receive `accept_channel2`. - channel_value_satoshis, latest_monitor_update_id: 0, @@ -2636,7 +2737,6 @@ impl ChannelContext where SP::Target: SignerProvider { destination_script, cur_counterparty_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER, - value_to_self_msat, pending_inbound_htlcs: Vec::new(), pending_outbound_htlcs: Vec::new(), @@ -2664,13 +2764,6 @@ impl ChannelContext where SP::Target: SignerProvider { signer_pending_closing: false, signer_pending_channel_ready: false, - // We'll add our counterparty's `funding_satoshis` to these max commitment output assertions - // when we receive `accept_channel2`. - #[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, last_received_closing_sig: None, pending_counterparty_closing_signed: None, @@ -2690,8 +2783,6 @@ impl ChannelContext where SP::Target: SignerProvider { // We'll adjust this to include our counterparty's `funding_satoshis` when we // receive `accept_channel2`. 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, @@ -2724,11 +2815,6 @@ impl ChannelContext where SP::Target: SignerProvider { 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, @@ -2746,10 +2832,10 @@ impl ChannelContext where SP::Target: SignerProvider { local_initiated_shutdown: None, is_manual_broadcast: false, next_funding_txid: None, - }) - } + }; - pub(crate) fn get_value_to_self_msat(&self) -> u64 {self.value_to_self_msat} + Ok((funding, channel_context)) + } /// Allowed in any state (including after shutdown) pub fn get_update_time_counter(&self) -> u32 { @@ -2913,8 +2999,9 @@ impl ChannelContext where SP::Target: SignerProvider { /// Performs checks against necessary constraints after receiving either an `accept_channel` or /// `accept_channel2` message. pub fn do_accept_channel_checks( - &mut self, default_limits: &ChannelHandshakeLimits, their_features: &InitFeatures, - common_fields: &msgs::CommonAcceptChannelFields, channel_reserve_satoshis: u64, + &mut self, funding: &mut FundingScope, default_limits: &ChannelHandshakeLimits, + their_features: &InitFeatures, common_fields: &msgs::CommonAcceptChannelFields, + channel_reserve_satoshis: u64, ) -> Result<(), ChannelError> { let peer_limits = if let Some(ref limits) = self.inbound_handshake_limits_override { limits } else { default_limits }; @@ -2928,17 +3015,17 @@ impl ChannelContext where SP::Target: SignerProvider { if common_fields.dust_limit_satoshis > 21000000 * 100000000 { return Err(ChannelError::close(format!("Peer never wants payout outputs? dust_limit_satoshis was {}", common_fields.dust_limit_satoshis))); } - if channel_reserve_satoshis > self.channel_value_satoshis { - return Err(ChannelError::close(format!("Bogus channel_reserve_satoshis ({}). Must not be greater than ({})", channel_reserve_satoshis, self.channel_value_satoshis))); + if channel_reserve_satoshis > funding.channel_value_satoshis { + return Err(ChannelError::close(format!("Bogus channel_reserve_satoshis ({}). Must not be greater than ({})", channel_reserve_satoshis, funding.channel_value_satoshis))); } - if common_fields.dust_limit_satoshis > self.holder_selected_channel_reserve_satoshis { - return Err(ChannelError::close(format!("Dust limit ({}) is bigger than our channel reserve ({})", common_fields.dust_limit_satoshis, self.holder_selected_channel_reserve_satoshis))); + if common_fields.dust_limit_satoshis > funding.holder_selected_channel_reserve_satoshis { + return Err(ChannelError::close(format!("Dust limit ({}) is bigger than our channel reserve ({})", common_fields.dust_limit_satoshis, funding.holder_selected_channel_reserve_satoshis))); } - if channel_reserve_satoshis > self.channel_value_satoshis - self.holder_selected_channel_reserve_satoshis { + if channel_reserve_satoshis > funding.channel_value_satoshis - funding.holder_selected_channel_reserve_satoshis { return Err(ChannelError::close(format!("Bogus channel_reserve_satoshis ({}). Must not be greater than channel value minus our reserve ({})", - channel_reserve_satoshis, self.channel_value_satoshis - self.holder_selected_channel_reserve_satoshis))); + channel_reserve_satoshis, funding.channel_value_satoshis - funding.holder_selected_channel_reserve_satoshis))); } - let full_channel_value_msat = (self.channel_value_satoshis - channel_reserve_satoshis) * 1000; + let full_channel_value_msat = (funding.channel_value_satoshis - channel_reserve_satoshis) * 1000; if common_fields.htlc_minimum_msat >= full_channel_value_msat { return Err(ChannelError::close(format!("Minimum htlc value ({}) is full channel value ({})", common_fields.htlc_minimum_msat, full_channel_value_msat))); } @@ -3012,8 +3099,8 @@ impl ChannelContext where SP::Target: SignerProvider { } else { None }; self.counterparty_dust_limit_satoshis = common_fields.dust_limit_satoshis; - self.counterparty_max_htlc_value_in_flight_msat = cmp::min(common_fields.max_htlc_value_in_flight_msat, self.channel_value_satoshis * 1000); - self.counterparty_selected_channel_reserve_satoshis = Some(channel_reserve_satoshis); + self.counterparty_max_htlc_value_in_flight_msat = cmp::min(common_fields.max_htlc_value_in_flight_msat, funding.channel_value_satoshis * 1000); + funding.counterparty_selected_channel_reserve_satoshis = Some(channel_reserve_satoshis); self.counterparty_htlc_minimum_msat = common_fields.htlc_minimum_msat; self.counterparty_max_accepted_htlcs = common_fields.max_accepted_htlcs; @@ -3090,20 +3177,8 @@ impl ChannelContext where SP::Target: SignerProvider { } /// Allowed in any state (including after shutdown), but will return none before TheirInitSent - pub fn get_holder_htlc_maximum_msat(&self) -> Option { - self.get_htlc_maximum_msat(self.holder_max_htlc_value_in_flight_msat) - } - - /// Allowed in any state (including after shutdown) - pub fn get_announced_htlc_max_msat(&self) -> u64 { - return cmp::min( - // Upper bound by capacity. We make it a bit less than full capacity to prevent attempts - // to use full capacity. This is an effort to reduce routing failures, because in many cases - // channel might have been used to route very small values (either by honest users or as DoS). - self.channel_value_satoshis * 1000 * 9 / 10, - - self.counterparty_max_htlc_value_in_flight_msat - ); + pub fn get_holder_htlc_maximum_msat(&self, funding: &FundingScope) -> Option { + funding.get_htlc_maximum_msat(self.holder_max_htlc_value_in_flight_msat) } /// Allowed in any state (including after shutdown) @@ -3112,22 +3187,8 @@ impl ChannelContext where SP::Target: SignerProvider { } /// Allowed in any state (including after shutdown), but will return none before TheirInitSent - pub fn get_counterparty_htlc_maximum_msat(&self) -> Option { - self.get_htlc_maximum_msat(self.counterparty_max_htlc_value_in_flight_msat) - } - - fn get_htlc_maximum_msat(&self, party_max_htlc_value_in_flight_msat: u64) -> Option { - self.counterparty_selected_channel_reserve_satoshis.map(|counterparty_reserve| { - let holder_reserve = self.holder_selected_channel_reserve_satoshis; - cmp::min( - (self.channel_value_satoshis - counterparty_reserve - holder_reserve) * 1000, - party_max_htlc_value_in_flight_msat - ) - }) - } - - pub fn get_value_satoshis(&self) -> u64 { - self.channel_value_satoshis + pub fn get_counterparty_htlc_maximum_msat(&self, funding: &FundingScope) -> Option { + funding.get_htlc_maximum_msat(self.counterparty_max_htlc_value_in_flight_msat) } pub fn get_fee_proportional_millionths(&self) -> u32 { @@ -3265,7 +3326,7 @@ impl ChannelContext where SP::Target: SignerProvider { /// generated by the peer which proposed adding the HTLCs, and thus we need to understand both /// which peer generated this transaction and "to whom" this transaction flows. #[inline] - fn build_commitment_transaction(&self, commitment_number: u64, keys: &TxCreationKeys, local: bool, generated_by_local: bool, logger: &L) -> CommitmentStats + fn build_commitment_transaction(&self, funding: &FundingScope, commitment_number: u64, keys: &TxCreationKeys, local: bool, generated_by_local: bool, logger: &L) -> CommitmentStats where L::Target: Logger { let mut included_dust_htlcs: Vec<(HTLCOutputInCommitment, Option<&HTLCSource>)> = Vec::new(); @@ -3414,13 +3475,13 @@ impl ChannelContext where SP::Target: SignerProvider { } } - let value_to_self_msat: i64 = (self.value_to_self_msat - local_htlc_total_msat) as i64 + value_to_self_msat_offset; + let value_to_self_msat: i64 = (funding.value_to_self_msat - local_htlc_total_msat) as i64 + value_to_self_msat_offset; assert!(value_to_self_msat >= 0); // Note that in case they have several just-awaiting-last-RAA fulfills in-progress (ie // AwaitingRemoteRevokeToRemove or AwaitingRemovedRemoteRevoke) we may have allowed them to // "violate" their reserve value by couting those against it. Thus, we have to convert // everything to i64 before subtracting as otherwise we can overflow. - let value_to_remote_msat: i64 = (self.channel_value_satoshis * 1000) as i64 - (self.value_to_self_msat as i64) - (remote_htlc_total_msat as i64) - value_to_self_msat_offset; + let value_to_remote_msat: i64 = (funding.channel_value_satoshis * 1000) as i64 - (funding.value_to_self_msat as i64) - (remote_htlc_total_msat as i64) - value_to_self_msat_offset; assert!(value_to_remote_msat >= 0); #[cfg(debug_assertions)] @@ -3428,13 +3489,13 @@ impl ChannelContext where SP::Target: SignerProvider { // Make sure that the to_self/to_remote is always either past the appropriate // channel_reserve *or* it is making progress towards it. let mut broadcaster_max_commitment_tx_output = if generated_by_local { - self.holder_max_commitment_tx_output.lock().unwrap() + funding.holder_max_commitment_tx_output.lock().unwrap() } else { - self.counterparty_max_commitment_tx_output.lock().unwrap() + funding.counterparty_max_commitment_tx_output.lock().unwrap() }; - debug_assert!(broadcaster_max_commitment_tx_output.0 <= value_to_self_msat as u64 || value_to_self_msat / 1000 >= self.counterparty_selected_channel_reserve_satoshis.unwrap() as i64); + debug_assert!(broadcaster_max_commitment_tx_output.0 <= value_to_self_msat as u64 || value_to_self_msat / 1000 >= funding.counterparty_selected_channel_reserve_satoshis.unwrap() as i64); broadcaster_max_commitment_tx_output.0 = cmp::max(broadcaster_max_commitment_tx_output.0, value_to_self_msat as u64); - debug_assert!(broadcaster_max_commitment_tx_output.1 <= value_to_remote_msat as u64 || value_to_remote_msat / 1000 >= self.holder_selected_channel_reserve_satoshis as i64); + debug_assert!(broadcaster_max_commitment_tx_output.1 <= value_to_remote_msat as u64 || value_to_remote_msat / 1000 >= funding.holder_selected_channel_reserve_satoshis as i64); broadcaster_max_commitment_tx_output.1 = cmp::max(broadcaster_max_commitment_tx_output.1, value_to_remote_msat as u64); } @@ -3768,9 +3829,11 @@ impl ChannelContext where SP::Target: SignerProvider { /// 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, fee_estimator: &LowerBoundedFeeEstimator) - -> AvailableBalances - where F::Target: FeeEstimator + pub fn get_available_balances( + &self, funding: &FundingScope, fee_estimator: &LowerBoundedFeeEstimator, + ) -> AvailableBalances + where + F::Target: FeeEstimator, { let context = &self; // Note that we have to handle overflow due to the case mentioned in the docs in general @@ -3779,10 +3842,10 @@ impl ChannelContext where SP::Target: SignerProvider { let dust_exposure_limiting_feerate = self.get_dust_exposure_limiting_feerate(&fee_estimator); let htlc_stats = context.get_pending_htlc_stats(None, dust_exposure_limiting_feerate); - let outbound_capacity_msat = context.value_to_self_msat + let outbound_capacity_msat = funding.value_to_self_msat .saturating_sub(htlc_stats.pending_outbound_htlcs_value_msat) .saturating_sub( - context.counterparty_selected_channel_reserve_satoshis.unwrap_or(0) * 1000); + funding.counterparty_selected_channel_reserve_satoshis.unwrap_or(0) * 1000); let mut available_capacity_msat = outbound_capacity_msat; @@ -3805,9 +3868,9 @@ impl ChannelContext where SP::Target: SignerProvider { } let htlc_above_dust = HTLCCandidate::new(real_dust_limit_timeout_sat * 1000, HTLCInitiator::LocalOffered); - let mut max_reserved_commit_tx_fee_msat = context.next_local_commit_tx_fee_msat(htlc_above_dust, Some(())); + let mut max_reserved_commit_tx_fee_msat = context.next_local_commit_tx_fee_msat(&funding, htlc_above_dust, Some(())); let htlc_dust = HTLCCandidate::new(real_dust_limit_timeout_sat * 1000 - 1, HTLCInitiator::LocalOffered); - let mut min_reserved_commit_tx_fee_msat = context.next_local_commit_tx_fee_msat(htlc_dust, Some(())); + let mut min_reserved_commit_tx_fee_msat = context.next_local_commit_tx_fee_msat(&funding, htlc_dust, Some(())); if !context.get_channel_type().supports_anchors_zero_fee_htlc_tx() { max_reserved_commit_tx_fee_msat *= FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE; min_reserved_commit_tx_fee_msat *= FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE; @@ -3836,10 +3899,10 @@ impl ChannelContext where SP::Target: SignerProvider { } 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(Some(htlc_above_dust), None); + let max_reserved_commit_tx_fee_msat = context.next_remote_commit_tx_fee_msat(&funding, Some(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) + let holder_selected_chan_reserve_msat = funding.holder_selected_channel_reserve_satoshis * 1000; + let remote_balance_msat = (funding.channel_value_satoshis * 1000 - funding.value_to_self_msat) .saturating_sub(htlc_stats.pending_inbound_htlcs_value_msat); if remote_balance_msat < max_reserved_commit_tx_fee_msat + holder_selected_chan_reserve_msat + anchor_outputs_value_msat { @@ -3912,10 +3975,10 @@ impl ChannelContext where SP::Target: SignerProvider { #[allow(deprecated)] // TODO: Remove once balance_msat is removed. AvailableBalances { - inbound_capacity_msat: cmp::max(context.channel_value_satoshis as i64 * 1000 - - context.value_to_self_msat as i64 + inbound_capacity_msat: cmp::max(funding.channel_value_satoshis as i64 * 1000 + - funding.value_to_self_msat as i64 - htlc_stats.pending_inbound_htlcs_value_msat as i64 - - context.holder_selected_channel_reserve_satoshis as i64 * 1000, + - funding.holder_selected_channel_reserve_satoshis as i64 * 1000, 0) as u64, outbound_capacity_msat, next_outbound_htlc_limit_msat: available_capacity_msat, @@ -3923,11 +3986,6 @@ impl ChannelContext where SP::Target: SignerProvider { } } - pub fn get_holder_counterparty_selected_channel_reserve_satoshis(&self) -> (u64, Option) { - 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. /// @@ -3938,7 +3996,9 @@ impl ChannelContext where SP::Target: SignerProvider { /// 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 { + fn next_local_commit_tx_fee_msat( + &self, _funding: &FundingScope, htlc: HTLCCandidate, fee_spike_buffer_htlc: Option<()>, + ) -> u64 { let context = &self; assert!(context.is_outbound()); @@ -4027,7 +4087,7 @@ impl ChannelContext where SP::Target: SignerProvider { }, feerate: context.feerate_per_kw, }; - *context.next_local_commitment_tx_fee_info_cached.lock().unwrap() = Some(commitment_tx_info); + *_funding.next_local_commitment_tx_fee_info_cached.lock().unwrap() = Some(commitment_tx_info); } res } @@ -4042,7 +4102,9 @@ impl ChannelContext where SP::Target: SignerProvider { /// 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: Option, fee_spike_buffer_htlc: Option<()>) -> u64 { + fn next_remote_commit_tx_fee_msat( + &self, _funding: &FundingScope, htlc: Option, fee_spike_buffer_htlc: Option<()>, + ) -> u64 { debug_assert!(htlc.is_some() || fee_spike_buffer_htlc.is_some(), "At least one of the options must be set"); let context = &self; @@ -4121,7 +4183,7 @@ impl ChannelContext where SP::Target: SignerProvider { }, feerate: context.feerate_per_kw, }; - *context.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = Some(commitment_tx_info); + *_funding.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = Some(commitment_tx_info); } res } @@ -4174,7 +4236,7 @@ impl ChannelContext where SP::Target: SignerProvider { /// those explicitly stated to be allowed after shutdown completes, eg some simple getters). /// Also returns the list of payment_hashes for channels which we can safely fail backwards /// immediately (others we will have to allow to time out). - pub fn force_shutdown(&mut self, should_broadcast: bool, closure_reason: ClosureReason) -> ShutdownResult { + pub fn force_shutdown(&mut self, funding: &FundingScope, should_broadcast: bool, closure_reason: ClosureReason) -> ShutdownResult { // Note that we MUST only generate a monitor update that indicates force-closure - we're // called during initialization prior to the chain_monitor in the encompassing ChannelManager // being fully configured in some cases. Thus, its likely any monitor events we generate will @@ -4223,12 +4285,12 @@ impl ChannelContext where SP::Target: SignerProvider { unbroadcasted_batch_funding_txid, channel_id: self.channel_id, user_channel_id: self.user_id, - channel_capacity_satoshis: self.channel_value_satoshis, + channel_capacity_satoshis: funding.channel_value_satoshis, counterparty_node_id: self.counterparty_node_id, unbroadcasted_funding_tx, is_manual_broadcast: self.is_manual_broadcast, channel_funding_txo: self.get_funding_txo(), - last_local_balance_msat: self.value_to_self_msat, + last_local_balance_msat: funding.value_to_self_msat, } } @@ -4322,7 +4384,7 @@ impl ChannelContext where SP::Target: SignerProvider { } fn get_initial_counterparty_commitment_signature( - &self, logger: &L + &self, funding: &FundingScope, logger: &L ) -> Result where SP::Target: SignerProvider, @@ -4330,7 +4392,7 @@ impl ChannelContext where SP::Target: SignerProvider { { let counterparty_keys = self.build_remote_transaction_keys(); let counterparty_initial_commitment_tx = self.build_commitment_transaction( - self.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx; + funding, self.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx; match self.holder_signer { // TODO (taproot|arik): move match into calling method for Taproot ChannelSignerType::Ecdsa(ref ecdsa) => { @@ -4349,7 +4411,7 @@ impl ChannelContext where SP::Target: SignerProvider { } fn get_initial_commitment_signed( - &mut self, logger: &L + &mut self, funding: &FundingScope, logger: &L ) -> Result where SP::Target: SignerProvider, @@ -4361,7 +4423,7 @@ impl ChannelContext where SP::Target: SignerProvider { panic!("Tried to get an initial commitment_signed messsage at a time other than immediately after initial handshake completion (or tried to get funding_created twice)"); } - let signature = match self.get_initial_counterparty_commitment_signature(logger) { + let signature = match self.get_initial_counterparty_commitment_signature(funding, logger) { Ok(res) => res, Err(e) => { log_error!(logger, "Got bad signatures: {:?}!", e); @@ -4383,7 +4445,7 @@ impl ChannelContext where SP::Target: SignerProvider { #[cfg(all(test, dual_funding))] pub fn get_initial_counterparty_commitment_signature_for_test( - &mut self, logger: &L, channel_transaction_parameters: ChannelTransactionParameters, + &mut self, funding: &FundingScope, logger: &L, channel_transaction_parameters: ChannelTransactionParameters, counterparty_cur_commitment_point_override: PublicKey, ) -> Result where @@ -4392,7 +4454,7 @@ impl ChannelContext where SP::Target: SignerProvider { { self.counterparty_cur_commitment_point = Some(counterparty_cur_commitment_point_override); self.channel_transaction_parameters = channel_transaction_parameters; - self.get_initial_counterparty_commitment_signature(logger) + self.get_initial_counterparty_commitment_signature(funding, logger) } } @@ -4502,6 +4564,7 @@ pub(super) struct DualFundingChannelContext { // 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 FundedChannel where SP::Target: SignerProvider { + pub funding: FundingScope, pub context: ChannelContext, pub interactive_tx_signing_session: Option, holder_commitment_point: HolderCommitmentPoint, @@ -4648,8 +4711,8 @@ impl FundedChannel where assert!(self.context.pending_update_fee.is_none()); let mut total_fee_satoshis = proposed_total_fee_satoshis; - let mut value_to_holder: i64 = (self.context.value_to_self_msat as i64) / 1000 - if self.context.is_outbound() { total_fee_satoshis as i64 } else { 0 }; - let mut value_to_counterparty: i64 = ((self.context.channel_value_satoshis * 1000 - self.context.value_to_self_msat) as i64 / 1000) - if self.context.is_outbound() { 0 } else { total_fee_satoshis as i64 }; + let mut value_to_holder: i64 = (self.funding.value_to_self_msat as i64) / 1000 - if self.context.is_outbound() { total_fee_satoshis as i64 } else { 0 }; + let mut value_to_counterparty: i64 = ((self.funding.channel_value_satoshis * 1000 - self.funding.value_to_self_msat) as i64 / 1000) - if self.context.is_outbound() { 0 } else { total_fee_satoshis as i64 }; if value_to_holder < 0 { assert!(self.context.is_outbound()); @@ -5087,7 +5150,7 @@ impl FundedChannel where if self.context.channel_state.is_peer_disconnected() { return Err(ChannelError::close("Peer sent update_add_htlc when we needed a channel_reestablish".to_owned())); } - if msg.amount_msat > self.context.channel_value_satoshis * 1000 { + if msg.amount_msat > self.funding.channel_value_satoshis * 1000 { return Err(ChannelError::close("Remote side tried to send more than the total value of the channel".to_owned())); } if msg.amount_msat == 0 { @@ -5128,9 +5191,9 @@ impl FundedChannel where } let pending_value_to_self_msat = - self.context.value_to_self_msat + htlc_stats.pending_inbound_htlcs_value_msat - removed_outbound_total_msat; + self.funding.value_to_self_msat + htlc_stats.pending_inbound_htlcs_value_msat - removed_outbound_total_msat; let pending_remote_value_msat = - self.context.channel_value_satoshis * 1000 - pending_value_to_self_msat; + self.funding.channel_value_satoshis * 1000 - pending_value_to_self_msat; if pending_remote_value_msat < msg.amount_msat { return Err(ChannelError::close("Remote HTLC add would overdraw remaining funds".to_owned())); } @@ -5140,7 +5203,7 @@ impl FundedChannel where { let remote_commit_tx_fee_msat = if self.context.is_outbound() { 0 } else { let htlc_candidate = HTLCCandidate::new(msg.amount_msat, HTLCInitiator::RemoteOffered); - self.context.next_remote_commit_tx_fee_msat(Some(htlc_candidate), None) // Don't include the extra fee spike buffer HTLC in calculations + self.context.next_remote_commit_tx_fee_msat(&self.funding, Some(htlc_candidate), None) // Don't include the extra fee spike buffer HTLC in calculations }; let anchor_outputs_value_msat = if !self.context.is_outbound() && self.context.get_channel_type().supports_anchors_zero_fee_htlc_tx() { ANCHOR_OUTPUT_VALUE_SATOSHI * 2 * 1000 @@ -5150,7 +5213,7 @@ impl FundedChannel where if pending_remote_value_msat.saturating_sub(msg.amount_msat).saturating_sub(anchor_outputs_value_msat) < remote_commit_tx_fee_msat { return Err(ChannelError::close("Remote HTLC add would not leave enough to pay for fees".to_owned())); }; - if pending_remote_value_msat.saturating_sub(msg.amount_msat).saturating_sub(remote_commit_tx_fee_msat).saturating_sub(anchor_outputs_value_msat) < self.context.holder_selected_channel_reserve_satoshis * 1000 { + if pending_remote_value_msat.saturating_sub(msg.amount_msat).saturating_sub(remote_commit_tx_fee_msat).saturating_sub(anchor_outputs_value_msat) < self.funding.holder_selected_channel_reserve_satoshis * 1000 { return Err(ChannelError::close("Remote HTLC add would put them under remote reserve value".to_owned())); } } @@ -5163,8 +5226,8 @@ impl FundedChannel where if self.context.is_outbound() { // 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.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 + anchor_outputs_value_msat { + let local_commit_tx_fee_msat = self.context.next_local_commit_tx_fee_msat(&self.funding, htlc_candidate, None); + if self.funding.value_to_self_msat < self.funding.counterparty_selected_channel_reserve_satoshis.unwrap() * 1000 + local_commit_tx_fee_msat + anchor_outputs_value_msat { return Err(ChannelError::close("Cannot accept HTLC that would put our balance under counterparty-announced channel reserve value".to_owned())); } } @@ -5309,11 +5372,11 @@ impl FundedChannel where let keys = self.context.build_holder_transaction_keys(self.holder_commitment_point.current_point()); - let commitment_stats = self.context.build_commitment_transaction(self.holder_commitment_point.transaction_number(), &keys, true, false, logger); + let commitment_stats = self.context.build_commitment_transaction(&self.funding, self.holder_commitment_point.transaction_number(), &keys, true, false, logger); let commitment_txid = { let trusted_tx = commitment_stats.tx.trust(); let bitcoin_tx = trusted_tx.built_transaction(); - let sighash = bitcoin_tx.get_sighash_all(&funding_script, self.context.channel_value_satoshis); + let sighash = bitcoin_tx.get_sighash_all(&funding_script, self.funding.channel_value_satoshis); log_trace!(logger, "Checking commitment tx signature {} by key {} against tx {} (sighash {}) with redeemscript {} in channel {}", log_bytes!(msg.signature.serialize_compact()[..]), @@ -5333,7 +5396,7 @@ impl FundedChannel where } else { false }; if update_fee { debug_assert!(!self.context.is_outbound()); - let counterparty_reserve_we_require_msat = self.context.holder_selected_channel_reserve_satoshis * 1000; + let counterparty_reserve_we_require_msat = self.funding.holder_selected_channel_reserve_satoshis * 1000; if commitment_stats.remote_balance_msat < commitment_stats.total_fee_sat * 1000 + counterparty_reserve_we_require_msat { return Err(ChannelError::close("Funding remote cannot afford proposed new fee".to_owned())); } @@ -5341,8 +5404,8 @@ impl FundedChannel where #[cfg(any(test, fuzzing))] { if self.context.is_outbound() { - let projected_commit_tx_info = self.context.next_local_commitment_tx_fee_info_cached.lock().unwrap().take(); - *self.context.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = None; + let projected_commit_tx_info = self.funding.next_local_commitment_tx_fee_info_cached.lock().unwrap().take(); + *self.funding.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = None; if let Some(info) = projected_commit_tx_info { let total_pending_htlcs = self.context.pending_inbound_htlcs.len() + self.context.pending_outbound_htlcs.len() + self.context.holding_cell_htlc_updates.len(); @@ -5711,8 +5774,8 @@ impl FundedChannel where #[cfg(any(test, fuzzing))] { - *self.context.next_local_commitment_tx_fee_info_cached.lock().unwrap() = None; - *self.context.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = None; + *self.funding.next_local_commitment_tx_fee_info_cached.lock().unwrap() = None; + *self.funding.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = None; } match &self.context.holder_signer { @@ -5857,7 +5920,7 @@ impl FundedChannel where } } } - self.context.value_to_self_msat = (self.context.value_to_self_msat as i64 + value_to_self_msat_diff) as u64; + self.funding.value_to_self_msat = (self.funding.value_to_self_msat as i64 + value_to_self_msat_diff) as u64; if let Some((feerate, update_state)) = self.context.pending_update_fee { match update_state { @@ -6061,10 +6124,10 @@ impl FundedChannel where let dust_exposure_limiting_feerate = self.context.get_dust_exposure_limiting_feerate(&fee_estimator); let htlc_stats = self.context.get_pending_htlc_stats(Some(feerate_per_kw), dust_exposure_limiting_feerate); let keys = self.context.build_holder_transaction_keys(self.holder_commitment_point.current_point()); - let commitment_stats = self.context.build_commitment_transaction(self.holder_commitment_point.transaction_number(), &keys, true, true, logger); + let commitment_stats = self.context.build_commitment_transaction(&self.funding, self.holder_commitment_point.transaction_number(), &keys, true, true, logger); let buffer_fee_msat = commit_tx_fee_sat(feerate_per_kw, commitment_stats.num_nondust_htlcs + htlc_stats.on_holder_tx_outbound_holding_cell_htlcs_count as usize + CONCURRENT_INBOUND_HTLC_FEE_BUFFER as usize, self.context.get_channel_type()) * 1000; let holder_balance_msat = commitment_stats.local_balance_msat - htlc_stats.outbound_holding_cell_msat; - if holder_balance_msat < buffer_fee_msat + self.context.counterparty_selected_channel_reserve_satoshis.unwrap() * 1000 { + if holder_balance_msat < buffer_fee_msat + self.funding.counterparty_selected_channel_reserve_satoshis.unwrap() * 1000 { //TODO: auto-close after a number of failures? log_debug!(logger, "Cannot afford to send new feerate at {}", feerate_per_kw); return None; @@ -6362,7 +6425,7 @@ impl FundedChannel where } let funding_signed = if self.context.signer_pending_funding && !self.context.is_outbound() { 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 + 1, &counterparty_keys, false, false, logger).tx; + let counterparty_initial_commitment_tx = self.context.build_commitment_transaction(&self.funding, self.context.cur_counterparty_commitment_transaction_number + 1, &counterparty_keys, false, false, logger).tx; self.context.get_funding_signed_msg(logger, counterparty_initial_commitment_tx) } else { None }; // Provide a `channel_ready` message if we need to, but only if we're _not_ still pending @@ -6407,7 +6470,7 @@ impl FundedChannel where let signed_tx = if let (Some(ClosingSigned { signature, .. }), Some(counterparty_sig)) = (closing_signed.as_ref(), self.context.last_received_closing_sig) { let funding_redeemscript = self.context.get_funding_redeemscript(); - let sighash = closing_tx.trust().get_sighash_all(&funding_redeemscript, self.context.channel_value_satoshis); + let sighash = closing_tx.trust().get_sighash_all(&funding_redeemscript, self.funding.channel_value_satoshis); debug_assert!(self.context.secp_ctx.verify_ecdsa(&sighash, &counterparty_sig, &self.context.get_counterparty_pubkeys().funding_pubkey).is_ok()); Some(self.build_signed_closing_transaction(&closing_tx, &counterparty_sig, signature)) @@ -6416,7 +6479,7 @@ impl FundedChannel where (closing_signed, signed_tx, shutdown_result) } Err(err) => { - let shutdown = self.context.force_shutdown(true, ClosureReason::ProcessingError {err: err.to_string()}); + let shutdown = self.context.force_shutdown(&self.funding, true, ClosureReason::ProcessingError {err: err.to_string()}); (None, None, Some(shutdown)) } } @@ -6822,7 +6885,7 @@ impl FundedChannel where cmp::max(normal_feerate as u64 * tx_weight / 1000 + self.context.config.options.force_close_avoidance_max_fee_satoshis, proposed_max_feerate as u64 * tx_weight / 1000) } else { - self.context.channel_value_satoshis - (self.context.value_to_self_msat + 999) / 1000 + self.funding.channel_value_satoshis - (self.funding.value_to_self_msat + 999) / 1000 }; self.context.closing_fee_limits = Some((proposed_total_fee_satoshis, proposed_max_total_fee_satoshis)); @@ -7073,12 +7136,12 @@ impl FundedChannel where unbroadcasted_batch_funding_txid: self.context.unbroadcasted_batch_funding_txid(), channel_id: self.context.channel_id, user_channel_id: self.context.user_id, - channel_capacity_satoshis: self.context.channel_value_satoshis, + channel_capacity_satoshis: self.funding.channel_value_satoshis, counterparty_node_id: self.context.counterparty_node_id, unbroadcasted_funding_tx: self.context.unbroadcasted_funding(), is_manual_broadcast: self.context.is_manual_broadcast, channel_funding_txo: self.context.get_funding_txo(), - last_local_balance_msat: self.context.value_to_self_msat, + last_local_balance_msat: self.funding.value_to_self_msat, } } @@ -7118,7 +7181,7 @@ impl FundedChannel where if used_total_fee != msg.fee_satoshis { return Err(ChannelError::close(format!("Remote sent us a closing_signed with a fee other than the value they can claim. Fee in message: {}. Actual closing tx fee: {}", msg.fee_satoshis, used_total_fee))); } - let sighash = closing_tx.trust().get_sighash_all(&funding_redeemscript, self.context.channel_value_satoshis); + let sighash = closing_tx.trust().get_sighash_all(&funding_redeemscript, self.funding.channel_value_satoshis); match self.context.secp_ctx.verify_ecdsa(&sighash, &msg.signature, &self.context.get_counterparty_pubkeys().funding_pubkey) { Ok(_) => {}, @@ -7127,7 +7190,7 @@ impl FundedChannel where // limits, so check for that case by re-checking the signature here. skip_remote_output = true; closing_tx = self.build_closing_transaction(msg.fee_satoshis, skip_remote_output)?.0; - let sighash = closing_tx.trust().get_sighash_all(&funding_redeemscript, self.context.channel_value_satoshis); + let sighash = closing_tx.trust().get_sighash_all(&funding_redeemscript, self.funding.channel_value_satoshis); secp_check!(self.context.secp_ctx.verify_ecdsa(&sighash, &msg.signature, self.context.counterparty_funding_pubkey()), "Invalid closing tx signature from peer".to_owned()); }, }; @@ -7193,7 +7256,7 @@ impl FundedChannel where if !self.context.is_outbound() { // They have to pay, so pick the highest fee in the overlapping range. // We should never set an upper bound aside from their full balance - debug_assert_eq!(our_max_fee, self.context.channel_value_satoshis - (self.context.value_to_self_msat + 999) / 1000); + debug_assert_eq!(our_max_fee, self.funding.channel_value_satoshis - (self.funding.value_to_self_msat + 999) / 1000); propose_fee!(cmp::min(max_fee_satoshis, our_max_fee)); } else { if msg.fee_satoshis < our_min_fee || msg.fee_satoshis > our_max_fee { @@ -7340,9 +7403,9 @@ impl FundedChannel where } let pending_value_to_self_msat = - self.context.value_to_self_msat + htlc_stats.pending_inbound_htlcs_value_msat - removed_outbound_total_msat; + self.funding.value_to_self_msat + htlc_stats.pending_inbound_htlcs_value_msat - removed_outbound_total_msat; let pending_remote_value_msat = - self.context.channel_value_satoshis * 1000 - pending_value_to_self_msat; + self.funding.channel_value_satoshis * 1000 - pending_value_to_self_msat; if !self.context.is_outbound() { // `Some(())` is for the fee spike buffer we keep for the remote. This deviates from @@ -7352,11 +7415,11 @@ impl FundedChannel where // // A `None` `HTLCCandidate` is used as in this case because we're already accounting for // the incoming HTLC as it has been fully committed by both sides. - let mut remote_fee_cost_incl_stuck_buffer_msat = self.context.next_remote_commit_tx_fee_msat(None, Some(())); + let mut remote_fee_cost_incl_stuck_buffer_msat = self.context.next_remote_commit_tx_fee_msat(&self.funding, None, Some(())); if !self.context.get_channel_type().supports_anchors_zero_fee_htlc_tx() { remote_fee_cost_incl_stuck_buffer_msat *= FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE; } - if pending_remote_value_msat.saturating_sub(self.context.holder_selected_channel_reserve_satoshis * 1000).saturating_sub(anchor_outputs_value_msat) < remote_fee_cost_incl_stuck_buffer_msat { + if pending_remote_value_msat.saturating_sub(self.funding.holder_selected_channel_reserve_satoshis * 1000).saturating_sub(anchor_outputs_value_msat) < remote_fee_cost_incl_stuck_buffer_msat { log_info!(logger, "Attempting to fail HTLC due to fee spike buffer violation in channel {}. Rebalancing is required.", &self.context.channel_id()); return Err(("Fee spike buffer violation", 0x1000|7)); } @@ -7385,9 +7448,9 @@ impl FundedChannel where #[cfg(test)] pub fn get_value_stat(&self) -> ChannelValueStat { ChannelValueStat { - value_to_self_msat: self.context.value_to_self_msat, - channel_value_msat: self.context.channel_value_satoshis * 1000, - channel_reserve_msat: self.context.counterparty_selected_channel_reserve_satoshis.unwrap() * 1000, + value_to_self_msat: self.funding.value_to_self_msat, + channel_value_msat: self.funding.channel_value_satoshis * 1000, + channel_reserve_msat: self.funding.counterparty_selected_channel_reserve_satoshis.unwrap() * 1000, pending_outbound_htlcs_amount_msat: self.context.pending_outbound_htlcs.iter().map(|ref h| h.amount_msat).sum::(), pending_inbound_htlcs_amount_msat: self.context.pending_inbound_htlcs.iter().map(|ref h| h.amount_msat).sum::(), holding_cell_outbound_amount_msat: { @@ -7650,7 +7713,7 @@ impl FundedChannel where if tx.compute_txid() == funding_txo.txid { let txo_idx = funding_txo.index as usize; if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != self.context.get_funding_redeemscript().to_p2wsh() || - tx.output[txo_idx].value.to_sat() != self.context.channel_value_satoshis { + tx.output[txo_idx].value.to_sat() != self.funding.channel_value_satoshis { if self.context.is_outbound() { // If we generated the funding transaction and it doesn't match what it // should, the client is really broken and we should just panic and @@ -8140,7 +8203,7 @@ impl FundedChannel where { return Err(ChannelError::Ignore("Cannot send HTLC until channel is fully established and we haven't started shutting down".to_owned())); } - let channel_total_msat = self.context.channel_value_satoshis * 1000; + let channel_total_msat = self.funding.channel_value_satoshis * 1000; if amount_msat > channel_total_msat { return Err(ChannelError::Ignore(format!("Cannot send amount {}, because it is more than the total value of the channel {}", amount_msat, channel_total_msat))); } @@ -8149,7 +8212,7 @@ impl FundedChannel where return Err(ChannelError::Ignore("Cannot send 0-msat HTLC".to_owned())); } - let available_balances = self.context.get_available_balances(fee_estimator); + let available_balances = self.context.get_available_balances(&self.funding, fee_estimator); 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))); @@ -8288,14 +8351,14 @@ impl FundedChannel where where L::Target: Logger { let counterparty_keys = self.context.build_remote_transaction_keys(); - let commitment_stats = self.context.build_commitment_transaction(self.context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, true, logger); + let commitment_stats = self.context.build_commitment_transaction(&self.funding, self.context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, true, logger); let counterparty_commitment_tx = commitment_stats.tx; #[cfg(any(test, fuzzing))] { if !self.context.is_outbound() { - let projected_commit_tx_info = self.context.next_remote_commitment_tx_fee_info_cached.lock().unwrap().take(); - *self.context.next_local_commitment_tx_fee_info_cached.lock().unwrap() = None; + let projected_commit_tx_info = self.funding.next_remote_commitment_tx_fee_info_cached.lock().unwrap().take(); + *self.funding.next_local_commitment_tx_fee_info_cached.lock().unwrap() = None; if let Some(info) = projected_commit_tx_info { let total_pending_htlcs = self.context.pending_inbound_htlcs.len() + self.context.pending_outbound_htlcs.len(); if info.total_pending_htlcs == total_pending_htlcs @@ -8320,7 +8383,7 @@ impl FundedChannel where self.build_commitment_no_state_update(logger); let counterparty_keys = self.context.build_remote_transaction_keys(); - let commitment_stats = self.context.build_commitment_transaction(self.context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, true, logger); + let commitment_stats = self.context.build_commitment_transaction(&self.funding, self.context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, true, logger); let counterparty_commitment_txid = commitment_stats.tx.trust().txid(); match &self.context.holder_signer { @@ -8503,6 +8566,8 @@ impl FundedChannel where Ok((shutdown, monitor_update, dropped_outbound_htlcs)) } + // Miscellaneous utilities + pub fn inflight_htlc_sources(&self) -> impl Iterator { self.context.holding_cell_htlc_updates.iter() .flat_map(|htlc_update| { @@ -8514,10 +8579,22 @@ impl FundedChannel where }) .chain(self.context.pending_outbound_htlcs.iter().map(|htlc| (&htlc.source, &htlc.payment_hash))) } + + pub fn get_announced_htlc_max_msat(&self) -> u64 { + return cmp::min( + // Upper bound by capacity. We make it a bit less than full capacity to prevent attempts + // to use full capacity. This is an effort to reduce routing failures, because in many cases + // channel might have been used to route very small values (either by honest users or as DoS). + self.funding.channel_value_satoshis * 1000 * 9 / 10, + + self.context.counterparty_max_htlc_value_in_flight_msat + ); + } } /// A not-yet-funded outbound (from holder) channel using V1 channel establishment. pub(super) struct OutboundV1Channel where SP::Target: SignerProvider { + pub funding: FundingScope, pub context: ChannelContext, pub unfunded_context: UnfundedChannelContext, /// We tried to send an `open_channel` message but our commitment point wasn't ready. @@ -8549,7 +8626,7 @@ impl OutboundV1Channel where SP::Target: SignerProvider { let holder_signer = signer_provider.derive_channel_signer(channel_value_satoshis, channel_keys_id); let pubkeys = holder_signer.pubkeys().clone(); - let context = ChannelContext::new_for_outbound_channel( + let (funding, context) = ChannelContext::new_for_outbound_channel( fee_estimator, entropy_source, signer_provider, @@ -8575,14 +8652,14 @@ impl OutboundV1Channel where SP::Target: SignerProvider { // We initialize `signer_pending_open_channel` to false, and leave setting the flag // for when we try to generate the open_channel message. - let chan = Self { context, unfunded_context, signer_pending_open_channel: false }; + let chan = Self { funding, context, unfunded_context, signer_pending_open_channel: false }; Ok(chan) } /// Only allowed after [`ChannelContext::channel_transaction_parameters`] is set. fn get_funding_created_msg(&mut self, logger: &L) -> Option where L::Target: Logger { 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_initial_commitment_tx = self.context.build_commitment_transaction(&self.funding, self.context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx; let signature = match &self.context.holder_signer { // TODO (taproot|arik): move match into calling method for Taproot ChannelSignerType::Ecdsa(ecdsa) => { @@ -8709,7 +8786,7 @@ impl OutboundV1Channel where SP::Target: SignerProvider { common_fields: msgs::CommonOpenChannelFields { chain_hash, temporary_channel_id: self.context.channel_id, - funding_satoshis: self.context.channel_value_satoshis, + funding_satoshis: self.funding.channel_value_satoshis, dust_limit_satoshis: self.context.holder_dust_limit_satoshis, max_htlc_value_in_flight_msat: self.context.holder_max_htlc_value_in_flight_msat, htlc_minimum_msat: self.context.holder_htlc_minimum_msat, @@ -8729,8 +8806,8 @@ impl OutboundV1Channel where SP::Target: SignerProvider { }), channel_type: Some(self.context.channel_type.clone()), }, - push_msat: self.context.channel_value_satoshis * 1000 - self.context.value_to_self_msat, - channel_reserve_satoshis: self.context.holder_selected_channel_reserve_satoshis, + push_msat: self.funding.channel_value_satoshis * 1000 - self.funding.value_to_self_msat, + channel_reserve_satoshis: self.funding.holder_selected_channel_reserve_satoshis, }) } @@ -8740,7 +8817,9 @@ impl OutboundV1Channel where SP::Target: SignerProvider { their_features: &InitFeatures ) -> Result<(), ChannelError> { self.context.do_accept_channel_checks( - default_limits, their_features, &msg.common_fields, msg.channel_reserve_satoshis) + &mut self.funding, default_limits, their_features, &msg.common_fields, + msg.channel_reserve_satoshis, + ) } /// Handles a funding_signed message from the remote end. @@ -8775,6 +8854,7 @@ impl OutboundV1Channel where SP::Target: SignerProvider { log_info!(logger, "Received funding_signed from peer for channel {}", &self.context.channel_id()); let mut channel = FundedChannel { + funding: self.funding, context: self.context, interactive_tx_signing_session: None, holder_commitment_point, @@ -8815,6 +8895,7 @@ impl OutboundV1Channel where SP::Target: SignerProvider { /// A not-yet-funded inbound (from counterparty) channel using V1 channel establishment. pub(super) struct InboundV1Channel where SP::Target: SignerProvider { + pub funding: FundingScope, pub context: ChannelContext, pub unfunded_context: UnfundedChannelContext, pub signer_pending_accept_channel: bool, @@ -8883,7 +8964,7 @@ impl InboundV1Channel where SP::Target: SignerProvider { htlc_basepoint: HtlcBasepoint::from(msg.common_fields.htlc_basepoint) }; - let context = ChannelContext::new_for_inbound_channel( + let (funding, context) = ChannelContext::new_for_inbound_channel( fee_estimator, entropy_source, signer_provider, @@ -8907,7 +8988,7 @@ impl InboundV1Channel where SP::Target: SignerProvider { unfunded_channel_age_ticks: 0, holder_commitment_point: HolderCommitmentPoint::new(&context.holder_signer, &context.secp_ctx), }; - let chan = Self { context, unfunded_context, signer_pending_accept_channel: false }; + let chan = Self { funding, context, unfunded_context, signer_pending_accept_channel: false }; Ok(chan) } @@ -8976,7 +9057,7 @@ impl InboundV1Channel where SP::Target: SignerProvider { }), channel_type: Some(self.context.channel_type.clone()), }, - channel_reserve_satoshis: self.context.holder_selected_channel_reserve_satoshis, + channel_reserve_satoshis: self.funding.holder_selected_channel_reserve_satoshis, #[cfg(taproot)] next_local_nonce: None, }) @@ -9040,6 +9121,7 @@ impl InboundV1Channel where SP::Target: SignerProvider { // Promote the channel to a full-fledged one now that we have updated the state and have a // `ChannelMonitor`. let mut channel = FundedChannel { + funding: self.funding, context: self.context, interactive_tx_signing_session: None, holder_commitment_point, @@ -9073,6 +9155,7 @@ impl InboundV1Channel where SP::Target: SignerProvider { // A not-yet-funded channel using V2 channel establishment. pub(super) struct PendingV2Channel where SP::Target: SignerProvider { + pub funding: FundingScope, pub context: ChannelContext, pub unfunded_context: UnfundedChannelContext, pub dual_funding_context: DualFundingChannelContext, @@ -9109,7 +9192,7 @@ impl PendingV2Channel where SP::Target: SignerProvider { "Provided current chain height of {} doesn't make sense for a height-based timelock for the funding transaction", current_chain_height) })?; - let context = ChannelContext::new_for_outbound_channel( + let (funding, context) = ChannelContext::new_for_outbound_channel( fee_estimator, entropy_source, signer_provider, @@ -9133,6 +9216,7 @@ impl PendingV2Channel where SP::Target: SignerProvider { holder_commitment_point: HolderCommitmentPoint::new(&context.holder_signer, &context.secp_ctx), }; let chan = Self { + funding, context, unfunded_context, dual_funding_context: DualFundingChannelContext { @@ -9188,7 +9272,7 @@ impl PendingV2Channel where SP::Target: SignerProvider { common_fields: msgs::CommonOpenChannelFields { chain_hash, temporary_channel_id: self.context.temporary_channel_id.unwrap(), - funding_satoshis: self.context.channel_value_satoshis, + funding_satoshis: self.funding.channel_value_satoshis, dust_limit_satoshis: self.context.holder_dust_limit_satoshis, max_htlc_value_in_flight_msat: self.context.holder_max_htlc_value_in_flight_msat, htlc_minimum_msat: self.context.holder_htlc_minimum_msat, @@ -9255,7 +9339,7 @@ impl PendingV2Channel where SP::Target: SignerProvider { htlc_basepoint: HtlcBasepoint(msg.common_fields.htlc_basepoint) }; - let mut context = ChannelContext::new_for_inbound_channel( + let (funding, mut context) = ChannelContext::new_for_inbound_channel( fee_estimator, entropy_source, signer_provider, @@ -9299,7 +9383,7 @@ impl PendingV2Channel where SP::Target: SignerProvider { is_initiator: false, inputs_to_contribute: our_funding_inputs, outputs_to_contribute: Vec::new(), - expected_remote_shared_funding_output: Some((context.get_funding_redeemscript().to_p2wsh(), context.channel_value_satoshis)), + expected_remote_shared_funding_output: Some((context.get_funding_redeemscript().to_p2wsh(), funding.channel_value_satoshis)), } ).map_err(|_| ChannelError::Close(( "V2 channel rejected due to sender error".into(), @@ -9311,6 +9395,7 @@ impl PendingV2Channel where SP::Target: SignerProvider { holder_commitment_point: HolderCommitmentPoint::new(&context.holder_signer, &context.secp_ctx), }; Ok(Self { + funding, context, dual_funding_context, interactive_tx_constructor, @@ -9503,7 +9588,7 @@ impl Writeable for FundedChannel where SP::Target: SignerProvider } channel_state.to_u32().write(writer)?; } - self.context.channel_value_satoshis.write(writer)?; + self.funding.channel_value_satoshis.write(writer)?; self.context.latest_monitor_update_id.write(writer)?; @@ -9517,7 +9602,7 @@ impl Writeable for FundedChannel where SP::Target: SignerProvider self.holder_commitment_point.transaction_number().write(writer)?; self.context.cur_counterparty_commitment_transaction_number.write(writer)?; - self.context.value_to_self_msat.write(writer)?; + self.funding.value_to_self_msat.write(writer)?; let mut dropped_inbound_htlcs = 0; for htlc in self.context.pending_inbound_htlcs.iter() { @@ -9698,7 +9783,7 @@ impl Writeable for FundedChannel where SP::Target: SignerProvider self.context.counterparty_max_htlc_value_in_flight_msat.write(writer)?; // Note that this field is ignored by 0.0.99+ as the TLV Optional variant is used instead. - self.context.counterparty_selected_channel_reserve_satoshis.unwrap_or(0).write(writer)?; + self.funding.counterparty_selected_channel_reserve_satoshis.unwrap_or(0).write(writer)?; self.context.counterparty_htlc_minimum_msat.write(writer)?; self.context.holder_htlc_minimum_msat.write(writer)?; @@ -9742,13 +9827,13 @@ impl Writeable for FundedChannel where SP::Target: SignerProvider // a different percentage of the channel value then 10%, which older versions of LDK used // to set it to before the percentage was made configurable. let serialized_holder_selected_reserve = - if self.context.holder_selected_channel_reserve_satoshis != get_legacy_default_holder_selected_channel_reserve_satoshis(self.context.channel_value_satoshis) - { Some(self.context.holder_selected_channel_reserve_satoshis) } else { None }; + if self.funding.holder_selected_channel_reserve_satoshis != get_legacy_default_holder_selected_channel_reserve_satoshis(self.funding.channel_value_satoshis) + { Some(self.funding.holder_selected_channel_reserve_satoshis) } else { None }; let mut old_max_in_flight_percent_config = UserConfig::default().channel_handshake_config; old_max_in_flight_percent_config.max_inbound_htlc_value_in_flight_percent_of_channel = MAX_IN_FLIGHT_PERCENT_LEGACY; let serialized_holder_htlc_max_in_flight = - if self.context.holder_max_htlc_value_in_flight_msat != get_holder_max_htlc_value_in_flight_msat(self.context.channel_value_satoshis, &old_max_in_flight_percent_config) + if self.context.holder_max_htlc_value_in_flight_msat != get_holder_max_htlc_value_in_flight_msat(self.funding.channel_value_satoshis, &old_max_in_flight_percent_config) { Some(self.context.holder_max_htlc_value_in_flight_msat) } else { None }; let channel_pending_event_emitted = Some(self.context.channel_pending_event_emitted); @@ -9782,7 +9867,7 @@ impl Writeable for FundedChannel where SP::Target: SignerProvider // override that. (1, self.context.minimum_depth, option), (2, chan_type, option), - (3, self.context.counterparty_selected_channel_reserve_satoshis, option), + (3, self.funding.counterparty_selected_channel_reserve_satoshis, option), (4, serialized_holder_selected_reserve, option), (5, self.context.config, required), (6, serialized_holder_htlc_max_in_flight, option), @@ -10284,6 +10369,22 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch }; Ok(FundedChannel { + funding: FundingScope { + channel_value_satoshis, + value_to_self_msat, + counterparty_selected_channel_reserve_satoshis, + holder_selected_channel_reserve_satoshis: holder_selected_channel_reserve_satoshis.unwrap(), + + #[cfg(debug_assertions)] + holder_max_commitment_tx_output: Mutex::new((0, 0)), + #[cfg(debug_assertions)] + counterparty_max_commitment_tx_output: Mutex::new((0, 0)), + + #[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), + }, context: ChannelContext { user_id, @@ -10300,7 +10401,6 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch channel_state, announcement_sigs_state: announcement_sigs_state.unwrap(), secp_ctx, - channel_value_satoshis, latest_monitor_update_id, @@ -10309,7 +10409,6 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch destination_script, cur_counterparty_commitment_transaction_number, - value_to_self_msat, holder_max_accepted_htlcs, pending_inbound_htlcs, @@ -10340,11 +10439,6 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch update_time_counter, feerate_per_kw, - #[cfg(debug_assertions)] - holder_max_commitment_tx_output: Mutex::new((0, 0)), - #[cfg(debug_assertions)] - counterparty_max_commitment_tx_output: Mutex::new((0, 0)), - last_sent_closing_fee: None, last_received_closing_sig: None, pending_counterparty_closing_signed: None, @@ -10361,8 +10455,6 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch holder_dust_limit_satoshis, counterparty_max_htlc_value_in_flight_msat, holder_max_htlc_value_in_flight_msat: holder_max_htlc_value_in_flight_msat.unwrap(), - counterparty_selected_channel_reserve_satoshis, - holder_selected_channel_reserve_satoshis: holder_selected_channel_reserve_satoshis.unwrap(), counterparty_htlc_minimum_msat, holder_htlc_minimum_msat, counterparty_max_accepted_htlcs, @@ -10387,11 +10479,6 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch announcement_sigs, - #[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, @@ -10661,7 +10748,7 @@ mod tests { // 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.context.next_local_commit_tx_fee_msat(htlc_candidate, None); + let local_commit_tx_fee = node_a_chan.context.next_local_commit_tx_fee_msat(&node_a_chan.funding, htlc_candidate, None); let local_commit_fee_0_htlcs = commit_tx_fee_sat(node_a_chan.context.feerate_per_kw, 0, node_a_chan.context.get_channel_type()) * 1000; assert_eq!(local_commit_tx_fee, local_commit_fee_0_htlcs); @@ -10670,7 +10757,7 @@ mod tests { node_a_chan.context.channel_transaction_parameters.is_outbound_from_holder = false; let remote_commit_fee_3_htlcs = commit_tx_fee_sat(node_a_chan.context.feerate_per_kw, 3, node_a_chan.context.get_channel_type()) * 1000; let htlc_candidate = HTLCCandidate::new(htlc_amount_msat, HTLCInitiator::LocalOffered); - let remote_commit_tx_fee = node_a_chan.context.next_remote_commit_tx_fee_msat(Some(htlc_candidate), None); + let remote_commit_tx_fee = node_a_chan.context.next_remote_commit_tx_fee_msat(&node_a_chan.funding, Some(htlc_candidate), None); assert_eq!(remote_commit_tx_fee, remote_commit_fee_3_htlcs); } @@ -10698,13 +10785,13 @@ mod tests { // counted as dust when it shouldn't be. let htlc_amt_above_timeout = ((253 * htlc_timeout_tx_weight(chan.context.get_channel_type()) / 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.context.next_local_commit_tx_fee_msat(htlc_candidate, None); + let commitment_tx_fee = chan.context.next_local_commit_tx_fee_msat(&chan.funding, 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.get_channel_type()) / 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.context.next_local_commit_tx_fee_msat(htlc_candidate, None); + let commitment_tx_fee = chan.context.next_local_commit_tx_fee_msat(&chan.funding, htlc_candidate, None); assert_eq!(commitment_tx_fee, commitment_tx_fee_0_htlcs); chan.context.channel_transaction_parameters.is_outbound_from_holder = false; @@ -10712,13 +10799,13 @@ mod tests { // 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.get_channel_type()) / 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.context.next_remote_commit_tx_fee_msat(Some(htlc_candidate), None); + let commitment_tx_fee = chan.context.next_remote_commit_tx_fee_msat(&chan.funding, Some(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.get_channel_type()) / 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.context.next_remote_commit_tx_fee_msat(Some(htlc_candidate), None); + let commitment_tx_fee = chan.context.next_remote_commit_tx_fee_msat(&chan.funding, Some(htlc_candidate), None); assert_eq!(commitment_tx_fee, commitment_tx_fee_1_htlc); } @@ -10803,12 +10890,12 @@ mod tests { // `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 mut chan_1 = OutboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_2_percent), 10000000, 100000, 42, &config_2_percent, 0, 42, None, &logger).unwrap(); - let chan_1_value_msat = chan_1.context.channel_value_satoshis * 1000; + let chan_1_value_msat = chan_1.funding.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 = OutboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_99_percent), 10000000, 100000, 42, &config_99_percent, 0, 42, None, &logger).unwrap(); - let chan_2_value_msat = chan_2.context.channel_value_satoshis * 1000; + let chan_2_value_msat = chan_2.funding.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); let chan_1_open_channel_msg = chan_1.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); @@ -10817,38 +10904,38 @@ mod tests { // `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_3 = InboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&config_2_percent), &channelmanager::provided_init_features(&config_2_percent), &chan_1_open_channel_msg, 7, &config_2_percent, 0, &&logger, /*is_0conf=*/false).unwrap(); - let chan_3_value_msat = chan_3.context.channel_value_satoshis * 1000; + let chan_3_value_msat = chan_3.funding.channel_value_satoshis * 1000; assert_eq!(chan_3.context.holder_max_htlc_value_in_flight_msat, (chan_3_value_msat as f64 * 0.02) as u64); // Test with the upper bound - 1 of valid values (99%). let chan_4 = InboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&config_99_percent), &channelmanager::provided_init_features(&config_99_percent), &chan_1_open_channel_msg, 7, &config_99_percent, 0, &&logger, /*is_0conf=*/false).unwrap(); - let chan_4_value_msat = chan_4.context.channel_value_satoshis * 1000; + let chan_4_value_msat = chan_4.funding.channel_value_satoshis * 1000; assert_eq!(chan_4.context.holder_max_htlc_value_in_flight_msat, (chan_4_value_msat as f64 * 0.99) as u64); // Test that `OutboundV1Channel::new` 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 = OutboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_0_percent), 10000000, 100000, 42, &config_0_percent, 0, 42, None, &logger).unwrap(); - let chan_5_value_msat = chan_5.context.channel_value_satoshis * 1000; + let chan_5_value_msat = chan_5.funding.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 `OutboundV1Channel::new` 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 = OutboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_101_percent), 10000000, 100000, 42, &config_101_percent, 0, 42, None, &logger).unwrap(); - let chan_6_value_msat = chan_6.context.channel_value_satoshis * 1000; + let chan_6_value_msat = chan_6.funding.channel_value_satoshis * 1000; assert_eq!(chan_6.context.holder_max_htlc_value_in_flight_msat, chan_6_value_msat); // Test that `InboundV1Channel::new` 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_7 = InboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&config_0_percent), &channelmanager::provided_init_features(&config_0_percent), &chan_1_open_channel_msg, 7, &config_0_percent, 0, &&logger, /*is_0conf=*/false).unwrap(); - let chan_7_value_msat = chan_7.context.channel_value_satoshis * 1000; + let chan_7_value_msat = chan_7.funding.channel_value_satoshis * 1000; assert_eq!(chan_7.context.holder_max_htlc_value_in_flight_msat, (chan_7_value_msat as f64 * 0.01) as u64); // Test that `InboundV1Channel::new` 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_8 = InboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&config_101_percent), &channelmanager::provided_init_features(&config_101_percent), &chan_1_open_channel_msg, 7, &config_101_percent, 0, &&logger, /*is_0conf=*/false).unwrap(); - let chan_8_value_msat = chan_8.context.channel_value_satoshis * 1000; + let chan_8_value_msat = chan_8.funding.channel_value_satoshis * 1000; assert_eq!(chan_8.context.holder_max_htlc_value_in_flight_msat, chan_8_value_msat); } @@ -10889,8 +10976,8 @@ mod tests { outbound_node_config.channel_handshake_config.their_channel_reserve_proportional_millionths = (outbound_selected_channel_reserve_perc * 1_000_000.0) as u32; let mut chan = OutboundV1Channel::<&TestKeysInterface>::new(&&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, None, &logger).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); + let expected_outbound_selected_chan_reserve = cmp::max(MIN_THEIR_CHAN_RESERVE_SATOSHIS, (chan.funding.channel_value_satoshis as f64 * outbound_selected_channel_reserve_perc) as u64); + assert_eq!(chan.funding.holder_selected_channel_reserve_satoshis, expected_outbound_selected_chan_reserve); let chan_open_channel_msg = chan.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); let mut inbound_node_config = UserConfig::default(); @@ -10899,10 +10986,10 @@ mod tests { if outbound_selected_channel_reserve_perc + inbound_selected_channel_reserve_perc < 1.0 { let chan_inbound_node = InboundV1Channel::<&TestKeysInterface>::new(&&fee_est, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&inbound_node_config), &channelmanager::provided_init_features(&outbound_node_config), &chan_open_channel_msg, 7, &inbound_node_config, 0, &&logger, /*is_0conf=*/false).unwrap(); - let expected_inbound_selected_chan_reserve = cmp::max(MIN_THEIR_CHAN_RESERVE_SATOSHIS, (chan.context.channel_value_satoshis as f64 * inbound_selected_channel_reserve_perc) as u64); + let expected_inbound_selected_chan_reserve = cmp::max(MIN_THEIR_CHAN_RESERVE_SATOSHIS, (chan.funding.channel_value_satoshis as f64 * inbound_selected_channel_reserve_perc) as u64); - assert_eq!(chan_inbound_node.context.holder_selected_channel_reserve_satoshis, expected_inbound_selected_chan_reserve); - assert_eq!(chan_inbound_node.context.counterparty_selected_channel_reserve_satoshis.unwrap(), expected_outbound_selected_chan_reserve); + assert_eq!(chan_inbound_node.funding.holder_selected_channel_reserve_satoshis, expected_inbound_selected_chan_reserve); + assert_eq!(chan_inbound_node.funding.counterparty_selected_channel_reserve_satoshis.unwrap(), expected_outbound_selected_chan_reserve); } else { // Channel Negotiations failed let result = InboundV1Channel::<&TestKeysInterface>::new(&&fee_est, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&inbound_node_config), &channelmanager::provided_init_features(&outbound_node_config), &chan_open_channel_msg, 7, &inbound_node_config, 0, &&logger, /*is_0conf=*/false); @@ -11159,7 +11246,7 @@ mod tests { config.channel_handshake_config.announce_for_forwarding = false; let mut chan = OutboundV1Channel::<&Keys>::new(&LowerBoundedFeeEstimator::new(&feeest), &&keys_provider, &&keys_provider, counterparty_node_id, &channelmanager::provided_init_features(&config), 10_000_000, 0, 42, &config, 0, 42, None, &*logger).unwrap(); // Nothing uses their network key in this test chan.context.holder_dust_limit_satoshis = 546; - chan.context.counterparty_selected_channel_reserve_satoshis = Some(0); // Filled in in accept_channel + chan.funding.counterparty_selected_channel_reserve_satoshis = Some(0); // Filled in in accept_channel let funding_info = OutPoint{ txid: Txid::from_str("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(), index: 0 }; @@ -11215,7 +11302,7 @@ mod tests { $( { $htlc_idx: expr, $counterparty_htlc_sig_hex: expr, $htlc_sig_hex: expr, $htlc_tx_hex: expr } ), * } ) => { { let (commitment_tx, htlcs): (_, Vec) = { - let mut commitment_stats = chan.context.build_commitment_transaction(0xffffffffffff - 42, &keys, true, false, &logger); + let mut commitment_stats = chan.context.build_commitment_transaction(&chan.funding, 0xffffffffffff - 42, &keys, true, false, &logger); let htlcs = commitment_stats.htlcs_included.drain(..) .filter_map(|(htlc, _)| if htlc.transaction_output_index.is_some() { Some(htlc) } else { None }) @@ -11226,7 +11313,7 @@ mod tests { let unsigned_tx = trusted_tx.built_transaction(); let redeemscript = chan.context.get_funding_redeemscript(); let counterparty_signature = Signature::from_der(&>::from_hex($counterparty_sig_hex).unwrap()[..]).unwrap(); - let sighash = unsigned_tx.get_sighash_all(&redeemscript, chan.context.channel_value_satoshis); + let sighash = unsigned_tx.get_sighash_all(&redeemscript, chan.funding.channel_value_satoshis); log_trace!(logger, "unsigned_tx = {}", serialize(&unsigned_tx.transaction).as_hex()); assert!(secp_ctx.verify_ecdsa(&sighash, &counterparty_signature, chan.context.counterparty_funding_pubkey()).is_ok(), "verify counterparty commitment sig"); @@ -11286,7 +11373,7 @@ mod tests { let htlc_counterparty_sig = htlc_counterparty_sig_iter.next().unwrap(); let htlc_holder_sig = signer.sign_holder_htlc_transaction(&htlc_tx, 0, &HTLCDescriptor { channel_derivation_parameters: ChannelDerivationParameters { - value_satoshis: chan.context.channel_value_satoshis, + value_satoshis: chan.funding.channel_value_satoshis, keys_id: chan.context.channel_keys_id, transaction_parameters: chan.context.channel_transaction_parameters.clone(), }, @@ -11318,7 +11405,7 @@ mod tests { "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b80024a010000000000002200202b1b5854183c12d3316565972c4668929d314d81c5dcdbb21cb45fe8a9a8114f10529800000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e0400473044022007cf6b405e9c9b4f527b0ecad9d8bb661fabb8b12abf7d1c0b3ad1855db3ed490220616d5c1eeadccc63bd775a131149455d62d95a42c2a1b01cc7821fc42dce7778014730440220655bf909fb6fa81d086f1336ac72c97906dce29d1b166e305c99152d810e26e1022051f577faa46412c46707aaac46b65d50053550a66334e00a44af2706f27a865801475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220", {}); // simple commitment tx with no HTLCs - chan.context.value_to_self_msat = 7000000000; + chan.funding.value_to_self_msat = 7000000000; test_commitment!("3045022100c3127b33dcc741dd6b05b1e63cbd1a9a7d816f37af9b6756fa2376b056f032370220408b96279808fe57eb7e463710804cdf4f108388bc5cf722d8c848d2c7f9f3b0", "30440220616210b2cc4d3afb601013c373bbd8aac54febd9f15400379a8cb65ce7deca60022034236c010991beb7ff770510561ae8dc885b8d38d1947248c38f2ae055647142", @@ -11392,7 +11479,7 @@ mod tests { }); // commitment tx with all five HTLCs untrimmed (minimum feerate) - chan.context.value_to_self_msat = 6993000000; // 7000000000 - 7000000 + chan.funding.value_to_self_msat = 6993000000; // 7000000000 - 7000000 chan.context.feerate_per_kw = 0; test_commitment!("3044022009b048187705a8cbc9ad73adbe5af148c3d012e1f067961486c822c7af08158c022006d66f3704cfab3eb2dc49dae24e4aa22a6910fc9b424007583204e3621af2e5", @@ -11426,7 +11513,7 @@ mod tests { } ); // commitment tx with seven outputs untrimmed (maximum feerate) - chan.context.value_to_self_msat = 6993000000; // 7000000000 - 7000000 + chan.funding.value_to_self_msat = 6993000000; // 7000000000 - 7000000 chan.context.feerate_per_kw = 647; test_commitment!("3045022100a135f9e8a5ed25f7277446c67956b00ce6f610ead2bdec2c2f686155b7814772022059f1f6e1a8b336a68efcc1af3fe4d422d4827332b5b067501b099c47b7b5b5ee", @@ -11460,7 +11547,7 @@ mod tests { } ); // commitment tx with six outputs untrimmed (minimum feerate) - chan.context.value_to_self_msat = 6993000000; // 7000000000 - 7000000 + chan.funding.value_to_self_msat = 6993000000; // 7000000000 - 7000000 chan.context.feerate_per_kw = 648; test_commitment!("304402203948f900a5506b8de36a4d8502f94f21dd84fd9c2314ab427d52feaa7a0a19f2022059b6a37a4adaa2c5419dc8aea63c6e2a2ec4c4bde46207f6dc1fcd22152fc6e5", @@ -11489,7 +11576,7 @@ mod tests { } ); // anchors: commitment tx with six outputs untrimmed (minimum dust limit) - chan.context.value_to_self_msat = 6993000000; // 7000000000 - 7000000 + chan.funding.value_to_self_msat = 6993000000; // 7000000000 - 7000000 chan.context.feerate_per_kw = 645; chan.context.holder_dust_limit_satoshis = 1001; @@ -11519,7 +11606,7 @@ mod tests { } ); // commitment tx with six outputs untrimmed (maximum feerate) - chan.context.value_to_self_msat = 6993000000; // 7000000000 - 7000000 + chan.funding.value_to_self_msat = 6993000000; // 7000000000 - 7000000 chan.context.feerate_per_kw = 2069; chan.context.holder_dust_limit_satoshis = 546; @@ -11549,7 +11636,7 @@ mod tests { } ); // commitment tx with five outputs untrimmed (minimum feerate) - chan.context.value_to_self_msat = 6993000000; // 7000000000 - 7000000 + chan.funding.value_to_self_msat = 6993000000; // 7000000000 - 7000000 chan.context.feerate_per_kw = 2070; test_commitment!("304402204ca1ba260dee913d318271d86e10ca0f5883026fb5653155cff600fb40895223022037b145204b7054a40e08bb1fefbd826f827b40838d3e501423bcc57924bcb50c", @@ -11573,7 +11660,7 @@ mod tests { } ); // commitment tx with five outputs untrimmed (maximum feerate) - chan.context.value_to_self_msat = 6993000000; // 7000000000 - 7000000 + chan.funding.value_to_self_msat = 6993000000; // 7000000000 - 7000000 chan.context.feerate_per_kw = 2194; test_commitment!("304402204bb3d6e279d71d9da414c82de42f1f954267c762b2e2eb8b76bc3be4ea07d4b0022014febc009c5edc8c3fc5d94015de163200f780046f1c293bfed8568f08b70fb3", @@ -11597,7 +11684,7 @@ mod tests { } ); // commitment tx with four outputs untrimmed (minimum feerate) - chan.context.value_to_self_msat = 6993000000; // 7000000000 - 7000000 + chan.funding.value_to_self_msat = 6993000000; // 7000000000 - 7000000 chan.context.feerate_per_kw = 2195; test_commitment!("304402201a8c1b1f9671cd9e46c7323a104d7047cc48d3ee80d40d4512e0c72b8dc65666022066d7f9a2ce18c9eb22d2739ffcce05721c767f9b607622a31b6ea5793ddce403", @@ -11616,7 +11703,7 @@ mod tests { } ); // anchors: commitment tx with four outputs untrimmed (minimum dust limit) - chan.context.value_to_self_msat = 6993000000; // 7000000000 - 7000000 + chan.funding.value_to_self_msat = 6993000000; // 7000000000 - 7000000 chan.context.feerate_per_kw = 2185; chan.context.holder_dust_limit_satoshis = 2001; let cached_channel_type = chan.context.channel_type; @@ -11638,7 +11725,7 @@ mod tests { } ); // commitment tx with four outputs untrimmed (maximum feerate) - chan.context.value_to_self_msat = 6993000000; // 7000000000 - 7000000 + chan.funding.value_to_self_msat = 6993000000; // 7000000000 - 7000000 chan.context.feerate_per_kw = 3702; chan.context.holder_dust_limit_satoshis = 546; chan.context.channel_type = cached_channel_type.clone(); @@ -11659,7 +11746,7 @@ mod tests { } ); // commitment tx with three outputs untrimmed (minimum feerate) - chan.context.value_to_self_msat = 6993000000; // 7000000000 - 7000000 + chan.funding.value_to_self_msat = 6993000000; // 7000000000 - 7000000 chan.context.feerate_per_kw = 3703; test_commitment!("3045022100b495d239772a237ff2cf354b1b11be152fd852704cb184e7356d13f2fb1e5e430220723db5cdb9cbd6ead7bfd3deb419cf41053a932418cbb22a67b581f40bc1f13e", @@ -11673,7 +11760,7 @@ mod tests { } ); // anchors: commitment tx with three outputs untrimmed (minimum dust limit) - chan.context.value_to_self_msat = 6993000000; // 7000000000 - 7000000 + chan.funding.value_to_self_msat = 6993000000; // 7000000000 - 7000000 chan.context.feerate_per_kw = 3687; chan.context.holder_dust_limit_satoshis = 3001; chan.context.channel_type = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(); @@ -11689,7 +11776,7 @@ mod tests { } ); // commitment tx with three outputs untrimmed (maximum feerate) - chan.context.value_to_self_msat = 6993000000; // 7000000000 - 7000000 + chan.funding.value_to_self_msat = 6993000000; // 7000000000 - 7000000 chan.context.feerate_per_kw = 4914; chan.context.holder_dust_limit_satoshis = 546; chan.context.channel_type = cached_channel_type.clone(); @@ -11705,7 +11792,7 @@ mod tests { } ); // commitment tx with two outputs untrimmed (minimum feerate) - chan.context.value_to_self_msat = 6993000000; // 7000000000 - 7000000 + chan.funding.value_to_self_msat = 6993000000; // 7000000000 - 7000000 chan.context.feerate_per_kw = 4915; chan.context.holder_dust_limit_satoshis = 546; @@ -11714,7 +11801,7 @@ mod tests { "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8002c0c62d0000000000160014cc1b07838e387deacd0e5232e1e8b49f4c29e484fa926a00000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e04004830450221008a953551f4d67cb4df3037207fc082ddaf6be84d417b0bd14c80aab66f1b01a402207508796dc75034b2dee876fe01dc05a08b019f3e5d689ac8842ade2f1befccf50147304402203a286936e74870ca1459c700c71202af0381910a6bfab687ef494ef1bc3e02c902202506c362d0e3bee15e802aa729bf378e051644648253513f1c085b264cc2a72001475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220", {}); // anchors: commitment tx with two outputs untrimmed (minimum dust limit) - chan.context.value_to_self_msat = 6993000000; // 7000000000 - 7000000 + chan.funding.value_to_self_msat = 6993000000; // 7000000000 - 7000000 chan.context.feerate_per_kw = 4894; chan.context.holder_dust_limit_satoshis = 4001; chan.context.channel_type = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(); @@ -11724,7 +11811,7 @@ mod tests { "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b80044a010000000000002200202b1b5854183c12d3316565972c4668929d314d81c5dcdbb21cb45fe8a9a8114f4a01000000000000220020e9e86e4823faa62e222ebc858a226636856158f07e69898da3b0d1af0ddb3994c0c62d0000000000220020f3394e1e619b0eca1f91be2fb5ab4dfc59ba5b84ebe014ad1d43a564d012994ad0886a00000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e04004830450221009f16ac85d232e4eddb3fcd750a68ebf0b58e3356eaada45d3513ede7e817bf4c02207c2b043b4e5f971261975406cb955219fa56bffe5d834a833694b5abc1ce4cfd01483045022100e784a66b1588575801e237d35e510fd92a81ae3a4a2a1b90c031ad803d07b3f3022021bc5f16501f167607d63b681442da193eb0a76b4b7fd25c2ed4f8b28fd35b9501475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220", {}); // commitment tx with two outputs untrimmed (maximum feerate) - chan.context.value_to_self_msat = 6993000000; // 7000000000 - 7000000 + chan.funding.value_to_self_msat = 6993000000; // 7000000000 - 7000000 chan.context.feerate_per_kw = 9651180; chan.context.holder_dust_limit_satoshis = 546; chan.context.channel_type = cached_channel_type.clone(); @@ -11734,7 +11821,7 @@ mod tests { "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b800222020000000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80ec0c62d0000000000160014cc1b07838e387deacd0e5232e1e8b49f4c29e4840400483045022100e11b638c05c650c2f63a421d36ef8756c5ce82f2184278643520311cdf50aa200220259565fb9c8e4a87ccaf17f27a3b9ca4f20625754a0920d9c6c239d8156a11de0147304402200a8544eba1d216f5c5e530597665fa9bec56943c0f66d98fc3d028df52d84f7002201e45fa5c6bc3a506cc2553e7d1c0043a9811313fc39c954692c0d47cfce2bbd301475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220", {}); // commitment tx with one output untrimmed (minimum feerate) - chan.context.value_to_self_msat = 6993000000; // 7000000000 - 7000000 + chan.funding.value_to_self_msat = 6993000000; // 7000000000 - 7000000 chan.context.feerate_per_kw = 9651181; test_commitment!("304402202ade0142008309eb376736575ad58d03e5b115499709c6db0b46e36ff394b492022037b63d78d66404d6504d4c4ac13be346f3d1802928a6d3ad95a6a944227161a2", @@ -11742,7 +11829,7 @@ mod tests { "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8001c0c62d0000000000160014cc1b07838e387deacd0e5232e1e8b49f4c29e484040047304402207e8d51e0c570a5868a78414f4e0cbfaed1106b171b9581542c30718ee4eb95ba02203af84194c97adf98898c9afe2f2ed4a7f8dba05a2dfab28ac9d9c604aa49a3790147304402202ade0142008309eb376736575ad58d03e5b115499709c6db0b46e36ff394b492022037b63d78d66404d6504d4c4ac13be346f3d1802928a6d3ad95a6a944227161a201475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220", {}); // anchors: commitment tx with one output untrimmed (minimum dust limit) - chan.context.value_to_self_msat = 6993000000; // 7000000000 - 7000000 + chan.funding.value_to_self_msat = 6993000000; // 7000000000 - 7000000 chan.context.feerate_per_kw = 6216010; chan.context.holder_dust_limit_satoshis = 4001; chan.context.channel_type = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(); @@ -11752,7 +11839,7 @@ mod tests { "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b80024a01000000000000220020e9e86e4823faa62e222ebc858a226636856158f07e69898da3b0d1af0ddb3994c0c62d0000000000220020f3394e1e619b0eca1f91be2fb5ab4dfc59ba5b84ebe014ad1d43a564d012994a04004830450221009ad80792e3038fe6968d12ff23e6888a565c3ddd065037f357445f01675d63f3022018384915e5f1f4ae157e15debf4f49b61c8d9d2b073c7d6f97c4a68caa3ed4c1014830450221008fd5dbff02e4b59020d4cd23a3c30d3e287065fda75a0a09b402980adf68ccda022001e0b8b620cd915ddff11f1de32addf23d81d51b90e6841b2cb8dcaf3faa5ecf01475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220", {}); // commitment tx with fee greater than funder amount - chan.context.value_to_self_msat = 6993000000; // 7000000000 - 7000000 + chan.funding.value_to_self_msat = 6993000000; // 7000000000 - 7000000 chan.context.feerate_per_kw = 9651936; chan.context.holder_dust_limit_satoshis = 546; chan.context.channel_type = cached_channel_type; @@ -11762,7 +11849,7 @@ mod tests { "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8001c0c62d0000000000160014cc1b07838e387deacd0e5232e1e8b49f4c29e484040047304402207e8d51e0c570a5868a78414f4e0cbfaed1106b171b9581542c30718ee4eb95ba02203af84194c97adf98898c9afe2f2ed4a7f8dba05a2dfab28ac9d9c604aa49a3790147304402202ade0142008309eb376736575ad58d03e5b115499709c6db0b46e36ff394b492022037b63d78d66404d6504d4c4ac13be346f3d1802928a6d3ad95a6a944227161a201475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220", {}); // commitment tx with 3 htlc outputs, 2 offered having the same amount and preimage - chan.context.value_to_self_msat = 7_000_000_000 - 2_000_000; + chan.funding.value_to_self_msat = 7_000_000_000 - 2_000_000; chan.context.feerate_per_kw = 253; chan.context.pending_inbound_htlcs.clear(); chan.context.pending_inbound_htlcs.push({ diff --git a/lightning/src/ln/channel_state.rs b/lightning/src/ln/channel_state.rs index cbea8d54776..614160b5a30 100644 --- a/lightning/src/ln/channel_state.rs +++ b/lightning/src/ln/channel_state.rs @@ -15,7 +15,7 @@ use bitcoin::secp256k1::PublicKey; use crate::chain::chaininterface::{FeeEstimator, LowerBoundedFeeEstimator}; use crate::chain::transaction::OutPoint; -use crate::ln::channel::ChannelContext; +use crate::ln::channel::{ChannelContext, FundingScope}; use crate::ln::types::ChannelId; use crate::sign::SignerProvider; use crate::types::features::{ChannelTypeFeatures, InitFeatures}; @@ -476,16 +476,16 @@ impl ChannelDetails { } pub(super) fn from_channel_context( - context: &ChannelContext, best_block_height: u32, latest_features: InitFeatures, - fee_estimator: &LowerBoundedFeeEstimator, + context: &ChannelContext, funding: &FundingScope, best_block_height: u32, + latest_features: InitFeatures, fee_estimator: &LowerBoundedFeeEstimator, ) -> Self where SP::Target: SignerProvider, F::Target: FeeEstimator, { - let balance = context.get_available_balances(fee_estimator); + let balance = context.get_available_balances(funding, fee_estimator); let (to_remote_reserve_satoshis, to_self_reserve_satoshis) = - context.get_holder_counterparty_selected_channel_reserve_satoshis(); + funding.get_holder_counterparty_selected_channel_reserve_satoshis(); #[allow(deprecated)] // TODO: Remove once balance_msat is removed. ChannelDetails { channel_id: context.channel_id(), @@ -504,7 +504,7 @@ impl ChannelDetails { } else { None }, - outbound_htlc_maximum_msat: context.get_counterparty_htlc_maximum_msat(), + outbound_htlc_maximum_msat: context.get_counterparty_htlc_maximum_msat(funding), }, funding_txo: context.get_funding_txo(), // Note that accept_channel (or open_channel) is always the first message, so @@ -521,7 +521,7 @@ impl ChannelDetails { None }, inbound_scid_alias: context.latest_inbound_scid_alias(), - channel_value_satoshis: context.get_value_satoshis(), + channel_value_satoshis: funding.get_value_satoshis(), feerate_sat_per_1000_weight: Some(context.get_feerate_sat_per_1000_weight()), unspendable_punishment_reserve: to_self_reserve_satoshis, inbound_capacity_msat: balance.inbound_capacity_msat, @@ -537,7 +537,7 @@ impl ChannelDetails { is_usable: context.is_live(), is_announced: context.should_announce(), inbound_htlc_minimum_msat: Some(context.get_holder_htlc_minimum_msat()), - inbound_htlc_maximum_msat: context.get_holder_htlc_maximum_msat(), + inbound_htlc_maximum_msat: context.get_holder_htlc_maximum_msat(funding), config: Some(context.config()), channel_shutdown_state: Some(context.shutdown_state()), pending_inbound_htlcs: context.get_pending_inbound_htlc_details(), diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 5342cc649d3..4c99c8a4b11 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -3064,7 +3064,7 @@ macro_rules! locked_close_channel { /// Returns (boolean indicating if we should remove the Channel object from memory, a mapped error) macro_rules! convert_channel_err { - ($self: ident, $peer_state: expr, $err: expr, $context: expr, $channel_id: expr, MANUAL_CHANNEL_UPDATE, $channel_update: expr) => { + ($self: ident, $peer_state: expr, $err: expr, $context: expr, $funding: expr, $channel_id: expr, MANUAL_CHANNEL_UPDATE, $channel_update: expr) => { match $err { ChannelError::Warn(msg) => { (false, MsgHandleErrInternal::from_chan_no_close(ChannelError::Warn(msg), *$channel_id)) @@ -3075,7 +3075,7 @@ macro_rules! convert_channel_err { ChannelError::Close((msg, reason)) => { let logger = WithChannelContext::from(&$self.logger, &$context, None); log_error!(logger, "Closing channel {} due to close-required error: {}", $channel_id, msg); - let mut shutdown_res = $context.force_shutdown(true, reason); + let mut shutdown_res = $context.force_shutdown($funding, true, reason); locked_close_channel!($self, $peer_state, $context, &mut shutdown_res); let err = MsgHandleErrInternal::from_finish_shutdown(msg, *$channel_id, shutdown_res, $channel_update); @@ -3087,10 +3087,10 @@ macro_rules! convert_channel_err { } }; ($self: ident, $peer_state: expr, $err: expr, $funded_channel: expr, $channel_id: expr, FUNDED_CHANNEL) => { - convert_channel_err!($self, $peer_state, $err, $funded_channel.context, $channel_id, MANUAL_CHANNEL_UPDATE, { $self.get_channel_update_for_broadcast(&$funded_channel).ok() }) + convert_channel_err!($self, $peer_state, $err, $funded_channel.context, &$funded_channel.funding, $channel_id, MANUAL_CHANNEL_UPDATE, { $self.get_channel_update_for_broadcast(&$funded_channel).ok() }) }; - ($self: ident, $peer_state: expr, $err: expr, $context: expr, $channel_id: expr, UNFUNDED_CHANNEL) => { - convert_channel_err!($self, $peer_state, $err, $context, $channel_id, MANUAL_CHANNEL_UPDATE, None) + ($self: ident, $peer_state: expr, $err: expr, $context: expr, $funding: expr, $channel_id: expr, UNFUNDED_CHANNEL) => { + convert_channel_err!($self, $peer_state, $err, $context, $funding, $channel_id, MANUAL_CHANNEL_UPDATE, None) }; ($self: ident, $peer_state: expr, $err: expr, $channel: expr, $channel_id: expr) => { match $channel.as_funded_mut() { @@ -3098,7 +3098,8 @@ macro_rules! convert_channel_err { convert_channel_err!($self, $peer_state, $err, funded_channel, $channel_id, FUNDED_CHANNEL) }, None => { - convert_channel_err!($self, $peer_state, $err, $channel.context_mut(), $channel_id, UNFUNDED_CHANNEL) + let (funding, context) = $channel.funding_and_context_mut(); + convert_channel_err!($self, $peer_state, $err, context, funding, $channel_id, UNFUNDED_CHANNEL) }, } }; @@ -3761,7 +3762,7 @@ where .filter_map(|(chan_id, chan)| chan.as_funded().map(|chan| (chan_id, chan))) .filter(f) .map(|(_channel_id, channel)| { - ChannelDetails::from_channel_context(&channel.context, best_block_height, + ChannelDetails::from_channel_context(&channel.context, &channel.funding, best_block_height, peer_state.latest_features.clone(), &self.fee_estimator) }) ); @@ -3786,8 +3787,8 @@ where for (_cp_id, peer_state_mutex) in per_peer_state.iter() { let mut peer_state_lock = peer_state_mutex.lock().unwrap(); let peer_state = &mut *peer_state_lock; - for context in peer_state.channel_by_id.iter().map(|(_, chan)| chan.context()) { - let details = ChannelDetails::from_channel_context(context, best_block_height, + for (context, funding) in peer_state.channel_by_id.iter().map(|(_, chan)| (chan.context(), chan.funding())) { + let details = ChannelDetails::from_channel_context(context, funding, best_block_height, peer_state.latest_features.clone(), &self.fee_estimator); res.push(details); } @@ -3818,12 +3819,12 @@ where let mut peer_state_lock = peer_state_mutex.lock().unwrap(); let peer_state = &mut *peer_state_lock; let features = &peer_state.latest_features; - let context_to_details = |context| { - ChannelDetails::from_channel_context(context, best_block_height, features.clone(), &self.fee_estimator) + let context_to_details = |(context, funding)| { + ChannelDetails::from_channel_context(context, funding, best_block_height, features.clone(), &self.fee_estimator) }; return peer_state.channel_by_id .iter() - .map(|(_, chan)| chan.context()) + .map(|(_, chan)| (chan.context(), chan.funding())) .map(context_to_details) .collect(); } @@ -3910,7 +3911,7 @@ where peer_state_lock, peer_state, per_peer_state, chan); } } else { - let mut shutdown_res = chan_entry.get_mut().context_mut() + let mut shutdown_res = chan_entry.get_mut() .force_shutdown(false, ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) }); remove_channel_entry!(self, peer_state, chan_entry, shutdown_res); shutdown_result = Some(shutdown_res); @@ -4091,7 +4092,7 @@ where if let Some(peer_state_mutex) = per_peer_state.get(&counterparty_node_id) { let mut peer_state = peer_state_mutex.lock().unwrap(); if let Some(mut chan) = peer_state.channel_by_id.remove(&channel_id) { - let mut close_res = chan.context_mut().force_shutdown(false, ClosureReason::FundingBatchClosure); + let mut close_res = chan.force_shutdown(false, ClosureReason::FundingBatchClosure); locked_close_channel!(self, &mut *peer_state, chan.context(), close_res); shutdown_results.push(close_res); } @@ -4155,13 +4156,13 @@ where let (mut shutdown_res, update_opt) = match chan_entry.get_mut().as_funded_mut() { Some(chan) => { ( - chan.context.force_shutdown(broadcast, closure_reason), + chan.context.force_shutdown(&chan.funding, broadcast, closure_reason), self.get_channel_update_for_broadcast(&chan).ok(), ) }, None => { // Unfunded channel has no update - (chan_entry.get_mut().context_mut().force_shutdown(false, closure_reason), None) + (chan_entry.get_mut().force_shutdown(false, closure_reason), None) }, }; let chan = remove_channel_entry!(self, peer_state, chan_entry, shutdown_res); @@ -4544,7 +4545,7 @@ where channel_flags: (!were_node_one) as u8 | ((!enabled as u8) << 1), cltv_expiry_delta: chan.context.get_cltv_expiry_delta(), htlc_minimum_msat: chan.context.get_counterparty_htlc_minimum_msat(), - htlc_maximum_msat: chan.context.get_announced_htlc_max_msat(), + htlc_maximum_msat: chan.get_announced_htlc_max_msat(), fee_base_msat: chan.context.get_outbound_forwarding_fee_base_msat(), fee_proportional_millionths: chan.context.get_fee_proportional_millionths(), excess_data: Vec::new(), @@ -5124,7 +5125,7 @@ where let err = if let ChannelError::Close((msg, reason)) = $err { let channel_id = $chan.context.channel_id(); counterparty = $chan.context.get_counterparty_node_id(); - let shutdown_res = $chan.context.force_shutdown(false, reason); + let shutdown_res = $chan.context.force_shutdown(&$chan.funding, false, reason); MsgHandleErrInternal::from_finish_shutdown(msg, channel_id, shutdown_res, None) } else { unreachable!(); }; @@ -5192,7 +5193,7 @@ where mem::drop(peer_state_lock); mem::drop(per_peer_state); let reason = ClosureReason::ProcessingError { err: err.clone() }; - self.finish_close_channel(chan.context.force_shutdown(true, reason)); + self.finish_close_channel(chan.context.force_shutdown(&chan.funding, true, reason)); return Err(APIError::ChannelUnavailable { err }); } } @@ -5359,7 +5360,7 @@ where let outpoint = match &funding { FundingType::Checked(tx) => { for (idx, outp) in tx.output.iter().enumerate() { - if outp.script_pubkey == expected_spk && outp.value.to_sat() == chan.context.get_value_satoshis() { + if outp.script_pubkey == expected_spk && outp.value.to_sat() == chan.funding.get_value_satoshis() { if output_index.is_some() { return Err("Multiple outputs matched the expected script and value"); } @@ -5405,7 +5406,7 @@ where .and_then(|mut peer_state| peer_state.channel_by_id.remove(&channel_id).map(|chan| (chan, peer_state))) .map(|(mut chan, mut peer_state)| { let closure_reason = ClosureReason::ProcessingError { err: e.clone() }; - let mut close_res = chan.context_mut().force_shutdown(false, closure_reason); + let mut close_res = chan.force_shutdown(false, closure_reason); locked_close_channel!(self, peer_state, chan.context(), close_res); shutdown_results.push(close_res); peer_state.pending_msg_events.push(events::MessageSendEvent::HandleError { @@ -5987,7 +5988,7 @@ where let maybe_optimal_channel = peer_state.channel_by_id.values_mut() .filter_map(Channel::as_funded_mut) .filter_map(|chan| { - let balances = chan.context.get_available_balances(&self.fee_estimator); + let balances = chan.context.get_available_balances(&chan.funding, &self.fee_estimator); if outgoing_amt_msat <= balances.next_outbound_htlc_limit_msat && outgoing_amt_msat >= balances.next_outbound_htlc_minimum_msat && chan.context.is_usable() { @@ -6649,12 +6650,13 @@ where chan.context_mut().maybe_expire_prev_config(); let unfunded_context = chan.unfunded_context_mut().expect("channel should be unfunded"); if unfunded_context.should_expire_unfunded_channel() { - let context = chan.context_mut(); + let context = chan.context(); let logger = WithChannelContext::from(&self.logger, context, None); log_error!(logger, "Force-closing pending channel with ID {} for not establishing in a timely manner", context.channel_id()); - let mut close_res = context.force_shutdown(false, ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) }); + let mut close_res = chan.force_shutdown(false, ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) }); + let context = chan.context_mut(); locked_close_channel!(self, peer_state, context, close_res); shutdown_channels.push(close_res); pending_msg_events.push(MessageSendEvent::HandleError { @@ -8159,7 +8161,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ match chan.get_mut().as_unfunded_outbound_v1_mut() { Some(unfunded_chan) => { try_channel_entry!(self, peer_state, unfunded_chan.accept_channel(msg, &self.default_configuration.channel_handshake_limits, &peer_state.latest_features), chan); - (unfunded_chan.context.get_value_satoshis(), unfunded_chan.context.get_funding_redeemscript().to_p2wsh(), unfunded_chan.context.get_user_id()) + (unfunded_chan.funding.get_value_satoshis(), unfunded_chan.context.get_funding_redeemscript().to_p2wsh(), unfunded_chan.context.get_user_id()) }, None => { return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got an unexpected accept_channel message from peer with counterparty_node_id {}", counterparty_node_id), msg.common_fields.temporary_channel_id)); @@ -8208,7 +8210,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ // Really we should be returning the channel_id the peer expects based // on their funding info here, but they're horribly confused anyway, so // there's not a lot we can do to save them. - return Err(convert_channel_err!(self, peer_state, err, inbound_chan.context, &msg.temporary_channel_id, UNFUNDED_CHANNEL).1); + return Err(convert_channel_err!(self, peer_state, err, inbound_chan.context, &inbound_chan.funding, &msg.temporary_channel_id, UNFUNDED_CHANNEL).1); }, } }, @@ -8230,7 +8232,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ // Thus, we must first unset the funding outpoint on the channel. let err = ChannelError::close($err.to_owned()); chan.unset_funding_info(); - return Err(convert_channel_err!(self, peer_state, err, chan.context, &funded_channel_id, UNFUNDED_CHANNEL).1); + return Err(convert_channel_err!(self, peer_state, err, chan.context, &chan.funding, &funded_channel_id, UNFUNDED_CHANNEL).1); } } } match peer_state.channel_by_id.entry(funded_channel_id) { @@ -8703,10 +8705,9 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ } }, None => { - let context = chan_entry.get_mut().context_mut(); - let logger = WithChannelContext::from(&self.logger, context, None); + let logger = WithChannelContext::from(&self.logger, chan_entry.get().context(), None); log_error!(logger, "Immediately closing unfunded channel {} as peer asked to cooperatively shut it down (which is unnecessary)", &msg.channel_id); - let mut close_res = context.force_shutdown(false, ClosureReason::CounterpartyCoopClosedUnfundedChannel); + let mut close_res = chan_entry.get_mut().force_shutdown(false, ClosureReason::CounterpartyCoopClosedUnfundedChannel); remove_channel_entry!(self, peer_state, chan_entry, close_res); finish_shutdown = Some(close_res); }, @@ -9446,7 +9447,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ } else { ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(true) } }; - let mut shutdown_res = chan_entry.get_mut().context_mut().force_shutdown(false, reason.clone()); + let mut shutdown_res = chan_entry.get_mut().force_shutdown(false, reason.clone()); let chan = remove_channel_entry!(self, peer_state, chan_entry, shutdown_res); failed_channels.push(shutdown_res); if let Some(funded_chan) = chan.as_funded() { @@ -11280,7 +11281,7 @@ where // It looks like our counterparty went on-chain or funding transaction was // reorged out of the main chain. Close the channel. let reason_message = format!("{}", reason); - let mut close_res = funded_channel.context.force_shutdown(true, reason); + let mut close_res = funded_channel.context.force_shutdown(&funded_channel.funding, true, reason); locked_close_channel!(self, peer_state, &funded_channel.context, close_res); failed_channels.push(close_res); if let Ok(update) = self.get_channel_update_for_broadcast(&funded_channel) { @@ -11711,8 +11712,8 @@ where return true; } // Clean up for removal. + let mut close_res = chan.force_shutdown(false, ClosureReason::DisconnectedPeer); let context = chan.context_mut(); - let mut close_res = context.force_shutdown(false, ClosureReason::DisconnectedPeer); locked_close_channel!(self, peer_state, &context, close_res); failed_channels.push(close_res); false @@ -13486,7 +13487,7 @@ where log_error!(logger, " The ChannelMonitor for channel {} is at counterparty commitment transaction number {} but the ChannelManager is at counterparty commitment transaction number {}.", &channel.context.channel_id(), monitor.get_cur_counterparty_commitment_number(), channel.get_cur_counterparty_commitment_transaction_number()); } - let mut shutdown_result = channel.context.force_shutdown(true, ClosureReason::OutdatedChannelManager); + let mut shutdown_result = channel.context.force_shutdown(&channel.funding, true, ClosureReason::OutdatedChannelManager); if shutdown_result.unbroadcasted_batch_funding_txid.is_some() { return Err(DecodeError::InvalidValue); } @@ -13513,9 +13514,9 @@ where user_channel_id: channel.context.get_user_id(), reason: ClosureReason::OutdatedChannelManager, counterparty_node_id: Some(channel.context.get_counterparty_node_id()), - channel_capacity_sats: Some(channel.context.get_value_satoshis()), + channel_capacity_sats: Some(channel.funding.get_value_satoshis()), channel_funding_txo: channel.context.get_funding_txo(), - last_local_balance_msat: Some(channel.context.get_value_to_self_msat()), + last_local_balance_msat: Some(channel.funding.get_value_to_self_msat()), }, None)); for (channel_htlc_source, payment_hash) in channel.inflight_htlc_sources() { let mut found_htlc = false; @@ -13555,15 +13556,15 @@ where // If we were persisted and shut down while the initial ChannelMonitor persistence // was in-progress, we never broadcasted the funding transaction and can still // safely discard the channel. - let _ = channel.context.force_shutdown(false, ClosureReason::DisconnectedPeer); + let _ = channel.context.force_shutdown(&channel.funding, false, ClosureReason::DisconnectedPeer); channel_closures.push_back((events::Event::ChannelClosed { channel_id: channel.context.channel_id(), user_channel_id: channel.context.get_user_id(), reason: ClosureReason::DisconnectedPeer, counterparty_node_id: Some(channel.context.get_counterparty_node_id()), - channel_capacity_sats: Some(channel.context.get_value_satoshis()), + channel_capacity_sats: Some(channel.funding.get_value_satoshis()), channel_funding_txo: channel.context.get_funding_txo(), - last_local_balance_msat: Some(channel.context.get_value_to_self_msat()), + last_local_balance_msat: Some(channel.funding.get_value_to_self_msat()), }, None)); } else { log_error!(logger, "Missing ChannelMonitor for channel {} needed by ChannelManager.", &channel.context.channel_id()); diff --git a/lightning/src/ln/dual_funding_tests.rs b/lightning/src/ln/dual_funding_tests.rs index c6c1d503900..7f0f9c2023e 100644 --- a/lightning/src/ln/dual_funding_tests.rs +++ b/lightning/src/ln/dual_funding_tests.rs @@ -187,6 +187,7 @@ fn do_test_v2_channel_establishment( signature: channel .context .get_initial_counterparty_commitment_signature_for_test( + &channel.funding, &&logger_a, channel_transaction_parameters, accept_channel_v2_msg.common_fields.first_per_commitment_point, diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 66e21d64d54..f8787e1b657 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -217,9 +217,8 @@ fn do_test_counterparty_no_reserve(send_from_initiator: bool) { let channel = get_channel_ref!(sender_node, counterparty_node, sender_node_per_peer_lock, sender_node_peer_state_lock, temp_channel_id); assert!(channel.is_unfunded_v1()); - let chan_context = channel.context_mut(); - chan_context.holder_selected_channel_reserve_satoshis = 0; - chan_context.holder_max_htlc_value_in_flight_msat = 100_000_000; + channel.funding_mut().holder_selected_channel_reserve_satoshis = 0; + channel.context_mut().holder_max_htlc_value_in_flight_msat = 100_000_000; } let funding_tx = sign_funding_transaction(&nodes[0], &nodes[1], 100_000, temp_channel_id);