Skip to content

Commit c8b8a4a

Browse files
committed
Handle receiving channel_reestablish with next_funding_txid
This follows the the specification closely in branching without being too verbose, so that it should be easy to follow the logic. See: https://github.com/lightning/bolts/blob/aa5207a/02-peer-protocol.md?plain=1#L2520-L2531
1 parent a917b01 commit c8b8a4a

File tree

3 files changed

+140
-20
lines changed

3 files changed

+140
-20
lines changed

lightning/src/ln/channel.rs

+100-8
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,8 @@ pub(super) struct ReestablishResponses {
10741074
pub order: RAACommitmentOrder,
10751075
pub announcement_sigs: Option<msgs::AnnouncementSignatures>,
10761076
pub shutdown_msg: Option<msgs::Shutdown>,
1077+
pub tx_signatures: Option<msgs::TxSignatures>,
1078+
pub tx_abort: Option<msgs::TxAbort>,
10771079
}
10781080

10791081
/// The first message we send to our peer after connection
@@ -2540,7 +2542,7 @@ impl<SP: Deref> PendingV2Channel<SP> where SP::Target: SignerProvider {
25402542

25412543
let mut output_index = None;
25422544
let expected_spk = self.funding.get_funding_redeemscript().to_p2wsh();
2543-
for (idx, outp) in signing_session.unsigned_tx.outputs().enumerate() {
2545+
for (idx, outp) in signing_session.unsigned_tx().outputs().enumerate() {
25442546
if outp.script_pubkey() == &expected_spk && outp.value() == self.funding.get_value_satoshis() {
25452547
if output_index.is_some() {
25462548
return Err(ChannelError::Close(
@@ -2553,7 +2555,7 @@ impl<SP: Deref> PendingV2Channel<SP> where SP::Target: SignerProvider {
25532555
}
25542556
}
25552557
let outpoint = if let Some(output_index) = output_index {
2556-
OutPoint { txid: signing_session.unsigned_tx.compute_txid(), index: output_index }
2558+
OutPoint { txid: signing_session.unsigned_tx().compute_txid(), index: output_index }
25572559
} else {
25582560
return Err(ChannelError::Close(
25592561
(
@@ -2567,7 +2569,7 @@ impl<SP: Deref> PendingV2Channel<SP> where SP::Target: SignerProvider {
25672569
let commitment_signed = self.context.get_initial_commitment_signed(&self.funding, logger);
25682570
let commitment_signed = match commitment_signed {
25692571
Ok(commitment_signed) => {
2570-
self.funding.funding_transaction = Some(signing_session.unsigned_tx.build_unsigned_tx());
2572+
self.funding.funding_transaction = Some(signing_session.unsigned_tx().build_unsigned_tx());
25712573
commitment_signed
25722574
},
25732575
Err(err) => {
@@ -6543,7 +6545,7 @@ impl<SP: Deref> FundedChannel<SP> where
65436545
}
65446546

65456547
if let Some(ref mut signing_session) = self.interactive_tx_signing_session {
6546-
if msg.tx_hash != signing_session.unsigned_tx.compute_txid() {
6548+
if msg.tx_hash != signing_session.unsigned_tx().compute_txid() {
65476549
return Err(ChannelError::Close(
65486550
(
65496551
"The txid for the transaction does not match".to_string(),
@@ -7193,7 +7195,10 @@ impl<SP: Deref> FundedChannel<SP> where
71937195
}
71947196

71957197
if msg.next_local_commitment_number >= INITIAL_COMMITMENT_NUMBER || msg.next_remote_commitment_number >= INITIAL_COMMITMENT_NUMBER ||
7196-
msg.next_local_commitment_number == 0 {
7198+
(msg.next_local_commitment_number == 0 && msg.next_funding_txid.is_none()) {
7199+
// Note: This also covers the following case in the V2 channel establishment specification:
7200+
// if `next_funding_txid` is not set, and `next_commitment_number` is zero:
7201+
// MUST immediately fail the channel and broadcast any relevant latest commitment transaction.
71977202
return Err(ChannelError::close("Peer sent an invalid channel_reestablish to force close in a non-standard way".to_owned()));
71987203
}
71997204

@@ -7257,6 +7262,8 @@ impl<SP: Deref> FundedChannel<SP> where
72577262
raa: None, commitment_update: None,
72587263
order: RAACommitmentOrder::CommitmentFirst,
72597264
shutdown_msg, announcement_sigs,
7265+
tx_signatures: None,
7266+
tx_abort: None,
72607267
});
72617268
}
72627269

@@ -7266,6 +7273,8 @@ impl<SP: Deref> FundedChannel<SP> where
72667273
raa: None, commitment_update: None,
72677274
order: RAACommitmentOrder::CommitmentFirst,
72687275
shutdown_msg, announcement_sigs,
7276+
tx_signatures: None,
7277+
tx_abort: None,
72697278
});
72707279
}
72717280

@@ -7308,11 +7317,90 @@ impl<SP: Deref> FundedChannel<SP> where
73087317
log_debug!(logger, "Reconnected channel {} with no loss", &self.context.channel_id());
73097318
}
73107319

7320+
// if next_funding_txid is set:
7321+
let (commitment_update, tx_signatures, tx_abort) = if let Some(next_funding_txid) = msg.next_funding_txid {
7322+
if let Some(session) = &self.interactive_tx_signing_session {
7323+
// if next_funding_txid matches the latest interactive funding transaction:
7324+
let our_next_funding_txid = session.unsigned_tx().compute_txid();
7325+
if our_next_funding_txid == next_funding_txid {
7326+
debug_assert_eq!(session.unsigned_tx().compute_txid(), self.maybe_get_next_funding_txid().unwrap());
7327+
7328+
let commitment_update = if !session.has_received_tx_signatures() && msg.next_local_commitment_number == 0 {
7329+
// if it has not received tx_signatures for that funding transaction AND
7330+
// if next_commitment_number is zero:
7331+
// MUST retransmit its commitment_signed for that funding transaction.
7332+
let commitment_signed = self.context.get_initial_commitment_signed(&self.funding, logger)?;
7333+
Some(msgs::CommitmentUpdate {
7334+
commitment_signed: vec![commitment_signed],
7335+
update_add_htlcs: vec![],
7336+
update_fulfill_htlcs: vec![],
7337+
update_fail_htlcs: vec![],
7338+
update_fail_malformed_htlcs: vec![],
7339+
update_fee: None,
7340+
})
7341+
} else { None };
7342+
// TODO(dual_funding): For async signing support we need to hold back `tx_signatures` until the `commitment_signed` is ready.
7343+
let tx_signatures = if (
7344+
// if it has not received tx_signatures for that funding transaction AND
7345+
// if it has already received commitment_signed AND it should sign first, as specified in the tx_signatures requirements:
7346+
// MUST send its tx_signatures for that funding transaction.
7347+
!session.has_received_tx_signatures() && session.has_received_commitment_signed() && session.holder_sends_tx_signatures_first()
7348+
// else if it has already received tx_signatures for that funding transaction:
7349+
// MUST send its tx_signatures for that funding transaction.
7350+
) || session.has_received_tx_signatures() {
7351+
if self.context.channel_state.is_monitor_update_in_progress() {
7352+
// The `monitor_pending_tx_signatures` field should have already been set in `commitment_signed_initial_v2`
7353+
// if we were up first for signing and had a monitor update in progress, but check again just in case.
7354+
debug_assert!(self.context.monitor_pending_tx_signatures.is_some(), "monitor_pending_tx_signatures should already be set");
7355+
log_debug!(logger, "Not sending tx_signatures: a monitor update is in progress. Setting monitor_pending_tx_signatures.");
7356+
if self.context.monitor_pending_tx_signatures.is_none() {
7357+
self.context.monitor_pending_tx_signatures = session.holder_tx_signatures().clone();
7358+
}
7359+
None
7360+
} else {
7361+
// If `holder_tx_signatures` is `None` here, the `tx_signatures` message will be sent
7362+
// when the holder provides their witnesses as this will queue a `tx_signatures` if the
7363+
// holder must send one.
7364+
session.holder_tx_signatures().clone()
7365+
}
7366+
} else {
7367+
None
7368+
};
7369+
if !session.has_received_commitment_signed() {
7370+
self.context.expecting_peer_commitment_signed = true;
7371+
}
7372+
(commitment_update, tx_signatures, None)
7373+
} else {
7374+
// The `next_funding_txid` does not match the latest interactive funding transaction so we
7375+
// MUST send tx_abort to let the remote know that they can forget this funding transaction.
7376+
(None, None, Some(msgs::TxAbort {
7377+
channel_id: self.context.channel_id(),
7378+
data: format!(
7379+
"next_funding_txid {} does match our latest interactive funding txid {}",
7380+
next_funding_txid, our_next_funding_txid,
7381+
).into_bytes() }))
7382+
}
7383+
} else {
7384+
// We'll just send a `tx_abort` here if we don't have a signing session for this channel
7385+
// on reestablish and tell our peer to just forget about it.
7386+
// Our peer is doing something strange, but it doesn't warrant closing the channel.
7387+
(None, None, Some(msgs::TxAbort {
7388+
channel_id: self.context.channel_id(),
7389+
data:
7390+
"No active signing session. The associated funding transaction may have already been broadcast.".as_bytes().to_vec() }))
7391+
}
7392+
} else {
7393+
// Don't send anything related to interactive signing if `next_funding_txid` is not set.
7394+
(None, None, None)
7395+
};
7396+
73117397
Ok(ReestablishResponses {
73127398
channel_ready, shutdown_msg, announcement_sigs,
73137399
raa: required_revoke,
7314-
commitment_update: None,
7400+
commitment_update,
73157401
order: self.context.resend_order.clone(),
7402+
tx_signatures,
7403+
tx_abort,
73167404
})
73177405
} else if msg.next_local_commitment_number == next_counterparty_commitment_number - 1 {
73187406
if required_revoke.is_some() || self.context.signer_pending_revoke_and_ack {
@@ -7327,6 +7415,8 @@ impl<SP: Deref> FundedChannel<SP> where
73277415
channel_ready, shutdown_msg, announcement_sigs,
73287416
commitment_update: None, raa: None,
73297417
order: self.context.resend_order.clone(),
7418+
tx_signatures: None,
7419+
tx_abort: None,
73307420
})
73317421
} else {
73327422
let commitment_update = if self.context.resend_order == RAACommitmentOrder::RevokeAndACKFirst
@@ -7349,6 +7439,8 @@ impl<SP: Deref> FundedChannel<SP> where
73497439
channel_ready, shutdown_msg, announcement_sigs,
73507440
raa, commitment_update,
73517441
order: self.context.resend_order.clone(),
7442+
tx_signatures: None,
7443+
tx_abort: None,
73527444
})
73537445
}
73547446
} else if msg.next_local_commitment_number < next_counterparty_commitment_number {
@@ -8636,9 +8728,9 @@ impl<SP: Deref> FundedChannel<SP> where
86368728
// to the txid of that interactive transaction, else we MUST NOT set it.
86378729
if let Some(signing_session) = &self.interactive_tx_signing_session {
86388730
// Since we have a signing_session, this implies we've sent an initial `commitment_signed`...
8639-
if !signing_session.counterparty_sent_tx_signatures {
8731+
if !signing_session.has_received_tx_signatures() {
86408732
// ...but we didn't receive a `tx_signatures` from the counterparty yet.
8641-
Some(signing_session.unsigned_tx.compute_txid())
8733+
Some(signing_session.unsigned_tx().compute_txid())
86428734
} else {
86438735
// ...and we received a `tx_signatures` from the counterparty.
86448736
None

lightning/src/ln/channelmanager.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -3253,7 +3253,7 @@ macro_rules! handle_monitor_update_completion {
32533253
&mut $peer_state.pending_msg_events, $chan, updates.raa,
32543254
updates.commitment_update, updates.order, updates.accepted_htlcs, updates.pending_update_adds,
32553255
updates.funding_broadcastable, updates.channel_ready,
3256-
updates.announcement_sigs, updates.tx_signatures);
3256+
updates.announcement_sigs, updates.tx_signatures, None);
32573257
if let Some(upd) = channel_update {
32583258
$peer_state.pending_msg_events.push(upd);
32593259
}
@@ -7665,10 +7665,10 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
76657665
pending_forwards: Vec<(PendingHTLCInfo, u64)>, pending_update_adds: Vec<msgs::UpdateAddHTLC>,
76667666
funding_broadcastable: Option<Transaction>,
76677667
channel_ready: Option<msgs::ChannelReady>, announcement_sigs: Option<msgs::AnnouncementSignatures>,
7668-
tx_signatures: Option<msgs::TxSignatures>
7668+
tx_signatures: Option<msgs::TxSignatures>, tx_abort: Option<msgs::TxAbort>,
76697669
) -> (Option<(u64, Option<PublicKey>, OutPoint, ChannelId, u128, Vec<(PendingHTLCInfo, u64)>)>, Option<(u64, Vec<msgs::UpdateAddHTLC>)>) {
76707670
let logger = WithChannelContext::from(&self.logger, &channel.context, None);
7671-
log_trace!(logger, "Handling channel resumption for channel {} with {} RAA, {} commitment update, {} pending forwards, {} pending update_add_htlcs, {}broadcasting funding, {} channel ready, {} announcement, {} tx_signatures",
7671+
log_trace!(logger, "Handling channel resumption for channel {} with {} RAA, {} commitment update, {} pending forwards, {} pending update_add_htlcs, {}broadcasting funding, {} channel ready, {} announcement, {} tx_signatures, {} tx_abort",
76727672
&channel.context.channel_id(),
76737673
if raa.is_some() { "an" } else { "no" },
76747674
if commitment_update.is_some() { "a" } else { "no" },
@@ -7677,6 +7677,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
76777677
if channel_ready.is_some() { "sending" } else { "without" },
76787678
if announcement_sigs.is_some() { "sending" } else { "without" },
76797679
if tx_signatures.is_some() { "sending" } else { "without" },
7680+
if tx_abort.is_some() { "sending" } else { "without" },
76807681
);
76817682

76827683
let counterparty_node_id = channel.context.get_counterparty_node_id();
@@ -7710,6 +7711,12 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
77107711
msg,
77117712
});
77127713
}
7714+
if let Some(msg) = tx_abort {
7715+
pending_msg_events.push(MessageSendEvent::SendTxAbort {
7716+
node_id: counterparty_node_id,
7717+
msg,
7718+
});
7719+
}
77137720

77147721
macro_rules! handle_cs { () => {
77157722
if let Some(update) = commitment_update {
@@ -9491,7 +9498,8 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
94919498
let need_lnd_workaround = chan.context.workaround_lnd_bug_4006.take();
94929499
let (htlc_forwards, decode_update_add_htlcs) = self.handle_channel_resumption(
94939500
&mut peer_state.pending_msg_events, chan, responses.raa, responses.commitment_update, responses.order,
9494-
Vec::new(), Vec::new(), None, responses.channel_ready, responses.announcement_sigs, None);
9501+
Vec::new(), Vec::new(), None, responses.channel_ready, responses.announcement_sigs,
9502+
responses.tx_signatures, responses.tx_abort);
94959503
debug_assert!(htlc_forwards.is_none());
94969504
debug_assert!(decode_update_add_htlcs.is_none());
94979505
if let Some(upd) = channel_update {

lightning/src/ln/interactivetxs.rs

+28-8
Original file line numberDiff line numberDiff line change
@@ -309,16 +309,36 @@ impl ConstructedTransaction {
309309
/// https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#sharing-funding-signatures-tx_signatures
310310
#[derive(Debug, Clone, PartialEq)]
311311
pub(crate) struct InteractiveTxSigningSession {
312-
pub unsigned_tx: ConstructedTransaction,
313-
pub counterparty_sent_tx_signatures: bool,
312+
unsigned_tx: ConstructedTransaction,
313+
has_received_tx_signatures: bool,
314314
holder_sends_tx_signatures_first: bool,
315-
received_commitment_signed: bool,
315+
has_received_commitment_signed: bool,
316316
holder_tx_signatures: Option<TxSignatures>,
317317
}
318318

319319
impl InteractiveTxSigningSession {
320+
pub fn unsigned_tx(&self) -> &ConstructedTransaction {
321+
&self.unsigned_tx
322+
}
323+
324+
pub fn has_received_tx_signatures(&self) -> bool {
325+
self.has_received_tx_signatures
326+
}
327+
328+
pub fn holder_sends_tx_signatures_first(&self) -> bool {
329+
self.holder_sends_tx_signatures_first
330+
}
331+
332+
pub fn has_received_commitment_signed(&self) -> bool {
333+
self.has_received_commitment_signed
334+
}
335+
336+
pub fn holder_tx_signatures(&self) -> &Option<TxSignatures> {
337+
&self.holder_tx_signatures
338+
}
339+
320340
pub fn received_commitment_signed(&mut self) -> Option<TxSignatures> {
321-
self.received_commitment_signed = true;
341+
self.has_received_commitment_signed = true;
322342
if self.holder_sends_tx_signatures_first {
323343
self.holder_tx_signatures.clone()
324344
} else {
@@ -327,7 +347,7 @@ impl InteractiveTxSigningSession {
327347
}
328348

329349
pub fn get_tx_signatures(&self) -> Option<TxSignatures> {
330-
if self.received_commitment_signed {
350+
if self.has_received_commitment_signed {
331351
self.holder_tx_signatures.clone()
332352
} else {
333353
None
@@ -352,7 +372,7 @@ impl InteractiveTxSigningSession {
352372
return Err(());
353373
}
354374
self.unsigned_tx.add_remote_witnesses(tx_signatures.witnesses.clone());
355-
self.counterparty_sent_tx_signatures = true;
375+
self.has_received_tx_signatures = true;
356376

357377
let holder_tx_signatures = if !self.holder_sends_tx_signatures_first {
358378
self.holder_tx_signatures.clone()
@@ -1008,9 +1028,9 @@ macro_rules! define_state_transitions {
10081028
let signing_session = InteractiveTxSigningSession {
10091029
holder_sends_tx_signatures_first: tx.holder_sends_tx_signatures_first,
10101030
unsigned_tx: tx,
1011-
received_commitment_signed: false,
1031+
has_received_commitment_signed: false,
10121032
holder_tx_signatures: None,
1013-
counterparty_sent_tx_signatures: false,
1033+
has_received_tx_signatures: false,
10141034
};
10151035
Ok(NegotiationComplete(signing_session))
10161036
}

0 commit comments

Comments
 (0)