Merge pull request #2942 from benthecarman/node-id-slice
[rust-lightning] / lightning / src / ln / channel.rs
index 4446801ed48c484d201944c681c5ccbea03d2ce7..33df5303814da439f5f83fd33a2acf53963b115f 100644 (file)
@@ -1161,6 +1161,10 @@ impl_writeable_tlv_based!(PendingChannelMonitorUpdate, {
 pub(super) enum ChannelPhase<SP: Deref> where SP::Target: SignerProvider {
        UnfundedOutboundV1(OutboundV1Channel<SP>),
        UnfundedInboundV1(InboundV1Channel<SP>),
+       #[cfg(dual_funding)]
+       UnfundedOutboundV2(OutboundV2Channel<SP>),
+       #[cfg(dual_funding)]
+       UnfundedInboundV2(InboundV2Channel<SP>),
        Funded(Channel<SP>),
 }
 
@@ -1173,6 +1177,10 @@ impl<'a, SP: Deref> ChannelPhase<SP> where
                        ChannelPhase::Funded(chan) => &chan.context,
                        ChannelPhase::UnfundedOutboundV1(chan) => &chan.context,
                        ChannelPhase::UnfundedInboundV1(chan) => &chan.context,
+                       #[cfg(dual_funding)]
+                       ChannelPhase::UnfundedOutboundV2(chan) => &chan.context,
+                       #[cfg(dual_funding)]
+                       ChannelPhase::UnfundedInboundV2(chan) => &chan.context,
                }
        }
 
@@ -1181,6 +1189,10 @@ impl<'a, SP: Deref> ChannelPhase<SP> where
                        ChannelPhase::Funded(ref mut chan) => &mut chan.context,
                        ChannelPhase::UnfundedOutboundV1(ref mut chan) => &mut chan.context,
                        ChannelPhase::UnfundedInboundV1(ref mut chan) => &mut chan.context,
+                       #[cfg(dual_funding)]
+                       ChannelPhase::UnfundedOutboundV2(ref mut chan) => &mut chan.context,
+                       #[cfg(dual_funding)]
+                       ChannelPhase::UnfundedInboundV2(ref mut chan) => &mut chan.context,
                }
        }
 }
@@ -1835,6 +1847,223 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
                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(),
+
+                       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
@@ -3147,6 +3376,49 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
                        _ => todo!()
                }
        }
+
+       /// If we receive an error message when attempting to open a channel, it may only be a rejection
+       /// of the channel type we tried, not of our ability to open any channel at all. We can see if a
+       /// downgrade of channel features would be possible so that we can still open the channel.
+       pub(crate) fn maybe_downgrade_channel_features<F: Deref>(
+               &mut self, fee_estimator: &LowerBoundedFeeEstimator<F>
+       ) -> Result<(), ()>
+       where
+               F::Target: FeeEstimator
+       {
+               if !self.is_outbound() ||
+                       !matches!(
+                               self.channel_state, ChannelState::NegotiatingFunding(flags)
+                               if flags == NegotiatingFundingFlags::OUR_INIT_SENT
+                       )
+               {
+                       return Err(());
+               }
+               if self.channel_type == ChannelTypeFeatures::only_static_remote_key() {
+                       // We've exhausted our options
+                       return Err(());
+               }
+               // We support opening a few different types of channels. Try removing our additional
+               // features one by one until we've either arrived at our default or the counterparty has
+               // accepted one.
+               //
+               // Due to the order below, we may not negotiate `option_anchors_zero_fee_htlc_tx` if the
+               // counterparty doesn't support `option_scid_privacy`. Since `get_initial_channel_type`
+               // checks whether the counterparty supports every feature, this would only happen if the
+               // counterparty is advertising the feature, but rejecting channels proposing the feature for
+               // whatever reason.
+               if self.channel_type.supports_anchors_zero_fee_htlc_tx() {
+                       self.channel_type.clear_anchors_zero_fee_htlc_tx();
+                       self.feerate_per_kw = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::NonAnchorChannelFee);
+                       assert!(!self.channel_transaction_parameters.channel_type_features.supports_anchors_nonzero_fee_htlc_tx());
+               } else if self.channel_type.supports_scid_privacy() {
+                       self.channel_type.clear_scid_privacy();
+               } else {
+                       self.channel_type = ChannelTypeFeatures::only_static_remote_key();
+               }
+               self.channel_transaction_parameters.channel_type_features = self.channel_type.clone();
+               Ok(())
+       }
 }
 
 // Internal utility functions for channels
