Skip to content

Commit c0193fe

Browse files
committed
Buffer interactive-tx initial commitment signed from counterparty
This is crucial to enable the splice cancellation use case. When we process the initial commitment signed from our counterparty, we queue a monitor update that cannot be undone. To give the user a chance to abort the splice negotiation before it's committed to, we buffer the message until a successful call to `Channel::funding_transaction_signed` and process it then. Note that this is currently only done for splice and RBF attempts, as if we want to abort a dual funding negotiation, we can just force close the channel as it hasn't been funded yet.
1 parent 6c4aa85 commit c0193fe

File tree

3 files changed

+372
-100
lines changed

3 files changed

+372
-100
lines changed

lightning/src/ln/channel.rs

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2110,6 +2110,7 @@ where
21102110
Some(FundingNegotiation::AwaitingSignatures {
21112111
is_initiator,
21122112
funding,
2113+
initial_commitment_signed_from_counterparty: None,
21132114
});
21142115
Some(interactive_tx_constructor)
21152116
} else {
@@ -2203,9 +2204,33 @@ where
22032204
// which must always come after the initial commitment signed is sent.
22042205
.unwrap_or(true);
22052206
let res = if has_negotiated_pending_splice && !session_received_commitment_signed {
2206-
funded_channel
2207-
.splice_initial_commitment_signed(msg, fee_estimator, logger)
2208-
.map(|monitor_update_opt| (None, monitor_update_opt))
2207+
let has_holder_tx_signatures = funded_channel
2208+
.context
2209+
.interactive_tx_signing_session
2210+
.as_ref()
2211+
.map(|session| session.holder_tx_signatures().is_some())
2212+
.unwrap_or(false);
2213+
2214+
// We delay processing this until the user manually approves the splice via
2215+
// [`Channel::funding_transaction_signed`], as otherwise, there would be a
2216+
// [`ChannelMonitorUpdateStep::RenegotiatedFunding`] committed that we would
2217+
// need to undo if they no longer wish to proceed.
2218+
if has_holder_tx_signatures {
2219+
funded_channel
2220+
.splice_initial_commitment_signed(msg, fee_estimator, logger)
2221+
.map(|monitor_update_opt| (None, monitor_update_opt))
2222+
} else {
2223+
let pending_splice = funded_channel.pending_splice.as_mut()
2224+
.expect("We have a pending splice negotiated");
2225+
let funding_negotiation = pending_splice.funding_negotiation.as_mut()
2226+
.expect("We have a pending splice negotiated");
2227+
if let FundingNegotiation::AwaitingSignatures {
2228+
ref mut initial_commitment_signed_from_counterparty, ..
2229+
} = funding_negotiation {
2230+
*initial_commitment_signed_from_counterparty = Some(msg.clone());
2231+
}
2232+
Ok((None, None))
2233+
}
22092234
} else {
22102235
funded_channel.commitment_signed(msg, fee_estimator, logger)
22112236
.map(|monitor_update_opt| (None, monitor_update_opt))
@@ -2689,13 +2714,25 @@ enum FundingNegotiation {
26892714
AwaitingSignatures {
26902715
funding: FundingScope,
26912716
is_initiator: bool,
2717+
/// The initial [`msgs::CommitmentSigned`] message received for the [`FundingScope`] above.
2718+
/// We delay processing this until the user manually approves the splice via
2719+
/// [`Channel::funding_transaction_signed`], as otherwise, there would be a
2720+
/// [`ChannelMonitorUpdateStep::RenegotiatedFunding`] committed that we would need to undo
2721+
/// if they no longer wish to proceed.
2722+
///
2723+
/// Note that this doesn't need to be done with dual-funded channels as there is no
2724+
/// equivalent monitor update for them, and we can just force close the channel.
2725+
///
2726+
/// This field is not persisted as the message should be resent on reconnections.
2727+
initial_commitment_signed_from_counterparty: Option<msgs::CommitmentSigned>,
26922728
},
26932729
}
26942730

26952731
impl_writeable_tlv_based_enum_upgradable!(FundingNegotiation,
26962732
(0, AwaitingSignatures) => {
26972733
(1, funding, required),
26982734
(3, is_initiator, required),
2735+
(_unused, initial_commitment_signed_from_counterparty, (static_value, None)),
26992736
},
27002737
unread_variants: AwaitingAck, ConstructingTransaction
27012738
);
@@ -9023,6 +9060,50 @@ where
90239060
}
90249061
}
90259062

9063+
/// If a counterparty's initial `msgs::CommitmentSigned` is currently pending to be processed
9064+
/// due to the holder signatures not being avialable at the time of receipt, attempt to process
9065+
/// it now if they are available.
9066+
pub fn maybe_process_pending_counterparty_initial_commitment_signed<F: Deref, L: Deref>(
9067+
&mut self, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
9068+
) -> Result<Option<ChannelMonitorUpdate>, ChannelError>
9069+
where
9070+
F::Target: FeeEstimator,
9071+
L::Target: Logger,
9072+
{
9073+
if self
9074+
.context
9075+
.interactive_tx_signing_session
9076+
.as_ref()
9077+
.map(|signing_session| {
9078+
!signing_session.has_local_contribution()
9079+
&& signing_session.holder_tx_signatures().is_none()
9080+
})
9081+
.unwrap_or(true)
9082+
{
9083+
return Ok(None);
9084+
}
9085+
9086+
if let Some(commit_sig) = self
9087+
.pending_splice
9088+
.as_mut()
9089+
.and_then(|pending_splice| pending_splice.funding_negotiation.as_mut())
9090+
.and_then(|funding_negotiation| {
9091+
if let FundingNegotiation::AwaitingSignatures {
9092+
ref mut initial_commitment_signed_from_counterparty,
9093+
..
9094+
} = funding_negotiation
9095+
{
9096+
initial_commitment_signed_from_counterparty.take()
9097+
} else {
9098+
None
9099+
}
9100+
}) {
9101+
self.splice_initial_commitment_signed(&commit_sig, fee_estimator, logger)
9102+
} else {
9103+
Ok(None)
9104+
}
9105+
}
9106+
90269107
fn on_tx_signatures_exchange<'a, L: Deref>(
90279108
&mut self, funding_tx: Transaction, best_block_height: u32,
90289109
logger: &WithChannelContext<'a, L>,
@@ -9106,6 +9187,7 @@ where
91069187
// or we're waiting for our counterparty to send theirs first.
91079188
return Ok(FundingTxSigned {
91089189
commitment_signed: None,
9190+
91099191
tx_signatures: None,
91109192
funding_tx: None,
91119193
splice_negotiated: None,

0 commit comments

Comments
 (0)