Skip to content

Commit e39fb8a

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 df5db06 commit e39fb8a

File tree

3 files changed

+478
-109
lines changed

3 files changed

+478
-109
lines changed

lightning/src/ln/channel.rs

Lines changed: 80 additions & 10 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
}
@@ -2110,6 +2111,7 @@ where
21102111
Some(FundingNegotiation::AwaitingSignatures {
21112112
is_initiator,
21122113
funding,
2114+
initial_commitment_signed_from_counterparty: None,
21132115
});
21142116
Some(interactive_tx_constructor)
21152117
} else {
@@ -2203,9 +2205,33 @@ where
22032205
// which must always come after the initial commitment signed is sent.
22042206
.unwrap_or(true);
22052207
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))
2208+
let has_holder_tx_signatures = funded_channel
2209+
.context
2210+
.interactive_tx_signing_session
2211+
.as_ref()
2212+
.map(|session| session.holder_tx_signatures().is_some())
2213+
.unwrap_or(false);
2214+
2215+
// We delay processing this until the user manually approves the splice via
2216+
// [`FundedChannel::funding_transaction_signed`], as otherwise, there would be a
2217+
// [`ChannelMonitorUpdateStep::RenegotiatedFunding`] committed that we would
2218+
// need to undo if they no longer wish to proceed.
2219+
if has_holder_tx_signatures {
2220+
funded_channel
2221+
.splice_initial_commitment_signed(msg, fee_estimator, logger)
2222+
.map(|monitor_update_opt| (None, monitor_update_opt))
2223+
} else {
2224+
let pending_splice = funded_channel.pending_splice.as_mut()
2225+
.expect("We have a pending splice negotiated");
2226+
let funding_negotiation = pending_splice.funding_negotiation.as_mut()
2227+
.expect("We have a pending splice negotiated");
2228+
if let FundingNegotiation::AwaitingSignatures {
2229+
ref mut initial_commitment_signed_from_counterparty, ..
2230+
} = funding_negotiation {
2231+
*initial_commitment_signed_from_counterparty = Some(msg.clone());
2232+
}
2233+
Ok((None, None))
2234+
}
22092235
} else {
22102236
funded_channel.commitment_signed(msg, fee_estimator, logger)
22112237
.map(|monitor_update_opt| (None, monitor_update_opt))
@@ -2689,13 +2715,25 @@ enum FundingNegotiation {
26892715
AwaitingSignatures {
26902716
funding: FundingScope,
26912717
is_initiator: bool,
2718+
/// The initial [`msgs::CommitmentSigned`] message received for the [`FundingScope`] above.
2719+
/// We delay processing this until the user manually approves the splice via
2720+
/// [`FundedChannel::funding_transaction_signed`], as otherwise, there would be a
2721+
/// [`ChannelMonitorUpdateStep::RenegotiatedFunding`] committed that we would need to undo
2722+
/// if they no longer wish to proceed.
2723+
///
2724+
/// Note that this doesn't need to be done with dual-funded channels as there is no
2725+
/// equivalent monitor update for them, and we can just force close the channel.
2726+
///
2727+
/// This field is not persisted as the message should be resent on reconnections.
2728+
initial_commitment_signed_from_counterparty: Option<msgs::CommitmentSigned>,
26922729
},
26932730
}
26942731

26952732
impl_writeable_tlv_based_enum_upgradable!(FundingNegotiation,
26962733
(0, AwaitingSignatures) => {
26972734
(1, funding, required),
26982735
(3, is_initiator, required),
2736+
(_unused, initial_commitment_signed_from_counterparty, (static_value, None)),
26992737
},
27002738
unread_variants: AwaitingAck, ConstructingTransaction
27012739
);
@@ -6844,7 +6882,7 @@ type BestBlockUpdatedRes = (
68446882
);
68456883

68466884
/// The result of handling a `tx_complete` message during interactive transaction construction.
6847-
pub(crate) struct TxCompleteResult {
6885+
pub(super) struct TxCompleteResult {
68486886
/// The message to send to the counterparty, if any.
68496887
pub interactive_tx_msg_send: Option<InteractiveTxMessageSend>,
68506888

@@ -6858,10 +6896,15 @@ pub(crate) struct TxCompleteResult {
68586896
}
68596897

68606898
/// The result of signing a funding transaction negotiated using the interactive-tx protocol.
6861-
pub struct FundingTxSigned {
6899+
pub(super) struct FundingTxSigned {
68626900
/// The initial `commitment_signed` message to send to the counterparty, if necessary.
68636901
pub commitment_signed: Option<msgs::CommitmentSigned>,
68646902

6903+
/// The result of processing a buffered initial commitment signed from our counterparty,
6904+
/// if any.
6905+
pub counterparty_initial_commitment_signed_result:
6906+
Option<Result<Option<ChannelMonitorUpdate>, ChannelError>>,
6907+
68656908
/// Signatures that should be sent to the counterparty, if necessary.
68666909
pub tx_signatures: Option<msgs::TxSignatures>,
68676910

@@ -9081,11 +9124,12 @@ where
90819124
}
90829125
}
90839126

9084-
pub fn funding_transaction_signed<L: Deref>(
9127+
pub fn funding_transaction_signed<F: Deref, L: Deref>(
90859128
&mut self, funding_txid_signed: Txid, witnesses: Vec<Witness>, best_block_height: u32,
9086-
logger: &L,
9129+
fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
90879130
) -> Result<FundingTxSigned, APIError>
90889131
where
9132+
F::Target: FeeEstimator,
90899133
L::Target: Logger,
90909134
{
90919135
let signing_session =
@@ -9106,6 +9150,7 @@ where
91069150
// or we're waiting for our counterparty to send theirs first.
91079151
return Ok(FundingTxSigned {
91089152
commitment_signed: None,
9153+
counterparty_initial_commitment_signed_result: None,
91099154
tx_signatures: None,
91109155
funding_tx: None,
91119156
splice_negotiated: None,
@@ -9120,6 +9165,7 @@ where
91209165
// no longer have the signing session present.
91219166
return Ok(FundingTxSigned {
91229167
commitment_signed: None,
9168+
counterparty_initial_commitment_signed_result: None,
91239169
tx_signatures: None,
91249170
funding_tx: None,
91259171
splice_negotiated: None,
@@ -9189,8 +9235,31 @@ where
91899235
.unwrap_or(&self.funding);
91909236
let commitment_signed = self.context.get_initial_commitment_signed_v2(funding, &&logger);
91919237

9238+
// If we have a pending splice with a buffered initial commitment_signed from our
9239+
// counterparty, process it now that we have provided our signatures.
9240+
let counterparty_initial_commitment_signed_result = if let Some(commit_sig) = self
9241+
.pending_splice
9242+
.as_mut()
9243+
.and_then(|pending_splice| pending_splice.funding_negotiation.as_mut())
9244+
.and_then(|funding_negotiation| {
9245+
if let FundingNegotiation::AwaitingSignatures {
9246+
ref mut initial_commitment_signed_from_counterparty,
9247+
..
9248+
} = funding_negotiation
9249+
{
9250+
initial_commitment_signed_from_counterparty.take()
9251+
} else {
9252+
None
9253+
}
9254+
}) {
9255+
Some(self.splice_initial_commitment_signed(&commit_sig, fee_estimator, &&logger))
9256+
} else {
9257+
None
9258+
};
9259+
91929260
Ok(FundingTxSigned {
91939261
commitment_signed,
9262+
counterparty_initial_commitment_signed_result,
91949263
tx_signatures,
91959264
funding_tx,
91969265
splice_negotiated,
@@ -9264,6 +9333,7 @@ where
92649333

92659334
Ok(FundingTxSigned {
92669335
commitment_signed: None,
9336+
counterparty_initial_commitment_signed_result: None,
92679337
tx_signatures: holder_tx_signatures,
92689338
funding_tx,
92699339
splice_negotiated,

0 commit comments

Comments
 (0)