@@ -3198,6 +3470,7 @@ pub(crate) fn get_legacy_default_holder_selected_channel_reserve_satoshis(channe
 ///
 /// This is used both for outbound and inbound channels and has lower bound
 /// of `dust_limit_satoshis`.
+#[cfg(dual_funding)]
 fn get_v2_channel_reserve_satoshis(channel_value_satoshis: u64, dust_limit_satoshis: u64) -> u64 {
        // Fixed at 1% of channel value by spec.
        let (q, _) = channel_value_satoshis.overflowing_div(100);
@@ -3813,7 +4086,7 @@ impl<SP: Deref> Channel<SP> where
 
                log_info!(logger, "Received channel_ready from peer for channel {}", &self.context.channel_id());
 
-               Ok(self.get_announcement_sigs(node_signer, chain_hash, user_config, best_block.height(), logger))
+               Ok(self.get_announcement_sigs(node_signer, chain_hash, user_config, best_block.height, logger))
        }
 
        pub fn update_add_htlc<F, FE: Deref, L: Deref>(
@@ -5202,7 +5475,7 @@ impl<SP: Deref> Channel<SP> where
 
                let shutdown_msg = self.get_outbound_shutdown();
 
-               let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, best_block.height(), logger);
+               let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, best_block.height, logger);
 
                if matches!(self.context.channel_state, ChannelState::AwaitingChannelReady(_)) {
                        // If we're waiting on a monitor update, we shouldn't re-send any channel_ready's.
@@ -6948,202 +7221,40 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
        where ES::Target: EntropySource,
              F::Target: FeeEstimator
        {
-               let holder_selected_contest_delay = config.channel_handshake_config.our_to_self_delay;
-               let channel_keys_id = signer_provider.generate_channel_keys_id(false, channel_value_satoshis, user_id);
-               let holder_signer = signer_provider.derive_channel_signer(channel_value_satoshis, channel_keys_id);
-               let pubkeys = holder_signer.pubkeys().clone();
-
-               if !their_features.supports_wumbo() && channel_value_satoshis > MAX_FUNDING_SATOSHIS_NO_WUMBO {
-                       return Err(APIError::APIMisuseError{err: format!("funding_value must not exceed {}, it was {}", MAX_FUNDING_SATOSHIS_NO_WUMBO, channel_value_satoshis)});
-               }
-               if channel_value_satoshis >= TOTAL_BITCOIN_SUPPLY_SATOSHIS {
-                       return Err(APIError::APIMisuseError{err: format!("funding_value must be smaller than the total bitcoin supply, it was {}", channel_value_satoshis)});
-               }
-               let channel_value_msat = channel_value_satoshis * 1000;
-               if push_msat > channel_value_msat {
-                       return Err(APIError::APIMisuseError { err: format!("Push value ({}) was larger than channel_value ({})", push_msat, channel_value_msat) });
-               }
-               if holder_selected_contest_delay < BREAKDOWN_TIMEOUT {
-                       return Err(APIError::APIMisuseError {err: format!("Configured with an unreasonable our_to_self_delay ({}) putting user funds at risks", holder_selected_contest_delay)});
-               }
                let holder_selected_channel_reserve_satoshis = get_holder_selected_channel_reserve_satoshis(channel_value_satoshis, config);
                if holder_selected_channel_reserve_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
                        // Protocol level safety check in place, although it should never happen because
                        // of `MIN_THEIR_CHAN_RESERVE_SATOSHIS`
-                       return Err(APIError::APIMisuseError { err: format!("Holder selected channel  reserve below implemention limit dust_limit_satoshis {}", holder_selected_channel_reserve_satoshis) });
+                       return Err(APIError::APIMisuseError { err: format!("Holder selected channel reserve below \
+                               implemention limit dust_limit_satoshis {}", holder_selected_channel_reserve_satoshis) });
                }
 
-               let channel_type = Self::get_initial_channel_type(&config, their_features);
-               debug_assert!(channel_type.is_subset(&channelmanager::provided_channel_type_features(&config)));
-
-               let (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 {
-                       context: ChannelContext {
-                               user_id,
-
-                               config: LegacyChannelConfig {
-                                       options: config.channel_config.clone(),
-                                       announced_channel: config.channel_handshake_config.announced_channel,
-                                       commit_upfront_shutdown_pubkey: config.channel_handshake_config.commit_upfront_shutdown_pubkey,
-                               },
-
-                               prev_config: None,
-
-                               inbound_handshake_limits_override: Some(config.channel_handshake_limits.clone()),
-
-                               channel_id: temporary_channel_id,
-                               temporary_channel_id: Some(temporary_channel_id),
-                               channel_state: ChannelState::NegotiatingFunding(NegotiatingFundingFlags::OUR_INIT_SENT),
-                               announcement_sigs_state: AnnouncementSigsState::NotSent,
-                               secp_ctx,
-                               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(),
-
-                               signer_pending_commitment_update: false,
-                               signer_pending_funding: false,
-
-                               #[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,
-                               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,
+               let channel_keys_id = signer_provider.generate_channel_keys_id(false, channel_value_satoshis, user_id);
+               let holder_signer = signer_provider.derive_channel_signer(channel_value_satoshis, channel_keys_id);
+               let pubkeys = holder_signer.pubkeys().clone();
 
-                               counterparty_cur_commitment_point: None,
-                               counterparty_prev_commitment_point: None,
+               let chan = Self {
+                       context: ChannelContext::new_for_outbound_channel(
+                               fee_estimator,
+                               entropy_source,
+                               signer_provider,
                                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,
+                               their_features,
+                               channel_value_satoshis,
+                               push_msat,
+                               user_id,
+                               config,
+                               current_chain_height,
                                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,
+                               temporary_channel_id,
+                               holder_selected_channel_reserve_satoshis,
                                channel_keys_id,
-
-                               blocked_monitor_updates: Vec::new(),
-                               local_initiated_shutdown: None,
-                       },
+                               holder_signer,
+                               pubkeys,
+                       )?,
                        unfunded_context: UnfundedChannelContext { unfunded_channel_age_ticks: 0 }
-               })
+               };
+               Ok(chan)
        }
 
        /// Only allowed after [`ChannelContext::channel_transaction_parameters`] is set.
