Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 57 additions & 4 deletions lightning/src/ln/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,19 @@ pub(super) struct Channel<Signer: Sign> {
#[cfg(not(test))]
closing_fee_limits: Option<(u64, u64)>,

/// Flag that ensures that `accept_inbound_channel` must be called before `funding_created`
/// is executed successfully. The reason for this flag is that when the
/// `UserConfig::manually_accept_inbound_channels` config flag is set to true, inbound channels
/// are required to be manually accepted by the node operator before the `msgs::AcceptChannel`
/// message is created and sent out. During the manual accept process, `accept_inbound_channel`
/// is called by `ChannelManager::accept_inbound_channel`.
///
/// The flag counteracts that a counterparty node could theoretically send a
/// `msgs::FundingCreated` message before the node operator has manually accepted an inbound
/// channel request made by the counterparty node. That would execute `funding_created` before
/// `accept_inbound_channel`, and `funding_created` should therefore not execute successfully.
inbound_awaiting_accept: bool,

/// The hash of the block in which the funding transaction was included.
funding_tx_confirmed_in: Option<BlockHash>,
funding_tx_confirmation_height: u32,
Expand Down Expand Up @@ -883,6 +896,8 @@ impl<Signer: Sign> Channel<Signer> {
closing_fee_limits: None,
target_closing_feerate_sats_per_kw: None,

inbound_awaiting_accept: false,

funding_tx_confirmed_in: None,
funding_tx_confirmation_height: 0,
short_channel_id: None,
Expand Down Expand Up @@ -1182,6 +1197,8 @@ impl<Signer: Sign> Channel<Signer> {
closing_fee_limits: None,
target_closing_feerate_sats_per_kw: None,

inbound_awaiting_accept: true,

funding_tx_confirmed_in: None,
funding_tx_confirmation_height: 0,
short_channel_id: None,
Expand Down Expand Up @@ -1973,6 +1990,9 @@ impl<Signer: Sign> Channel<Signer> {
// channel.
return Err(ChannelError::Close("Received funding_created after we got the channel!".to_owned()));
}
if self.inbound_awaiting_accept {
return Err(ChannelError::Close("FundingCreated message received before the channel was accepted".to_owned()));
}
if self.commitment_secrets.get_min_seen_secret() != (1 << 48) ||
self.cur_counterparty_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER ||
self.cur_holder_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER {
Expand Down Expand Up @@ -4645,7 +4665,15 @@ impl<Signer: Sign> Channel<Signer> {
}
}

pub fn get_accept_channel(&self) -> msgs::AcceptChannel {
pub fn inbound_is_awaiting_accept(&self) -> bool {
self.inbound_awaiting_accept
}

/// Marks an inbound channel as accepted and generates a [`msgs::AcceptChannel`] message which
/// should be sent back to the counterparty node.
///
/// [`msgs::AcceptChannel`]: crate::ln::msgs::AcceptChannel
pub fn accept_inbound_channel(&mut self) -> msgs::AcceptChannel {
if self.is_outbound() {
panic!("Tried to send accept_channel for an outbound channel?");
}
Expand All @@ -4655,7 +4683,21 @@ impl<Signer: Sign> Channel<Signer> {
if self.cur_holder_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER {
panic!("Tried to send an accept_channel for a channel that has already advanced");
}
if !self.inbound_awaiting_accept {
panic!("The inbound channel has already been accepted");
}

self.inbound_awaiting_accept = false;

self.generate_accept_channel_message()
}

/// This function is used to explicitly generate a [`msgs::AcceptChannel`] message for an
/// inbound channel. If the intention is to accept an inbound channel, use
/// [`Channel::accept_inbound_channel`] instead.
///
/// [`msgs::AcceptChannel`]: crate::ln::msgs::AcceptChannel
fn generate_accept_channel_message(&self) -> msgs::AcceptChannel {
let first_per_commitment_point = self.holder_signer.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx);
let keys = self.get_holder_pubkeys();

Expand All @@ -4681,6 +4723,15 @@ impl<Signer: Sign> Channel<Signer> {
}
}

/// Enables the possibility for tests to extract a [`msgs::AcceptChannel`] message for an
/// inbound channel without accepting it.
///
/// [`msgs::AcceptChannel`]: crate::ln::msgs::AcceptChannel
#[cfg(test)]
pub fn get_accept_channel_message(&self) -> msgs::AcceptChannel {
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.build_remote_transaction_keys()?;
Expand Down Expand Up @@ -6064,6 +6115,8 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<(&'a K, u32)> for Channel<Signer>
closing_fee_limits: None,
target_closing_feerate_sats_per_kw,

inbound_awaiting_accept: false,

funding_tx_confirmed_in,
funding_tx_confirmation_height,
short_channel_id,
Expand Down Expand Up @@ -6281,10 +6334,10 @@ mod tests {
// 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 node_b_chan = Channel::<EnforcingSigner>::new_from_req(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), &open_channel_msg, 7, &config, 0, &&logger).unwrap();
let mut node_b_chan = Channel::<EnforcingSigner>::new_from_req(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), &open_channel_msg, 7, &config, 0, &&logger).unwrap();

// Node B --> Node A: accept channel, explicitly setting B's dust limit.
let mut accept_channel_msg = node_b_chan.get_accept_channel();
let mut accept_channel_msg = node_b_chan.accept_inbound_channel();
accept_channel_msg.dust_limit_satoshis = 546;
node_a_chan.accept_channel(&accept_channel_msg, &config.peer_channel_config_limits, &InitFeatures::known()).unwrap();
node_a_chan.holder_dust_limit_satoshis = 1560;
Expand Down Expand Up @@ -6402,7 +6455,7 @@ mod tests {
let mut node_b_chan = Channel::<EnforcingSigner>::new_from_req(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), &open_channel_msg, 7, &config, 0, &&logger).unwrap();

// Node B --> Node A: accept channel
let accept_channel_msg = node_b_chan.get_accept_channel();
let accept_channel_msg = node_b_chan.accept_inbound_channel();
node_a_chan.accept_channel(&accept_channel_msg, &config.peer_channel_config_limits, &InitFeatures::known()).unwrap();

// Node A --> Node B: funding created
Expand Down
51 changes: 46 additions & 5 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4081,6 +4081,34 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
}
}

/// Called to accept a request to open a channel after [`Event::OpenChannelRequest`] has been
/// triggered.
///
/// The `temporary_channel_id` parameter indicates which inbound channel should be accepted.
///
/// [`Event::OpenChannelRequest`]: crate::util::events::Event::OpenChannelRequest
pub fn accept_inbound_channel(&self, temporary_channel_id: &[u8; 32]) -> Result<(), APIError> {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);

let mut channel_state_lock = self.channel_state.lock().unwrap();
let channel_state = &mut *channel_state_lock;
match channel_state.by_id.entry(temporary_channel_id.clone()) {
hash_map::Entry::Occupied(mut channel) => {
if !channel.get().inbound_is_awaiting_accept() {
return Err(APIError::APIMisuseError { err: "The channel isn't currently awaiting to be accepted.".to_owned() });
}
channel_state.pending_msg_events.push(events::MessageSendEvent::SendAcceptChannel {
node_id: channel.get().get_counterparty_node_id(),
msg: channel.get_mut().accept_inbound_channel(),
});
}
hash_map::Entry::Vacant(_) => {
return Err(APIError::ChannelUnavailable { err: "Can't accept a channel that doesn't exist".to_owned() });
}
}
Ok(())
}

fn internal_open_channel(&self, counterparty_node_id: &PublicKey, their_features: InitFeatures, msg: &msgs::OpenChannel) -> Result<(), MsgHandleErrInternal> {
if msg.chain_hash != self.genesis_hash {
return Err(MsgHandleErrInternal::send_err_msg_no_close("Unknown genesis block hash".to_owned(), msg.temporary_channel_id.clone()));
Expand All @@ -4090,18 +4118,31 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
return Err(MsgHandleErrInternal::send_err_msg_no_close("No inbound channels accepted".to_owned(), msg.temporary_channel_id.clone()));
}

let channel = Channel::new_from_req(&self.fee_estimator, &self.keys_manager, counterparty_node_id.clone(),
let mut channel = Channel::new_from_req(&self.fee_estimator, &self.keys_manager, counterparty_node_id.clone(),
&their_features, msg, 0, &self.default_configuration, self.best_block.read().unwrap().height(), &self.logger)
.map_err(|e| MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id))?;
let mut channel_state_lock = self.channel_state.lock().unwrap();
let channel_state = &mut *channel_state_lock;
match channel_state.by_id.entry(channel.channel_id()) {
hash_map::Entry::Occupied(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("temporary_channel_id collision!".to_owned(), msg.temporary_channel_id.clone())),
hash_map::Entry::Vacant(entry) => {
channel_state.pending_msg_events.push(events::MessageSendEvent::SendAcceptChannel {
node_id: counterparty_node_id.clone(),
msg: channel.get_accept_channel(),
});
if !self.default_configuration.manually_accept_inbound_channels {
channel_state.pending_msg_events.push(events::MessageSendEvent::SendAcceptChannel {
node_id: counterparty_node_id.clone(),
msg: channel.accept_inbound_channel(),
});
} else {
let mut pending_events = self.pending_events.lock().unwrap();
pending_events.push(
events::Event::OpenChannelRequest {
temporary_channel_id: msg.temporary_channel_id.clone(),
counterparty_node_id: counterparty_node_id.clone(),
funding_satoshis: msg.funding_satoshis,
push_msat: msg.push_msat,
}
);
}

entry.insert(channel);
}
}
Expand Down
Loading