/// See [`ChannelContext::sent_message_awaiting_response`] for more information.
pub(crate) const DISCONNECT_PEER_AWAITING_RESPONSE_TICKS: usize = 2;
+/// The number of ticks that may elapse while we're waiting for an unfunded outbound/inbound channel
+/// to be promoted to a [`Channel`] since the unfunded channel was created. An unfunded channel
+/// exceeding this age limit will be force-closed and purged from memory.
+pub(crate) const UNFUNDED_CHANNEL_AGE_LIMIT_TICKS: usize = 60;
+
struct PendingChannelMonitorUpdate {
update: ChannelMonitorUpdate,
}
(0, update, required),
});
+/// Contains all state common to unfunded inbound/outbound channels.
+pub(super) struct UnfundedChannelContext {
+ /// A counter tracking how many ticks have elapsed since this unfunded channel was
+ /// created. If this unfunded channel reaches peer has yet to respond after reaching
+ /// `UNFUNDED_CHANNEL_AGE_LIMIT_TICKS`, it will be force-closed and purged from memory.
+ ///
+ /// This is so that we don't keep channels around that haven't progressed to a funded state
+ /// in a timely manner.
+ unfunded_channel_age_ticks: usize,
+}
+
+impl UnfundedChannelContext {
+ /// Determines whether we should force-close and purge this unfunded channel from memory due to it
+ /// having reached the unfunded channel age limit.
+ ///
+ /// This should be called on every [`super::channelmanager::ChannelManager::timer_tick_occurred`].
+ pub fn should_expire_unfunded_channel(&mut self) -> bool {
+ self.unfunded_channel_age_ticks += 1;
+ self.unfunded_channel_age_ticks >= UNFUNDED_CHANNEL_AGE_LIMIT_TICKS
+ }
+}
+
/// Contains everything about the channel including state, and various flags.
pub(super) struct ChannelContext<Signer: ChannelSigner> {
config: LegacyChannelConfig,
&self.channel_type
}
- /// Guaranteed to be Some after both ChannelReady messages have been exchanged (and, thus,
- /// is_usable() returns true).
- /// Allowed in any state (including after shutdown)
+ /// Gets the channel's `short_channel_id`.
+ ///
+ /// Will return `None` if the channel hasn't been confirmed yet.
pub fn get_short_channel_id(&self) -> Option<u64> {
self.short_channel_id
}
}
/// Returns the funding_txo we either got from our peer, or were given by
- /// get_outbound_funding_created.
+ /// get_funding_created.
pub fn get_funding_txo(&self) -> Option<OutPoint> {
self.channel_transaction_parameters.funding_outpoint
}
#[inline]
/// Creates a set of keys for build_commitment_transaction to generate a transaction which we
/// will sign and send to our counterparty.
- /// If an Err is returned, it is a ChannelError::Close (for get_outbound_funding_created)
+ /// If an Err is returned, it is a ChannelError::Close (for get_funding_created)
fn build_remote_transaction_keys(&self) -> TxCreationKeys {
//TODO: Ensure that the payment_key derived here ends up in the library users' wallet as we
//may see payments to it!
// TODO: We should refactor this to be an Inbound/OutboundChannel until initial setup handshaking
// has been completed, and then turn into a Channel to get compiler-time enforcement of things like
-// calling channel_id() before we're set up or things like get_outbound_funding_signed on an
+// calling channel_id() before we're set up or things like get_funding_signed on an
// inbound channel.
//
// Holder designates channel data owned for the benefit of the user client.
return Err(ChannelError::Ignore("Cannot get a ChannelAnnouncement if the channel is not currently usable".to_owned()));
}
+ let short_channel_id = self.context.get_short_channel_id()
+ .ok_or(ChannelError::Ignore("Cannot get a ChannelAnnouncement if the channel has not been confirmed yet".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 msg = msgs::UnsignedChannelAnnouncement {
features: channelmanager::provided_channel_features(&user_config),
chain_hash,
- short_channel_id: self.context.get_short_channel_id().unwrap(),
+ short_channel_id,
node_id_1: if were_node_one { node_id } else { counterparty_node_id },
node_id_2: if were_node_one { counterparty_node_id } else { node_id },
bitcoin_key_1: NodeId::from_pubkey(if were_node_one { &self.context.get_holder_pubkeys().funding_pubkey } else { self.context.counterparty_funding_pubkey() }),
},
Ok(v) => v
};
+ let short_channel_id = match self.context.get_short_channel_id() {
+ Some(scid) => scid,
+ None => return None,
+ };
+
self.context.announcement_sigs_state = AnnouncementSigsState::MessageSent;
Some(msgs::AnnouncementSignatures {
channel_id: self.context.channel_id(),
- short_channel_id: self.context.get_short_channel_id().unwrap(),
+ short_channel_id,
node_signature: our_node_sig,
bitcoin_signature: our_bitcoin_sig,
})
/// A not-yet-funded outbound (from holder) channel using V1 channel establishment.
pub(super) struct OutboundV1Channel<Signer: ChannelSigner> {
pub context: ChannelContext<Signer>,
+ pub unfunded_context: UnfundedChannelContext,
}
impl<Signer: WriteableEcdsaChannelSigner> OutboundV1Channel<Signer> {
channel_keys_id,
blocked_monitor_updates: Vec::new(),
- }
+ },
+ unfunded_context: UnfundedChannelContext { unfunded_channel_age_ticks: 0 }
})
}
- /// 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 {
+ /// If an Err is returned, it is a ChannelError::Close (for get_funding_created)
+ fn get_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)
/// 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)
+ pub fn get_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!");
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) {
+ let signature = match self.get_funding_created_signature(logger) {
Ok(res) => res,
Err(e) => {
log_error!(logger, "Got bad signatures: {:?}!", e);
/// A not-yet-funded inbound (from counterparty) channel using V1 channel establishment.
pub(super) struct InboundV1Channel<Signer: ChannelSigner> {
pub context: ChannelContext<Signer>,
+ pub unfunded_context: UnfundedChannelContext,
}
impl<Signer: WriteableEcdsaChannelSigner> InboundV1Channel<Signer> {
channel_keys_id,
blocked_monitor_updates: Vec::new(),
- }
+ },
+ unfunded_context: UnfundedChannelContext { unfunded_channel_age_ticks: 0 }
};
Ok(chan)
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 (mut node_a_chan, funding_created_msg) = node_a_chan.get_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).map_err(|_| ()).unwrap();
// Node B --> Node A: funding signed
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 (mut node_a_chan, funding_created_msg) = node_a_chan.get_funding_created(tx.clone(), funding_outpoint, &&logger).map_err(|_| ()).unwrap();
let (mut node_b_chan, funding_signed_msg, _) = node_b_chan.funding_created(&funding_created_msg, best_block, &&keys_provider, &&logger).map_err(|_| ()).unwrap();
// Node B --> Node A: funding signed
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 (mut node_a_chan, funding_created_msg) = node_a_chan.get_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).map_err(|_| ()).unwrap();
// Node B --> Node A: funding signed