diff --git a/lightning-types/src/features.rs b/lightning-types/src/features.rs index aca4bb6e5a9..148242e6fce 100644 --- a/lightning-types/src/features.rs +++ b/lightning-types/src/features.rs @@ -80,6 +80,8 @@ //! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#channel-quiescence) for more information). //! - `ZeroFeeCommitments` - A channel type which always uses zero transaction fee on commitment transactions. //! (see [BOLT PR #1228](https://github.com/lightning/bolts/pull/1228) for more info). +//! - `Splice` - Allows replacing the currently-locked funding transaction with a new one +//! (see [BOLT PR #1160](https://github.com/lightning/bolts/pull/1160) for more information). //! //! LDK knows about the following features, but does not support them: //! - `AnchorsNonzeroFeeHtlcTx` - the initial version of anchor outputs, which was later found to be @@ -163,7 +165,7 @@ mod sealed { // Byte 6 ZeroConf, // Byte 7 - Trampoline | SimpleClose, + Trampoline | SimpleClose | Splice, ] ); define_context!( @@ -184,7 +186,7 @@ mod sealed { // Byte 6 ZeroConf | Keysend, // Byte 7 - Trampoline | SimpleClose, + Trampoline | SimpleClose | Splice, // Byte 8 - 31 ,,,,,,,,,,,,,,,,,,,,,,,, // Byte 32 @@ -673,9 +675,20 @@ mod sealed { supports_simple_close, requires_simple_close ); - // By default, allocate enough bytes to cover up to SimpleClose. Update this as new features are + define_feature!( + 63, + Splice, + [InitContext, NodeContext], + "Feature flags for channel splicing.", + set_splicing_optional, + set_splicing_required, + clear_splicing, + supports_splicing, + requires_splicing + ); + // By default, allocate enough bytes to cover up to Splice. Update this as new features are // added which we expect to appear commonly across contexts. - pub(super) const MIN_FEATURES_ALLOCATION_BYTES: usize = (61 + 7) / 8; + pub(super) const MIN_FEATURES_ALLOCATION_BYTES: usize = (63 + 7) / 8; define_feature!( 259, DnsResolver, @@ -1369,6 +1382,7 @@ mod tests { init_features.set_zero_conf_optional(); init_features.set_quiescence_optional(); init_features.set_simple_close_optional(); + init_features.set_splicing_optional(); assert!(init_features.initial_routing_sync()); assert!(!init_features.supports_upfront_shutdown_script()); @@ -1384,7 +1398,7 @@ mod tests { // - onion_messages // - option_channel_type | option_scid_alias // - option_zeroconf - // - option_simple_close + // - option_simple_close | option_splice assert_eq!(node_features.flags.len(), 8); assert_eq!(node_features.flags[0], 0b00000001); assert_eq!(node_features.flags[1], 0b01010001); @@ -1393,7 +1407,7 @@ mod tests { assert_eq!(node_features.flags[4], 0b10001000); assert_eq!(node_features.flags[5], 0b10100000); assert_eq!(node_features.flags[6], 0b00001000); - assert_eq!(node_features.flags[7], 0b00100000); + assert_eq!(node_features.flags[7], 0b10100000); } // Check that cleared flags are kept blank when converting back: diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index f12ea13b102..a93e532954b 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -54,9 +54,10 @@ use crate::ln::channel_state::{ OutboundHTLCDetails, OutboundHTLCStateDetails, }; use crate::ln::channelmanager::{ - self, FundingConfirmedMessage, HTLCFailureMsg, HTLCSource, OpenChannelMessage, - PaymentClaimDetails, PendingHTLCInfo, PendingHTLCStatus, RAACommitmentOrder, SentHTLCId, - BREAKDOWN_TIMEOUT, MAX_LOCAL_BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA, + self, ChannelReadyOrder, FundingConfirmedMessage, HTLCFailureMsg, HTLCSource, + OpenChannelMessage, PaymentClaimDetails, PendingHTLCInfo, PendingHTLCStatus, + RAACommitmentOrder, SentHTLCId, BREAKDOWN_TIMEOUT, MAX_LOCAL_BREAKDOWN_TIMEOUT, + MIN_CLTV_EXPIRY_DELTA, }; use crate::ln::funding::FundingTxInput; #[cfg(splicing)] @@ -1181,13 +1182,14 @@ pub enum UpdateFulfillCommitFetch { pub(super) struct MonitorRestoreUpdates { pub raa: Option, pub commitment_update: Option, - pub order: RAACommitmentOrder, + pub commitment_order: RAACommitmentOrder, pub accepted_htlcs: Vec<(PendingHTLCInfo, u64)>, pub failed_htlcs: Vec<(HTLCSource, PaymentHash, HTLCFailReason)>, pub finalized_claimed_htlcs: Vec<(HTLCSource, Option)>, pub pending_update_adds: Vec, pub funding_broadcastable: Option, pub channel_ready: Option, + pub channel_ready_order: ChannelReadyOrder, pub announcement_sigs: Option, pub tx_signatures: Option, } @@ -1210,13 +1212,15 @@ pub(super) struct SignerResumeUpdates { /// The return value of `channel_reestablish` pub(super) struct ReestablishResponses { pub channel_ready: Option, + pub channel_ready_order: ChannelReadyOrder, pub raa: Option, pub commitment_update: Option, - pub order: RAACommitmentOrder, + pub commitment_order: RAACommitmentOrder, pub announcement_sigs: Option, pub shutdown_msg: Option, pub tx_signatures: Option, pub tx_abort: Option, + pub inferred_splice_locked: Option, } /// The first message we send to our peer after connection @@ -6434,6 +6438,7 @@ macro_rules! promote_splice_funding { core::mem::swap(&mut $self.funding, $funding); $self.interactive_tx_signing_session = None; $self.pending_splice = None; + $self.context.announcement_sigs = None; $self.context.announcement_sigs_state = AnnouncementSigsState::NotSent; // The swap above places the previous `FundingScope` into `pending_funding`. @@ -8705,6 +8710,15 @@ where } } + // FIXME: Is this correct? + let channel_ready_order = if self.interactive_tx_signing_session.is_some() { + ChannelReadyOrder::SignaturesFirst + } else if matches!(self.context.channel_state, ChannelState::AwaitingChannelReady(_)) { + ChannelReadyOrder::SignaturesFirst + } else { + ChannelReadyOrder::ChannelReadyFirst + }; + // We will never broadcast the funding transaction when we're in MonitorUpdateInProgress // (and we assume the user never directly broadcasts the funding transaction and waits for // us to do it). Thus, we can only ever hit monitor_pending_channel_ready when we're @@ -8733,9 +8747,10 @@ where self.context.monitor_pending_revoke_and_ack = false; self.context.monitor_pending_commitment_signed = false; return MonitorRestoreUpdates { - raa: None, commitment_update: None, order: RAACommitmentOrder::RevokeAndACKFirst, + raa: None, commitment_update: None, commitment_order: RAACommitmentOrder::RevokeAndACKFirst, accepted_htlcs, failed_htlcs, finalized_claimed_htlcs, pending_update_adds, - funding_broadcastable, channel_ready, announcement_sigs, tx_signatures: None + funding_broadcastable, channel_ready, announcement_sigs, tx_signatures: None, + channel_ready_order, }; } @@ -8758,14 +8773,15 @@ where self.context.monitor_pending_revoke_and_ack = false; self.context.monitor_pending_commitment_signed = false; - let order = self.context.resend_order.clone(); + let commitment_order = self.context.resend_order.clone(); log_debug!(logger, "Restored monitor updating in channel {} resulting in {}{} commitment update and {} RAA, with {} first", &self.context.channel_id(), if funding_broadcastable.is_some() { "a funding broadcastable, " } else { "" }, if commitment_update.is_some() { "a" } else { "no" }, if raa.is_some() { "an" } else { "no" }, - match order { RAACommitmentOrder::CommitmentFirst => "commitment", RAACommitmentOrder::RevokeAndACKFirst => "RAA"}); + match commitment_order { RAACommitmentOrder::CommitmentFirst => "commitment", RAACommitmentOrder::RevokeAndACKFirst => "RAA"}); MonitorRestoreUpdates { - raa, commitment_update, order, accepted_htlcs, failed_htlcs, finalized_claimed_htlcs, - pending_update_adds, funding_broadcastable, channel_ready, announcement_sigs, tx_signatures: None + raa, commitment_update, commitment_order, accepted_htlcs, failed_htlcs, finalized_claimed_htlcs, + pending_update_adds, funding_broadcastable, channel_ready, announcement_sigs, tx_signatures: None, + channel_ready_order, } } @@ -9104,15 +9120,14 @@ where return Err(ChannelError::close("Peer sent a loose channel_reestablish not after reconnect".to_owned())); } - if msg.next_local_commitment_number >= INITIAL_COMMITMENT_NUMBER || msg.next_remote_commitment_number >= INITIAL_COMMITMENT_NUMBER || - (msg.next_local_commitment_number == 0 && msg.next_funding_txid.is_none()) { - // Note: This also covers the following case in the V2 channel establishment specification: - // if `next_funding_txid` is not set, and `next_commitment_number` is zero: - // MUST immediately fail the channel and broadcast any relevant latest commitment transaction. + if msg.next_local_commitment_number == 0 + || msg.next_local_commitment_number >= INITIAL_COMMITMENT_NUMBER + || msg.next_remote_commitment_number >= INITIAL_COMMITMENT_NUMBER + { return Err(ChannelError::close("Peer sent an invalid channel_reestablish to force close in a non-standard way".to_owned())); } - let our_commitment_transaction = INITIAL_COMMITMENT_NUMBER - self.holder_commitment_point.next_transaction_number() - 1; + let our_commitment_transaction = INITIAL_COMMITMENT_NUMBER - self.holder_commitment_point.current_transaction_number(); if msg.next_remote_commitment_number > 0 { let expected_point = self.context.holder_signer.as_ref() .get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - msg.next_remote_commitment_number + 1, &self.context.secp_ctx) @@ -9156,8 +9171,121 @@ where let shutdown_msg = self.get_outbound_shutdown(); + // A receiving node: + // - if `my_current_funding_locked` is included with the `announcement_signatures` bit + // set in the `retransmit_flags`: + // - if `announce_channel` is set for this channel and the receiving node is ready + // to send `announcement_signatures` for the corresponding splice transaction: + // - MUST retransmit `announcement_signatures`. + if let Some(funding_locked) = &msg.my_current_funding_locked { + if funding_locked.should_retransmit(msgs::FundingLockedFlags::AnnouncementSignatures) { + if self.funding.get_funding_txid() == Some(funding_locked.txid) { + self.context.announcement_sigs_state = AnnouncementSigsState::NotSent; + } + } + } + let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, best_block.height, logger); + let mut commitment_update = None; + let mut tx_signatures = None; + let mut tx_abort = None; + + // if next_funding is set: + if let Some(next_funding) = &msg.next_funding { + // - if `next_funding` matches the latest interactive funding transaction + // or the current channel funding transaction: + if let Some(session) = &self.interactive_tx_signing_session { + let our_next_funding_txid = session.unsigned_tx().compute_txid(); + if our_next_funding_txid != next_funding.txid { + return Err(ChannelError::close(format!( + "Unexpected next_funding txid: {}; expected: {}", + next_funding.txid, our_next_funding_txid, + ))); + } + + if !session.has_received_commitment_signed() { + self.context.expecting_peer_commitment_signed = true; + } + + // TODO(splicing): Add comment for spec requirements + if next_funding.should_retransmit(msgs::NextFundingFlag::CommitmentSigned) { + #[cfg(splicing)] + let funding = self + .pending_splice + .as_ref() + .and_then(|pending_splice| pending_splice.funding_negotiation.as_ref()) + .and_then(|funding_negotiation| { + if let FundingNegotiation::AwaitingSignatures(funding) = &funding_negotiation { + Some(funding) + } else { + None + } + }) + .or_else(|| Some(&self.funding)) + .filter(|funding| funding.get_funding_txid() == Some(next_funding.txid)) + .ok_or_else(|| { + let message = "Failed to find funding for new commitment_signed".to_owned(); + ChannelError::Close( + ( + message.clone(), + ClosureReason::HolderForceClosed { message, broadcasted_latest_txn: Some(false) }, + ) + ) + })?; + #[cfg(not(splicing))] + let funding = &self.funding; + + let commitment_signed = self.context.get_initial_commitment_signed_v2(&funding, logger) + // TODO(splicing): Support async signing + .ok_or_else(|| { + let message = "Failed to get signatures for new commitment_signed".to_owned(); + ChannelError::Close( + ( + message.clone(), + ClosureReason::HolderForceClosed { message, broadcasted_latest_txn: Some(false) }, + ) + ) + })?; + + commitment_update = Some(msgs::CommitmentUpdate { + commitment_signed: vec![commitment_signed], + update_add_htlcs: vec![], + update_fulfill_htlcs: vec![], + update_fail_htlcs: vec![], + update_fail_malformed_htlcs: vec![], + update_fee: None, + }); + } + + // - if it has already received `commitment_signed` and it should sign first + // - MUST send its `tx_signatures` for that funding transaction. + // + // - if it has already received `tx_signatures` for that funding transaction: + // - MUST send its `tx_signatures` for that funding transaction. + if (session.has_received_commitment_signed() && session.holder_sends_tx_signatures_first()) + || self.context.channel_state.is_their_tx_signatures_sent() + { + // If `holder_tx_signatures` is `None` here, the `tx_signatures` message will be sent + // when the holder provides their witnesses as this will queue a `tx_signatures` if the + // holder must send one. + if session.holder_tx_signatures().is_none() { + log_debug!(logger, "Waiting for funding transaction signatures to be provided"); + } else { + tx_signatures = session.holder_tx_signatures().clone(); + } + } + } else { + // We'll just send a `tx_abort` here if we don't have a signing session for this channel + // on reestablish and tell our peer to just forget about it. + // Our peer is doing something strange, but it doesn't warrant closing the channel. + tx_abort = Some(msgs::TxAbort { + channel_id: self.context.channel_id(), + data: + "No active signing session. The associated funding transaction may have already been broadcast.".as_bytes().to_vec() }); + } + } + if matches!(self.context.channel_state, ChannelState::AwaitingChannelReady(_)) { // If we're waiting on a monitor update, we shouldn't re-send any channel_ready's. if !self.context.channel_state.is_our_channel_ready() || @@ -9165,25 +9293,29 @@ where if msg.next_remote_commitment_number != 0 { return Err(ChannelError::close("Peer claimed they saw a revoke_and_ack but we haven't sent channel_ready yet".to_owned())); } - // Short circuit the whole handler as there is nothing we can resend them + return Ok(ReestablishResponses { channel_ready: None, - raa: None, commitment_update: None, - order: RAACommitmentOrder::CommitmentFirst, + channel_ready_order: ChannelReadyOrder::SignaturesFirst, + raa: None, commitment_update, + commitment_order: self.context.resend_order.clone(), shutdown_msg, announcement_sigs, - tx_signatures: None, + tx_signatures, tx_abort: None, + inferred_splice_locked: None, }); } // We have OurChannelReady set! return Ok(ReestablishResponses { channel_ready: self.get_channel_ready(logger), - raa: None, commitment_update: None, - order: RAACommitmentOrder::CommitmentFirst, + channel_ready_order: ChannelReadyOrder::SignaturesFirst, + raa: None, commitment_update, + commitment_order: self.context.resend_order.clone(), shutdown_msg, announcement_sigs, - tx_signatures: None, - tx_abort: None, + tx_signatures, + tx_abort, + inferred_splice_locked: None, }); } @@ -9219,6 +9351,30 @@ where self.get_channel_ready(logger) } else { None }; + // A receiving node: + // - if splice transactions are pending and `my_current_funding_locked` matches one of + // those splice transactions, for which it hasn't received `splice_locked` yet: + // - MUST process `my_current_funding_locked` as if it was receiving `splice_locked` + // for this `txid`. + #[cfg(splicing)] + let inferred_splice_locked = msg.my_current_funding_locked.as_ref().and_then(|funding_locked| { + self.pending_funding + .iter() + .find(|funding| funding.get_funding_txid() == Some(funding_locked.txid)) + .and_then(|_| { + self.pending_splice.as_ref().and_then(|pending_splice| { + (Some(funding_locked.txid) != pending_splice.received_funding_txid) + .then(|| funding_locked.txid) + }) + }) + .map(|splice_txid| msgs::SpliceLocked { + channel_id: self.context.channel_id, + splice_txid, + }) + }); + #[cfg(not(splicing))] + let inferred_splice_locked = None; + if msg.next_local_commitment_number == next_counterparty_commitment_number { if required_revoke.is_some() || self.context.signer_pending_revoke_and_ack { log_debug!(logger, "Reconnected channel {} with only lost outbound RAA", &self.context.channel_id()); @@ -9226,94 +9382,31 @@ where log_debug!(logger, "Reconnected channel {} with no loss", &self.context.channel_id()); } - // if next_funding_txid is set: - let (commitment_update, tx_signatures, tx_abort) = if let Some(next_funding_txid) = msg.next_funding_txid { - if let Some(session) = &self.interactive_tx_signing_session { - // if next_funding_txid matches the latest interactive funding transaction: - let our_next_funding_txid = session.unsigned_tx().compute_txid(); - if our_next_funding_txid == next_funding_txid { - debug_assert_eq!(session.unsigned_tx().compute_txid(), self.maybe_get_next_funding_txid().unwrap()); - - let commitment_update = if !self.context.channel_state.is_their_tx_signatures_sent() && msg.next_local_commitment_number == 0 { - // if it has not received tx_signatures for that funding transaction AND - // if next_commitment_number is zero: - // MUST retransmit its commitment_signed for that funding transaction. - let commitment_signed = self.context.get_initial_commitment_signed_v2(&self.funding, logger) - // TODO(splicing): Support async signing - .ok_or_else(|| { - let message = "Failed to get signatures for new commitment_signed".to_owned(); - ChannelError::Close( - ( - message.clone(), - ClosureReason::HolderForceClosed { message, broadcasted_latest_txn: Some(false) }, - ) - )})?; - Some(msgs::CommitmentUpdate { - commitment_signed: vec![commitment_signed], - update_add_htlcs: vec![], - update_fulfill_htlcs: vec![], - update_fail_htlcs: vec![], - update_fail_malformed_htlcs: vec![], - update_fee: None, - }) - } else { None }; - let tx_signatures = if ( - // if it has not received tx_signatures for that funding transaction AND - // if it has already received commitment_signed AND it should sign first, as specified in the tx_signatures requirements: - // MUST send its tx_signatures for that funding transaction. - !self.context.channel_state.is_their_tx_signatures_sent() && session.has_received_commitment_signed() && session.holder_sends_tx_signatures_first() - // else if it has already received tx_signatures for that funding transaction: - // MUST send its tx_signatures for that funding transaction. - ) || self.context.channel_state.is_their_tx_signatures_sent() { - // If `holder_tx_signatures` is `None` here, the `tx_signatures` message will be sent - // when the holder provides their witnesses as this will queue a `tx_signatures` if the - // holder must send one. - if session.holder_tx_signatures().is_none() { - log_debug!(logger, "Waiting for funding transaction signatures to be provided"); - None - } else { - session.holder_tx_signatures().clone() - } - } else { - None - }; - if !session.has_received_commitment_signed() { - self.context.expecting_peer_commitment_signed = true; - } - (commitment_update, tx_signatures, None) - } else { - // The `next_funding_txid` does not match the latest interactive funding transaction so we - // MUST send tx_abort to let the remote know that they can forget this funding transaction. - (None, None, Some(msgs::TxAbort { - channel_id: self.context.channel_id(), - data: format!( - "next_funding_txid {} does match our latest interactive funding txid {}", - next_funding_txid, our_next_funding_txid, - ).into_bytes() })) - } - } else { - // We'll just send a `tx_abort` here if we don't have a signing session for this channel - // on reestablish and tell our peer to just forget about it. - // Our peer is doing something strange, but it doesn't warrant closing the channel. - (None, None, Some(msgs::TxAbort { - channel_id: self.context.channel_id(), - data: - "No active signing session. The associated funding transaction may have already been broadcast.".as_bytes().to_vec() })) - } + let channel_ready_order = if commitment_update.is_some() || tx_signatures.is_some() { + ChannelReadyOrder::SignaturesFirst } else { - // Don't send anything related to interactive signing if `next_funding_txid` is not set. - (None, None, None) + ChannelReadyOrder::ChannelReadyFirst }; Ok(ReestablishResponses { - channel_ready, shutdown_msg, announcement_sigs, + channel_ready, + channel_ready_order, + shutdown_msg, + announcement_sigs, raa: required_revoke, commitment_update, - order: self.context.resend_order.clone(), + commitment_order: self.context.resend_order.clone(), tx_signatures, tx_abort, + inferred_splice_locked, }) } else if msg.next_local_commitment_number == next_counterparty_commitment_number - 1 { + debug_assert!(commitment_update.is_none()); + + // TODO(splicing): Assert in a test that we don't retransmit tx_signatures instead + #[cfg(test)] + assert!(tx_signatures.is_none()); + if required_revoke.is_some() || self.context.signer_pending_revoke_and_ack { log_debug!(logger, "Reconnected channel {} with lost outbound RAA and lost remote commitment tx", &self.context.channel_id()); } else { @@ -9323,11 +9416,14 @@ where if self.context.channel_state.is_monitor_update_in_progress() { self.context.monitor_pending_commitment_signed = true; Ok(ReestablishResponses { - channel_ready, shutdown_msg, announcement_sigs, + channel_ready, + channel_ready_order: ChannelReadyOrder::ChannelReadyFirst, + shutdown_msg, announcement_sigs, commitment_update: None, raa: None, - order: self.context.resend_order.clone(), + commitment_order: self.context.resend_order.clone(), tx_signatures: None, - tx_abort: None, + tx_abort, + inferred_splice_locked, }) } else { let commitment_update = if self.context.resend_order == RAACommitmentOrder::RevokeAndACKFirst @@ -9347,11 +9443,14 @@ where required_revoke }; Ok(ReestablishResponses { - channel_ready, shutdown_msg, announcement_sigs, + channel_ready, + channel_ready_order: ChannelReadyOrder::ChannelReadyFirst, + shutdown_msg, announcement_sigs, raa, commitment_update, - order: self.context.resend_order.clone(), + commitment_order: self.context.resend_order.clone(), tx_signatures: None, - tx_abort: None, + tx_abort, + inferred_splice_locked, }) } } else if msg.next_local_commitment_number < next_counterparty_commitment_number { @@ -10943,15 +11042,29 @@ where } #[rustfmt::skip] - fn maybe_get_next_funding_txid(&self) -> Option { + fn maybe_get_next_funding(&self) -> Option { // If we've sent `commtiment_signed` for an interactively constructed transaction - // during a signing session, but have not received `tx_signatures` we MUST set `next_funding_txid` + // during a signing session, but have not received `tx_signatures` we MUST set `next_funding` // to the txid of that interactive transaction, else we MUST NOT set it. if self.context.channel_state.is_interactive_signing() { // Since we have a signing_session, this implies we've sent an initial `commitment_signed`... if !self.context.channel_state.is_their_tx_signatures_sent() { // ...but we didn't receive a `tx_signatures` from the counterparty yet. - self.interactive_tx_signing_session.as_ref().map(|signing_session| signing_session.unsigned_tx().compute_txid()) + self.interactive_tx_signing_session + .as_ref() + .map(|signing_session| { + let mut next_funding = msgs::NextFunding { + txid: signing_session.unsigned_tx().compute_txid(), + retransmit_flags: 0, + }; + + // TODO(splicing): Add comment for spec requirements + if !signing_session.has_received_commitment_signed() { + next_funding.retransmit(msgs::NextFundingFlag::CommitmentSigned); + } + + next_funding + }) } else { // ...and we received a `tx_signatures` from the counterparty. None @@ -10962,6 +11075,40 @@ where } } + #[cfg(splicing)] + fn maybe_get_my_current_funding_locked(&self) -> Option { + self.pending_splice + .as_ref() + .and_then(|pending_splice| pending_splice.sent_funding_txid) + .or_else(|| { + self.is_our_channel_ready().then(|| self.funding.get_funding_txid()).flatten() + }) + .map(|txid| { + let mut funding_locked = msgs::FundingLocked { txid, retransmit_flags: 0 }; + + // - if `my_current_funding_locked` is included: + // - if `announce_channel` is set for this channel: + // - if it has not received `announcement_signatures` for that transaction: + // - MUST set the `announcement_signatures` bit to `1` in `retransmit_flags`. + // - otherwise: + // - MUST set the `announcement_signatures` bit to `0` in `retransmit_flags`. + if self.context.config.announce_for_forwarding { + if self.funding.get_funding_txid() != Some(txid) + || self.context.announcement_sigs.is_none() + { + funding_locked.retransmit(msgs::FundingLockedFlags::AnnouncementSignatures); + } + } + + funding_locked + }) + } + + #[cfg(not(splicing))] + fn maybe_get_my_current_funding_locked(&self) -> Option { + None + } + /// May panic if called on a channel that wasn't immediately-previously /// self.remove_uncommitted_htlcs_and_mark_paused()'d #[rustfmt::skip] @@ -11012,7 +11159,8 @@ where next_remote_commitment_number: INITIAL_COMMITMENT_NUMBER - self.context.counterparty_next_commitment_transaction_number - 1, your_last_per_commitment_secret: remote_last_secret, my_current_per_commitment_point: dummy_pubkey, - next_funding_txid: self.maybe_get_next_funding_txid(), + next_funding: self.maybe_get_next_funding(), + my_current_funding_locked: self.maybe_get_my_current_funding_locked(), } } diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index ef2630daedb..3a846c56e24 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -917,6 +917,16 @@ pub(super) enum RAACommitmentOrder { RevokeAndACKFirst, } +/// Similar to scenarios used by [`RAACommitmentOrder`], this determines whether a `channel_ready` +/// message should be sent first (i.e., prior to a `commitment_update`) or after the initial +/// `commitment_update` and `tx_signatures` for channel funding. +pub(super) enum ChannelReadyOrder { + /// Send `channel_ready` message first. + ChannelReadyFirst, + /// Send initial `commitment_update` and `tx_signatures` first. + SignaturesFirst, +} + /// Information about a payment which is currently being claimed. #[derive(Clone, Debug, PartialEq, Eq)] struct ClaimingPayment { @@ -3394,9 +3404,10 @@ macro_rules! handle_monitor_update_completion { let (htlc_forwards, decode_update_add_htlcs) = $self.handle_channel_resumption( &mut $peer_state.pending_msg_events, $chan, updates.raa, - updates.commitment_update, updates.order, updates.accepted_htlcs, updates.pending_update_adds, - updates.funding_broadcastable, updates.channel_ready, - updates.announcement_sigs, updates.tx_signatures, None); + updates.commitment_update, updates.commitment_order, updates.accepted_htlcs, + updates.pending_update_adds, updates.funding_broadcastable, updates.channel_ready, + updates.announcement_sigs, updates.tx_signatures, None, updates.channel_ready_order, + ); if let Some(upd) = channel_update { $peer_state.pending_msg_events.push(upd); } @@ -8900,11 +8911,12 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ #[rustfmt::skip] fn handle_channel_resumption(&self, pending_msg_events: &mut Vec, channel: &mut FundedChannel, raa: Option, - commitment_update: Option, order: RAACommitmentOrder, + commitment_update: Option, commitment_order: RAACommitmentOrder, pending_forwards: Vec<(PendingHTLCInfo, u64)>, pending_update_adds: Vec, funding_broadcastable: Option, channel_ready: Option, announcement_sigs: Option, tx_signatures: Option, tx_abort: Option, + channel_ready_order: ChannelReadyOrder, ) -> (Option<(u64, PublicKey, OutPoint, ChannelId, u128, Vec<(PendingHTLCInfo, u64)>)>, Option<(u64, Vec)>) { let logger = WithChannelContext::from(&self.logger, &channel.context, None); 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", @@ -8935,27 +8947,17 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ decode_update_add_htlcs = Some((short_channel_id, pending_update_adds)); } - if let Some(msg) = channel_ready { - send_channel_ready!(self, pending_msg_events, channel, msg); - } - if let Some(msg) = announcement_sigs { - pending_msg_events.push(MessageSendEvent::SendAnnouncementSignatures { - node_id: counterparty_node_id, - msg, - }); - } - // TODO(dual_funding): For async signing support we need to hold back `tx_signatures` until the `commitment_signed` is ready. - if let Some(msg) = tx_signatures { - pending_msg_events.push(MessageSendEvent::SendTxSignatures { - node_id: counterparty_node_id, - msg, - }); - } - if let Some(msg) = tx_abort { - pending_msg_events.push(MessageSendEvent::SendTxAbort { - node_id: counterparty_node_id, - msg, - }); + if let ChannelReadyOrder::ChannelReadyFirst = channel_ready_order { + if let Some(msg) = &channel_ready { + send_channel_ready!(self, pending_msg_events, channel, msg.clone()); + } + + if let Some(msg) = &announcement_sigs { + pending_msg_events.push(MessageSendEvent::SendAnnouncementSignatures { + node_id: counterparty_node_id, + msg: msg.clone(), + }); + } } macro_rules! handle_cs { () => { @@ -8975,7 +8977,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ }); } } } - match order { + match commitment_order { RAACommitmentOrder::CommitmentFirst => { handle_cs!(); handle_raa!(); @@ -8986,6 +8988,33 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ }, } + // TODO(dual_funding): For async signing support we need to hold back `tx_signatures` until the `commitment_signed` is ready. + if let Some(msg) = tx_signatures { + pending_msg_events.push(MessageSendEvent::SendTxSignatures { + node_id: counterparty_node_id, + msg, + }); + } + if let Some(msg) = tx_abort { + pending_msg_events.push(MessageSendEvent::SendTxAbort { + node_id: counterparty_node_id, + msg, + }); + } + + if let ChannelReadyOrder::SignaturesFirst = channel_ready_order { + if let Some(msg) = channel_ready { + send_channel_ready!(self, pending_msg_events, channel, msg); + } + + if let Some(msg) = announcement_sigs { + pending_msg_events.push(MessageSendEvent::SendAnnouncementSignatures { + node_id: counterparty_node_id, + msg, + }); + } + } + if let Some(tx) = funding_broadcastable { if channel.context.is_manual_broadcast() { log_info!(logger, "Not broadcasting funding transaction with txid {} as it is manually managed", tx.compute_txid()); @@ -11005,7 +11034,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ #[rustfmt::skip] fn internal_channel_reestablish(&self, counterparty_node_id: &PublicKey, msg: &msgs::ChannelReestablish) -> Result { - let need_lnd_workaround = { + let (inferred_splice_locked, need_lnd_workaround) = { let per_peer_state = self.per_peer_state.read().unwrap(); let peer_state_mutex = per_peer_state.get(counterparty_node_id) @@ -11048,15 +11077,17 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ } let need_lnd_workaround = chan.context.workaround_lnd_bug_4006.take(); let (htlc_forwards, decode_update_add_htlcs) = self.handle_channel_resumption( - &mut peer_state.pending_msg_events, chan, responses.raa, responses.commitment_update, responses.order, + &mut peer_state.pending_msg_events, chan, responses.raa, responses.commitment_update, responses.commitment_order, Vec::new(), Vec::new(), None, responses.channel_ready, responses.announcement_sigs, - responses.tx_signatures, responses.tx_abort); + responses.tx_signatures, responses.tx_abort, responses.channel_ready_order, + ); debug_assert!(htlc_forwards.is_none()); debug_assert!(decode_update_add_htlcs.is_none()); if let Some(upd) = channel_update { peer_state.pending_msg_events.push(upd); } - need_lnd_workaround + + (responses.inferred_splice_locked, need_lnd_workaround) } else { return try_channel_entry!(self, peer_state, Err(ChannelError::close( "Got a channel_reestablish message for an unfunded channel!".into())), chan_entry); @@ -11087,7 +11118,8 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ next_remote_commitment_number: 0, your_last_per_commitment_secret: [1u8; 32], my_current_per_commitment_point: PublicKey::from_slice(&[2u8; 33]).unwrap(), - next_funding_txid: None, + next_funding: None, + my_current_funding_locked: None, }, }); return Err(MsgHandleErrInternal::send_err_msg_no_close( @@ -11101,6 +11133,15 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ if let Some(channel_ready_msg) = need_lnd_workaround { self.internal_channel_ready(counterparty_node_id, &channel_ready_msg)?; } + + #[cfg(not(splicing))] + let _ = inferred_splice_locked; + #[cfg(splicing)] + if let Some(splice_locked) = inferred_splice_locked { + self.internal_splice_locked(counterparty_node_id, &splice_locked)?; + return Ok(NotifyOption::DoPersist); + } + Ok(NotifyOption::SkipPersistHandleEvents) } diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 71f73e04c2f..30e4d50fa4e 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -919,13 +919,84 @@ pub struct ChannelReestablish { /// removed it from its state. /// /// If we've sent `commtiment_signed` for an interactively constructed transaction - /// during a signing session, but have not received `tx_signatures` we MUST set `next_funding_txid` + /// during a signing session, but have not received `tx_signatures` we MUST set `next_funding` /// to the txid of that interactive transaction, else we MUST NOT set it. /// /// See the spec for further details on this: /// * `channel_reestablish`-sending node: https:///github.com/lightning/bolts/blob/247e83d/02-peer-protocol.md?plain=1#L2466-L2470 /// * `channel_reestablish`-receiving node: https:///github.com/lightning/bolts/blob/247e83d/02-peer-protocol.md?plain=1#L2520-L2531 - pub next_funding_txid: Option, + pub next_funding: Option, + /// The last funding txid sent by the sending node, which may be: + /// - the txid of the last `splice_locked` it sent, otherwise + /// - the txid of the funding transaction if it sent `channel_ready`, or else + /// - `None` if it has never sent `channel_ready` or `splice_locked` + /// + /// Also contains a bitfield indicating which messages should be retransmitted. + pub my_current_funding_locked: Option, +} + +/// Information exchanged during channel reestablishment about the next funding from interactive +/// transaction construction. +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct NextFunding { + /// The txid of the interactive transaction construction. + pub txid: Txid, + + /// A bitfield indicating which messages should be retransmitted by the receiving node. + /// + /// See [`NextFundingFlag`] for details. + pub retransmit_flags: u8, +} + +impl NextFunding { + /// Sets the bit in `retransmit_flags` for retransmitting the message corresponding to `flag`. + pub fn retransmit(&mut self, flag: NextFundingFlag) { + self.retransmit_flags |= 1 << flag as u8; + } + + /// Returns whether the message corresponding to `flag` should be retransmitted. + pub fn should_retransmit(&self, flag: NextFundingFlag) -> bool { + self.retransmit_flags & (1 << flag as u8) != 0 + } +} + +/// Bit positions used in [`NextFunding::retransmit_flags`] for requesting message retransmission. +#[repr(u8)] +pub enum NextFundingFlag { + /// Retransmit `commitment_signed`. + CommitmentSigned = 0, +} + +/// Information exchanged during channel reestablishment about the last funding locked. +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct FundingLocked { + /// The last txid sent by the sending node, which may be either from the last `splice_locked` or + /// for the initial funding transaction if it sent `channel_ready`. + pub txid: Txid, + + /// A bitfield indicating which messages should be retransmitted by the receiving node. + /// + /// See [`FundingLockedFlags`] for details. + pub retransmit_flags: u8, +} + +impl FundingLocked { + /// Sets the bit in `retransmit_flags` for retransmitting the message corresponding to `flag`. + pub fn retransmit(&mut self, flag: FundingLockedFlags) { + self.retransmit_flags |= 1 << flag as u8; + } + + /// Returns whether the message corresponding to `flag` should be retransmitted. + pub fn should_retransmit(&self, flag: FundingLockedFlags) -> bool { + self.retransmit_flags & (1 << flag as u8) != 0 + } +} + +/// Bit positions used in [`FundingLocked::retransmit_flags`] for requesting message retransmission. +#[repr(u8)] +pub enum FundingLockedFlags { + /// Retransmit `announcement_signatures`. + AnnouncementSignatures = 0, } /// An [`announcement_signatures`] message to be sent to or received from a peer. @@ -2851,7 +2922,18 @@ impl_writeable_msg!(ChannelReestablish, { your_last_per_commitment_secret, my_current_per_commitment_point, }, { - (0, next_funding_txid, option), + (1, next_funding, option), + (5, my_current_funding_locked, option), +}); + +impl_writeable!(NextFunding, { + txid, + retransmit_flags +}); + +impl_writeable!(FundingLocked, { + txid, + retransmit_flags }); impl_writeable_msg!(ClosingSigned, @@ -4320,7 +4402,8 @@ mod tests { next_remote_commitment_number: 4, your_last_per_commitment_secret: [9; 32], my_current_per_commitment_point: public_key, - next_funding_txid: None, + next_funding: None, + my_current_funding_locked: None, }; let encoded_value = cr.encode(); @@ -4365,13 +4448,17 @@ mod tests { next_remote_commitment_number: 4, your_last_per_commitment_secret: [9; 32], my_current_per_commitment_point: public_key, - next_funding_txid: Some(Txid::from_raw_hash( - bitcoin::hashes::Hash::from_slice(&[ - 48, 167, 250, 69, 152, 48, 103, 172, 164, 99, 59, 19, 23, 11, 92, 84, 15, 80, - 4, 12, 98, 82, 75, 31, 201, 11, 91, 23, 98, 23, 53, 124, - ]) - .unwrap(), - )), + next_funding: Some(msgs::NextFunding { + txid: Txid::from_raw_hash( + bitcoin::hashes::Hash::from_slice(&[ + 48, 167, 250, 69, 152, 48, 103, 172, 164, 99, 59, 19, 23, 11, 92, 84, 15, + 80, 4, 12, 98, 82, 75, 31, 201, 11, 91, 23, 98, 23, 53, 124, + ]) + .unwrap(), + ), + retransmit_flags: 1, + }), + my_current_funding_locked: None, }; let encoded_value = cr.encode(); @@ -4387,10 +4474,69 @@ mod tests { 3, 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, // my_current_per_commitment_point - 0, // Type (next_funding_txid) - 32, // Length + 1, // Type (next_funding) + 33, // Length 48, 167, 250, 69, 152, 48, 103, 172, 164, 99, 59, 19, 23, 11, 92, 84, 15, 80, 4, - 12, 98, 82, 75, 31, 201, 11, 91, 23, 98, 23, 53, 124, // Value + 12, 98, 82, 75, 31, 201, 11, 91, 23, 98, 23, 53, 124, 1, // Value + ] + ); + } + + #[test] + fn encoding_channel_reestablish_with_funding_locked_txid() { + let public_key = { + let secp_ctx = Secp256k1::new(); + PublicKey::from_secret_key( + &secp_ctx, + &SecretKey::from_slice( + &>::from_hex( + "0101010101010101010101010101010101010101010101010101010101010101", + ) + .unwrap()[..], + ) + .unwrap(), + ) + }; + + let cr = msgs::ChannelReestablish { + channel_id: ChannelId::from_bytes([ + 4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, + 0, 0, 0, 0, + ]), + next_local_commitment_number: 3, + next_remote_commitment_number: 4, + your_last_per_commitment_secret: [9; 32], + my_current_per_commitment_point: public_key, + next_funding: None, + my_current_funding_locked: Some(msgs::FundingLocked { + txid: Txid::from_raw_hash( + bitcoin::hashes::Hash::from_slice(&[ + 21, 167, 250, 69, 152, 48, 103, 172, 164, 99, 59, 19, 23, 11, 92, 84, 15, + 80, 4, 12, 98, 82, 75, 31, 201, 11, 91, 23, 98, 23, 53, 124, + ]) + .unwrap(), + ), + retransmit_flags: 1, + }), + }; + + let encoded_value = cr.encode(); + assert_eq!( + encoded_value, + vec![ + 4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, + 0, 0, 0, 0, // channel_id + 0, 0, 0, 0, 0, 0, 0, 3, // next_local_commitment_number + 0, 0, 0, 0, 0, 0, 0, 4, // next_remote_commitment_number + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, // your_last_per_commitment_secret + 3, 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, + 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, + 143, // my_current_per_commitment_point + 5, // Type (my_current_funding_locked) + 33, // Length + 21, 167, 250, 69, 152, 48, 103, 172, 164, 99, 59, 19, 23, 11, 92, 84, 15, 80, 4, + 12, 98, 82, 75, 31, 201, 11, 91, 23, 98, 23, 53, 124, 1, // Value ] ); }