From 792e42ad46625783a4e9904b97eaf9dd18939762 Mon Sep 17 00:00:00 2001 From: optout <13562139+optout21@users.noreply.github.com> Date: Sat, 4 Nov 2023 22:41:33 +0100 Subject: [PATCH] Add Splicing wire message definitions --- lightning-net-tokio/src/lib.rs | 3 + lightning/src/events/mod.rs | 21 ++++ lightning/src/ln/channelmanager.rs | 22 +++++ lightning/src/ln/functional_test_utils.rs | 9 ++ lightning/src/ln/msgs.rs | 114 ++++++++++++++++++++++ lightning/src/ln/peer_handler.rs | 38 ++++++++ lightning/src/ln/wire.rs | 31 ++++++ lightning/src/util/test_utils.rs | 9 ++ 8 files changed, 247 insertions(+) diff --git a/lightning-net-tokio/src/lib.rs b/lightning-net-tokio/src/lib.rs index bac18b2b398..5be23be66c5 100644 --- a/lightning-net-tokio/src/lib.rs +++ b/lightning-net-tokio/src/lib.rs @@ -605,6 +605,9 @@ mod tests { fn handle_channel_update(&self, _their_node_id: &PublicKey, _msg: &ChannelUpdate) {} fn handle_open_channel_v2(&self, _their_node_id: &PublicKey, _msg: &OpenChannelV2) {} fn handle_accept_channel_v2(&self, _their_node_id: &PublicKey, _msg: &AcceptChannelV2) {} + fn handle_splice(&self, _their_node_id: &PublicKey, _msg: &Splice) {} + fn handle_splice_ack(&self, _their_node_id: &PublicKey, _msg: &SpliceAck) {} + fn handle_splice_locked(&self, _their_node_id: &PublicKey, _msg: &SpliceLocked) {} fn handle_tx_add_input(&self, _their_node_id: &PublicKey, _msg: &TxAddInput) {} fn handle_tx_add_output(&self, _their_node_id: &PublicKey, _msg: &TxAddOutput) {} fn handle_tx_remove_input(&self, _their_node_id: &PublicKey, _msg: &TxRemoveInput) {} diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs index 32192297432..95b77f44ed2 100644 --- a/lightning/src/events/mod.rs +++ b/lightning/src/events/mod.rs @@ -1642,6 +1642,27 @@ pub enum MessageSendEvent { /// The message which should be sent. msg: msgs::FundingSigned, }, + /// Used to indicate that a splice message should be sent to the peer with the given node id. + SendSplice { + /// The node_id of the node which should receive this message + node_id: PublicKey, + /// The message which should be sent. + msg: msgs::Splice, + }, + /// Used to indicate that a splice_ack message should be sent to the peer with the given node id. + SendSpliceAck { + /// The node_id of the node which should receive this message + node_id: PublicKey, + /// The message which should be sent. + msg: msgs::SpliceAck, + }, + /// Used to indicate that a splice_locked message should be sent to the peer with the given node id. + SendSpliceLocked { + /// The node_id of the node which should receive this message + node_id: PublicKey, + /// The message which should be sent. + msg: msgs::SpliceLocked, + }, /// Used to indicate that a tx_add_input message should be sent to the peer with the given node_id. SendTxAddInput { /// The node_id of the node which should receive this message diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 37a8feb446d..dbca483dac1 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -8521,6 +8521,24 @@ where }); } + fn handle_splice(&self, counterparty_node_id: &PublicKey, msg: &msgs::Splice) { + let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close( + "Splicing not supported".to_owned(), + msg.channel_id.clone())), *counterparty_node_id); + } + + fn handle_splice_ack(&self, counterparty_node_id: &PublicKey, msg: &msgs::SpliceAck) { + let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close( + "Splicing not supported (splice_ack)".to_owned(), + msg.channel_id.clone())), *counterparty_node_id); + } + + fn handle_splice_locked(&self, counterparty_node_id: &PublicKey, msg: &msgs::SpliceLocked) { + let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close( + "Splicing not supported (splice_locked)".to_owned(), + msg.channel_id.clone())), *counterparty_node_id); + } + fn handle_shutdown(&self, counterparty_node_id: &PublicKey, msg: &msgs::Shutdown) { let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self); let _ = handle_error!(self, self.internal_shutdown(counterparty_node_id, msg), *counterparty_node_id); @@ -8689,6 +8707,10 @@ where // Common Channel Establishment &events::MessageSendEvent::SendChannelReady { .. } => false, &events::MessageSendEvent::SendAnnouncementSignatures { .. } => false, + // Splicing + &events::MessageSendEvent::SendSplice { .. } => false, + &events::MessageSendEvent::SendSpliceAck { .. } => false, + &events::MessageSendEvent::SendSpliceLocked { .. } => false, // Interactive Transaction Construction &events::MessageSendEvent::SendTxAddInput { .. } => false, &events::MessageSendEvent::SendTxAddOutput { .. } => false, diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index 08dbda3fe75..dc18d20becd 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -814,6 +814,15 @@ pub fn remove_first_msg_event_to_node(msg_node_id: &PublicKey, msg_events: &mut MessageSendEvent::SendOpenChannelV2 { node_id, .. } => { node_id == msg_node_id }, + MessageSendEvent::SendSplice { node_id, .. } => { + node_id == msg_node_id + }, + MessageSendEvent::SendSpliceAck { node_id, .. } => { + node_id == msg_node_id + }, + MessageSendEvent::SendSpliceLocked { node_id, .. } => { + node_id == msg_node_id + }, MessageSendEvent::SendTxAddInput { node_id, .. } => { node_id == msg_node_id }, diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 6043bf35b99..a3ef0118d75 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -441,6 +441,53 @@ pub struct ChannelReady { pub short_channel_id_alias: Option, } +/// A splice message to be sent by or received from the splice initiator. +/// TODO(splicing): Is using 'splice initiator' role OK? +/// TODO(splicing): Can the channel acceptor later be the splice initiator? +/// +// TODO(splicing): Add spec link for `splice`; still in draft, using from https://github.com/lightning/bolts/pull/863 +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Splice { + /// The channel ID where splicing is intended + pub channel_id: ChannelId, + /// The genesis hash of the blockchain where the channel is intended to be spliced + pub chain_hash: ChainHash, + /// The intended change in channel capacity: the amount to be added (positive value) + /// or removed (negative value) by the sender (splice initiator) by splicing into/from the channel. + pub relative_satoshis: i64, + /// The feerate for the new funding transaction, set by the splice initiator + pub funding_feerate_perkw: u32, + /// The locktime for the new funding transaction + pub locktime: u32, + /// The key of the sender (splice initiator) controlling the new funding transaction + pub funding_pubkey: PublicKey, +} + +/// A splice_ack message to be received by or sent to the splice initiator. +/// +// TODO(splicing): Add spec link for `splice_ack`; still in draft, using from https://github.com/lightning/bolts/pull/863 +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SpliceAck { + /// The channel ID where splicing is intended + pub channel_id: ChannelId, + /// The genesis hash of the blockchain where the channel is intended to be spliced + pub chain_hash: ChainHash, + /// The intended change in channel capacity: the amount to be added (positive value) + /// or removed (negative value) by the sender (splice acceptor) by splicing into/from the channel. + pub relative_satoshis: i64, + /// The key of the sender (splice acceptor) controlling the new funding transaction + pub funding_pubkey: PublicKey, +} + +/// A splice_locked message to be sent to or received from a peer. +/// +// TODO(splicing): Add spec link for `splice_locked`; still in draft, using from https://github.com/lightning/bolts/pull/863 +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SpliceLocked { + /// The channel ID + pub channel_id: ChannelId, +} + /// A tx_add_input message for adding an input during interactive transaction construction /// // TODO(dual_funding): Add spec link for `tx_add_input`. @@ -1409,6 +1456,14 @@ pub trait ChannelMessageHandler : MessageSendEventsProvider { /// Handle an incoming `closing_signed` message from the given peer. fn handle_closing_signed(&self, their_node_id: &PublicKey, msg: &ClosingSigned); + // Splicing + /// Handle an incoming `splice` message from the given peer. + fn handle_splice(&self, their_node_id: &PublicKey, msg: &Splice); + /// Handle an incoming `splice_ack` message from the given peer. + fn handle_splice_ack(&self, their_node_id: &PublicKey, msg: &SpliceAck); + /// Handle an incoming `splice_locked` message from the given peer. + fn handle_splice_locked(&self, their_node_id: &PublicKey, msg: &SpliceLocked); + // Interactive channel construction /// Handle an incoming `tx_add_input message` from the given peer. fn handle_tx_add_input(&self, their_node_id: &PublicKey, msg: &TxAddInput); @@ -1813,6 +1868,26 @@ impl_writeable_msg!(AcceptChannelV2, { (2, require_confirmed_inputs, option), }); +impl_writeable_msg!(Splice, { + channel_id, + chain_hash, + relative_satoshis, + funding_feerate_perkw, + locktime, + funding_pubkey, +}, {}); + +impl_writeable_msg!(SpliceAck, { + channel_id, + chain_hash, + relative_satoshis, + funding_pubkey, +}, {}); + +impl_writeable_msg!(SpliceLocked, { + channel_id, +}, {}); + impl_writeable_msg!(TxAddInput, { channel_id, serial_id, @@ -3365,6 +3440,45 @@ mod tests { assert_eq!(encoded_value, target_value); } + #[test] + fn encoding_splice() { + let secp_ctx = Secp256k1::new(); + let (_, pubkey_1,) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx); + let splice = msgs::Splice { + chain_hash: ChainHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(), + channel_id: ChannelId::from_bytes([2; 32]), + relative_satoshis: 123456, + funding_feerate_perkw: 2000, + locktime: 0, + funding_pubkey: pubkey_1, + }; + let encoded_value = splice.encode(); + assert_eq!(hex::encode(encoded_value), "02020202020202020202020202020202020202020202020202020202020202026fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000000000000001e240000007d000000000031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"); + } + + #[test] + fn encoding_splice_ack() { + let secp_ctx = Secp256k1::new(); + let (_, pubkey_1,) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx); + let splice = msgs::SpliceAck { + chain_hash: ChainHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(), + channel_id: ChannelId::from_bytes([2; 32]), + relative_satoshis: 123456, + funding_pubkey: pubkey_1, + }; + let encoded_value = splice.encode(); + assert_eq!(hex::encode(encoded_value), "02020202020202020202020202020202020202020202020202020202020202026fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000000000000001e240031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"); + } + + #[test] + fn encoding_splice_locked() { + let splice = msgs::SpliceLocked { + channel_id: ChannelId::from_bytes([2; 32]), + }; + let encoded_value = splice.encode(); + assert_eq!(hex::encode(encoded_value), "0202020202020202020202020202020202020202020202020202020202020202"); + } + #[test] fn encoding_tx_add_input() { let tx_add_input = msgs::TxAddInput { diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index ba3a733d225..5c1d81dd6ed 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peer_handler.rs @@ -231,6 +231,15 @@ impl ChannelMessageHandler for ErroringMessageHandler { fn handle_closing_signed(&self, their_node_id: &PublicKey, msg: &msgs::ClosingSigned) { ErroringMessageHandler::push_error(self, their_node_id, msg.channel_id); } + fn handle_splice(&self, their_node_id: &PublicKey, msg: &msgs::Splice) { + ErroringMessageHandler::push_error(&self, their_node_id, msg.channel_id); + } + fn handle_splice_ack(&self, their_node_id: &PublicKey, msg: &msgs::SpliceAck) { + ErroringMessageHandler::push_error(&self, their_node_id, msg.channel_id); + } + fn handle_splice_locked(&self, their_node_id: &PublicKey, msg: &msgs::SpliceLocked) { + ErroringMessageHandler::push_error(&self, their_node_id, msg.channel_id); + } fn handle_update_add_htlc(&self, their_node_id: &PublicKey, msg: &msgs::UpdateAddHTLC) { ErroringMessageHandler::push_error(self, their_node_id, msg.channel_id); } @@ -1643,6 +1652,17 @@ impl { + self.message_handler.chan_handler.handle_splice(&their_node_id, &msg); + } + wire::Message::SpliceAck(msg) => { + self.message_handler.chan_handler.handle_splice_ack(&their_node_id, &msg); + } + wire::Message::SpliceLocked(msg) => { + self.message_handler.chan_handler.handle_splice_locked(&their_node_id, &msg); + } + // Interactive transaction construction messages: wire::Message::TxAddInput(msg) => { self.message_handler.chan_handler.handle_tx_add_input(&their_node_id, &msg); @@ -1960,6 +1980,24 @@ impl { + log_debug!(self.logger, "Handling SendSplice event in peer_handler for node {} for channel {}", + log_pubkey!(node_id), + &msg.channel_id); + self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg); + } + MessageSendEvent::SendSpliceAck { ref node_id, ref msg} => { + log_debug!(self.logger, "Handling SendSpliceAck event in peer_handler for node {} for channel {}", + log_pubkey!(node_id), + &msg.channel_id); + self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg); + } + MessageSendEvent::SendSpliceLocked { ref node_id, ref msg} => { + log_debug!(self.logger, "Handling SendSpliceLocked event in peer_handler for node {} for channel {}", + log_pubkey!(node_id), + &msg.channel_id); + self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg); + } MessageSendEvent::SendTxAddInput { ref node_id, ref msg } => { log_debug!(self.logger, "Handling SendTxAddInput event in peer_handler for node {} for channel {}", log_pubkey!(node_id), diff --git a/lightning/src/ln/wire.rs b/lightning/src/ln/wire.rs index 88e2ad7c1da..af39c896654 100644 --- a/lightning/src/ln/wire.rs +++ b/lightning/src/ln/wire.rs @@ -59,6 +59,9 @@ pub(crate) enum Message where T: core::fmt::Debug + Type + TestEq { AcceptChannelV2(msgs::AcceptChannelV2), FundingCreated(msgs::FundingCreated), FundingSigned(msgs::FundingSigned), + Splice(msgs::Splice), + SpliceAck(msgs::SpliceAck), + SpliceLocked(msgs::SpliceLocked), TxAddInput(msgs::TxAddInput), TxAddOutput(msgs::TxAddOutput), TxRemoveInput(msgs::TxRemoveInput), @@ -110,6 +113,9 @@ impl Writeable for Message where T: core::fmt::Debug + Type + TestEq { &Message::AcceptChannelV2(ref msg) => msg.write(writer), &Message::FundingCreated(ref msg) => msg.write(writer), &Message::FundingSigned(ref msg) => msg.write(writer), + &Message::Splice(ref msg) => msg.write(writer), + &Message::SpliceAck(ref msg) => msg.write(writer), + &Message::SpliceLocked(ref msg) => msg.write(writer), &Message::TxAddInput(ref msg) => msg.write(writer), &Message::TxAddOutput(ref msg) => msg.write(writer), &Message::TxRemoveInput(ref msg) => msg.write(writer), @@ -161,6 +167,9 @@ impl Type for Message where T: core::fmt::Debug + Type + TestEq { &Message::AcceptChannelV2(ref msg) => msg.type_id(), &Message::FundingCreated(ref msg) => msg.type_id(), &Message::FundingSigned(ref msg) => msg.type_id(), + &Message::Splice(ref msg) => msg.type_id(), + &Message::SpliceAck(ref msg) => msg.type_id(), + &Message::SpliceLocked(ref msg) => msg.type_id(), &Message::TxAddInput(ref msg) => msg.type_id(), &Message::TxAddOutput(ref msg) => msg.type_id(), &Message::TxRemoveInput(ref msg) => msg.type_id(), @@ -258,6 +267,15 @@ fn do_read(buffer: &mut R, message_type: u1 msgs::FundingSigned::TYPE => { Ok(Message::FundingSigned(Readable::read(buffer)?)) }, + msgs::Splice::TYPE => { + Ok(Message::Splice(Readable::read(buffer)?)) + }, + msgs::SpliceAck::TYPE => { + Ok(Message::SpliceAck(Readable::read(buffer)?)) + }, + msgs::SpliceLocked::TYPE => { + Ok(Message::SpliceLocked(Readable::read(buffer)?)) + }, msgs::TxAddInput::TYPE => { Ok(Message::TxAddInput(Readable::read(buffer)?)) }, @@ -464,6 +482,19 @@ impl Encode for msgs::AcceptChannelV2 { const TYPE: u16 = 65; } +impl Encode for msgs::Splice { + // TODO(splicing) Double check with spec; spec contains 74, which is probably wrong as it is used by tx_Abort; CLN uses 75 + const TYPE: u16 = 75; +} + +impl Encode for msgs::SpliceAck { + const TYPE: u16 = 76; +} + +impl Encode for msgs::SpliceLocked { + const TYPE: u16 = 77; +} + impl Encode for msgs::TxAddInput { const TYPE: u16 = 66; } diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index d42a84d361d..fa78f9f6ba5 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -651,6 +651,15 @@ impl msgs::ChannelMessageHandler for TestChannelMessageHandler { fn handle_closing_signed(&self, _their_node_id: &PublicKey, msg: &msgs::ClosingSigned) { self.received_msg(wire::Message::ClosingSigned(msg.clone())); } + fn handle_splice(&self, _their_node_id: &PublicKey, msg: &msgs::Splice) { + self.received_msg(wire::Message::Splice(msg.clone())); + } + fn handle_splice_ack(&self, _their_node_id: &PublicKey, msg: &msgs::SpliceAck) { + self.received_msg(wire::Message::SpliceAck(msg.clone())); + } + fn handle_splice_locked(&self, _their_node_id: &PublicKey, msg: &msgs::SpliceLocked) { + self.received_msg(wire::Message::SpliceLocked(msg.clone())); + } fn handle_update_add_htlc(&self, _their_node_id: &PublicKey, msg: &msgs::UpdateAddHTLC) { self.received_msg(wire::Message::UpdateAddHTLC(msg.clone())); }