@@ -7237,29 +7348,6 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
                Ok(funding_created)
        }
 
-       fn get_initial_channel_type(config: &UserConfig, their_features: &InitFeatures) -> ChannelTypeFeatures {
-               // The default channel type (ie the first one we try) depends on whether the channel is
-               // public - if it is, we just go with `only_static_remotekey` as it's the only option
-               // available. If it's private, we first try `scid_privacy` as it provides better privacy
-               // with no other changes, and fall back to `only_static_remotekey`.
-               let mut ret = ChannelTypeFeatures::only_static_remote_key();
-               if !config.channel_handshake_config.announced_channel &&
-                       config.channel_handshake_config.negotiate_scid_privacy &&
-                       their_features.supports_scid_privacy() {
-                       ret.set_scid_privacy_required();
-               }
-
-               // Optionally, if the user would like to negotiate the `anchors_zero_fee_htlc_tx` option, we
-               // set it now. If they don't understand it, we'll fall back to our default of
-               // `only_static_remotekey`.
-               if config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx &&
-                       their_features.supports_anchors_zero_fee_htlc_tx() {
-                       ret.set_anchors_zero_fee_htlc_tx_required();
-               }
-
-               ret
-       }
-
        /// If we receive an error message, it may only be a rejection of the channel type we tried,
        /// not of our ability to open any channel at all. Thus, on error, we should first call this
        /// and see if we get a new `OpenChannel` message, otherwise the channel is failed.
