Split commitment_signed handling by check-accept

When handling commitment_signed messages, a number of checks are
performed before a ChannelMonitorUpdate is created and returned. Once
splicing is added, these checks need to be performed on the primary
FundingScope and any pending scopes that resulted from splicing or RBF.

This commit splits the handling into a check and accept methods, taking
&self and &mut self, respectively. This ensures that the ChannelContext
is not modified between checks. Once all funding scopes have been
checked successfully, the accept portion of the code can then execute.
This commit is contained in:
Jeffrey Czyz 2025-02-28 14:34:28 -06:00
parent 5bc9ffaaf5
commit cbe84210d7
No known key found for this signature in database
GPG key ID: 3A4E08275D5E96D2
2 changed files with 40 additions and 16 deletions

View file

@ -531,6 +531,7 @@ impl_writeable_tlv_based_enum_upgradable!(OnchainEvent,
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum ChannelMonitorUpdateStep {
// Update LatestHolderCommitmentTXInfo in channel.rs if adding new fields to this variant.
LatestHolderCommitmentTXInfo {
commitment_tx: HolderCommitmentTransaction,
/// Note that LDK after 0.0.115 supports this only containing dust HTLCs (implying the

View file

@ -4707,6 +4707,14 @@ struct CommitmentTxInfoCached {
feerate: u32,
}
/// Partial data from ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo used to simplify the
/// return type of `FundedChannel::validate_commitment_signed`.
struct LatestHolderCommitmentTXInfo {
pub commitment_tx: HolderCommitmentTransaction,
pub htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
pub nondust_htlc_sources: Vec<HTLCSource>,
}
/// Contents of a wire message that fails an HTLC backwards. Useful for [`FundedChannel::fail_htlc`] to
/// fail with either [`msgs::UpdateFailMalformedHTLC`] or [`msgs::UpdateFailHTLC`] as needed.
trait FailHTLCContents {
@ -5495,22 +5503,9 @@ impl<SP: Deref> FundedChannel<SP> where
Ok(channel_monitor)
}
pub fn commitment_signed<L: Deref>(&mut self, msg: &msgs::CommitmentSigned, logger: &L) -> Result<Option<ChannelMonitorUpdate>, ChannelError>
fn validate_commitment_signed<L: Deref>(&self, msg: &msgs::CommitmentSigned, logger: &L) -> Result<LatestHolderCommitmentTXInfo, ChannelError>
where L::Target: Logger
{
if self.context.channel_state.is_quiescent() {
return Err(ChannelError::WarnAndDisconnect("Got commitment_signed message while quiescent".to_owned()));
}
if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
return Err(ChannelError::close("Got commitment signed message when channel was not in an operational state".to_owned()));
}
if self.context.channel_state.is_peer_disconnected() {
return Err(ChannelError::close("Peer sent commitment_signed when we needed a channel_reestablish".to_owned()));
}
if self.context.channel_state.is_both_sides_shutdown() && self.context.last_sent_closing_fee.is_some() {
return Err(ChannelError::close("Peer sent commitment_signed after we'd started exchanging closing_signeds".to_owned()));
}
let funding_script = self.funding.get_funding_redeemscript();
let keys = self.context.build_holder_transaction_keys(&self.funding, self.holder_commitment_point.current_point());
@ -5623,6 +5618,31 @@ impl<SP: Deref> FundedChannel<SP> where
self.context.holder_signer.as_ref().validate_holder_commitment(&holder_commitment_tx, commitment_stats.outbound_htlc_preimages)
.map_err(|_| ChannelError::close("Failed to validate our commitment".to_owned()))?;
Ok(LatestHolderCommitmentTXInfo {
commitment_tx: holder_commitment_tx,
htlc_outputs: htlcs_and_sigs,
nondust_htlc_sources,
})
}
pub fn commitment_signed<L: Deref>(&mut self, msg: &msgs::CommitmentSigned, logger: &L) -> Result<Option<ChannelMonitorUpdate>, ChannelError>
where L::Target: Logger
{
if self.context.channel_state.is_quiescent() {
return Err(ChannelError::WarnAndDisconnect("Got commitment_signed message while quiescent".to_owned()));
}
if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
return Err(ChannelError::close("Got commitment signed message when channel was not in an operational state".to_owned()));
}
if self.context.channel_state.is_peer_disconnected() {
return Err(ChannelError::close("Peer sent commitment_signed when we needed a channel_reestablish".to_owned()));
}
if self.context.channel_state.is_both_sides_shutdown() && self.context.last_sent_closing_fee.is_some() {
return Err(ChannelError::close("Peer sent commitment_signed after we'd started exchanging closing_signeds".to_owned()));
}
let commitment_tx_info = self.validate_commitment_signed(msg, logger)?;
// Update state now that we've passed all the can-fail calls...
let mut need_commitment = false;
if let &mut Some((_, ref mut update_state)) = &mut self.context.pending_update_fee {
@ -5662,13 +5682,16 @@ impl<SP: Deref> FundedChannel<SP> where
}
}
let LatestHolderCommitmentTXInfo {
commitment_tx, htlc_outputs, nondust_htlc_sources,
} = commitment_tx_info;
self.context.latest_monitor_update_id += 1;
let mut monitor_update = ChannelMonitorUpdate {
update_id: self.context.latest_monitor_update_id,
counterparty_node_id: Some(self.context.counterparty_node_id),
updates: vec![ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo {
commitment_tx: holder_commitment_tx,
htlc_outputs: htlcs_and_sigs,
commitment_tx,
htlc_outputs,
claimed_htlcs,
nondust_htlc_sources,
}],