+ let to_remote_satoshis = funders_amount_msat / 1000 - commitment_tx_fee - anchor_outputs_value;
+ // While it's reasonable for us to not meet the channel reserve initially (if they don't
+ // want to push much to us), our counterparty should always have more than our reserve.
+ if to_remote_satoshis < holder_selected_channel_reserve_satoshis {
+ return Err(ChannelError::Close("Insufficient funding amount for initial reserve".to_owned()));
+ }
+
+ let counterparty_shutdown_scriptpubkey = if their_features.supports_upfront_shutdown_script() {
+ match &open_channel_fields.shutdown_scriptpubkey {
+ &Some(ref script) => {
+ // Peer is signaling upfront_shutdown and has opt-out with a 0-length script. We don't enforce anything
+ if script.len() == 0 {
+ None
+ } else {
+ if !script::is_bolt2_compliant(&script, their_features) {
+ return Err(ChannelError::Close(format!("Peer is signaling upfront_shutdown but has provided an unacceptable scriptpubkey format: {}", script)))
+ }
+ Some(script.clone())
+ }
+ },
+ // Peer is signaling upfront shutdown but don't opt-out with correct mechanism (a.k.a 0-length script). Peer looks buggy, we fail the channel
+ &None => {
+ return Err(ChannelError::Close("Peer is signaling upfront_shutdown but we don't get any script. Use 0-length script to opt-out".to_owned()));
+ }
+ }
+ } else { None };
+
+ let shutdown_scriptpubkey = if config.channel_handshake_config.commit_upfront_shutdown_pubkey {
+ match signer_provider.get_shutdown_scriptpubkey() {
+ Ok(scriptpubkey) => Some(scriptpubkey),
+ Err(_) => return Err(ChannelError::Close("Failed to get upfront shutdown scriptpubkey".to_owned())),
+ }
+ } else { None };
+
+ if let Some(shutdown_scriptpubkey) = &shutdown_scriptpubkey {
+ if !shutdown_scriptpubkey.is_compatible(&their_features) {
+ return Err(ChannelError::Close(format!("Provided a scriptpubkey format not accepted by peer: {}", shutdown_scriptpubkey)));
+ }
+ }
+
+ let destination_script = match signer_provider.get_destination_script(channel_keys_id) {
+ Ok(script) => script,
+ Err(_) => return Err(ChannelError::Close("Failed to get destination script".to_owned())),
+ };
+
+ let mut secp_ctx = Secp256k1::new();
+ secp_ctx.seeded_randomize(&entropy_source.get_secure_random_bytes());
+
+ let minimum_depth = if is_0conf {
+ Some(0)
+ } else {
+ Some(cmp::max(config.channel_handshake_config.minimum_depth, 1))
+ };
+
+ let value_to_self_msat = our_funding_satoshis * 1000 + msg_push_msat;
+
+ // TODO(dual_funding): Checks for `funding_feerate_sat_per_1000_weight`?
+
+ let channel_context = ChannelContext {
+ user_id,
+
+ config: LegacyChannelConfig {
+ options: config.channel_config.clone(),
+ announced_channel,
+ commit_upfront_shutdown_pubkey: config.channel_handshake_config.commit_upfront_shutdown_pubkey,
+ },
+
+ prev_config: None,
+
+ inbound_handshake_limits_override: None,
+
+ temporary_channel_id: Some(open_channel_fields.temporary_channel_id),
+ channel_id: open_channel_fields.temporary_channel_id,
+ channel_state: ChannelState::NegotiatingFunding(
+ NegotiatingFundingFlags::OUR_INIT_SENT | NegotiatingFundingFlags::THEIR_INIT_SENT
+ ),
+ announcement_sigs_state: AnnouncementSigsState::NotSent,
+ secp_ctx,
+
+ latest_monitor_update_id: 0,
+
+ holder_signer: ChannelSignerType::Ecdsa(holder_signer),
+ shutdown_scriptpubkey,
+ destination_script,
+
+ cur_holder_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER,
+ cur_counterparty_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER,
+ value_to_self_msat,
+
+ pending_inbound_htlcs: Vec::new(),
+ pending_outbound_htlcs: Vec::new(),
+ holding_cell_htlc_updates: Vec::new(),
+ pending_update_fee: None,
+ holding_cell_update_fee: None,
+ next_holder_htlc_id: 0,
+ next_counterparty_htlc_id: 0,
+ update_time_counter: 1,
+
+ resend_order: RAACommitmentOrder::CommitmentFirst,
+
+ monitor_pending_channel_ready: false,
+ monitor_pending_revoke_and_ack: false,
+ monitor_pending_commitment_signed: false,
+ monitor_pending_forwards: Vec::new(),
+ monitor_pending_failures: Vec::new(),
+ monitor_pending_finalized_fulfills: Vec::new(),
+ monitor_pending_update_adds: Vec::new(),
+
+ signer_pending_commitment_update: false,
+ signer_pending_funding: 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,
+ pending_counterparty_closing_signed: None,
+ expecting_peer_commitment_signed: false,
+ closing_fee_limits: None,
+ target_closing_feerate_sats_per_kw: None,
+
+ funding_tx_confirmed_in: None,
+ funding_tx_confirmation_height: 0,
+ short_channel_id: None,
+ 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,
+ holder_max_accepted_htlcs: cmp::min(config.channel_handshake_config.our_max_accepted_htlcs, MAX_HTLCS),
+ minimum_depth,
+
+ counterparty_forwarding_info: None,
+
+ channel_transaction_parameters: ChannelTransactionParameters {
+ holder_pubkeys: pubkeys,
+ holder_selected_contest_delay: config.channel_handshake_config.our_to_self_delay,
+ is_outbound_from_holder: false,
+ counterparty_parameters: Some(CounterpartyChannelTransactionParameters {
+ selected_contest_delay: open_channel_fields.to_self_delay,
+ pubkeys: counterparty_pubkeys,
+ }),
+ funding_outpoint: None,
+ channel_type_features: channel_type.clone()
+ },
+ funding_transaction: None,
+ is_batch_funding: None,
+
+ counterparty_cur_commitment_point: Some(open_channel_fields.first_per_commitment_point),
+ counterparty_prev_commitment_point: None,
+ counterparty_node_id,
+
+ counterparty_shutdown_scriptpubkey,
+
+ commitment_secrets: CounterpartyCommitmentSecrets::new(),
+
+ channel_update_status: ChannelUpdateStatus::Enabled,
+ closing_signed_in_flight: false,
+
+ announcement_sigs: None,
+
+ #[cfg(any(test, fuzzing))]
+ next_local_commitment_tx_fee_info_cached: Mutex::new(None),
+ #[cfg(any(test, fuzzing))]
+ next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
+
+ workaround_lnd_bug_4006: None,
+ sent_message_awaiting_response: None,
+
+ latest_inbound_scid_alias: None,
+ outbound_scid_alias: 0,
+
+ channel_pending_event_emitted: false,
+ channel_ready_event_emitted: false,
+
+ #[cfg(any(test, fuzzing))]
+ historical_inbound_htlc_fulfills: new_hash_set(),
+
+ channel_type,
+ channel_keys_id,
+
+ local_initiated_shutdown: None,
+
+ blocked_monitor_updates: Vec::new(),
+ };
+
+ Ok(channel_context)
+ }
+
+ fn new_for_outbound_channel<'a, ES: Deref, F: Deref>(
+ fee_estimator: &'a LowerBoundedFeeEstimator<F>,
+ entropy_source: &'a ES,
+ signer_provider: &'a SP,
+ counterparty_node_id: PublicKey,
+ their_features: &'a InitFeatures,
+ funding_satoshis: u64,
+ push_msat: u64,
+ user_id: u128,
+ config: &'a UserConfig,
+ current_chain_height: u32,
+ outbound_scid_alias: u64,
+ temporary_channel_id: Option<ChannelId>,
+ holder_selected_channel_reserve_satoshis: u64,
+ channel_keys_id: [u8; 32],
+ holder_signer: <SP::Target as SignerProvider>::EcdsaSigner,
+ pubkeys: ChannelPublicKeys,
+ ) -> Result<ChannelContext<SP>, APIError>
+ where
+ ES::Target: EntropySource,
+ F::Target: FeeEstimator,
+ SP::Target: SignerProvider,
+ {
+ // This will be updated with the counterparty contribution if this is a dual-funded channel
+ let channel_value_satoshis = funding_satoshis;
+
+ let holder_selected_contest_delay = config.channel_handshake_config.our_to_self_delay;
+
+ if !their_features.supports_wumbo() && channel_value_satoshis > MAX_FUNDING_SATOSHIS_NO_WUMBO {
+ return Err(APIError::APIMisuseError{err: format!("funding_value must not exceed {}, it was {}", MAX_FUNDING_SATOSHIS_NO_WUMBO, channel_value_satoshis)});
+ }
+ if channel_value_satoshis >= TOTAL_BITCOIN_SUPPLY_SATOSHIS {
+ return Err(APIError::APIMisuseError{err: format!("funding_value must be smaller than the total bitcoin supply, it was {}", channel_value_satoshis)});
+ }
+ let channel_value_msat = channel_value_satoshis * 1000;
+ if push_msat > channel_value_msat {
+ return Err(APIError::APIMisuseError { err: format!("Push value ({}) was larger than channel_value ({})", push_msat, channel_value_msat) });
+ }
+ if holder_selected_contest_delay < BREAKDOWN_TIMEOUT {
+ return Err(APIError::APIMisuseError {err: format!("Configured with an unreasonable our_to_self_delay ({}) putting user funds at risks", holder_selected_contest_delay)});
+ }
+
+ let channel_type = get_initial_channel_type(&config, their_features);
+ debug_assert!(channel_type.is_subset(&channelmanager::provided_channel_type_features(&config)));
+
+ let (commitment_conf_target, anchor_outputs_value_msat) = if channel_type.supports_anchors_zero_fee_htlc_tx() {
+ (ConfirmationTarget::AnchorChannelFee, ANCHOR_OUTPUT_VALUE_SATOSHI * 2 * 1000)
+ } else {
+ (ConfirmationTarget::NonAnchorChannelFee, 0)
+ };
+ let commitment_feerate = fee_estimator.bounded_sat_per_1000_weight(commitment_conf_target);
+
+ let value_to_self_msat = channel_value_satoshis * 1000 - push_msat;
+ let commitment_tx_fee = commit_tx_fee_msat(commitment_feerate, MIN_AFFORDABLE_HTLC_COUNT, &channel_type);
+ if value_to_self_msat.saturating_sub(anchor_outputs_value_msat) < commitment_tx_fee {
+ return Err(APIError::APIMisuseError{ err: format!("Funding amount ({}) can't even pay fee for initial commitment transaction fee of {}.", value_to_self_msat / 1000, commitment_tx_fee / 1000) });
+ }
+
+ let mut secp_ctx = Secp256k1::new();
+ secp_ctx.seeded_randomize(&entropy_source.get_secure_random_bytes());
+
+ let shutdown_scriptpubkey = if config.channel_handshake_config.commit_upfront_shutdown_pubkey {
+ match signer_provider.get_shutdown_scriptpubkey() {
+ Ok(scriptpubkey) => Some(scriptpubkey),
+ Err(_) => return Err(APIError::ChannelUnavailable { err: "Failed to get shutdown scriptpubkey".to_owned()}),
+ }
+ } else { None };
+
+ if let Some(shutdown_scriptpubkey) = &shutdown_scriptpubkey {
+ if !shutdown_scriptpubkey.is_compatible(&their_features) {
+ return Err(APIError::IncompatibleShutdownScript { script: shutdown_scriptpubkey.clone() });
+ }
+ }
+
+ let destination_script = match signer_provider.get_destination_script(channel_keys_id) {
+ Ok(script) => script,
+ Err(_) => return Err(APIError::ChannelUnavailable { err: "Failed to get destination script".to_owned()}),
+ };
+
+ let temporary_channel_id = temporary_channel_id.unwrap_or_else(|| ChannelId::temporary_from_entropy_source(entropy_source));
+
+ Ok(Self {
+ user_id,
+
+ config: LegacyChannelConfig {
+ options: config.channel_config.clone(),
+ announced_channel: config.channel_handshake_config.announced_channel,
+ commit_upfront_shutdown_pubkey: config.channel_handshake_config.commit_upfront_shutdown_pubkey,
+ },
+
+ prev_config: None,
+
+ inbound_handshake_limits_override: Some(config.channel_handshake_limits.clone()),
+
+ channel_id: temporary_channel_id,
+ temporary_channel_id: Some(temporary_channel_id),
+ channel_state: ChannelState::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,
+
+ holder_signer: ChannelSignerType::Ecdsa(holder_signer),
+ shutdown_scriptpubkey,
+ destination_script,
+
+ cur_holder_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER,
+ cur_counterparty_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER,
+ value_to_self_msat,
+
+ pending_inbound_htlcs: Vec::new(),
+ pending_outbound_htlcs: Vec::new(),
+ holding_cell_htlc_updates: Vec::new(),
+ pending_update_fee: None,
+ holding_cell_update_fee: None,
+ next_holder_htlc_id: 0,
+ next_counterparty_htlc_id: 0,
+ update_time_counter: 1,
+
+ resend_order: RAACommitmentOrder::CommitmentFirst,
+
+ monitor_pending_channel_ready: false,
+ monitor_pending_revoke_and_ack: false,
+ monitor_pending_commitment_signed: false,
+ monitor_pending_forwards: Vec::new(),
+ monitor_pending_failures: Vec::new(),
+ monitor_pending_finalized_fulfills: Vec::new(),
+ monitor_pending_update_adds: Vec::new(),
+
+ signer_pending_commitment_update: false,
+ signer_pending_funding: 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,
+ pending_counterparty_closing_signed: None,
+ expecting_peer_commitment_signed: false,
+ closing_fee_limits: None,
+ target_closing_feerate_sats_per_kw: None,
+
+ funding_tx_confirmed_in: None,
+ funding_tx_confirmation_height: 0,
+ short_channel_id: None,
+ channel_creation_height: current_chain_height,
+
+ feerate_per_kw: commitment_feerate,
+ counterparty_dust_limit_satoshis: 0,
+ holder_dust_limit_satoshis: MIN_CHAN_DUST_LIMIT_SATOSHIS,
+ counterparty_max_htlc_value_in_flight_msat: 0,
+ // 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,
+ holder_max_accepted_htlcs: cmp::min(config.channel_handshake_config.our_max_accepted_htlcs, MAX_HTLCS),
+ minimum_depth: None, // Filled in in accept_channel
+
+ counterparty_forwarding_info: None,
+
+ channel_transaction_parameters: ChannelTransactionParameters {
+ holder_pubkeys: pubkeys,
+ holder_selected_contest_delay: config.channel_handshake_config.our_to_self_delay,
+ is_outbound_from_holder: true,
+ counterparty_parameters: None,
+ funding_outpoint: None,
+ channel_type_features: channel_type.clone()
+ },
+ funding_transaction: None,
+ is_batch_funding: None,
+
+ counterparty_cur_commitment_point: None,
+ counterparty_prev_commitment_point: None,
+ counterparty_node_id,
+
+ counterparty_shutdown_scriptpubkey: None,
+
+ commitment_secrets: CounterpartyCommitmentSecrets::new(),
+
+ channel_update_status: ChannelUpdateStatus::Enabled,
+ closing_signed_in_flight: false,
+
+ announcement_sigs: None,
+
+ #[cfg(any(test, fuzzing))]
+ next_local_commitment_tx_fee_info_cached: Mutex::new(None),
+ #[cfg(any(test, fuzzing))]
+ next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
+
+ workaround_lnd_bug_4006: None,
+ sent_message_awaiting_response: None,
+
+ latest_inbound_scid_alias: None,
+ outbound_scid_alias,
+
+ channel_pending_event_emitted: false,
+ channel_ready_event_emitted: false,
+
+ #[cfg(any(test, fuzzing))]
+ historical_inbound_htlc_fulfills: new_hash_set(),
+
+ channel_type,
+ channel_keys_id,
+
+ blocked_monitor_updates: Vec::new(),
+ local_initiated_shutdown: None,
+ })
+ }
+
+ /// Allowed in any state (including after shutdown)
+ pub fn get_update_time_counter(&self) -> u32 {
+ self.update_time_counter
+ }
+
+ pub fn get_latest_monitor_update_id(&self) -> u64 {
+ self.latest_monitor_update_id
+ }
+
+ pub fn should_announce(&self) -> bool {
+ self.config.announced_channel
+ }
+
+ pub fn is_outbound(&self) -> bool {
+ self.channel_transaction_parameters.is_outbound_from_holder
+ }
+
+ /// Gets the fee we'd want to charge for adding an HTLC output to this Channel
+ /// Allowed in any state (including after shutdown)
+ pub fn get_outbound_forwarding_fee_base_msat(&self) -> u32 {
+ self.config.options.forwarding_fee_base_msat
+ }
+
+ /// Returns true if we've ever received a message from the remote end for this Channel
+ pub fn have_received_message(&self) -> bool {
+ self.channel_state > ChannelState::NegotiatingFunding(NegotiatingFundingFlags::OUR_INIT_SENT)
+ }
+
+ /// Returns true if this channel is fully established and not known to be closing.
+ /// Allowed in any state (including after shutdown)
+ pub fn is_usable(&self) -> bool {
+ matches!(self.channel_state, ChannelState::ChannelReady(_)) &&
+ !self.channel_state.is_local_shutdown_sent() &&
+ !self.channel_state.is_remote_shutdown_sent() &&
+ !self.monitor_pending_channel_ready
+ }
+
+ /// shutdown state returns the state of the channel in its various stages of shutdown
+ pub fn shutdown_state(&self) -> ChannelShutdownState {
+ match self.channel_state {
+ ChannelState::AwaitingChannelReady(_)|ChannelState::ChannelReady(_) =>
+ if self.channel_state.is_local_shutdown_sent() && !self.channel_state.is_remote_shutdown_sent() {
+ ChannelShutdownState::ShutdownInitiated
+ } else if (self.channel_state.is_local_shutdown_sent() || self.channel_state.is_remote_shutdown_sent()) && !self.closing_negotiation_ready() {
+ ChannelShutdownState::ResolvingHTLCs
+ } else if (self.channel_state.is_local_shutdown_sent() || self.channel_state.is_remote_shutdown_sent()) && self.closing_negotiation_ready() {
+ ChannelShutdownState::NegotiatingClosingFee
+ } else {
+ ChannelShutdownState::NotShuttingDown
+ },
+ ChannelState::ShutdownComplete => ChannelShutdownState::ShutdownComplete,
+ _ => ChannelShutdownState::NotShuttingDown,
+ }
+ }
+
+ fn closing_negotiation_ready(&self) -> bool {
+ let is_ready_to_close = match self.channel_state {
+ ChannelState::AwaitingChannelReady(flags) =>
+ flags & FundedStateFlags::ALL == FundedStateFlags::LOCAL_SHUTDOWN_SENT | FundedStateFlags::REMOTE_SHUTDOWN_SENT,
+ ChannelState::ChannelReady(flags) =>
+ flags == FundedStateFlags::LOCAL_SHUTDOWN_SENT | FundedStateFlags::REMOTE_SHUTDOWN_SENT,
+ _ => false,
+ };
+ self.pending_inbound_htlcs.is_empty() &&
+ self.pending_outbound_htlcs.is_empty() &&
+ self.pending_update_fee.is_none() &&
+ is_ready_to_close
+ }
+
+ /// Returns true if this channel is currently available for use. This is a superset of
+ /// is_usable() and considers things like the channel being temporarily disabled.
+ /// Allowed in any state (including after shutdown)
+ pub fn is_live(&self) -> bool {
+ self.is_usable() && !self.channel_state.is_peer_disconnected()
+ }
+
+ // Public utilities:
+
+ pub fn channel_id(&self) -> ChannelId {
+ self.channel_id
+ }
+
+ // Return the `temporary_channel_id` used during channel establishment.
+ //
+ // Will return `None` for channels created prior to LDK version 0.0.115.
+ pub fn temporary_channel_id(&self) -> Option<ChannelId> {
+ self.temporary_channel_id
+ }
+
+ pub fn minimum_depth(&self) -> Option<u32> {
+ self.minimum_depth
+ }