}
}
+impl fmt::Display for ChannelError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ &ChannelError::Ignore(ref e) => write!(f, "{}", e),
+ &ChannelError::Warn(ref e) => write!(f, "{}", e),
+ &ChannelError::Close(ref e) => write!(f, "{}", e),
+ }
+ }
+}
+
macro_rules! secp_check {
($res: expr, $err: expr) => {
match $res {
}
impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
- /// If we receive an error message, it may only be a rejection of the channel type we tried,
- /// not of our ability to open any channel at all. Thus, on error, we should first call this
- /// and see if we get a new `OpenChannel` message, otherwise the channel is failed.
- pub(crate) fn maybe_handle_error_without_close(&mut self, chain_hash: BlockHash) -> Result<msgs::OpenChannel, ()> {
- if !self.context.is_outbound() || self.context.channel_state != ChannelState::OurInitSent as u32 { return Err(()); }
- if self.context.channel_type == ChannelTypeFeatures::only_static_remote_key() {
- // We've exhausted our options
- return Err(());
- }
- // We support opening a few different types of channels. Try removing our additional
- // features one by one until we've either arrived at our default or the counterparty has
- // accepted one.
- //
- // Due to the order below, we may not negotiate `option_anchors_zero_fee_htlc_tx` if the
- // counterparty doesn't support `option_scid_privacy`. Since `get_initial_channel_type`
- // checks whether the counterparty supports every feature, this would only happen if the
- // counterparty is advertising the feature, but rejecting channels proposing the feature for
- // whatever reason.
- if self.context.channel_type.supports_anchors_zero_fee_htlc_tx() {
- self.context.channel_type.clear_anchors_zero_fee_htlc_tx();
- assert!(self.context.channel_transaction_parameters.opt_non_zero_fee_anchors.is_none());
- self.context.channel_transaction_parameters.opt_anchors = None;
- } else if self.context.channel_type.supports_scid_privacy() {
- self.context.channel_type.clear_scid_privacy();
- } else {
- self.context.channel_type = ChannelTypeFeatures::only_static_remote_key();
- }
- Ok(self.get_open_channel(chain_hash))
- }
-
// Constructors:
fn check_remote_fee<F: Deref, L: Deref>(fee_estimator: &LowerBoundedFeeEstimator<F>,
// Message handlers:
- pub fn accept_channel(&mut self, msg: &msgs::AcceptChannel, default_limits: &ChannelHandshakeLimits, their_features: &InitFeatures) -> Result<(), ChannelError> {
- let peer_limits = if let Some(ref limits) = self.context.inbound_handshake_limits_override { limits } else { default_limits };
-
- // Check sanity of message fields:
- if !self.context.is_outbound() {
- return Err(ChannelError::Close("Got an accept_channel message from an inbound peer".to_owned()));
- }
- if self.context.channel_state != ChannelState::OurInitSent as u32 {
- return Err(ChannelError::Close("Got an accept_channel message at a strange time".to_owned()));
- }
- if msg.dust_limit_satoshis > 21000000 * 100000000 {
- return Err(ChannelError::Close(format!("Peer never wants payout outputs? dust_limit_satoshis was {}", msg.dust_limit_satoshis)));
- }
- if msg.channel_reserve_satoshis > self.context.channel_value_satoshis {
- return Err(ChannelError::Close(format!("Bogus channel_reserve_satoshis ({}). Must not be greater than ({})", msg.channel_reserve_satoshis, self.context.channel_value_satoshis)));
- }
- if msg.dust_limit_satoshis > self.context.holder_selected_channel_reserve_satoshis {
- return Err(ChannelError::Close(format!("Dust limit ({}) is bigger than our channel reserve ({})", msg.dust_limit_satoshis, self.context.holder_selected_channel_reserve_satoshis)));
- }
- if msg.channel_reserve_satoshis > self.context.channel_value_satoshis - self.context.holder_selected_channel_reserve_satoshis {
- return Err(ChannelError::Close(format!("Bogus channel_reserve_satoshis ({}). Must not be greater than channel value minus our reserve ({})",
- msg.channel_reserve_satoshis, self.context.channel_value_satoshis - self.context.holder_selected_channel_reserve_satoshis)));
- }
- let full_channel_value_msat = (self.context.channel_value_satoshis - msg.channel_reserve_satoshis) * 1000;
- if msg.htlc_minimum_msat >= full_channel_value_msat {
- return Err(ChannelError::Close(format!("Minimum htlc value ({}) is full channel value ({})", msg.htlc_minimum_msat, full_channel_value_msat)));
- }
- let max_delay_acceptable = u16::min(peer_limits.their_to_self_delay, MAX_LOCAL_BREAKDOWN_TIMEOUT);
- if msg.to_self_delay > max_delay_acceptable {
- return Err(ChannelError::Close(format!("They wanted our payments to be delayed by a needlessly long period. Upper limit: {}. Actual: {}", max_delay_acceptable, msg.to_self_delay)));
- }
- if msg.max_accepted_htlcs < 1 {
- return Err(ChannelError::Close("0 max_accepted_htlcs makes for a useless channel".to_owned()));
- }
- if msg.max_accepted_htlcs > MAX_HTLCS {
- return Err(ChannelError::Close(format!("max_accepted_htlcs was {}. It must not be larger than {}", msg.max_accepted_htlcs, MAX_HTLCS)));
- }
-
- // Now check against optional parameters as set by config...
- if msg.htlc_minimum_msat > peer_limits.max_htlc_minimum_msat {
- return Err(ChannelError::Close(format!("htlc_minimum_msat ({}) is higher than the user specified limit ({})", msg.htlc_minimum_msat, peer_limits.max_htlc_minimum_msat)));
- }
- if msg.max_htlc_value_in_flight_msat < peer_limits.min_max_htlc_value_in_flight_msat {
- return Err(ChannelError::Close(format!("max_htlc_value_in_flight_msat ({}) is less than the user specified limit ({})", msg.max_htlc_value_in_flight_msat, peer_limits.min_max_htlc_value_in_flight_msat)));
- }
- if msg.channel_reserve_satoshis > peer_limits.max_channel_reserve_satoshis {
- return Err(ChannelError::Close(format!("channel_reserve_satoshis ({}) is higher than the user specified limit ({})", msg.channel_reserve_satoshis, peer_limits.max_channel_reserve_satoshis)));
- }
- if msg.max_accepted_htlcs < peer_limits.min_max_accepted_htlcs {
- return Err(ChannelError::Close(format!("max_accepted_htlcs ({}) is less than the user specified limit ({})", msg.max_accepted_htlcs, peer_limits.min_max_accepted_htlcs)));
- }
- if msg.dust_limit_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
- return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is less than the implementation limit ({})", msg.dust_limit_satoshis, MIN_CHAN_DUST_LIMIT_SATOSHIS)));
- }
- if msg.dust_limit_satoshis > MAX_CHAN_DUST_LIMIT_SATOSHIS {
- return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is greater than the implementation limit ({})", msg.dust_limit_satoshis, MAX_CHAN_DUST_LIMIT_SATOSHIS)));
- }
- if msg.minimum_depth > peer_limits.max_minimum_depth {
- return Err(ChannelError::Close(format!("We consider the minimum depth to be unreasonably large. Expected minimum: ({}). Actual: ({})", peer_limits.max_minimum_depth, msg.minimum_depth)));
- }
-
- if let Some(ty) = &msg.channel_type {
- if *ty != self.context.channel_type {
- return Err(ChannelError::Close("Channel Type in accept_channel didn't match the one sent in open_channel.".to_owned()));
- }
- } else if their_features.supports_channel_type() {
- // Assume they've accepted the channel type as they said they understand it.
- } else {
- let channel_type = ChannelTypeFeatures::from_init(&their_features);
- if channel_type != ChannelTypeFeatures::only_static_remote_key() {
- return Err(ChannelError::Close("Only static_remote_key is supported for non-negotiated channel types".to_owned()));
- }
- self.context.channel_type = channel_type;
- }
-
- let counterparty_shutdown_scriptpubkey = if their_features.supports_upfront_shutdown_script() {
- match &msg.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 };
-
- self.context.counterparty_dust_limit_satoshis = msg.dust_limit_satoshis;
- self.context.counterparty_max_htlc_value_in_flight_msat = cmp::min(msg.max_htlc_value_in_flight_msat, self.context.channel_value_satoshis * 1000);
- self.context.counterparty_selected_channel_reserve_satoshis = Some(msg.channel_reserve_satoshis);
- self.context.counterparty_htlc_minimum_msat = msg.htlc_minimum_msat;
- self.context.counterparty_max_accepted_htlcs = msg.max_accepted_htlcs;
-
- if peer_limits.trust_own_funding_0conf {
- self.context.minimum_depth = Some(msg.minimum_depth);
- } else {
- self.context.minimum_depth = Some(cmp::max(1, msg.minimum_depth));
- }
-
- let counterparty_pubkeys = ChannelPublicKeys {
- funding_pubkey: msg.funding_pubkey,
- revocation_basepoint: msg.revocation_basepoint,
- payment_point: msg.payment_point,
- delayed_payment_basepoint: msg.delayed_payment_basepoint,
- htlc_basepoint: msg.htlc_basepoint
- };
-
- self.context.channel_transaction_parameters.counterparty_parameters = Some(CounterpartyChannelTransactionParameters {
- selected_contest_delay: msg.to_self_delay,
- pubkeys: counterparty_pubkeys,
- });
-
- self.context.counterparty_cur_commitment_point = Some(msg.first_per_commitment_point);
- self.context.counterparty_shutdown_scriptpubkey = counterparty_shutdown_scriptpubkey;
-
- self.context.channel_state = ChannelState::OurInitSent as u32 | ChannelState::TheirInitSent as u32;
- self.context.inbound_handshake_limits_override = None; // We're done enforcing limits on our peer's handshake now.
-
- Ok(())
- }
-
fn funding_created_signature<L: Deref>(&mut self, sig: &Signature, logger: &L) -> Result<(Txid, CommitmentTransaction, Signature), ChannelError> where L::Target: Logger {
let funding_script = self.context.get_funding_redeemscript();
// Methods to get unprompted messages to send to the remote end (or where we already returned
// something in the handler for the message that prompted this message):
- pub fn get_open_channel(&self, chain_hash: BlockHash) -> msgs::OpenChannel {
- if !self.context.is_outbound() {
- panic!("Tried to open a channel for an inbound channel?");
- }
- if self.context.channel_state != ChannelState::OurInitSent as u32 {
- panic!("Cannot generate an open_channel after we've moved forward");
- }
-
- if self.context.cur_holder_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER {
- panic!("Tried to send an open_channel for a channel that has already advanced");
- }
-
- let first_per_commitment_point = self.context.holder_signer.get_per_commitment_point(self.context.cur_holder_commitment_transaction_number, &self.context.secp_ctx);
- let keys = self.context.get_holder_pubkeys();
-
- msgs::OpenChannel {
- chain_hash,
- temporary_channel_id: self.context.channel_id,
- funding_satoshis: self.context.channel_value_satoshis,
- push_msat: self.context.channel_value_satoshis * 1000 - self.context.value_to_self_msat,
- dust_limit_satoshis: self.context.holder_dust_limit_satoshis,
- max_htlc_value_in_flight_msat: self.context.holder_max_htlc_value_in_flight_msat,
- channel_reserve_satoshis: self.context.holder_selected_channel_reserve_satoshis,
- htlc_minimum_msat: self.context.holder_htlc_minimum_msat,
- feerate_per_kw: self.context.feerate_per_kw as u32,
- 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,
- payment_point: keys.payment_point,
- delayed_payment_basepoint: keys.delayed_payment_basepoint,
- htlc_basepoint: keys.htlc_basepoint,
- 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()),
- }
- }
-
pub fn inbound_is_awaiting_accept(&self) -> bool {
self.context.inbound_awaiting_accept
}
self.generate_accept_channel_message()
}
- /// If an Err is returned, it is a ChannelError::Close (for get_outbound_funding_created)
- fn get_outbound_funding_created_signature<L: Deref>(&mut self, logger: &L) -> Result<Signature, ChannelError> 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;
- Ok(self.context.holder_signer.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &self.context.secp_ctx)
- .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?.0)
- }
-
- /// Updates channel state with knowledge of the funding transaction's txid/index, and generates
- /// a funding_created message for the remote peer.
- /// Panics if called at some time other than immediately after initial handshake, if called twice,
- /// or if called on an inbound channel.
- /// Note that channel_id changes during this call!
- /// Do NOT broadcast the funding transaction until after a successful funding_signed call!
- /// If an Err is returned, it is a ChannelError::Close.
- pub fn get_outbound_funding_created<L: Deref>(&mut self, funding_transaction: Transaction, funding_txo: OutPoint, logger: &L) -> Result<msgs::FundingCreated, ChannelError> where L::Target: Logger {
- if !self.context.is_outbound() {
- panic!("Tried to create outbound funding_created message on an inbound channel!");
- }
- if self.context.channel_state != (ChannelState::OurInitSent as u32 | ChannelState::TheirInitSent as u32) {
- panic!("Tried to get a funding_created messsage at a time other than immediately after initial handshake completion (or tried to get funding_created twice)");
+ /// Gets an UnsignedChannelAnnouncement for this channel. The channel must be publicly
+ /// announceable and available for use (have exchanged ChannelReady messages in both
+ /// directions). Should be used for both broadcasted announcements and in response to an
+ /// AnnouncementSignatures message from the remote peer.
+ ///
+ /// Will only fail if we're not in a state where channel_announcement may be sent (including
+ /// closing).
+ ///
+ /// This will only return ChannelError::Ignore upon failure.
+ fn get_channel_announcement<NS: Deref>(
+ &self, node_signer: &NS, chain_hash: BlockHash, user_config: &UserConfig,
+ ) -> Result<msgs::UnsignedChannelAnnouncement, ChannelError> where NS::Target: NodeSigner {
+ if !self.context.config.announced_channel {
+ return Err(ChannelError::Ignore("Channel is not available for public announcements".to_owned()));
}
- if self.context.commitment_secrets.get_min_seen_secret() != (1 << 48) ||
- self.context.cur_counterparty_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER ||
- self.context.cur_holder_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER {
- panic!("Should not have advanced channel commitment tx numbers prior to funding_created");
+ if !self.context.is_usable() {
+ return Err(ChannelError::Ignore("Cannot get a ChannelAnnouncement if the channel is not currently usable".to_owned()));
}
- self.context.channel_transaction_parameters.funding_outpoint = Some(funding_txo);
- self.context.holder_signer.provide_channel_parameters(&self.context.channel_transaction_parameters);
-
- let signature = match self.get_outbound_funding_created_signature(logger) {
- Ok(res) => res,
- Err(e) => {
- log_error!(logger, "Got bad signatures: {:?}!", e);
- self.context.channel_transaction_parameters.funding_outpoint = None;
- return Err(e);
- }
- };
-
- let temporary_channel_id = self.context.channel_id;
-
- // Now that we're past error-generating stuff, update our local state:
-
- self.context.channel_state = ChannelState::FundingCreated as u32;
- self.context.channel_id = funding_txo.to_channel_id();
- self.context.funding_transaction = Some(funding_transaction);
-
- Ok(msgs::FundingCreated {
- temporary_channel_id,
- funding_txid: funding_txo.txid,
- funding_output_index: funding_txo.index,
- signature,
- #[cfg(taproot)]
- partial_signature_with_nonce: None,
- #[cfg(taproot)]
- next_local_nonce: None,
- })
- }
-
- /// Gets an UnsignedChannelAnnouncement for this channel. The channel must be publicly
- /// announceable and available for use (have exchanged ChannelReady messages in both
- /// directions). Should be used for both broadcasted announcements and in response to an
- /// AnnouncementSignatures message from the remote peer.
- ///
- /// Will only fail if we're not in a state where channel_announcement may be sent (including
- /// closing).
- ///
- /// This will only return ChannelError::Ignore upon failure.
- fn get_channel_announcement<NS: Deref>(
- &self, node_signer: &NS, chain_hash: BlockHash, user_config: &UserConfig,
- ) -> Result<msgs::UnsignedChannelAnnouncement, ChannelError> where NS::Target: NodeSigner {
- if !self.context.config.announced_channel {
- return Err(ChannelError::Ignore("Channel is not available for public announcements".to_owned()));
- }
- if !self.context.is_usable() {
- return Err(ChannelError::Ignore("Cannot get a ChannelAnnouncement if the channel is not currently usable".to_owned()));
- }
-
- let node_id = NodeId::from_pubkey(&node_signer.get_node_id(Recipient::Node)
- .map_err(|_| ChannelError::Ignore("Failed to retrieve own public key".to_owned()))?);
- let counterparty_node_id = NodeId::from_pubkey(&self.context.get_counterparty_node_id());
- let were_node_one = node_id.as_slice() < counterparty_node_id.as_slice();
+ let node_id = NodeId::from_pubkey(&node_signer.get_node_id(Recipient::Node)
+ .map_err(|_| ChannelError::Ignore("Failed to retrieve own public key".to_owned()))?);
+ let counterparty_node_id = NodeId::from_pubkey(&self.context.get_counterparty_node_id());
+ let were_node_one = node_id.as_slice() < counterparty_node_id.as_slice();
let msg = msgs::UnsignedChannelAnnouncement {
features: channelmanager::provided_channel_features(&user_config),
}
impl<Signer: WriteableEcdsaChannelSigner> OutboundV1Channel<Signer> {
- fn get_initial_channel_type(config: &UserConfig, their_features: &InitFeatures) -> ChannelTypeFeatures {
- // The default channel type (ie the first one we try) depends on whether the channel is
- // public - if it is, we just go with `only_static_remotekey` as it's the only option
- // available. If it's private, we first try `scid_privacy` as it provides better privacy
- // with no other changes, and fall back to `only_static_remotekey`.
- let mut ret = ChannelTypeFeatures::only_static_remote_key();
- if !config.channel_handshake_config.announced_channel &&
- config.channel_handshake_config.negotiate_scid_privacy &&
- their_features.supports_scid_privacy() {
- ret.set_scid_privacy_required();
- }
-
- // Optionally, if the user would like to negotiate the `anchors_zero_fee_htlc_tx` option, we
- // set it now. If they don't understand it, we'll fall back to our default of
- // `only_static_remotekey`.
- #[cfg(anchors)]
- { // Attributes are not allowed on if expressions on our current MSRV of 1.41.
- if config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx &&
- their_features.supports_anchors_zero_fee_htlc_tx() {
- ret.set_anchors_zero_fee_htlc_tx_required();
- }
- }
-
- ret
- }
-
- pub fn new_outbound<ES: Deref, SP: Deref, F: Deref>(
+ pub fn new<ES: Deref, SP: Deref, F: Deref>(
fee_estimator: &LowerBoundedFeeEstimator<F>, entropy_source: &ES, signer_provider: &SP, counterparty_node_id: PublicKey, their_features: &InitFeatures,
channel_value_satoshis: u64, push_msat: u64, user_id: u128, config: &UserConfig, current_chain_height: u32,
outbound_scid_alias: u64
- ) -> Result<Channel<Signer>, APIError>
+ ) -> Result<OutboundV1Channel<Signer>, APIError>
where ES::Target: EntropySource,
SP::Target: SignerProvider<Signer = Signer>,
F::Target: FeeEstimator,
let temporary_channel_id = entropy_source.get_secure_random_bytes();
- Ok(Channel {
+ Ok(Self {
context: ChannelContext {
user_id,
}
})
}
+
+ /// If an Err is returned, it is a ChannelError::Close (for get_outbound_funding_created)
+ fn get_outbound_funding_created_signature<L: Deref>(&mut self, logger: &L) -> Result<Signature, ChannelError> 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;
+ Ok(self.context.holder_signer.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &self.context.secp_ctx)
+ .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?.0)
+ }
+
+ /// Updates channel state with knowledge of the funding transaction's txid/index, and generates
+ /// a funding_created message for the remote peer.
+ /// Panics if called at some time other than immediately after initial handshake, if called twice,
+ /// or if called on an inbound channel.
+ /// Note that channel_id changes during this call!
+ /// Do NOT broadcast the funding transaction until after a successful funding_signed call!
+ /// If an Err is returned, it is a ChannelError::Close.
+ pub fn get_outbound_funding_created<L: Deref>(mut self, funding_transaction: Transaction, funding_txo: OutPoint, logger: &L)
+ -> Result<(Channel<Signer>, msgs::FundingCreated), (Self, ChannelError)> where L::Target: Logger {
+ if !self.context.is_outbound() {
+ panic!("Tried to create outbound funding_created message on an inbound channel!");
+ }
+ if self.context.channel_state != (ChannelState::OurInitSent as u32 | ChannelState::TheirInitSent as u32) {
+ panic!("Tried to get a funding_created messsage at a time other than immediately after initial handshake completion (or tried to get funding_created twice)");
+ }
+ if self.context.commitment_secrets.get_min_seen_secret() != (1 << 48) ||
+ self.context.cur_counterparty_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER ||
+ self.context.cur_holder_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER {
+ panic!("Should not have advanced channel commitment tx numbers prior to funding_created");
+ }
+
+ self.context.channel_transaction_parameters.funding_outpoint = Some(funding_txo);
+ self.context.holder_signer.provide_channel_parameters(&self.context.channel_transaction_parameters);
+
+ let signature = match self.get_outbound_funding_created_signature(logger) {
+ Ok(res) => res,
+ Err(e) => {
+ log_error!(logger, "Got bad signatures: {:?}!", e);
+ self.context.channel_transaction_parameters.funding_outpoint = None;
+ return Err((self, e));
+ }
+ };
+
+ let temporary_channel_id = self.context.channel_id;
+
+ // Now that we're past error-generating stuff, update our local state:
+
+ self.context.channel_state = ChannelState::FundingCreated as u32;
+ self.context.channel_id = funding_txo.to_channel_id();
+ self.context.funding_transaction = Some(funding_transaction);
+
+ let channel = Channel {
+ context: self.context,
+ };
+
+ Ok((channel, msgs::FundingCreated {
+ temporary_channel_id,
+ funding_txid: funding_txo.txid,
+ funding_output_index: funding_txo.index,
+ signature,
+ #[cfg(taproot)]
+ partial_signature_with_nonce: None,
+ #[cfg(taproot)]
+ next_local_nonce: None,
+ }))
+ }
+
+ fn get_initial_channel_type(config: &UserConfig, their_features: &InitFeatures) -> ChannelTypeFeatures {
+ // The default channel type (ie the first one we try) depends on whether the channel is
+ // public - if it is, we just go with `only_static_remotekey` as it's the only option
+ // available. If it's private, we first try `scid_privacy` as it provides better privacy
+ // with no other changes, and fall back to `only_static_remotekey`.
+ let mut ret = ChannelTypeFeatures::only_static_remote_key();
+ if !config.channel_handshake_config.announced_channel &&
+ config.channel_handshake_config.negotiate_scid_privacy &&
+ their_features.supports_scid_privacy() {
+ ret.set_scid_privacy_required();
+ }
+
+ // Optionally, if the user would like to negotiate the `anchors_zero_fee_htlc_tx` option, we
+ // set it now. If they don't understand it, we'll fall back to our default of
+ // `only_static_remotekey`.
+ #[cfg(anchors)]
+ { // Attributes are not allowed on if expressions on our current MSRV of 1.41.
+ if config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx &&
+ their_features.supports_anchors_zero_fee_htlc_tx() {
+ ret.set_anchors_zero_fee_htlc_tx_required();
+ }
+ }
+
+ ret
+ }
+
+ /// If we receive an error message, it may only be a rejection of the channel type we tried,
+ /// not of our ability to open any channel at all. Thus, on error, we should first call this
+ /// and see if we get a new `OpenChannel` message, otherwise the channel is failed.
+ pub(crate) fn maybe_handle_error_without_close(&mut self, chain_hash: BlockHash) -> Result<msgs::OpenChannel, ()> {
+ if !self.context.is_outbound() || self.context.channel_state != ChannelState::OurInitSent as u32 { return Err(()); }
+ if self.context.channel_type == ChannelTypeFeatures::only_static_remote_key() {
+ // We've exhausted our options
+ return Err(());
+ }
+ // We support opening a few different types of channels. Try removing our additional
+ // features one by one until we've either arrived at our default or the counterparty has
+ // accepted one.
+ //
+ // Due to the order below, we may not negotiate `option_anchors_zero_fee_htlc_tx` if the
+ // counterparty doesn't support `option_scid_privacy`. Since `get_initial_channel_type`
+ // checks whether the counterparty supports every feature, this would only happen if the
+ // counterparty is advertising the feature, but rejecting channels proposing the feature for
+ // whatever reason.
+ if self.context.channel_type.supports_anchors_zero_fee_htlc_tx() {
+ self.context.channel_type.clear_anchors_zero_fee_htlc_tx();
+ assert!(self.context.channel_transaction_parameters.opt_non_zero_fee_anchors.is_none());
+ self.context.channel_transaction_parameters.opt_anchors = None;
+ } else if self.context.channel_type.supports_scid_privacy() {
+ self.context.channel_type.clear_scid_privacy();
+ } else {
+ self.context.channel_type = ChannelTypeFeatures::only_static_remote_key();
+ }
+ Ok(self.get_open_channel(chain_hash))
+ }
+
+ pub fn get_open_channel(&self, chain_hash: BlockHash) -> msgs::OpenChannel {
+ if !self.context.is_outbound() {
+ panic!("Tried to open a channel for an inbound channel?");
+ }
+ if self.context.channel_state != ChannelState::OurInitSent as u32 {
+ panic!("Cannot generate an open_channel after we've moved forward");
+ }
+
+ if self.context.cur_holder_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER {
+ panic!("Tried to send an open_channel for a channel that has already advanced");
+ }
+
+ let first_per_commitment_point = self.context.holder_signer.get_per_commitment_point(self.context.cur_holder_commitment_transaction_number, &self.context.secp_ctx);
+ let keys = self.context.get_holder_pubkeys();
+
+ msgs::OpenChannel {
+ chain_hash,
+ temporary_channel_id: self.context.channel_id,
+ funding_satoshis: self.context.channel_value_satoshis,
+ push_msat: self.context.channel_value_satoshis * 1000 - self.context.value_to_self_msat,
+ dust_limit_satoshis: self.context.holder_dust_limit_satoshis,
+ max_htlc_value_in_flight_msat: self.context.holder_max_htlc_value_in_flight_msat,
+ channel_reserve_satoshis: self.context.holder_selected_channel_reserve_satoshis,
+ htlc_minimum_msat: self.context.holder_htlc_minimum_msat,
+ feerate_per_kw: self.context.feerate_per_kw as u32,
+ 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,
+ payment_point: keys.payment_point,
+ delayed_payment_basepoint: keys.delayed_payment_basepoint,
+ htlc_basepoint: keys.htlc_basepoint,
+ 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()),
+ }
+ }
+
+ // Message handlers
+ pub fn accept_channel(&mut self, msg: &msgs::AcceptChannel, default_limits: &ChannelHandshakeLimits, their_features: &InitFeatures) -> Result<(), ChannelError> {
+ let peer_limits = if let Some(ref limits) = self.context.inbound_handshake_limits_override { limits } else { default_limits };
+
+ // Check sanity of message fields:
+ if !self.context.is_outbound() {
+ return Err(ChannelError::Close("Got an accept_channel message from an inbound peer".to_owned()));
+ }
+ if self.context.channel_state != ChannelState::OurInitSent as u32 {
+ return Err(ChannelError::Close("Got an accept_channel message at a strange time".to_owned()));
+ }
+ if msg.dust_limit_satoshis > 21000000 * 100000000 {
+ return Err(ChannelError::Close(format!("Peer never wants payout outputs? dust_limit_satoshis was {}", msg.dust_limit_satoshis)));
+ }
+ if msg.channel_reserve_satoshis > self.context.channel_value_satoshis {
+ return Err(ChannelError::Close(format!("Bogus channel_reserve_satoshis ({}). Must not be greater than ({})", msg.channel_reserve_satoshis, self.context.channel_value_satoshis)));
+ }
+ if msg.dust_limit_satoshis > self.context.holder_selected_channel_reserve_satoshis {
+ return Err(ChannelError::Close(format!("Dust limit ({}) is bigger than our channel reserve ({})", msg.dust_limit_satoshis, self.context.holder_selected_channel_reserve_satoshis)));
+ }
+ if msg.channel_reserve_satoshis > self.context.channel_value_satoshis - self.context.holder_selected_channel_reserve_satoshis {
+ return Err(ChannelError::Close(format!("Bogus channel_reserve_satoshis ({}). Must not be greater than channel value minus our reserve ({})",
+ msg.channel_reserve_satoshis, self.context.channel_value_satoshis - self.context.holder_selected_channel_reserve_satoshis)));
+ }
+ let full_channel_value_msat = (self.context.channel_value_satoshis - msg.channel_reserve_satoshis) * 1000;
+ if msg.htlc_minimum_msat >= full_channel_value_msat {
+ return Err(ChannelError::Close(format!("Minimum htlc value ({}) is full channel value ({})", msg.htlc_minimum_msat, full_channel_value_msat)));
+ }
+ let max_delay_acceptable = u16::min(peer_limits.their_to_self_delay, MAX_LOCAL_BREAKDOWN_TIMEOUT);
+ if msg.to_self_delay > max_delay_acceptable {
+ return Err(ChannelError::Close(format!("They wanted our payments to be delayed by a needlessly long period. Upper limit: {}. Actual: {}", max_delay_acceptable, msg.to_self_delay)));
+ }
+ if msg.max_accepted_htlcs < 1 {
+ return Err(ChannelError::Close("0 max_accepted_htlcs makes for a useless channel".to_owned()));
+ }
+ if msg.max_accepted_htlcs > MAX_HTLCS {
+ return Err(ChannelError::Close(format!("max_accepted_htlcs was {}. It must not be larger than {}", msg.max_accepted_htlcs, MAX_HTLCS)));
+ }
+
+ // Now check against optional parameters as set by config...
+ if msg.htlc_minimum_msat > peer_limits.max_htlc_minimum_msat {
+ return Err(ChannelError::Close(format!("htlc_minimum_msat ({}) is higher than the user specified limit ({})", msg.htlc_minimum_msat, peer_limits.max_htlc_minimum_msat)));
+ }
+ if msg.max_htlc_value_in_flight_msat < peer_limits.min_max_htlc_value_in_flight_msat {
+ return Err(ChannelError::Close(format!("max_htlc_value_in_flight_msat ({}) is less than the user specified limit ({})", msg.max_htlc_value_in_flight_msat, peer_limits.min_max_htlc_value_in_flight_msat)));
+ }
+ if msg.channel_reserve_satoshis > peer_limits.max_channel_reserve_satoshis {
+ return Err(ChannelError::Close(format!("channel_reserve_satoshis ({}) is higher than the user specified limit ({})", msg.channel_reserve_satoshis, peer_limits.max_channel_reserve_satoshis)));
+ }
+ if msg.max_accepted_htlcs < peer_limits.min_max_accepted_htlcs {
+ return Err(ChannelError::Close(format!("max_accepted_htlcs ({}) is less than the user specified limit ({})", msg.max_accepted_htlcs, peer_limits.min_max_accepted_htlcs)));
+ }
+ if msg.dust_limit_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
+ return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is less than the implementation limit ({})", msg.dust_limit_satoshis, MIN_CHAN_DUST_LIMIT_SATOSHIS)));
+ }
+ if msg.dust_limit_satoshis > MAX_CHAN_DUST_LIMIT_SATOSHIS {
+ return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is greater than the implementation limit ({})", msg.dust_limit_satoshis, MAX_CHAN_DUST_LIMIT_SATOSHIS)));
+ }
+ if msg.minimum_depth > peer_limits.max_minimum_depth {
+ return Err(ChannelError::Close(format!("We consider the minimum depth to be unreasonably large. Expected minimum: ({}). Actual: ({})", peer_limits.max_minimum_depth, msg.minimum_depth)));
+ }
+
+ if let Some(ty) = &msg.channel_type {
+ if *ty != self.context.channel_type {
+ return Err(ChannelError::Close("Channel Type in accept_channel didn't match the one sent in open_channel.".to_owned()));
+ }
+ } else if their_features.supports_channel_type() {
+ // Assume they've accepted the channel type as they said they understand it.
+ } else {
+ let channel_type = ChannelTypeFeatures::from_init(&their_features);
+ if channel_type != ChannelTypeFeatures::only_static_remote_key() {
+ return Err(ChannelError::Close("Only static_remote_key is supported for non-negotiated channel types".to_owned()));
+ }
+ self.context.channel_type = channel_type;
+ }
+
+ let counterparty_shutdown_scriptpubkey = if their_features.supports_upfront_shutdown_script() {
+ match &msg.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 };
+
+ self.context.counterparty_dust_limit_satoshis = msg.dust_limit_satoshis;
+ self.context.counterparty_max_htlc_value_in_flight_msat = cmp::min(msg.max_htlc_value_in_flight_msat, self.context.channel_value_satoshis * 1000);
+ self.context.counterparty_selected_channel_reserve_satoshis = Some(msg.channel_reserve_satoshis);
+ self.context.counterparty_htlc_minimum_msat = msg.htlc_minimum_msat;
+ self.context.counterparty_max_accepted_htlcs = msg.max_accepted_htlcs;
+
+ if peer_limits.trust_own_funding_0conf {
+ self.context.minimum_depth = Some(msg.minimum_depth);
+ } else {
+ self.context.minimum_depth = Some(cmp::max(1, msg.minimum_depth));
+ }
+
+ let counterparty_pubkeys = ChannelPublicKeys {
+ funding_pubkey: msg.funding_pubkey,
+ revocation_basepoint: msg.revocation_basepoint,
+ payment_point: msg.payment_point,
+ delayed_payment_basepoint: msg.delayed_payment_basepoint,
+ htlc_basepoint: msg.htlc_basepoint
+ };
+
+ self.context.channel_transaction_parameters.counterparty_parameters = Some(CounterpartyChannelTransactionParameters {
+ selected_contest_delay: msg.to_self_delay,
+ pubkeys: counterparty_pubkeys,
+ });
+
+ self.context.counterparty_cur_commitment_point = Some(msg.first_per_commitment_point);
+ self.context.counterparty_shutdown_scriptpubkey = counterparty_shutdown_scriptpubkey;
+
+ self.context.channel_state = ChannelState::OurInitSent as u32 | ChannelState::TheirInitSent as u32;
+ self.context.inbound_handshake_limits_override = None; // We're done enforcing limits on our peer's handshake now.
+
+ Ok(())
+ }
}
/// A not-yet-funded inbound (from counterparty) channel using V1 channel establishment.
let secp_ctx = Secp256k1::new();
let node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- match OutboundV1Channel::<EnforcingSigner>::new_outbound(&LowerBoundedFeeEstimator::new(&TestFeeEstimator { fee_est: 253 }), &&keys_provider, &&keys_provider, node_id, &features, 10000000, 100000, 42, &config, 0, 42) {
+ match OutboundV1Channel::<EnforcingSigner>::new(&LowerBoundedFeeEstimator::new(&TestFeeEstimator { fee_est: 253 }), &&keys_provider, &&keys_provider, node_id, &features, 10000000, 100000, 42, &config, 0, 42) {
Err(APIError::IncompatibleShutdownScript { script }) => {
assert_eq!(script.into_inner(), non_v0_segwit_shutdown_script.into_inner());
},
let node_a_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- let node_a_chan = OutboundV1Channel::<EnforcingSigner>::new_outbound(&bounded_fee_estimator, &&keys_provider, &&keys_provider, node_a_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
+ let node_a_chan = OutboundV1Channel::<EnforcingSigner>::new(&bounded_fee_estimator, &&keys_provider, &&keys_provider, node_a_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
// Now change the fee so we can check that the fee in the open_channel message is the
// same as the old fee.
let network = Network::Testnet;
let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
let logger = test_utils::TestLogger::new();
+ let best_block = BestBlock::from_network(network);
// Go through the flow of opening a channel between two nodes, making sure
// they have different dust limits.
// Create Node A's channel pointing to Node B's pubkey
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- let mut node_a_chan = OutboundV1Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
+ let mut node_a_chan = OutboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
// Create Node B's channel by receiving Node A's open_channel message
// Make sure A's dust limit is as we expect.
node_a_chan.accept_channel(&accept_channel_msg, &config.channel_handshake_limits, &channelmanager::provided_init_features(&config)).unwrap();
node_a_chan.context.holder_dust_limit_satoshis = 1560;
+ // Node A --> Node B: funding created
+ let output_script = node_a_chan.context.get_funding_redeemscript();
+ let tx = Transaction { version: 1, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: vec![TxOut {
+ value: 10000000, script_pubkey: output_script.clone(),
+ }]};
+ let funding_outpoint = OutPoint{ txid: tx.txid(), index: 0 };
+ let (mut node_a_chan, funding_created_msg) = node_a_chan.get_outbound_funding_created(tx.clone(), funding_outpoint, &&logger).map_err(|_| ()).unwrap();
+ let (funding_signed_msg, _) = node_b_chan.funding_created(&funding_created_msg, best_block, &&keys_provider, &&logger).unwrap();
+
+ // Node B --> Node A: funding signed
+ let _ = node_a_chan.funding_signed(&funding_signed_msg, best_block, &&keys_provider, &&logger).unwrap();
+
// Put some inbound and outbound HTLCs in A's channel.
let htlc_amount_msat = 11_092_000; // put an amount below A's effective dust limit but above B's.
node_a_chan.context.pending_inbound_htlcs.push(InboundHTLCOutput {
let node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- let mut chan = OutboundV1Channel::<EnforcingSigner>::new_outbound(&fee_est, &&keys_provider, &&keys_provider, node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
+ let mut chan = OutboundV1Channel::<EnforcingSigner>::new(&fee_est, &&keys_provider, &&keys_provider, node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
let commitment_tx_fee_0_htlcs = commit_tx_fee_msat(chan.context.feerate_per_kw, 0, chan.context.opt_anchors());
let commitment_tx_fee_1_htlc = commit_tx_fee_msat(chan.context.feerate_per_kw, 1, chan.context.opt_anchors());
// Create Node A's channel pointing to Node B's pubkey
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- let mut node_a_chan = OutboundV1Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
+ let mut node_a_chan = OutboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
// Create Node B's channel by receiving Node A's open_channel message
let open_channel_msg = node_a_chan.get_open_channel(chain_hash);
value: 10000000, script_pubkey: output_script.clone(),
}]};
let funding_outpoint = OutPoint{ txid: tx.txid(), index: 0 };
- let funding_created_msg = node_a_chan.get_outbound_funding_created(tx.clone(), funding_outpoint, &&logger).unwrap();
+ let (mut node_a_chan, funding_created_msg) = node_a_chan.get_outbound_funding_created(tx.clone(), funding_outpoint, &&logger).map_err(|_| ()).unwrap();
let (funding_signed_msg, _) = node_b_chan.funding_created(&funding_created_msg, best_block, &&keys_provider, &&logger).unwrap();
// Node B --> Node A: funding signed
- let _ = node_a_chan.funding_signed(&funding_signed_msg, best_block, &&keys_provider, &&logger);
+ let _ = node_a_chan.funding_signed(&funding_signed_msg, best_block, &&keys_provider, &&logger).unwrap();
// Now disconnect the two nodes and check that the commitment point in
// Node B's channel_reestablish message is sane.
let mut config_101_percent = UserConfig::default();
config_101_percent.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 101;
- // Test that `new_outbound` creates a channel with the correct value for
+ // Test that `OutboundV1Channel::new` creates a channel with the correct value for
// `holder_max_htlc_value_in_flight_msat`, when configured with a valid percentage value,
// which is set to the lower bound + 1 (2%) of the `channel_value`.
- let chan_1 = OutboundV1Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_2_percent), 10000000, 100000, 42, &config_2_percent, 0, 42).unwrap();
+ let chan_1 = OutboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_2_percent), 10000000, 100000, 42, &config_2_percent, 0, 42).unwrap();
let chan_1_value_msat = chan_1.context.channel_value_satoshis * 1000;
assert_eq!(chan_1.context.holder_max_htlc_value_in_flight_msat, (chan_1_value_msat as f64 * 0.02) as u64);
// Test with the upper bound - 1 of valid values (99%).
- let chan_2 = OutboundV1Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_99_percent), 10000000, 100000, 42, &config_99_percent, 0, 42).unwrap();
+ let chan_2 = OutboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_99_percent), 10000000, 100000, 42, &config_99_percent, 0, 42).unwrap();
let chan_2_value_msat = chan_2.context.channel_value_satoshis * 1000;
assert_eq!(chan_2.context.holder_max_htlc_value_in_flight_msat, (chan_2_value_msat as f64 * 0.99) as u64);
let chan_4_value_msat = chan_4.context.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 `new_outbound` uses the lower bound of the configurable percentage values (1%)
+ // 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::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_0_percent), 10000000, 100000, 42, &config_0_percent, 0, 42).unwrap();
+ let chan_5 = OutboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_0_percent), 10000000, 100000, 42, &config_0_percent, 0, 42).unwrap();
let chan_5_value_msat = chan_5.context.channel_value_satoshis * 1000;
assert_eq!(chan_5.context.holder_max_htlc_value_in_flight_msat, (chan_5_value_msat as f64 * 0.01) as u64);
- // Test that `new_outbound` uses the upper bound of the configurable percentage values
+ // 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::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_101_percent), 10000000, 100000, 42, &config_101_percent, 0, 42).unwrap();
+ let chan_6 = OutboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_101_percent), 10000000, 100000, 42, &config_101_percent, 0, 42).unwrap();
let chan_6_value_msat = chan_6.context.channel_value_satoshis * 1000;
assert_eq!(chan_6.context.holder_max_htlc_value_in_flight_msat, chan_6_value_msat);
#[test]
fn test_configured_holder_selected_channel_reserve_satoshis() {
- // Test that `new_outbound` and `new_from_req` create a channel with the correct
+ // Test that `OutboundV1Channel::new` and `new_from_req` create a channel with the correct
// channel reserves, when `their_channel_reserve_proportional_millionths` is configured.
test_self_and_counterparty_channel_reserve(10_000_000, 0.02, 0.02);
let mut outbound_node_config = UserConfig::default();
outbound_node_config.channel_handshake_config.their_channel_reserve_proportional_millionths = (outbound_selected_channel_reserve_perc * 1_000_000.0) as u32;
- let chan = OutboundV1Channel::<EnforcingSigner>::new_outbound(&&fee_est, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&outbound_node_config), channel_value_satoshis, 100_000, 42, &outbound_node_config, 0, 42).unwrap();
+ let chan = OutboundV1Channel::<EnforcingSigner>::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).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);
#[test]
fn channel_update() {
let feeest = LowerBoundedFeeEstimator::new(&TestFeeEstimator{fee_est: 15000});
+ let logger = test_utils::TestLogger::new();
let secp_ctx = Secp256k1::new();
let seed = [42; 32];
let network = Network::Testnet;
+ let best_block = BestBlock::from_network(network);
let chain_hash = genesis_block(network).header.block_hash();
let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
- // Create a channel.
+ // Create Node A's channel pointing to Node B's pubkey
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- let mut node_a_chan = OutboundV1Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
- assert!(node_a_chan.context.counterparty_forwarding_info.is_none());
- assert_eq!(node_a_chan.context.holder_htlc_minimum_msat, 1); // the default
- assert!(node_a_chan.context.counterparty_forwarding_info().is_none());
+ let mut node_a_chan = OutboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
+
+ // Create Node B's channel by receiving Node A's open_channel message
+ // Make sure A's dust limit is as we expect.
+ let open_channel_msg = node_a_chan.get_open_channel(genesis_block(network).header.block_hash());
+ let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap());
+ let mut node_b_chan = InboundV1Channel::<EnforcingSigner>::new_from_req(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config), &open_channel_msg, 7, &config, 0, &&logger, 42).unwrap();
+
+ // Node B --> Node A: accept channel, explicitly setting B's dust limit.
+ let mut accept_channel_msg = node_b_chan.accept_inbound_channel(0);
+ accept_channel_msg.dust_limit_satoshis = 546;
+ node_a_chan.accept_channel(&accept_channel_msg, &config.channel_handshake_limits, &channelmanager::provided_init_features(&config)).unwrap();
+ node_a_chan.context.holder_dust_limit_satoshis = 1560;
+
+ // Node A --> Node B: funding created
+ let output_script = node_a_chan.context.get_funding_redeemscript();
+ let tx = Transaction { version: 1, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: vec![TxOut {
+ value: 10000000, script_pubkey: output_script.clone(),
+ }]};
+ let funding_outpoint = OutPoint{ txid: tx.txid(), index: 0 };
+ let (mut node_a_chan, funding_created_msg) = node_a_chan.get_outbound_funding_created(tx.clone(), funding_outpoint, &&logger).map_err(|_| ()).unwrap();
+ let (funding_signed_msg, _) = node_b_chan.funding_created(&funding_created_msg, best_block, &&keys_provider, &&logger).unwrap();
+
+ // Node B --> Node A: funding signed
+ let _ = node_a_chan.funding_signed(&funding_signed_msg, best_block, &&keys_provider, &&logger).unwrap();
// Make sure that receiving a channel update will update the Channel as expected.
let update = ChannelUpdate {
let counterparty_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let mut config = UserConfig::default();
config.channel_handshake_config.announced_channel = false;
- let mut chan = Channel::<InMemorySigner>::new_outbound(&LowerBoundedFeeEstimator::new(&feeest), &&keys_provider, &&keys_provider, counterparty_node_id, &channelmanager::provided_init_features(&config), 10_000_000, 0, 42, &config, 0, 42).unwrap(); // Nothing uses their network key in this test
+ let mut chan = OutboundV1Channel::<InMemorySigner>::new(&LowerBoundedFeeEstimator::new(&feeest), &&keys_provider, &&keys_provider, counterparty_node_id, &channelmanager::provided_init_features(&config), 10_000_000, 0, 42, &config, 0, 42).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
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- let node_a_chan = OutboundV1Channel::<EnforcingSigner>::new_outbound(&feeest, &&keys_provider, &&keys_provider,
+ let node_a_chan = OutboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider,
node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
let mut channel_type_features = ChannelTypeFeatures::only_static_remote_key();
// It is not enough for just the initiator to signal `option_anchors_zero_fee_htlc_tx`, both
// need to signal it.
- let channel_a = OutboundV1Channel::<EnforcingSigner>::new_outbound(
+ let channel_a = OutboundV1Channel::<EnforcingSigner>::new(
&fee_estimator, &&keys_provider, &&keys_provider, node_id_b,
&channelmanager::provided_init_features(&UserConfig::default()), 10000000, 100000, 42,
&config, 0, 42
expected_channel_type.set_static_remote_key_required();
expected_channel_type.set_anchors_zero_fee_htlc_tx_required();
- let channel_a = OutboundV1Channel::<EnforcingSigner>::new_outbound(
+ let channel_a = OutboundV1Channel::<EnforcingSigner>::new(
&fee_estimator, &&keys_provider, &&keys_provider, node_id_b,
&channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42
).unwrap();
let raw_init_features = static_remote_key_required | simple_anchors_required;
let init_features_with_simple_anchors = InitFeatures::from_le_bytes(raw_init_features.to_le_bytes().to_vec());
- let channel_a = OutboundV1Channel::<EnforcingSigner>::new_outbound(
+ let channel_a = OutboundV1Channel::<EnforcingSigner>::new(
&fee_estimator, &&keys_provider, &&keys_provider, node_id_b,
&channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42
).unwrap();
// First, we'll try to open a channel between A and B where A requests a channel type for
// the original `option_anchors` feature (non zero fee htlc tx). This should be rejected by
// B as it's not supported by LDK.
- let channel_a = OutboundV1Channel::<EnforcingSigner>::new_outbound(
+ let channel_a = OutboundV1Channel::<EnforcingSigner>::new(
&fee_estimator, &&keys_provider, &&keys_provider, node_id_b,
&channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42
).unwrap();
// `anchors_zero_fee_htlc_tx`. B is malicious and tries to downgrade the channel type to the
// original `option_anchors` feature, which should be rejected by A as it's not supported by
// LDK.
- let mut channel_a = OutboundV1Channel::<EnforcingSigner>::new_outbound(
+ let mut channel_a = OutboundV1Channel::<EnforcingSigner>::new(
&fee_estimator, &&keys_provider, &&keys_provider, node_id_b, &simple_anchors_init,
10000000, 100000, 42, &config, 0, 42
).unwrap();
/// State we hold per-peer.
pub(super) struct PeerState<Signer: ChannelSigner> {
- /// `temporary_channel_id` or `channel_id` -> `channel`.
+ /// `channel_id` -> `Channel`.
///
- /// Holds all channels where the peer is the counterparty. Once a channel has been assigned a
- /// `channel_id`, the `temporary_channel_id` key in the map is updated and is replaced by the
- /// `channel_id`.
+ /// Holds all funded channels where the peer is the counterparty.
pub(super) channel_by_id: HashMap<[u8; 32], Channel<Signer>>,
/// `temporary_channel_id` -> `OutboundV1Channel`.
///
Err(err)
},
}
- } }
+ } };
+ ($self: ident, $internal: expr) => {
+ match $internal {
+ Ok(res) => Ok(res),
+ Err((chan, msg_handle_err)) => {
+ let counterparty_node_id = chan.get_counterparty_node_id();
+ handle_error!($self, Err(msg_handle_err), counterparty_node_id).map_err(|err| (chan, err))
+ },
+ }
+ };
}
macro_rules! update_maps_on_chan_removal {
shutdown_res, $self.get_channel_update_for_broadcast(&$channel).ok()))
},
}
+ };
+ ($self: ident, $err: expr, $channel_context: expr, $channel_id: expr, PREFUNDED) => {
+ match $err {
+ // We should only ever have `ChannelError::Close` when prefunded channels error.
+ // In any case, just close the channel.
+ ChannelError::Warn(msg) | ChannelError::Ignore(msg) | ChannelError::Close(msg) => {
+ log_error!($self.logger, "Closing prefunded channel {} due to an error: {}", log_bytes!($channel_id[..]), msg);
+ update_maps_on_chan_removal!($self, &$channel_context);
+ let shutdown_res = $channel_context.force_shutdown(false);
+ (true, MsgHandleErrInternal::from_finish_shutdown(msg, *$channel_id, $channel_context.get_user_id(),
+ shutdown_res, None))
+ },
+ }
}
}
}
}
+macro_rules! try_v1_outbound_chan_entry {
+ ($self: ident, $res: expr, $entry: expr) => {
+ match $res {
+ Ok(res) => res,
+ Err(e) => {
+ let (drop, res) = convert_chan_err!($self, e, $entry.get_mut().context, $entry.key(), PREFUNDED);
+ if drop {
+ $entry.remove_entry();
+ }
+ return Err(res);
+ }
+ }
+ }
+}
+
macro_rules! try_chan_entry {
($self: ident, $res: expr, $entry: expr) => {
match $res {
let outbound_scid_alias = self.create_and_insert_outbound_scid_alias();
let their_features = &peer_state.latest_features;
let config = if override_config.is_some() { override_config.as_ref().unwrap() } else { &self.default_configuration };
- match OutboundV1Channel::new_outbound(&self.fee_estimator, &self.entropy_source, &self.signer_provider, their_network_key,
+ match OutboundV1Channel::new(&self.fee_estimator, &self.entropy_source, &self.signer_provider, their_network_key,
their_features, channel_value_satoshis, push_msat, user_channel_id, config,
self.best_block.read().unwrap().height(), outbound_scid_alias)
{
let res = channel.get_open_channel(self.genesis_hash.clone());
let temporary_channel_id = channel.context.channel_id();
- match peer_state.channel_by_id.entry(temporary_channel_id) {
+ match peer_state.outbound_v1_channel_by_id.entry(temporary_channel_id) {
hash_map::Entry::Occupied(_) => {
if cfg!(fuzzing) {
return Err(APIError::APIMisuseError { err: "Fuzzy bad RNG".to_owned() });
Ok(temporary_channel_id)
}
- fn list_channels_with_filter<Fn: FnMut(&(&[u8; 32], &Channel<<SP::Target as SignerProvider>::Signer>)) -> bool + Copy>(&self, f: Fn) -> Vec<ChannelDetails> {
+ fn list_funded_channels_with_filter<Fn: FnMut(&(&[u8; 32], &Channel<<SP::Target as SignerProvider>::Signer>)) -> bool + Copy>(&self, f: Fn) -> Vec<ChannelDetails> {
// Allocate our best estimate of the number of channels we have in the `res`
// Vec. Sadly the `short_to_chan_info` map doesn't cover channels without
// a scid or a scid alias, and the `id_to_peer` shouldn't be used outside
/// Gets the list of open channels, in random order. See [`ChannelDetails`] field documentation for
/// more information.
pub fn list_channels(&self) -> Vec<ChannelDetails> {
- self.list_channels_with_filter(|_| true)
+ // Allocate our best estimate of the number of channels we have in the `res`
+ // Vec. Sadly the `short_to_chan_info` map doesn't cover channels without
+ // a scid or a scid alias, and the `id_to_peer` shouldn't be used outside
+ // of the ChannelMonitor handling. Therefore reallocations may still occur, but is
+ // unlikely as the `short_to_chan_info` map often contains 2 entries for
+ // the same channel.
+ let mut res = Vec::with_capacity(self.short_to_chan_info.read().unwrap().len());
+ {
+ let best_block_height = self.best_block.read().unwrap().height();
+ let per_peer_state = self.per_peer_state.read().unwrap();
+ 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 (_channel_id, channel) in peer_state.channel_by_id.iter() {
+ let details = ChannelDetails::from_channel_context(&channel.context, best_block_height,
+ peer_state.latest_features.clone());
+ res.push(details);
+ }
+ for (_channel_id, channel) in peer_state.outbound_v1_channel_by_id.iter() {
+ let details = ChannelDetails::from_channel_context(&channel.context, best_block_height,
+ peer_state.latest_features.clone());
+ res.push(details);
+ }
+ }
+ }
+ res
}
/// Gets the list of usable channels, in random order. Useful as an argument to
// Note we use is_live here instead of usable which leads to somewhat confused
// internal/external nomenclature, but that's ok cause that's probably what the user
// really wanted anyway.
- self.list_channels_with_filter(|&(_, ref channel)| channel.context.is_live())
+ self.list_funded_channels_with_filter(|&(_, ref channel)| channel.context.is_live())
}
/// Gets the list of channels we have with a given counterparty, in random order.
let per_peer_state = self.per_peer_state.read().unwrap();
let peer_state_mutex = per_peer_state.get(peer_node_id)
.ok_or_else(|| APIError::ChannelUnavailable { err: format!("Can't find a peer matching the passed counterparty node_id {}", peer_node_id) })?;
- let mut chan = {
+ let (update_opt, counterparty_node_id) = {
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
let peer_state = &mut *peer_state_lock;
+ let closure_reason = if let Some(peer_msg) = peer_msg {
+ ClosureReason::CounterpartyForceClosed { peer_msg: UntrustedString(peer_msg.to_string()) }
+ } else {
+ ClosureReason::HolderForceClosed
+ };
if let hash_map::Entry::Occupied(chan) = peer_state.channel_by_id.entry(channel_id.clone()) {
- if let Some(peer_msg) = peer_msg {
- self.issue_channel_close_events(&chan.get().context, ClosureReason::CounterpartyForceClosed { peer_msg: UntrustedString(peer_msg.to_string()) });
- } else {
- self.issue_channel_close_events(&chan.get().context, ClosureReason::HolderForceClosed);
- }
- remove_channel!(self, chan)
+ log_error!(self.logger, "Force-closing channel {}", log_bytes!(channel_id[..]));
+ self.issue_channel_close_events(&chan.get().context, closure_reason);
+ let mut chan = remove_channel!(self, chan);
+ self.finish_force_close_channel(chan.context.force_shutdown(broadcast));
+ (self.get_channel_update_for_broadcast(&chan).ok(), chan.context.get_counterparty_node_id())
+ } else if let hash_map::Entry::Occupied(chan) = peer_state.outbound_v1_channel_by_id.entry(channel_id.clone()) {
+ log_error!(self.logger, "Force-closing channel {}", log_bytes!(channel_id[..]));
+ self.issue_channel_close_events(&chan.get().context, closure_reason);
+ let mut chan = remove_channel!(self, chan);
+ self.finish_force_close_channel(chan.context.force_shutdown(false));
+ // Prefunded channel has no update
+ (None, chan.context.get_counterparty_node_id())
} else {
return Err(APIError::ChannelUnavailable{ err: format!("Channel with id {} not found for the passed counterparty node_id {}", log_bytes!(*channel_id), peer_node_id) });
}
};
- log_error!(self.logger, "Force-closing channel {}", log_bytes!(channel_id[..]));
- self.finish_force_close_channel(chan.context.force_shutdown(broadcast));
- if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
+ if let Some(update) = update_opt {
let mut peer_state = peer_state_mutex.lock().unwrap();
peer_state.pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
msg: update
});
}
- Ok(chan.context.get_counterparty_node_id())
+ Ok(counterparty_node_id)
}
fn force_close_sending_error(&self, channel_id: &[u8; 32], counterparty_node_id: &PublicKey, broadcast: bool) -> Result<(), APIError> {
/// Handles the generation of a funding transaction, optionally (for tests) with a function
/// which checks the correctness of the funding transaction given the associated channel.
- fn funding_transaction_generated_intern<FundingOutput: Fn(&Channel<<SP::Target as SignerProvider>::Signer>, &Transaction) -> Result<OutPoint, APIError>>(
+ fn funding_transaction_generated_intern<FundingOutput: Fn(&OutboundV1Channel<<SP::Target as SignerProvider>::Signer>, &Transaction) -> Result<OutPoint, APIError>>(
&self, temporary_channel_id: &[u8; 32], counterparty_node_id: &PublicKey, funding_transaction: Transaction, find_funding_output: FundingOutput
) -> Result<(), APIError> {
let per_peer_state = self.per_peer_state.read().unwrap();
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
let peer_state = &mut *peer_state_lock;
- let (msg, chan) = match peer_state.channel_by_id.remove(temporary_channel_id) {
- Some(mut chan) => {
+ let (chan, msg) = match peer_state.outbound_v1_channel_by_id.remove(temporary_channel_id) {
+ Some(chan) => {
let funding_txo = find_funding_output(&chan, &funding_transaction)?;
let funding_res = chan.get_outbound_funding_created(funding_transaction, funding_txo, &self.logger)
- .map_err(|e| if let ChannelError::Close(msg) = e {
- MsgHandleErrInternal::from_finish_shutdown(msg, chan.context.channel_id(), chan.context.get_user_id(), chan.context.force_shutdown(true), None)
+ .map_err(|(mut chan, e)| if let ChannelError::Close(msg) = e {
+ let channel_id = chan.context.channel_id();
+ let user_id = chan.context.get_user_id();
+ let shutdown_res = chan.context.force_shutdown(false);
+ (chan, MsgHandleErrInternal::from_finish_shutdown(msg, channel_id, user_id, shutdown_res, None))
} else { unreachable!(); });
match funding_res {
- Ok(funding_msg) => (funding_msg, chan),
- Err(_) => {
+ Ok((chan, funding_msg)) => (chan, funding_msg),
+ Err((chan, err)) => {
mem::drop(peer_state_lock);
mem::drop(per_peer_state);
- let _ = handle_error!(self, funding_res, chan.context.get_counterparty_node_id());
+ let _: Result<(), _> = handle_error!(self, Err(err), chan.context.get_counterparty_node_id());
return Err(APIError::ChannelUnavailable {
err: "Signer refused to sign the initial commitment transaction".to_owned()
});
chan.context.get_short_channel_id().unwrap_or(chan.context.outbound_scid_alias())
},
None => return Err(APIError::ChannelUnavailable {
- err: format!("Channel with id {} not found for the passed counterparty node_id {}", log_bytes!(*next_hop_channel_id), next_node_id)
+ err: format!("Funded channel with id {} not found for the passed counterparty node_id {}. Channel may still be opening.",
+ log_bytes!(*next_hop_channel_id), next_node_id)
})
}
};
// Get the number of peers with channels, but without funded ones. We don't care too much
// about peers that never open a channel, so we filter by peers that have at least one
// channel, and then limit the number of those with unfunded channels.
- let channeled_peers_without_funding = self.peers_without_funded_channels(|node| !node.channel_by_id.is_empty());
+ let channeled_peers_without_funding =
+ self.peers_without_funded_channels(|node| node.total_channel_count() > 0);
let per_peer_state = self.per_peer_state.read().unwrap();
let peer_state_mutex = per_peer_state.get(counterparty_node_id)
// If this peer already has some channels, a new channel won't increase our number of peers
// with unfunded channels, so as long as we aren't over the maximum number of unfunded
// channels per-peer we can accept channels from a peer with existing ones.
- if peer_state.channel_by_id.is_empty() &&
+ if peer_state.total_channel_count() == 0 &&
channeled_peers_without_funding >= MAX_UNFUNDED_CHANNEL_PEERS &&
!self.default_configuration.manually_accept_inbound_channels
{
})?;
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
let peer_state = &mut *peer_state_lock;
- match peer_state.channel_by_id.entry(msg.temporary_channel_id) {
+ match peer_state.outbound_v1_channel_by_id.entry(msg.temporary_channel_id) {
hash_map::Entry::Occupied(mut chan) => {
- try_chan_entry!(self, chan.get_mut().accept_channel(&msg, &self.default_configuration.channel_handshake_limits, &peer_state.latest_features), chan);
+ try_v1_outbound_chan_entry!(self, chan.get_mut().accept_channel(&msg, &self.default_configuration.channel_handshake_limits, &peer_state.latest_features), chan);
(chan.get().context.get_value_satoshis(), chan.get().context.get_funding_redeemscript().to_v0_p2wsh(), chan.get().context.get_user_id())
},
hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", counterparty_node_id), msg.temporary_channel_id))
}
true
});
+ peer_state.outbound_v1_channel_by_id.retain(|_, chan| {
+ update_maps_on_chan_removal!(self, &chan.context);
+ self.issue_channel_close_events(&chan.context, ClosureReason::DisconnectedPeer);
+ false
+ });
pending_msg_events.retain(|msg| {
match msg {
// V1 Channel Establishment
if peer_state_mutex_opt.is_none() { return; }
let mut peer_state_lock = peer_state_mutex_opt.unwrap().lock().unwrap();
let peer_state = &mut *peer_state_lock;
- peer_state.channel_by_id.keys().cloned().collect()
+ peer_state.channel_by_id.keys().cloned()
+ .chain(peer_state.outbound_v1_channel_by_id.keys().cloned())
+ .chain(peer_state.inbound_v1_channel_by_id.keys().cloned()).collect()
};
for channel_id in channel_ids {
// Untrusted messages from peer, we throw away the error if id points to a non-existent channel
if peer_state_mutex_opt.is_none() { return; }
let mut peer_state_lock = peer_state_mutex_opt.unwrap().lock().unwrap();
let peer_state = &mut *peer_state_lock;
- if let Some(chan) = peer_state.channel_by_id.get_mut(&msg.channel_id) {
+ if let Some(chan) = peer_state.outbound_v1_channel_by_id.get_mut(&msg.channel_id) {
if let Ok(msg) = chan.maybe_handle_error_without_close(self.genesis_hash) {
peer_state.pending_msg_events.push(events::MessageSendEvent::SendOpenChannel {
node_id: *counterparty_node_id,