Skip to content

Commit 710e775

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 9c66f77 commit 710e775

File tree

3 files changed

+482
-110
lines changed

3 files changed

+482
-110
lines changed

lightning/src/ln/channel.rs

Lines changed: 84 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1902,10 +1902,11 @@ where
19021902
}
19031903
}
19041904

1905-
pub fn tx_complete<L: Deref>(
1906-
&mut self, msg: &msgs::TxComplete, logger: &L,
1905+
pub fn tx_complete<F: Deref, L: Deref>(
1906+
&mut self, msg: &msgs::TxComplete, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
19071907
) -> Result<TxCompleteResult, (ChannelError, Option<SpliceFundingFailed>)>
19081908
where
1909+
F::Target: FeeEstimator,
19091910
L::Target: Logger,
19101911
{
19111912
let tx_complete_action = match self.interactive_tx_constructor_mut() {
@@ -1954,7 +1955,7 @@ where
19541955
let funding_tx_signed = if !has_local_contribution {
19551956
let funding_txid = signing_session.unsigned_tx().tx().compute_txid();
19561957
if let ChannelPhase::Funded(chan) = &mut self.phase {
1957-
chan.funding_transaction_signed(funding_txid, vec![], 0, logger).ok()
1958+
chan.funding_transaction_signed(funding_txid, vec![], 0, fee_estimator, logger).ok()
19581959
} else {
19591960
None
19601961
}
@@ -2104,7 +2105,11 @@ where
21042105
funding.channel_transaction_parameters.funding_outpoint =
21052106
Some(funding_outpoint);
21062107
pending_splice.funding_negotiation =
2107-
Some(FundingNegotiation::AwaitingSignatures { is_initiator, funding });
2108+
Some(FundingNegotiation::AwaitingSignatures {
2109+
is_initiator,
2110+
funding,
2111+
initial_commitment_signed_from_counterparty: None,
2112+
});
21082113
interactive_tx_constructor
21092114
} else {
21102115
// Replace the taken state for later error handling
@@ -2193,9 +2198,33 @@ where
21932198
// which must always come after the initial commitment signed is sent.
21942199
.unwrap_or(true);
21952200
let res = if has_negotiated_pending_splice && !session_received_commitment_signed {
2196-
funded_channel
2197-
.splice_initial_commitment_signed(msg, fee_estimator, logger)
2198-
.map(|monitor_update_opt| (None, monitor_update_opt))
2201+
let has_holder_tx_signatures = funded_channel
2202+
.context
2203+
.interactive_tx_signing_session
2204+
.as_ref()
2205+
.map(|session| session.holder_tx_signatures().is_some())
2206+
.unwrap_or(false);
2207+
2208+
// We delay processing this until the user manually approves the splice via
2209+
// [`FundedChannel::funding_transaction_signed`], as otherwise, there would be a
2210+
// [`ChannelMonitorUpdateStep::RenegotiatedFunding`] committed that we would
2211+
// need to undo if they no longer wish to proceed.
2212+
if has_holder_tx_signatures {
2213+
funded_channel
2214+
.splice_initial_commitment_signed(msg, fee_estimator, logger)
2215+
.map(|monitor_update_opt| (None, monitor_update_opt))
2216+
} else {
2217+
let pending_splice = funded_channel.pending_splice.as_mut()
2218+
.expect("We have a pending splice negotiated");
2219+
let funding_negotiation = pending_splice.funding_negotiation.as_mut()
2220+
.expect("We have a pending splice negotiated");
2221+
if let FundingNegotiation::AwaitingSignatures {
2222+
ref mut initial_commitment_signed_from_counterparty, ..
2223+
} = funding_negotiation {
2224+
*initial_commitment_signed_from_counterparty = Some(msg.clone());
2225+
}
2226+
Ok((None, None))
2227+
}
21992228
} else {
22002229
funded_channel.commitment_signed(msg, fee_estimator, logger)
22012230
.map(|monitor_update_opt| (None, monitor_update_opt))
@@ -2679,13 +2708,25 @@ enum FundingNegotiation {
26792708
AwaitingSignatures {
26802709
funding: FundingScope,
26812710
is_initiator: bool,
2711+
/// The initial [`msgs::CommitmentSigned`] message received for the [`FundingScope`] above.
2712+
/// We delay processing this until the user manually approves the splice via
2713+
/// [`FundedChannel::funding_transaction_signed`], as otherwise, there would be a
2714+
/// [`ChannelMonitorUpdateStep::RenegotiatedFunding`] committed that we would need to undo
2715+
/// if they no longer wish to proceed.
2716+
///
2717+
/// Note that this doesn't need to be done with dual-funded channels as there is no
2718+
/// equivalent monitor update for them, and we can just force close the channel.
2719+
///
2720+
/// This field is not persisted as the message should be resent on reconnections.
2721+
initial_commitment_signed_from_counterparty: Option<msgs::CommitmentSigned>,
26822722
},
26832723
}
26842724

26852725
impl_writeable_tlv_based_enum_upgradable!(FundingNegotiation,
26862726
(0, AwaitingSignatures) => {
26872727
(1, funding, required),
26882728
(3, is_initiator, required),
2729+
(_unused, initial_commitment_signed_from_counterparty, (static_value, None)),
26892730
},
26902731
unread_variants: AwaitingAck, ConstructingTransaction
26912732
);
@@ -6834,7 +6875,7 @@ type BestBlockUpdatedRes = (
68346875
);
68356876

68366877
/// The result of handling a `tx_complete` message during interactive transaction construction.
6837-
pub(crate) struct TxCompleteResult {
6878+
pub(super) struct TxCompleteResult {
68386879
/// The message to send to the counterparty, if any.
68396880
pub interactive_tx_msg_send: Option<InteractiveTxMessageSend>,
68406881

@@ -6848,10 +6889,15 @@ pub(crate) struct TxCompleteResult {
68486889
}
68496890

68506891
/// The result of signing a funding transaction negotiated using the interactive-tx protocol.
6851-
pub struct FundingTxSigned {
6892+
pub(super) struct FundingTxSigned {
68526893
/// The initial `commitment_signed` message to send to the counterparty, if necessary.
68536894
pub commitment_signed: Option<msgs::CommitmentSigned>,
68546895

6896+
/// The result of processing a buffered initial commitment signed from our counterparty,
6897+
/// if any.
6898+
pub counterparty_initial_commitment_signed_result:
6899+
Option<Result<Option<ChannelMonitorUpdate>, ChannelError>>,
6900+
68556901
/// Signatures that should be sent to the counterparty, if necessary.
68566902
pub tx_signatures: Option<msgs::TxSignatures>,
68576903

@@ -9071,11 +9117,12 @@ where
90719117
}
90729118
}
90739119

9074-
pub fn funding_transaction_signed<L: Deref>(
9120+
pub fn funding_transaction_signed<F: Deref, L: Deref>(
90759121
&mut self, funding_txid_signed: Txid, witnesses: Vec<Witness>, best_block_height: u32,
9076-
logger: &L,
9122+
fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
90779123
) -> Result<FundingTxSigned, APIError>
90789124
where
9125+
F::Target: FeeEstimator,
90799126
L::Target: Logger,
90809127
{
90819128
let signing_session =
@@ -9096,6 +9143,7 @@ where
90969143
// or we're waiting for our counterparty to send theirs first.
90979144
return Ok(FundingTxSigned {
90989145
commitment_signed: None,
9146+
counterparty_initial_commitment_signed_result: None,
90999147
tx_signatures: None,
91009148
funding_tx: None,
91019149
splice_negotiated: None,
@@ -9110,6 +9158,7 @@ where
91109158
// no longer have the signing session present.
91119159
return Ok(FundingTxSigned {
91129160
commitment_signed: None,
9161+
counterparty_initial_commitment_signed_result: None,
91139162
tx_signatures: None,
91149163
funding_tx: None,
91159164
splice_negotiated: None,
@@ -9179,8 +9228,31 @@ where
91799228
.unwrap_or(&self.funding);
91809229
let commitment_signed = self.context.get_initial_commitment_signed_v2(funding, &&logger);
91819230

9231+
// If we have a pending splice with a buffered initial commitment_signed from our
9232+
// counterparty, process it now that we have provided our signatures.
9233+
let counterparty_initial_commitment_signed_result = if let Some(commit_sig) = self
9234+
.pending_splice
9235+
.as_mut()
9236+
.and_then(|pending_splice| pending_splice.funding_negotiation.as_mut())
9237+
.and_then(|funding_negotiation| {
9238+
if let FundingNegotiation::AwaitingSignatures {
9239+
ref mut initial_commitment_signed_from_counterparty,
9240+
..
9241+
} = funding_negotiation
9242+
{
9243+
initial_commitment_signed_from_counterparty.take()
9244+
} else {
9245+
None
9246+
}
9247+
}) {
9248+
Some(self.splice_initial_commitment_signed(&commit_sig, fee_estimator, &&logger))
9249+
} else {
9250+
None
9251+
};
9252+
91829253
Ok(FundingTxSigned {
91839254
commitment_signed,
9255+
counterparty_initial_commitment_signed_result,
91849256
tx_signatures,
91859257
funding_tx,
91869258
splice_negotiated,
@@ -9254,6 +9326,7 @@ where
92549326

92559327
Ok(FundingTxSigned {
92569328
commitment_signed: None,
9329+
counterparty_initial_commitment_signed_result: None,
92579330
tx_signatures: holder_tx_signatures,
92589331
funding_tx,
92599332
splice_negotiated,

0 commit comments

Comments
 (0)