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 115d68acc14..a2a9ccc4f32 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -1215,6 +1215,7 @@ pub(super) struct ReestablishResponses { pub shutdown_msg: Option, pub tx_signatures: Option, pub tx_abort: Option, + pub implicit_splice_locked: Option, } /// The first message we send to our peer after connection @@ -6070,6 +6071,7 @@ macro_rules! promote_splice_funding { $self.interactive_tx_signing_session = None; $self.pending_splice = None; $self.pending_funding.clear(); + $self.context.announcement_sigs = None; $self.context.announcement_sigs_state = AnnouncementSigsState::NotSent; }; } @@ -8716,6 +8718,20 @@ 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::RetransmitFlag::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); if matches!(self.context.channel_state, ChannelState::AwaitingChannelReady(_)) { @@ -8733,6 +8749,7 @@ where shutdown_msg, announcement_sigs, tx_signatures: None, tx_abort: None, + implicit_splice_locked: None, }); } @@ -8744,6 +8761,7 @@ where shutdown_msg, announcement_sigs, tx_signatures: None, tx_abort: None, + implicit_splice_locked: None, }); } @@ -8779,91 +8797,130 @@ where self.get_channel_ready(logger) } else { 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()); - } else { - log_debug!(logger, "Reconnected channel {} with no loss", &self.context.channel_id()); - } + // 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 implicit_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 implicit_splice_locked = None; + + let mut commitment_update = None; + let mut tx_signatures = None; + let mut tx_abort = None; + + // if next_funding_txid is set: + if let Some(next_funding_txid) = msg.next_funding_txid { + // - if `next_funding_txid` 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 = self.maybe_get_next_funding_txid(); + if let Some(our_next_funding_txid) = our_next_funding_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 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() - } + if !session.has_received_commitment_signed() { + self.context.expecting_peer_commitment_signed = true; + } + + // - if `next_commitment_number` is equal to the commitment number of the + // `commitment_signed` message it sent for this funding transaction: + // - MUST retransmit its `commitment_signed` for that funding transaction. + if msg.next_local_commitment_number == next_counterparty_commitment_number { + // `next_counterparty_commitment_number` is guaranteed to always be the + // commitment number of the `commitment_signed` message we sent for this + // funding transaction. If they set `next_funding_txid`, then they should + // not have processed our `tx_signatures` yet, which implies that our state + // machine is still paused and no updates can happen that would increment + // our `next_counterparty_commitment_number`. + // + // If they did set `next_funding_txid` even after processing our + // `tx_signatures` erroneously, this may end up resulting in a force close. + 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) }, + ) + )})?; + 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 { - None - }; - if !session.has_received_commitment_signed() { - self.context.expecting_peer_commitment_signed = true; + tx_signatures = session.holder_tx_signatures().clone(); } - (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 { + // 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. + 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() })) + data: format!( + "Unexpected next_funding_txid {}", + next_funding_txid, + ).into_bytes() }); } } else { - // Don't send anything related to interactive signing if `next_funding_txid` is not set. - (None, None, None) - }; + // 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 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()); + } else { + log_debug!(logger, "Reconnected channel {} with no loss", &self.context.channel_id()); + } Ok(ReestablishResponses { channel_ready, shutdown_msg, announcement_sigs, @@ -8872,8 +8929,14 @@ where order: self.context.resend_order.clone(), tx_signatures, tx_abort, + implicit_splice_locked, }) } else if msg.next_local_commitment_number == next_counterparty_commitment_number - 1 { + // We've made an update so we must have exchanged `tx_signatures`, implying that + // `commitment_signed` was also exchanged. However, we may still need to retransmit our + // `tx_signatures` if the counterparty sent theirs first but didn't get to process ours. + debug_assert!(commitment_update.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 { @@ -8886,8 +8949,9 @@ where channel_ready, shutdown_msg, announcement_sigs, commitment_update: None, raa: None, order: self.context.resend_order.clone(), - tx_signatures: None, - tx_abort: None, + tx_signatures, + tx_abort, + implicit_splice_locked, }) } else { let commitment_update = if self.context.resend_order == RAACommitmentOrder::RevokeAndACKFirst @@ -8910,8 +8974,9 @@ where channel_ready, shutdown_msg, announcement_sigs, raa, commitment_update, order: self.context.resend_order.clone(), - tx_signatures: None, - tx_abort: None, + tx_signatures, + tx_abort, + implicit_splice_locked, }) } } else if msg.next_local_commitment_number < next_counterparty_commitment_number { @@ -10486,6 +10551,25 @@ where self.sign_channel_announcement(node_signer, announcement).ok() } + fn get_next_local_commitment_number(&self) -> u64 { + // The sending node: + // - if it has sent `commitment_signed` for an interactive transaction construction but + // it has not received `tx_signatures`: + // - MUST set `next_funding_txid` to the txid of that interactive transaction. + // - if it has not received `commitment_signed` for that interactive transaction: + // - MUST set `next_commitment_number` to the commitment number of the `commitment_signed` it sent. + if let Some(session) = &self.interactive_tx_signing_session { + if !self.context.channel_state.is_their_tx_signatures_sent() + && !session.has_received_commitment_signed() + { + return INITIAL_COMMITMENT_NUMBER + - self.holder_commitment_point.current_transaction_number(); + } + } + + INITIAL_COMMITMENT_NUMBER - self.holder_commitment_point.next_transaction_number() + } + #[rustfmt::skip] fn maybe_get_next_funding_txid(&self) -> Option { // If we've sent `commtiment_signed` for an interactively constructed transaction @@ -10506,6 +10590,39 @@ 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.funding.get_funding_txid() == Some(txid) + && self.context.config.announce_for_forwarding + && self.context.announcement_sigs.is_none() + { + funding_locked.retransmit(msgs::RetransmitFlag::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] @@ -10545,7 +10662,7 @@ where // next_local_commitment_number is the next commitment_signed number we expect to // receive (indicating if they need to resend one that we missed). - next_local_commitment_number: INITIAL_COMMITMENT_NUMBER - self.holder_commitment_point.next_transaction_number(), + next_local_commitment_number: self.get_next_local_commitment_number(), // We have to set next_remote_commitment_number to the next revoke_and_ack we expect to // receive, however we track it by the next commitment number for a remote transaction // (which is one further, as they always revoke previous commitment transaction, not @@ -10557,6 +10674,7 @@ where your_last_per_commitment_secret: remote_last_secret, my_current_per_commitment_point: dummy_pubkey, next_funding_txid: self.maybe_get_next_funding_txid(), + 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 af82f862878..9e7028f4e47 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -11002,7 +11002,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 (implicit_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) @@ -11053,7 +11053,8 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ if let Some(upd) = channel_update { peer_state.pending_msg_events.push(upd); } - need_lnd_workaround + + (responses.implicit_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); @@ -11085,6 +11086,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ your_last_per_commitment_secret: [1u8; 32], my_current_per_commitment_point: PublicKey::from_slice(&[2u8; 33]).unwrap(), next_funding_txid: None, + my_current_funding_locked: None, }, }); return Err(MsgHandleErrInternal::send_err_msg_no_close( @@ -11098,6 +11100,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 _ = implicit_splice_locked; + #[cfg(splicing)] + if let Some(splice_locked) = implicit_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 e0219a5523f..61021fbe5e6 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -927,6 +927,45 @@ pub struct ChannelReestablish { /// * `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, + /// 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 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 [`RetransmitFlag`] 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: RetransmitFlag) { + self.retransmit_flags |= 1 << flag as u8; + } + + /// Returns whether the message corresponding to `flag` should be retransmitted. + pub fn should_retransmit(&self, flag: RetransmitFlag) -> bool { + self.retransmit_flags & (1 << flag as u8) != 0 + } +} + +/// Bit positions used in [`FundingLocked::retransmit_flags`] for requesting message retransmission. +#[repr(u8)] +pub enum RetransmitFlag { + /// Retransmit `announcement_signatures`. + AnnouncementSignatures = 0, } /// An [`announcement_signatures`] message to be sent to or received from a peer. @@ -2811,6 +2850,12 @@ impl_writeable_msg!(ChannelReestablish, { my_current_per_commitment_point, }, { (0, next_funding_txid, option), + (5, my_current_funding_locked, option), +}); + +impl_writeable!(FundingLocked, { + txid, + retransmit_flags }); impl_writeable_msg!(ClosingSigned, @@ -4283,6 +4328,7 @@ mod tests { your_last_per_commitment_secret: [9; 32], my_current_per_commitment_point: public_key, next_funding_txid: None, + my_current_funding_locked: None, }; let encoded_value = cr.encode(); @@ -4334,6 +4380,7 @@ mod tests { ]) .unwrap(), )), + my_current_funding_locked: None, }; let encoded_value = cr.encode(); @@ -4357,6 +4404,65 @@ mod tests { ); } + #[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_txid: 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 + ] + ); + } + macro_rules! get_keys_from { ($slice: expr, $secp_ctx: expr) => {{ let privkey = SecretKey::from_slice(&>::from_hex($slice).unwrap()[..]).unwrap();