@@ -7269,37 +7357,7 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
        where
                F::Target: FeeEstimator
        {
-               if !self.context.is_outbound() ||
-                       !matches!(
-                               self.context.channel_state, ChannelState::NegotiatingFunding(flags)
-                               if flags == NegotiatingFundingFlags::OUR_INIT_SENT
-                       )
-               {
-                       return Err(());
-               }
-               if self.context.channel_type == ChannelTypeFeatures::only_static_remote_key() {
-                       // We've exhausted our options
-                       return Err(());
-               }
-               // We support opening a few different types of channels. Try removing our additional
-               // features one by one until we've either arrived at our default or the counterparty has
-               // accepted one.
-               //
-               // Due to the order below, we may not negotiate `option_anchors_zero_fee_htlc_tx` if the
-               // counterparty doesn't support `option_scid_privacy`. Since `get_initial_channel_type`
-               // checks whether the counterparty supports every feature, this would only happen if the
-               // counterparty is advertising the feature, but rejecting channels proposing the feature for
-               // whatever reason.
-               if self.context.channel_type.supports_anchors_zero_fee_htlc_tx() {
-                       self.context.channel_type.clear_anchors_zero_fee_htlc_tx();
-                       self.context.feerate_per_kw = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::NonAnchorChannelFee);
-                       assert!(!self.context.channel_transaction_parameters.channel_type_features.supports_anchors_nonzero_fee_htlc_tx());
-               } else if self.context.channel_type.supports_scid_privacy() {
-                       self.context.channel_type.clear_scid_privacy();
-               } else {
-                       self.context.channel_type = ChannelTypeFeatures::only_static_remote_key();
-               }
-               self.context.channel_transaction_parameters.channel_type_features = self.context.channel_type.clone();
+               self.context.maybe_downgrade_channel_features(fee_estimator)?;
                Ok(self.get_open_channel(chain_hash))
        }
 
@@ -7871,6 +7929,130 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
        }
 }
 
