From: Valentine Wallace Date: Mon, 17 Oct 2022 20:32:17 +0000 (-0400) Subject: Parameterize OnionMessenger by new CustomOnionMessageHandler trait X-Git-Tag: v0.0.112~6^2~5 X-Git-Url: http://git.bitcoin.ninja/?a=commitdiff_plain;h=75fd0f3cbbd1b788a8f9e3956ff78831669df55b;p=rust-lightning Parameterize OnionMessenger by new CustomOnionMessageHandler trait OnionMessenger::new will now take a custom onion message handler trait implementation. This handler will be used in upcoming commit(s) to handle inbound custom onion messages. The new trait also specifies what custom messages are supported via its associated type, CustomMessage. This associated type must implement a new CustomOnionMessagesContents trait, which requires custom messages to support being written, being read, and supplying their TLV type. --- diff --git a/fuzz/src/onion_message.rs b/fuzz/src/onion_message.rs index a2fe88afc..f5f9d26be 100644 --- a/fuzz/src/onion_message.rs +++ b/fuzz/src/onion_message.rs @@ -10,12 +10,12 @@ use lightning::ln::msgs::{self, DecodeError, OnionMessageHandler}; use lightning::ln::script::ShutdownScript; use lightning::util::enforcing_trait_impls::EnforcingSigner; use lightning::util::logger::Logger; -use lightning::util::ser::{Readable, Writer}; -use lightning::onion_message::OnionMessenger; +use lightning::util::ser::{MaybeReadableArgs, Readable, Writeable, Writer}; +use lightning::onion_message::{CustomOnionMessageContents, CustomOnionMessageHandler, OnionMessenger}; use utils::test_logger; -use std::io::Cursor; +use std::io::{self, Cursor}; use std::sync::atomic::{AtomicU64, Ordering}; #[inline] @@ -29,7 +29,8 @@ pub fn do_test(data: &[u8], logger: &L) { node_secret: secret, counter: AtomicU64::new(0), }; - let onion_messenger = OnionMessenger::new(&keys_manager, logger); + let custom_msg_handler = TestCustomMessageHandler {}; + let onion_messenger = OnionMessenger::new(&keys_manager, logger, &custom_msg_handler); let mut pk = [2; 33]; pk[1] = 0xff; let peer_node_id_not_used = PublicKey::from_slice(&pk).unwrap(); onion_messenger.handle_onion_message(&peer_node_id_not_used, &msg); @@ -49,6 +50,38 @@ pub extern "C" fn onion_message_run(data: *const u8, datalen: usize) { do_test(unsafe { std::slice::from_raw_parts(data, datalen) }, &logger); } +struct TestCustomMessage {} + +const CUSTOM_MESSAGE_TYPE: u64 = 4242; +const CUSTOM_MESSAGE_CONTENTS: [u8; 32] = [42; 32]; + +impl CustomOnionMessageContents for TestCustomMessage { + fn tlv_type(&self) -> u64 { + CUSTOM_MESSAGE_TYPE + } +} + +impl Writeable for TestCustomMessage { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + Ok(CUSTOM_MESSAGE_CONTENTS.write(w)?) + } +} + +impl MaybeReadableArgs for TestCustomMessage { + fn read(buffer: &mut R, _message_type: u64,) -> Result, DecodeError> where Self: Sized { + let mut buf = Vec::new(); + buffer.read_to_end(&mut buf)?; + return Ok(Some(TestCustomMessage {})) + } +} + +struct TestCustomMessageHandler {} + +impl CustomOnionMessageHandler for TestCustomMessageHandler { + type CustomMessage = TestCustomMessage; + fn handle_custom_message(&self, _msg: Self::CustomMessage) {} +} + pub struct VecWriter(pub Vec); impl Writer for VecWriter { fn write_all(&mut self, buf: &[u8]) -> Result<(), ::std::io::Error> { diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index d38afcbac..8a7b4632f 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peer_handler.rs @@ -21,11 +21,11 @@ use ln::features::{InitFeatures, NodeFeatures}; use ln::msgs; use ln::msgs::{ChannelMessageHandler, LightningError, NetAddress, OnionMessageHandler, RoutingMessageHandler}; use ln::channelmanager::{SimpleArcChannelManager, SimpleRefChannelManager}; -use util::ser::{VecWriter, Writeable, Writer}; +use util::ser::{MaybeReadableArgs, VecWriter, Writeable, Writer}; use ln::peer_channel_encryptor::{PeerChannelEncryptor,NextNoiseStep}; use ln::wire; use ln::wire::Encode; -use onion_message::{SimpleArcOnionMessenger, SimpleRefOnionMessenger}; +use onion_message::{CustomOnionMessageContents, CustomOnionMessageHandler, SimpleArcOnionMessenger, SimpleRefOnionMessenger}; use routing::gossip::{NetworkGraph, P2PGossipSync}; use util::atomic_counter::AtomicCounter; use util::crypto::sign; @@ -95,6 +95,23 @@ impl OnionMessageHandler for IgnoringMessageHandler { InitFeatures::empty() } } +impl CustomOnionMessageHandler for IgnoringMessageHandler { + type CustomMessage = Infallible; + fn handle_custom_message(&self, _msg: Self::CustomMessage) { + // Since we always return `None` in the read the handle method should never be called. + unreachable!(); + } +} +impl MaybeReadableArgs for Infallible { + fn read(_buffer: &mut R, _msg_type: u64) -> Result, msgs::DecodeError> where Self: Sized { + Ok(None) + } +} + +impl CustomOnionMessageContents for Infallible { + fn tlv_type(&self) -> u64 { unreachable!(); } +} + impl Deref for IgnoringMessageHandler { type Target = IgnoringMessageHandler; fn deref(&self) -> &Self { self } diff --git a/lightning/src/onion_message/functional_tests.rs b/lightning/src/onion_message/functional_tests.rs index 22389bf52..8c416bf65 100644 --- a/lightning/src/onion_message/functional_tests.rs +++ b/lightning/src/onion_message/functional_tests.rs @@ -11,19 +11,21 @@ use chain::keysinterface::{KeysInterface, Recipient}; use ln::features::InitFeatures; -use ln::msgs::{self, OnionMessageHandler}; -use super::{BlindedRoute, Destination, OnionMessenger, SendError}; +use ln::msgs::{self, DecodeError, OnionMessageHandler}; +use super::{BlindedRoute, CustomOnionMessageContents, CustomOnionMessageHandler, Destination, OnionMessenger, SendError}; use util::enforcing_trait_impls::EnforcingSigner; +use util::ser::{MaybeReadableArgs, Writeable, Writer}; use util::test_utils; use bitcoin::network::constants::Network; use bitcoin::secp256k1::{PublicKey, Secp256k1}; +use io; use sync::Arc; struct MessengerNode { keys_manager: Arc, - messenger: OnionMessenger, Arc>, + messenger: OnionMessenger, Arc, Arc>, logger: Arc, } @@ -34,6 +36,43 @@ impl MessengerNode { } } +#[derive(Clone)] +struct TestCustomMessage {} + +const CUSTOM_MESSAGE_TYPE: u64 = 4242; +const CUSTOM_MESSAGE_CONTENTS: [u8; 32] = [42; 32]; + +impl CustomOnionMessageContents for TestCustomMessage { + fn tlv_type(&self) -> u64 { + CUSTOM_MESSAGE_TYPE + } +} + +impl Writeable for TestCustomMessage { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + Ok(CUSTOM_MESSAGE_CONTENTS.write(w)?) + } +} + +impl MaybeReadableArgs for TestCustomMessage { + fn read(buffer: &mut R, message_type: u64) -> Result, DecodeError> where Self: Sized { + if message_type == CUSTOM_MESSAGE_TYPE { + let mut buf = Vec::new(); + buffer.read_to_end(&mut buf)?; + assert_eq!(buf, CUSTOM_MESSAGE_CONTENTS); + return Ok(Some(TestCustomMessage {})) + } + Ok(None) + } +} + +struct TestCustomMessageHandler {} + +impl CustomOnionMessageHandler for TestCustomMessageHandler { + type CustomMessage = TestCustomMessage; + fn handle_custom_message(&self, _msg: Self::CustomMessage) {} +} + fn create_nodes(num_messengers: u8) -> Vec { let mut nodes = Vec::new(); for i in 0..num_messengers { @@ -42,7 +81,7 @@ fn create_nodes(num_messengers: u8) -> Vec { let keys_manager = Arc::new(test_utils::TestKeysInterface::new(&seed, Network::Testnet)); nodes.push(MessengerNode { keys_manager: keys_manager.clone(), - messenger: OnionMessenger::new(keys_manager, logger.clone()), + messenger: OnionMessenger::new(keys_manager, logger.clone(), Arc::new(TestCustomMessageHandler {})), logger, }); } diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index e2409fc45..42be0202f 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -19,7 +19,9 @@ use chain::keysinterface::{InMemorySigner, KeysInterface, KeysManager, Recipient use ln::features::{InitFeatures, NodeFeatures}; use ln::msgs::{self, OnionMessageHandler}; use ln::onion_utils; +use ln::peer_handler::IgnoringMessageHandler; use super::blinded_route::{BlindedRoute, ForwardTlvs, ReceiveTlvs}; +pub use super::packet::CustomOnionMessageContents; use super::packet::{BIG_PACKET_HOP_DATA_LEN, ForwardControlTlvs, Packet, Payload, ReceiveControlTlvs, SMALL_PACKET_HOP_DATA_LEN}; use super::utils; use util::events::OnionMessageProvider; @@ -41,8 +43,12 @@ use prelude::*; /// # use bitcoin::hashes::_export::_core::time::Duration; /// # use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; /// # use lightning::chain::keysinterface::{InMemorySigner, KeysManager, KeysInterface}; -/// # use lightning::onion_message::{BlindedRoute, Destination, OnionMessenger}; +/// # use lightning::ln::msgs::DecodeError; +/// # use lightning::ln::peer_handler::IgnoringMessageHandler; +/// # use lightning::onion_message::{BlindedRoute, CustomOnionMessageContents, Destination, OnionMessenger}; /// # use lightning::util::logger::{Logger, Record}; +/// # use lightning::util::ser::{MaybeReadableArgs, Writeable, Writer}; +/// # use lightning::io; /// # use std::sync::Arc; /// # struct FakeLogger {}; /// # impl Logger for FakeLogger { @@ -58,12 +64,31 @@ use prelude::*; /// # let (hop_node_id2, hop_node_id3, hop_node_id4) = (hop_node_id1, hop_node_id1, /// hop_node_id1); /// # let destination_node_id = hop_node_id1; -/// # +/// # let your_custom_message_handler = IgnoringMessageHandler {}; /// // Create the onion messenger. This must use the same `keys_manager` as is passed to your /// // ChannelManager. -/// let onion_messenger = OnionMessenger::new(&keys_manager, logger); +/// let onion_messenger = OnionMessenger::new(&keys_manager, logger, your_custom_message_handler); /// -/// // Send an empty onion message to a node id. +/// # struct YourCustomMessage {} +/// impl Writeable for YourCustomMessage { +/// fn write(&self, w: &mut W) -> Result<(), io::Error> { +/// # Ok(()) +/// // Write your custom onion message to `w` +/// } +/// } +/// impl CustomOnionMessageContents for YourCustomMessage { +/// fn tlv_type(&self) -> u64 { +/// # let your_custom_message_type = 42; +/// your_custom_message_type +/// } +/// } +/// impl MaybeReadableArgs for YourCustomMessage { +/// fn read(r: &mut R, message_type: u64) -> Result, DecodeError> { +/// # unreachable!() +/// // Read your custom onion message of type `message_type` from `r`, or return `None` +/// // if the message type is unknown +/// } +/// } /// let intermediate_hops = [hop_node_id1, hop_node_id2]; /// let reply_path = None; /// onion_messenger.send_onion_message(&intermediate_hops, Destination::Node(destination_node_id), reply_path); @@ -81,17 +106,18 @@ use prelude::*; /// /// [offers]: /// [`OnionMessenger`]: crate::onion_message::OnionMessenger -pub struct OnionMessenger +pub struct OnionMessenger where K::Target: KeysInterface, L::Target: Logger, + CMH:: Target: CustomOnionMessageHandler, { keys_manager: K, logger: L, pending_messages: Mutex>>, secp_ctx: Secp256k1, + custom_handler: CMH, // Coming soon: // invoice_handler: InvoiceHandler, - // custom_handler: CustomHandler, // handles custom onion messages } /// The destination of an onion message. @@ -130,13 +156,32 @@ pub enum SendError { BufferFull, } -impl OnionMessenger +/// Handler for custom onion messages. If you are using [`SimpleArcOnionMessenger`], +/// [`SimpleRefOnionMessenger`], or prefer to ignore inbound custom onion messages, +/// [`IgnoringMessageHandler`] must be provided to [`OnionMessenger::new`]. Otherwise, a custom +/// implementation of this trait must be provided, with [`CustomMessage`] specifying the supported +/// message types. +/// +/// See [`OnionMessenger`] for example usage. +/// +/// [`IgnoringMessageHandler`]: crate::ln::peer_handler::IgnoringMessageHandler +/// [`CustomMessage`]: Self::CustomMessage +pub trait CustomOnionMessageHandler { + /// The message known to the handler. To support multiple message types, you may want to make this + /// an enum with a variant for each supported message. + type CustomMessage: CustomOnionMessageContents; + /// Called with the custom message that was received. + fn handle_custom_message(&self, msg: Self::CustomMessage); +} + +impl OnionMessenger where K::Target: KeysInterface, L::Target: Logger, + CMH::Target: CustomOnionMessageHandler, { /// Constructs a new `OnionMessenger` to send, forward, and delegate received onion messages to /// their respective handlers. - pub fn new(keys_manager: K, logger: L) -> Self { + pub fn new(keys_manager: K, logger: L, custom_handler: CMH) -> Self { let mut secp_ctx = Secp256k1::new(); secp_ctx.seeded_randomize(&keys_manager.get_secure_random_bytes()); OnionMessenger { @@ -144,6 +189,7 @@ impl OnionMessenger pending_messages: Mutex::new(HashMap::new()), secp_ctx, logger, + custom_handler, } } @@ -221,9 +267,10 @@ fn outbound_buffer_full(peer_node_id: &PublicKey, buffer: &HashMap OnionMessageHandler for OnionMessenger +impl OnionMessageHandler for OnionMessenger where K::Target: KeysInterface, L::Target: Logger, + CMH::Target: CustomOnionMessageHandler, { /// Handle an incoming onion message. Currently, if a message was destined for us we will log, but /// soon we'll delegate the onion message to a handler that can generate invoices or send @@ -361,9 +408,10 @@ impl OnionMessageHandler for OnionMessenger OnionMessageProvider for OnionMessenger +impl OnionMessageProvider for OnionMessenger where K::Target: KeysInterface, L::Target: Logger, + CMH::Target: CustomOnionMessageHandler, { fn next_onion_message_for_peer(&self, peer_node_id: PublicKey) -> Option { let mut pending_msgs = self.pending_messages.lock().unwrap(); @@ -383,7 +431,7 @@ impl OnionMessageProvider for OnionMessenger = OnionMessenger, Arc>; +pub type SimpleArcOnionMessenger = OnionMessenger, Arc, IgnoringMessageHandler>; /// Useful for simplifying the parameters of [`SimpleRefChannelManager`] and /// [`SimpleRefPeerManager`]. See their docs for more details. /// @@ -391,7 +439,7 @@ pub type SimpleArcOnionMessenger = OnionMessenger = OnionMessenger; +pub type SimpleRefOnionMessenger<'a, 'b, L> = OnionMessenger; /// Construct onion packet payloads and keys for sending an onion message along the given /// `unblinded_path` to the given `destination`. diff --git a/lightning/src/onion_message/mod.rs b/lightning/src/onion_message/mod.rs index 2e23b76ad..b80c540a6 100644 --- a/lightning/src/onion_message/mod.rs +++ b/lightning/src/onion_message/mod.rs @@ -29,5 +29,5 @@ mod functional_tests; // Re-export structs so they can be imported with just the `onion_message::` module prefix. pub use self::blinded_route::{BlindedRoute, BlindedHop}; -pub use self::messenger::{Destination, OnionMessenger, SendError, SimpleArcOnionMessenger, SimpleRefOnionMessenger}; +pub use self::messenger::{CustomOnionMessageContents, CustomOnionMessageHandler, Destination, OnionMessenger, SendError, SimpleArcOnionMessenger, SimpleRefOnionMessenger}; pub(crate) use self::packet::Packet; diff --git a/lightning/src/onion_message/packet.rs b/lightning/src/onion_message/packet.rs index 1337bdb14..2afdbdd65 100644 --- a/lightning/src/onion_message/packet.rs +++ b/lightning/src/onion_message/packet.rs @@ -16,7 +16,7 @@ use ln::msgs::DecodeError; use ln::onion_utils; use super::blinded_route::{BlindedRoute, ForwardTlvs, ReceiveTlvs}; use util::chacha20poly1305rfc::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter}; -use util::ser::{BigSize, FixedLengthReader, LengthRead, LengthReadable, LengthReadableArgs, Readable, ReadableArgs, Writeable, Writer}; +use util::ser::{BigSize, FixedLengthReader, LengthRead, LengthReadable, LengthReadableArgs, MaybeReadableArgs, Readable, ReadableArgs, Writeable, Writer}; use core::cmp; use io::{self, Read}; @@ -112,6 +112,13 @@ pub(super) enum Payload { // CustomMessage, // } +/// The contents of a custom onion message. Must implement `MaybeReadableArgs` where the `u64` +/// is the custom TLV type attempting to be read, and return `Ok(None)` if the TLV type is unknown. +pub trait CustomOnionMessageContents: Writeable + MaybeReadableArgs { + /// Returns the TLV type identifying the message contents. MUST be >= 64. + fn tlv_type(&self) -> u64; +} + /// Forward control TLVs in their blinded and unblinded form. pub(super) enum ForwardControlTlvs { /// If we're sending to a blinded route, the node that constructed the blinded route has provided