+// A not-yet-funded outbound (from holder) channel using V2 channel establishment.
+#[cfg(dual_funding)]
+pub(super) struct OutboundV2Channel<SP: Deref> where SP::Target: SignerProvider {
+       pub context: ChannelContext<SP>,
+       pub unfunded_context: UnfundedChannelContext,
+       #[cfg(dual_funding)]
+       pub dual_funding_context: DualFundingChannelContext,
+}
+
+#[cfg(dual_funding)]
+impl<SP: Deref> OutboundV2Channel<SP> where SP::Target: SignerProvider {
+       pub fn new<ES: Deref, F: Deref>(
+               fee_estimator: &LowerBoundedFeeEstimator<F>, entropy_source: &ES, signer_provider: &SP,
+               counterparty_node_id: PublicKey, their_features: &InitFeatures, funding_satoshis: u64,
+               user_id: u128, config: &UserConfig, current_chain_height: u32, outbound_scid_alias: u64,
+               funding_confirmation_target: ConfirmationTarget,
+       ) -> Result<OutboundV2Channel<SP>, APIError>
+       where ES::Target: EntropySource,
+             F::Target: FeeEstimator,
+       {
+               let channel_keys_id = signer_provider.generate_channel_keys_id(false, funding_satoshis, user_id);
+               let holder_signer = signer_provider.derive_channel_signer(funding_satoshis, channel_keys_id);
+               let pubkeys = holder_signer.pubkeys().clone();
+
+               let temporary_channel_id = Some(ChannelId::temporary_v2_from_revocation_basepoint(&pubkeys.revocation_basepoint));
+
+               let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
+                       funding_satoshis, MIN_CHAN_DUST_LIMIT_SATOSHIS);
+
+               let funding_feerate_sat_per_1000_weight = fee_estimator.bounded_sat_per_1000_weight(funding_confirmation_target);
+               let funding_tx_locktime = current_chain_height;
+
+               let chan = Self {
+                       context: ChannelContext::new_for_outbound_channel(
+                               fee_estimator,
+                               entropy_source,
+                               signer_provider,
+                               counterparty_node_id,
+                               their_features,
+                               funding_satoshis,
+                               0,
+                               user_id,
+                               config,
+                               current_chain_height,
+                               outbound_scid_alias,
+                               temporary_channel_id,
+                               holder_selected_channel_reserve_satoshis,
+                               channel_keys_id,
+                               holder_signer,
+                               pubkeys,
+                       )?,
+                       unfunded_context: UnfundedChannelContext { unfunded_channel_age_ticks: 0 },
+                       dual_funding_context: DualFundingChannelContext {
+                               our_funding_satoshis: funding_satoshis,
+                               their_funding_satoshis: 0,
+                               funding_tx_locktime,
+                               funding_feerate_sat_per_1000_weight,
+                       }
+               };
+               Ok(chan)
+       }
+
+       /// If we receive an error message, it may only be a rejection of the channel type we tried,
+       /// not of our ability to open any channel at all. Thus, on error, we should first call this
+       /// and see if we get a new `OpenChannelV2` message, otherwise the channel is failed.
+       pub(crate) fn maybe_handle_error_without_close<F: Deref>(
+               &mut self, chain_hash: ChainHash, fee_estimator: &LowerBoundedFeeEstimator<F>
+       ) -> Result<msgs::OpenChannelV2, ()>
+       where
+               F::Target: FeeEstimator
+       {
+               self.context.maybe_downgrade_channel_features(fee_estimator)?;
+               Ok(self.get_open_channel_v2(chain_hash))
+       }
+
+       pub fn get_open_channel_v2(&self, chain_hash: ChainHash) -> msgs::OpenChannelV2 {
+               if self.context.have_received_message() {
+                       debug_assert!(false, "Cannot generate an open_channel2 after we've moved forward");
+               }
+
+               if self.context.cur_holder_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER {
+                       debug_assert!(false, "Tried to send an open_channel2 for a channel that has already advanced");
+               }
+
+               let first_per_commitment_point = self.context.holder_signer.as_ref()
+                       .get_per_commitment_point(self.context.cur_holder_commitment_transaction_number,
+                               &self.context.secp_ctx);
+               let second_per_commitment_point = self.context.holder_signer.as_ref()
+                       .get_per_commitment_point(self.context.cur_holder_commitment_transaction_number - 1,
+                               &self.context.secp_ctx);
+               let keys = self.context.get_holder_pubkeys();
+
+               msgs::OpenChannelV2 {
+                       common_fields: msgs::CommonOpenChannelFields {
+                               chain_hash,
+                               temporary_channel_id: self.context.temporary_channel_id.unwrap(),
+                               funding_satoshis: self.context.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,
+                               commitment_feerate_sat_per_1000_weight: self.context.feerate_per_kw,
+                               to_self_delay: self.context.get_holder_selected_contest_delay(),
+                               max_accepted_htlcs: self.context.holder_max_accepted_htlcs,
+                               funding_pubkey: keys.funding_pubkey,
+                               revocation_basepoint: keys.revocation_basepoint.to_public_key(),
+                               payment_basepoint: keys.payment_point,
+                               delayed_payment_basepoint: keys.delayed_payment_basepoint.to_public_key(),
+                               htlc_basepoint: keys.htlc_basepoint.to_public_key(),
+                               first_per_commitment_point,
+                               channel_flags: if self.context.config.announced_channel {1} else {0},
+                               shutdown_scriptpubkey: Some(match &self.context.shutdown_scriptpubkey {
+                                       Some(script) => script.clone().into_inner(),
+                                       None => Builder::new().into_script(),
+                               }),
+                               channel_type: Some(self.context.channel_type.clone()),
+                       },
+                       funding_feerate_sat_per_1000_weight: self.context.feerate_per_kw,
+                       second_per_commitment_point,
+                       locktime: self.dual_funding_context.funding_tx_locktime,
+                       require_confirmed_inputs: None,
+               }
+       }
+}
+
 // A not-yet-funded inbound (from counterparty) channel using V2 channel establishment.
 #[cfg(dual_funding)]
 pub(super) struct InboundV2Channel<SP: Deref> where SP::Target: SignerProvider {
@@ -8025,6 +8207,31 @@ impl<SP: Deref> InboundV2Channel<SP> where SP::Target: SignerProvider {
        }
 }
 
+// Unfunded channel utilities
+
+fn get_initial_channel_type(config: &UserConfig, their_features: &InitFeatures) -> ChannelTypeFeatures {
+       // The default channel type (ie the first one we try) depends on whether the channel is
+       // public - if it is, we just go with `only_static_remotekey` as it's the only option
+       // available. If it's private, we first try `scid_privacy` as it provides better privacy
+       // with no other changes, and fall back to `only_static_remotekey`.
+       let mut ret = ChannelTypeFeatures::only_static_remote_key();
+       if !config.channel_handshake_config.announced_channel &&
+               config.channel_handshake_config.negotiate_scid_privacy &&
+               their_features.supports_scid_privacy() {
+               ret.set_scid_privacy_required();
+       }
+
+       // Optionally, if the user would like to negotiate the `anchors_zero_fee_htlc_tx` option, we
+       // set it now. If they don't understand it, we'll fall back to our default of
+       // `only_static_remotekey`.
+       if config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx &&
+               their_features.supports_anchors_zero_fee_htlc_tx() {
+               ret.set_anchors_zero_fee_htlc_tx_required();
+       }
+
+       ret
+}
+
 const SERIALIZATION_VERSION: u8 = 3;
 const MIN_SERIALIZATION_VERSION: u8 = 3;