From 1b9bd8f99e79320f3285b3b5342f876da255fbd0 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Fri, 27 May 2022 16:25:15 -0700 Subject: [PATCH 1/6] Add onion_messages module Git makes it difficult to add parts of a new file unless the file is already tracked. This was the quickest workaround to split up adding onion_message.rs in parts --- lightning/src/ln/mod.rs | 1 + lightning/src/ln/onion_message.rs | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 lightning/src/ln/onion_message.rs diff --git a/lightning/src/ln/mod.rs b/lightning/src/ln/mod.rs index 9522e83d18d..4de0ca17fc0 100644 --- a/lightning/src/ln/mod.rs +++ b/lightning/src/ln/mod.rs @@ -28,6 +28,7 @@ pub mod functional_test_utils; pub mod channelmanager; pub mod inbound_payment; pub mod msgs; +pub mod onion_message; pub mod peer_handler; pub mod chan_utils; pub mod features; diff --git a/lightning/src/ln/onion_message.rs b/lightning/src/ln/onion_message.rs new file mode 100644 index 00000000000..271236d3ec2 --- /dev/null +++ b/lightning/src/ln/onion_message.rs @@ -0,0 +1,10 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +//! Onion Messages: sending, receiving, forwarding, and ancillary utilities live here From a79d7412dad5a93396459ba66bb899f82bbc1a21 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Fri, 27 May 2022 16:54:40 -0700 Subject: [PATCH 2/6] Enable the construction of blinded routes Blinded routes can be provided as destinations for onion messages, when the recipient prefers to remain anonymous. We also add supporting utilities for constructing blinded path keys, and a ControlTlvs struct representing blinded payloads prior to being encoded/encrypted. These utilities and struct will be re-used in upcoming commits for sending and receiving/forwarding onion messages. --- lightning/src/ln/onion_message.rs | 92 +++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/lightning/src/ln/onion_message.rs b/lightning/src/ln/onion_message.rs index 271236d3ec2..be7877547b8 100644 --- a/lightning/src/ln/onion_message.rs +++ b/lightning/src/ln/onion_message.rs @@ -8,3 +8,95 @@ // licenses. //! Onion Messages: sending, receiving, forwarding, and ancillary utilities live here + +/// Onion messages have "control" TLVs and "data" TLVs. Control TLVs are used to control the +/// direction and routing of an onion message from hop to hop, whereas data TLVs contain the onion +/// message content itself. +pub(crate) enum ControlTlvs { + /// Control TLVs for the final recipient of an onion message. + Receive { + /// If `path_id` is `Some`, it is used to identify the blinded route that this onion message is + /// sending to. This is useful for receivers to check that said blinded route is being used in + /// the right context. + path_id: Option<[u8; 32]> + }, + /// Control TLVs for an intermediate forwarder of an onion message. + Forward { + /// The node id of the next hop in the onion message's path. + next_node_id: PublicKey, + /// Senders of onion messages have the option of specifying an overriding [`blinding_point`] + /// for forwarding nodes along the path. If this field is absent, forwarding nodes will + /// calculate the next hop's blinding point by multiplying the blinding point that they + /// received by a blinding factor. + /// + /// [`blinding_point`]: crate::ln::msgs::OnionMessage::blinding_point + next_blinding_override: Option, + } +} + +impl Writeable for ControlTlvs { + fn write(&self, writer: &mut W) -> Result<(), io::Error> {} +} + +impl Readable for ControlTlvs { + fn read(mut r: &mut R) -> Result {} +} + +/// Used to construct the blinded hops portion of a blinded route. These hops cannot be identified +/// by outside observers and thus can be used to hide the identity of the recipient. +pub struct BlindedNode { + /// The blinded node id of this hop in a blinded route. + pub blinded_node_id: PublicKey, + /// The encrypted payload intended for this hop in a blinded route. + // If we're sending to this blinded route, this payload will later be encoded into the + // [`EncryptedTlvs`] for the hop when constructing the onion packet for sending. + // + // [`EncryptedTlvs`]: EncryptedTlvs + pub encrypted_payload: Vec, +} + +/// Onion messages can be sent and received to blinded routes, which serve to hide the identity of +/// the recipient. +pub struct BlindedRoute { + /// To send to a blinded route, the sender first finds a route to the unblinded + /// `introduction_node_id`, which can unblind its [`encrypted_payload`] to find out the onion + /// message's next hop and forward it along. + /// + /// [`encrypted_payload`]: BlindedNode::encrypted_payload + pub introduction_node_id: PublicKey, + /// Creators of blinded routes supply the introduction node id's `blinding_point`, which the + /// introduction node will use in decrypting its [`encrypted_payload`] to forward the onion + /// message. + /// + /// [`encrypted_payload`]: BlindedNode::encrypted_payload + pub blinding_point: PublicKey, + /// The blinded hops of the blinded route. + pub blinded_hops: Vec, +} + +impl BlindedRoute { + /// Create a blinded route to be forwarded along `hops`. The last node pubkey in `node_pks` will + /// be the destination node. + pub fn new(node_pks: Vec, keys_manager: &K) -> Result + where K::Target: KeysInterface, + { + // calls Self::encrypt_payload + } + + fn encrypt_payload(payload: ControlTlvs, encrypted_tlvs_ss: SharedSecret) -> Vec {} +} + +#[allow(unused_assignments)] +#[inline] +fn construct_keys_callback (secp_ctx: &Secp256k1, unblinded_path: &Vec, session_priv: &SecretKey, mut callback: FType) -> Result<(), secp256k1::Error> {} + +/// Construct keys for constructing a blinded route along the given `unblinded_path`. +/// +/// Returns: `(encrypted_tlvs_keys, blinded_node_ids)` +/// where the encrypted tlvs keys are used to encrypt the blinded route's blinded payloads and the +/// blinded node ids are used to set the [`blinded_node_id`]s of the [`BlindedRoute`]. +fn construct_blinded_route_keys( + secp_ctx: &Secp256k1, unblinded_path: &Vec, session_priv: &SecretKey +) -> Result<(Vec, Vec), secp256k1::Error> { + // calls construct_keys_callback +} From 0e184cb2cd23c200bc51da3aebf31849d22714b0 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Sat, 4 Jun 2022 14:32:00 -0700 Subject: [PATCH 3/6] Add onion_messages::Packet and adapt relevant onion util We need to add a new Packet struct because onion message packet hop_data fields can be of variable length, whereas regular payment packets are always 1366 bytes. --- lightning/src/ln/onion_message.rs | 19 ++++++++++++++++ lightning/src/ln/onion_utils.rs | 38 ++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/lightning/src/ln/onion_message.rs b/lightning/src/ln/onion_message.rs index be7877547b8..e285d47c3df 100644 --- a/lightning/src/ln/onion_message.rs +++ b/lightning/src/ln/onion_message.rs @@ -9,6 +9,25 @@ //! Onion Messages: sending, receiving, forwarding, and ancillary utilities live here +#[derive(Clone, Debug, PartialEq)] +pub(crate) struct Packet { + pub(crate) version: u8, + /// We don't want to disconnect a peer just because they provide a bogus public key, so we hold a + /// Result instead of a PublicKey as we'd like. + pub(crate) public_key: Result, + // Unlike the onion packets used for payments, onion message packets can have payloads greater than 1300 bytes. + pub(crate) hop_data: Vec, + pub(crate) hmac: [u8; 32], +} + +impl Writeable for Packet { + fn write(&self, w: &mut W) -> Result<(), io::Error> {} +} + +impl ReadableArgs for Packet { + fn read(r: &mut R, len: u16) -> Result {} +} + /// Onion messages have "control" TLVs and "data" TLVs. Control TLVs are used to control the /// direction and routing of an onion message from hop to hop, whereas data TLVs contain the onion /// message content itself. diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index 1df374d7e00..6ebb5665e6e 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -185,6 +185,10 @@ fn shift_arr_right(arr: &mut [u8; ONION_DATA_LEN], amt: usize) { } } +#[inline] +fn shift_vec_right(vec: &mut Vec, amt: usize) { +} + pub(super) fn route_size_insane(payloads: &Vec) -> bool { let mut len = 0; for payload in payloads.iter() { @@ -206,7 +210,8 @@ pub(super) fn construct_onion_packet(payloads: Vec, onion_ke let mut chacha = ChaCha20::new(&prng_seed, &[0; 8]); chacha.process(&[0; ONION_DATA_LEN], &mut packet_data); - construct_onion_packet_with_init_noise(payloads, onion_keys, packet_data, associated_data) + construct_onion_packet_with_init_noise( + payloads, onion_keys, PacketData::Payment(packet_data), Some(associated_data)).try_into().unwrap() } #[cfg(test)] @@ -218,11 +223,38 @@ pub(super) fn construct_onion_packet_bogus_hopdata(payloads: Vec< let mut chacha = ChaCha20::new(&prng_seed, &[0; 8]); chacha.process(&[0; ONION_DATA_LEN], &mut packet_data); - construct_onion_packet_with_init_noise(payloads, onion_keys, packet_data, associated_data) + construct_onion_packet_with_init_noise( + payloads, onion_keys, PacketData::Payment(packet_data), Some(associated_data)).try_into().unwrap() +} + +enum PacketData { + Payment([u8; ONION_DATA_LEN]), + Message(Vec), +} + +impl PacketData { + fn len(&self) -> usize { + } +} + +impl TryFrom for onion_message::Packet { + type Error = (); + fn try_from(packet: Packet) -> Result { + } +} +impl TryFrom for msgs::OnionPacket { + type Error = (); + fn try_from(packet: Packet) -> Result { + } +} + +enum Packet { + Payment(msgs::OnionPacket), + Message(onion_message::Packet), } /// panics if route_size_insane(paylods) -fn construct_onion_packet_with_init_noise(mut payloads: Vec, onion_keys: Vec, mut packet_data: [u8; ONION_DATA_LEN], associated_data: &PaymentHash) -> msgs::OnionPacket { +fn construct_onion_packet_with_init_noise(mut payloads: Vec, onion_keys: Vec, mut packet_data: PacketData, associated_data: Option<&PaymentHash>) -> Packet { let filler = { const ONION_HOP_DATA_LEN: usize = 65; // We may decrease this eventually after TLV is common let mut res = Vec::with_capacity(ONION_HOP_DATA_LEN * (payloads.len() - 1)); From 6daa5337d3f9222bbe495167b0e127390ca467ce Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Sat, 4 Jun 2022 14:39:22 -0700 Subject: [PATCH 4/6] Add empty OnionMessenger, OnionMessageHandler trait, and boilerplate This fills in the boilerplate needed to hook up the OnionMessenger to send and receive messages through the PeerManager. It also sets up the OnionMessenger and its struct fields. --- lightning-background-processor/src/lib.rs | 4 +- lightning-net-tokio/src/lib.rs | 15 ++++--- lightning/src/ln/msgs.rs | 25 +++++++++++ lightning/src/ln/onion_message.rs | 55 +++++++++++++++++++++++ lightning/src/ln/peer_handler.rs | 53 ++++++++++++++-------- lightning/src/ln/wire.rs | 7 ++- lightning/src/util/events.rs | 7 +++ 7 files changed, 140 insertions(+), 26 deletions(-) diff --git a/lightning-background-processor/src/lib.rs b/lightning-background-processor/src/lib.rs index 2e947a7041a..803c70bc30f 100644 --- a/lightning-background-processor/src/lib.rs +++ b/lightning-background-processor/src/lib.rs @@ -178,6 +178,7 @@ impl BackgroundProcessor { P: 'static + Deref + Send + Sync, Descriptor: 'static + SocketDescriptor + Send + Sync, CMH: 'static + Deref + Send + Sync, + OMH: 'static + Deref + Send + Sync, RMH: 'static + Deref + Send + Sync, EH: 'static + EventHandler + Send, PS: 'static + Deref + Send, @@ -185,7 +186,7 @@ impl BackgroundProcessor { CM: 'static + Deref> + Send + Sync, PGS: 'static + Deref> + Send + Sync, UMH: 'static + Deref + Send + Sync, - PM: 'static + Deref> + Send + Sync, + PM: 'static + Deref> + Send + Sync, S: 'static + Deref + Send + Sync, SC: WriteableScore<'a>, RGS: 'static + Deref> + Send @@ -204,6 +205,7 @@ impl BackgroundProcessor { L::Target: 'static + Logger, P::Target: 'static + Persist, CMH::Target: 'static + ChannelMessageHandler, + OMH::Target: 'static + OnionMessageHandler, RMH::Target: 'static + RoutingMessageHandler, UMH::Target: 'static + CustomMessageHandler, PS::Target: 'static + Persister<'a, Signer, CW, T, K, F, L, SC>, diff --git a/lightning-net-tokio/src/lib.rs b/lightning-net-tokio/src/lib.rs index 2ac10762b04..d96afa82eec 100644 --- a/lightning-net-tokio/src/lib.rs +++ b/lightning-net-tokio/src/lib.rs @@ -120,9 +120,10 @@ struct Connection { id: u64, } impl Connection { - async fn poll_event_process(peer_manager: Arc, Arc, Arc, Arc>>, mut event_receiver: mpsc::Receiver<()>) where + async fn poll_event_process(peer_manager: Arc, Arc, Arc, Arc, Arc>>, mut event_receiver: mpsc::Receiver<()>) where CMH: ChannelMessageHandler + 'static + Send + Sync, RMH: RoutingMessageHandler + 'static + Send + Sync, + OMH: OnionMessageHandler + 'static + Send + Sync, L: Logger + 'static + ?Sized + Send + Sync, UMH: CustomMessageHandler + 'static + Send + Sync { loop { @@ -133,9 +134,10 @@ impl Connection { } } - async fn schedule_read(peer_manager: Arc, Arc, Arc, Arc>>, us: Arc>, mut reader: io::ReadHalf, mut read_wake_receiver: mpsc::Receiver<()>, mut write_avail_receiver: mpsc::Receiver<()>) where + async fn schedule_read(peer_manager: Arc, Arc, Arc, Arc, Arc>>, us: Arc>, mut reader: io::ReadHalf, mut read_wake_receiver: mpsc::Receiver<()>, mut write_avail_receiver: mpsc::Receiver<()>) where CMH: ChannelMessageHandler + 'static + Send + Sync, RMH: RoutingMessageHandler + 'static + Send + Sync, + OMH: OnionMessageHandler + 'static + Send + Sync, L: Logger + 'static + ?Sized + Send + Sync, UMH: CustomMessageHandler + 'static + Send + Sync { // Create a waker to wake up poll_event_process, above @@ -255,9 +257,10 @@ fn get_addr_from_stream(stream: &StdTcpStream) -> Option { /// The returned future will complete when the peer is disconnected and associated handling /// futures are freed, though, because all processing futures are spawned with tokio::spawn, you do /// not need to poll the provided future in order to make progress. -pub fn setup_inbound(peer_manager: Arc, Arc, Arc, Arc>>, stream: StdTcpStream) -> impl std::future::Future where +pub fn setup_inbound(peer_manager: Arc, Arc, Arc, Arc, Arc>>, stream: StdTcpStream) -> impl std::future::Future where CMH: ChannelMessageHandler + 'static + Send + Sync, RMH: RoutingMessageHandler + 'static + Send + Sync, + OMH: OnionMessageHandler + 'static + Send + Sync, L: Logger + 'static + ?Sized + Send + Sync, UMH: CustomMessageHandler + 'static + Send + Sync { let remote_addr = get_addr_from_stream(&stream); @@ -297,9 +300,10 @@ pub fn setup_inbound(peer_manager: Arc(peer_manager: Arc, Arc, Arc, Arc>>, their_node_id: PublicKey, stream: StdTcpStream) -> impl std::future::Future where +pub fn setup_outbound(peer_manager: Arc, Arc, Arc, Arc, Arc>>, their_node_id: PublicKey, stream: StdTcpStream) -> impl std::future::Future where CMH: ChannelMessageHandler + 'static + Send + Sync, RMH: RoutingMessageHandler + 'static + Send + Sync, + OMH: OnionMessageHandler + 'static + Send + Sync, L: Logger + 'static + ?Sized + Send + Sync, UMH: CustomMessageHandler + 'static + Send + Sync { let remote_addr = get_addr_from_stream(&stream); @@ -368,9 +372,10 @@ pub fn setup_outbound(peer_manager: Arc(peer_manager: Arc, Arc, Arc, Arc>>, their_node_id: PublicKey, addr: SocketAddr) -> Option> where +pub async fn connect_outbound(peer_manager: Arc, Arc, Arc, Arc, Arc>>, their_node_id: PublicKey, addr: SocketAddr) -> Option> where CMH: ChannelMessageHandler + 'static + Send + Sync, RMH: RoutingMessageHandler + 'static + Send + Sync, + OMH: OnionMessageHandler + 'static + Send + Sync, L: Logger + 'static + ?Sized + Send + Sync, UMH: CustomMessageHandler + 'static + Send + Sync { if let Ok(Ok(stream)) = time::timeout(Duration::from_secs(10), async { TcpStream::connect(&addr).await.map(|s| s.into_std().unwrap()) }).await { diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 0e5b2e07e7a..65310773b8c 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -304,6 +304,16 @@ pub struct UpdateAddHTLC { pub(crate) onion_routing_packet: OnionPacket, } + /// An onion message to be sent or received from a peer +#[derive(Clone, Debug, PartialEq)] +pub struct OnionMessage { + /// This blinding point is used in the shared secret that is used to decrypt the onion message + /// payload's `encrypted_data` field. + pub(crate) blinding_point: PublicKey, + pub(crate) len: u16, + pub(crate) onion_routing_packet: onion_message::Packet, +} + /// An update_fulfill_htlc message to be sent or received from a peer #[derive(Clone, Debug, PartialEq)] pub struct UpdateFulfillHTLC { @@ -912,6 +922,12 @@ pub trait RoutingMessageHandler : MessageSendEventsProvider { fn handle_query_short_channel_ids(&self, their_node_id: &PublicKey, msg: QueryShortChannelIds) -> Result<(), LightningError>; } +/// A trait to describe an object that can receive onion messages. +pub trait OnionMessageHandler : MessageSendEventsProvider { + /// Handle an incoming onion_message message from the given peer. + fn handle_onion_message(&self, their_node_id: &PublicKey, msg: &OnionMessage); +} + mod fuzzy_internal_msgs { use prelude::*; use ln::{PaymentPreimage, PaymentSecret}; @@ -1304,6 +1320,15 @@ impl_writeable_msg!(UpdateAddHTLC, { onion_routing_packet }, {}); +impl Readable for OnionMessage { + fn read(r: &mut R) -> Result { + } +} + +impl Writeable for OnionMessage { + } +} + impl Writeable for FinalOnionHopData { fn write(&self, w: &mut W) -> Result<(), io::Error> { self.payment_secret.0.write(w)?; diff --git a/lightning/src/ln/onion_message.rs b/lightning/src/ln/onion_message.rs index e285d47c3df..a98e86e87c5 100644 --- a/lightning/src/ln/onion_message.rs +++ b/lightning/src/ln/onion_message.rs @@ -105,6 +105,54 @@ impl BlindedRoute { fn encrypt_payload(payload: ControlTlvs, encrypted_tlvs_ss: SharedSecret) -> Vec {} } +/// A sender, receiver and forwarder of onion messages. In upcoming releases, this object will be +/// used to retrieve invoices and fulfill invoice requests from offers. +pub struct OnionMessenger + where K::Target: KeysInterface, + L::Target: Logger, +{ + keys_manager: K, + logger: L, + pending_msg_events: Mutex>, + secp_ctx: Secp256k1, + // Coming soon: + // invoice_handler: InvoiceHandler, + // custom_handler: CustomHandler, // handles custom onion messages +} + +impl OnionMessenger + where K::Target: KeysInterface, + L::Target: Logger, +{ + /// 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 { + let mut secp_ctx = Secp256k1::new(); + secp_ctx.seeded_randomize(&keys_manager.get_secure_random_bytes()); + OnionMessenger { + keys_manager, + pending_msg_events: Mutex::new(Vec::new()), + secp_ctx, + logger, + } + } +} + +impl OnionMessageHandler for OnionMessenger + where K::Target: KeysInterface, + L::Target: Logger, +{ + fn handle_onion_message(&self, _peer_node_id: &PublicKey, msg: &msgs::OnionMessage) {} +} + +impl MessageSendEventsProvider for OnionMessenger + where K::Target: KeysInterface, + L::Target: Logger, +{ + fn get_and_clear_pending_msg_events(&self) -> Vec { + } +} + #[allow(unused_assignments)] #[inline] fn construct_keys_callback (secp_ctx: &Secp256k1, unblinded_path: &Vec, session_priv: &SecretKey, mut callback: FType) -> Result<(), secp256k1::Error> {} @@ -119,3 +167,10 @@ fn construct_blinded_route_keys ) -> Result<(Vec, Vec), secp256k1::Error> { // calls construct_keys_callback } + +/// Useful for simplifying the parameters of [`SimpleArcChannelManager`] and +/// [`SimpleArcPeerManager`]. See their docs for more details. +pub type SimpleArcOnionMessenger = OnionMessenger, Arc>; +/// Useful for simplifying the parameters of [`SimpleRefChannelManager`] and +/// [`SimpleRefPeerManager`]. See their docs for more details. +pub type SimpleRefOnionMessenger<'a, 'b, L> = OnionMessenger; diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index d86cddb13db..03f36d44d0c 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peer_handler.rs @@ -76,6 +76,9 @@ impl RoutingMessageHandler for IgnoringMessageHandler { fn handle_query_channel_range(&self, _their_node_id: &PublicKey, _msg: msgs::QueryChannelRange) -> Result<(), LightningError> { Ok(()) } fn handle_query_short_channel_ids(&self, _their_node_id: &PublicKey, _msg: msgs::QueryShortChannelIds) -> Result<(), LightningError> { Ok(()) } } +impl OnionMessageHandler for IgnoringMessageHandler { + fn handle_onion_message(&self, _their_node_id: &PublicKey, _msg: &msgs::OnionMessage) {} +} impl Deref for IgnoringMessageHandler { type Target = IgnoringMessageHandler; fn deref(&self) -> &Self { self } @@ -199,9 +202,11 @@ impl Deref for ErroringMessageHandler { } /// Provides references to trait impls which handle different types of messages. -pub struct MessageHandler where +pub struct MessageHandler where CM::Target: ChannelMessageHandler, - RM::Target: RoutingMessageHandler { + RM::Target: RoutingMessageHandler, + OM::Target: OnionMessageHandler, +{ /// A message handler which handles messages specific to channels. Usually this is just a /// [`ChannelManager`] object or an [`ErroringMessageHandler`]. /// @@ -212,6 +217,12 @@ pub struct MessageHandler where /// /// [`P2PGossipSync`]: crate::routing::gossip::P2PGossipSync pub route_handler: RM, + + /// A message handler which handles onion messages. Using this is just an [`OnionMessenger`] object + /// or an [`IgnoringMessageHandler`]. + /// + /// [`OnionMessenger`]: crate::ln::onion_messages::OnionMessenger + pub onion_message_handler: OM, } /// Provides an object which can be used to send data to and which uniquely identifies a connection @@ -387,7 +398,7 @@ impl Peer { /// issues such as overly long function definitions. /// /// (C-not exported) as Arcs don't make sense in bindings -pub type SimpleArcPeerManager = PeerManager>, Arc, Arc, Arc>>, Arc, Arc>; +pub type SimpleArcPeerManager = PeerManager>, Arc, Arc, Arc>>, Arc>, Arc, Arc>; /// SimpleRefPeerManager is a type alias for a PeerManager reference, and is the reference /// counterpart to the SimpleArcPeerManager type alias. Use this type by default when you don't @@ -397,7 +408,7 @@ pub type SimpleArcPeerManager = PeerManager = PeerManager, &'e P2PGossipSync<&'g NetworkGraph, &'h C, &'f L>, &'f L, IgnoringMessageHandler>; +pub type SimpleRefPeerManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, SD, M, T, F, C, L> = PeerManager, &'e P2PGossipSync<&'g NetworkGraph, &'h C, &'f L>, SimpleRefOnionMessenger<'c, 'f, L>, &'f L, IgnoringMessageHandler>; /// A PeerManager manages a set of peers, described by their [`SocketDescriptor`] and marshalls /// socket events into messages which it passes on to its [`MessageHandler`]. @@ -418,12 +429,13 @@ pub type SimpleRefPeerManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, SD, M, T, F, C, L> /// you're using lightning-net-tokio. /// /// [`read_event`]: PeerManager::read_event -pub struct PeerManager where +pub struct PeerManager where CM::Target: ChannelMessageHandler, RM::Target: RoutingMessageHandler, + OM::Target: OnionMessageHandler, L::Target: Logger, CMH::Target: CustomMessageHandler { - message_handler: MessageHandler, + message_handler: MessageHandler, /// Connection state for each connected peer - we have an outer read-write lock which is taken /// as read while we're doing processing for a peer and taken write when a peer is being added /// or removed. @@ -482,31 +494,33 @@ macro_rules! encode_msg { }} } -impl PeerManager where +impl PeerManager where CM::Target: ChannelMessageHandler, + OM::Target: OnionMessageHandler, L::Target: Logger { - /// Constructs a new PeerManager with the given ChannelMessageHandler. No routing message - /// handler is used and network graph messages are ignored. + /// Constructs a new PeerManager with the given ChannelMessageHandler and OnionMessageHandler. No + /// routing message handler is used and network graph messages are ignored. /// /// ephemeral_random_data is used to derive per-connection ephemeral keys and must be /// cryptographically secure random bytes. /// /// (C-not exported) as we can't export a PeerManager with a dummy route handler - pub fn new_channel_only(channel_message_handler: CM, our_node_secret: SecretKey, ephemeral_random_data: &[u8; 32], logger: L) -> Self { + pub fn new_channel_only(channel_message_handler: CM, onion_message_handler: OM, our_node_secret: SecretKey, ephemeral_random_data: &[u8; 32], logger: L) -> Self { Self::new(MessageHandler { chan_handler: channel_message_handler, route_handler: IgnoringMessageHandler{}, + onion_message_handler, }, our_node_secret, ephemeral_random_data, logger, IgnoringMessageHandler{}) } } -impl PeerManager where +impl PeerManager where RM::Target: RoutingMessageHandler, L::Target: Logger { - /// Constructs a new PeerManager with the given RoutingMessageHandler. No channel message - /// handler is used and messages related to channels will be ignored (or generate error - /// messages). Note that some other lightning implementations time-out connections after some - /// time if no channel is built with the peer. + /// Constructs a new PeerManager with the given RoutingMessageHandler. No channel message handler + /// or onion message handler is used and messages related to channels will be ignored (or generate + /// error messages). Note that some other lightning implementations time-out connections after + /// some time if no channel is built with the peer. /// /// ephemeral_random_data is used to derive per-connection ephemeral keys and must be /// cryptographically secure random bytes. @@ -561,15 +575,16 @@ fn filter_addresses(ip_address: Option) -> Option { } } -impl PeerManager where +impl PeerManager where CM::Target: ChannelMessageHandler, RM::Target: RoutingMessageHandler, + OM::Target: OnionMessageHandler, L::Target: Logger, CMH::Target: CustomMessageHandler { /// Constructs a new PeerManager with the given message handlers and node_id secret key /// ephemeral_random_data is used to derive per-connection ephemeral keys and must be /// cryptographically secure random bytes. - pub fn new(message_handler: MessageHandler, our_node_secret: SecretKey, ephemeral_random_data: &[u8; 32], logger: L, custom_message_handler: CMH) -> Self { + pub fn new(message_handler: MessageHandler, our_node_secret: SecretKey, ephemeral_random_data: &[u8; 32], logger: L, custom_message_handler: CMH) -> Self { let mut ephemeral_key_midstate = Sha256::engine(); ephemeral_key_midstate.input(ephemeral_random_data); @@ -1904,7 +1919,7 @@ mod tests { cfgs } - fn create_network<'a>(peer_count: usize, cfgs: &'a Vec) -> Vec> { + fn create_network<'a>(peer_count: usize, cfgs: &'a Vec) -> Vec> { let mut peers = Vec::new(); for i in 0..peer_count { let node_secret = SecretKey::from_slice(&[42 + i as u8; 32]).unwrap(); @@ -1917,7 +1932,7 @@ mod tests { peers } - fn establish_connection<'a>(peer_a: &PeerManager, peer_b: &PeerManager) -> (FileDescriptor, FileDescriptor) { + fn establish_connection<'a>(peer_a: &PeerManager, peer_b: &PeerManager) -> (FileDescriptor, FileDescriptor) { let secp_ctx = Secp256k1::new(); let a_id = PublicKey::from_secret_key(&secp_ctx, &peer_a.our_node_secret); let mut fd_a = FileDescriptor { fd: 1, outbound_data: Arc::new(Mutex::new(Vec::new())) }; diff --git a/lightning/src/ln/wire.rs b/lightning/src/ln/wire.rs index cbf5c77d600..f3a2cbcf9e6 100644 --- a/lightning/src/ln/wire.rs +++ b/lightning/src/ln/wire.rs @@ -9,7 +9,7 @@ //! Wire encoding/decoding for Lightning messages according to [BOLT #1], and for //! custom message through the [`CustomMessageReader`] trait. -//! +//! //! [BOLT #1]: https://github.com/lightning/bolts/blob/master/01-messaging.md use io; @@ -60,6 +60,7 @@ pub(crate) enum Message where T: core::fmt::Debug + Type + TestEq { ChannelReady(msgs::ChannelReady), Shutdown(msgs::Shutdown), ClosingSigned(msgs::ClosingSigned), + OnionMessage(msgs::OnionMessage), UpdateAddHTLC(msgs::UpdateAddHTLC), UpdateFulfillHTLC(msgs::UpdateFulfillHTLC), UpdateFailHTLC(msgs::UpdateFailHTLC), @@ -344,6 +345,10 @@ impl Encode for msgs::ClosingSigned { const TYPE: u16 = 39; } +impl Encode for msgs::OnionMessage { + const TYPE: u16 = 513; +} + impl Encode for msgs::UpdateAddHTLC { const TYPE: u16 = 128; } diff --git a/lightning/src/util/events.rs b/lightning/src/util/events.rs index a2794c329a2..3e1a9f485e0 100644 --- a/lightning/src/util/events.rs +++ b/lightning/src/util/events.rs @@ -1030,6 +1030,13 @@ pub enum MessageSendEvent { /// The gossip_timestamp_filter which should be sent. msg: msgs::GossipTimestampFilter, }, + /// Sends an onion message. + SendOnionMessage { + /// The node_id of this message recipient + node_id: PublicKey, + /// The onion message which should be sent. + msg: msgs::OnionMessage, + }, } /// A trait indicating an object may generate message send events From e3ececcfa9bdecd0811427f3d1eb24040ef060c1 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Sat, 4 Jun 2022 14:43:49 -0700 Subject: [PATCH 5/6] Support sending onion messages This adds several utilities in service of then adding OnionMessenger::send_onion_message, which can send to either an unblinded pubkey or a blinded route. Sending custom TLVs and sending an onion message containing a reply path are not yet supported. --- lightning/src/ln/onion_message.rs | 83 ++++++++++++++++++++++++++++++- lightning/src/ln/onion_utils.rs | 3 ++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/lightning/src/ln/onion_message.rs b/lightning/src/ln/onion_message.rs index a98e86e87c5..41575536bea 100644 --- a/lightning/src/ln/onion_message.rs +++ b/lightning/src/ln/onion_message.rs @@ -9,6 +9,9 @@ //! Onion Messages: sending, receiving, forwarding, and ancillary utilities live here +pub(crate) const SMALL_PACKET_HOP_DATA_LEN: usize = 1300; +pub(crate) const BIG_PACKET_HOP_DATA_LEN: usize = 32768; + #[derive(Clone, Debug, PartialEq)] pub(crate) struct Packet { pub(crate) version: u8, @@ -20,6 +23,13 @@ pub(crate) struct Packet { pub(crate) hmac: [u8; 32], } +impl Packet { + fn len(&self) -> u16 { + // 32 (hmac) + 33 (public_key) + 1 (version) = 66 + self.hop_data.len() as u16 + 66 + } +} + impl Writeable for Packet { fn write(&self, w: &mut W) -> Result<(), io::Error> {} } @@ -28,6 +38,46 @@ impl ReadableArgs for Packet { fn read(r: &mut R, len: u16) -> Result {} } +/// The payload of an onion message. +pub(crate) struct Payload { + /// Onion message payloads contain an encrypted TLV stream, containing both "control" TLVs and + /// sometimes user-provided custom "data" TLVs. See [`EncryptedTlvs`] for more information. + encrypted_tlvs: EncryptedTlvs, + // Coming soon: + // * custom TLVs + // * `message: Message` field + // * `reply_path: Option` field +} + +// Coming soon: +// enum Message { +// InvoiceRequest(InvoiceRequest), +// Invoice(Invoice), +// InvoiceError(InvoiceError), +// CustomMessage, +// } + +/// We want to avoid encoding and encrypting separately in order to avoid an intermediate Vec, thus +/// we encode and encrypt at the same time using the `SharedSecret` here. +impl Writeable for (Payload, SharedSecret) { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + } +} + +/// Onion messages contain an encrypted TLV stream. This can be supplied by someone else, in the +/// case that we're sending to a blinded route, or created by us if we're constructing payloads for +/// unblinded hops in the onion message's path. +pub(crate) enum EncryptedTlvs { + /// If we're sending to a blinded route, the node that constructed the blinded route has provided + /// our onion message's `EncryptedTlvs`, already encrypted and encoded into bytes. + Blinded(Vec), + /// If we're receiving an onion message or constructing an onion message to send through any + /// unblinded nodes, we'll need to construct the onion message's `EncryptedTlvs` in their + /// unblinded state to avoid encoding them into an intermediate `Vec`. + // Below will later have an additional Vec + Unblinded(ControlTlvs), +} + /// Onion messages have "control" TLVs and "data" TLVs. Control TLVs are used to control the /// direction and routing of an onion message from hop to hop, whereas data TLVs contain the onion /// message content itself. @@ -105,6 +155,18 @@ impl BlindedRoute { fn encrypt_payload(payload: ControlTlvs, encrypted_tlvs_ss: SharedSecret) -> Vec {} } +/// The destination of an onion message. +pub enum Destination { + /// We're sending this onion message to a node. + Node(PublicKey), + /// We're sending this onion message to a blinded route. + BlindedRoute(BlindedRoute), +} + +impl Destination { + fn num_hops(&self) -> usize { +} + /// A sender, receiver and forwarder of onion messages. In upcoming releases, this object will be /// used to retrieve invoices and fulfill invoice requests from offers. pub struct OnionMessenger @@ -136,6 +198,10 @@ impl OnionMessenger logger, } } + + /// Send an empty onion message to `destination`, routing it through `intermediate_nodes`. + pub fn send_onion_message(&self, intermediate_nodes: Vec, destination: Destination) -> Result<(), secp256k1::Error> { + } } impl OnionMessageHandler for OnionMessenger @@ -153,9 +219,13 @@ impl MessageSendEventsProvider for OnionMessen } } +/// Build an onion message's payloads for encoding in the onion packet. +fn build_payloads(intermediate_nodes: Vec, destination: Destination, mut encrypted_tlvs_keys: Vec) -> Vec<(Payload, SharedSecret)> { +} + #[allow(unused_assignments)] #[inline] -fn construct_keys_callback (secp_ctx: &Secp256k1, unblinded_path: &Vec, session_priv: &SecretKey, mut callback: FType) -> Result<(), secp256k1::Error> {} +fn construct_keys_callback (secp_ctx: &Secp256k1, unblinded_path: &Vec, destination: Option<&Destination>, session_priv: &SecretKey, mut callback: FType) -> Result<(), secp256k1::Error> {} /// Construct keys for constructing a blinded route along the given `unblinded_path`. /// @@ -168,6 +238,17 @@ fn construct_blinded_route_keys // calls construct_keys_callback } +/// Construct keys for sending an onion message along the given `path`. +/// +/// Returns: `(encrypted_tlvs_keys, onion_packet_keys)` +/// where the encrypted tlvs keys are used to encrypt the [`EncryptedTlvs`] of the onion message and the +/// onion packet keys are used to encrypt the onion packet. +fn construct_sending_keys( + secp_ctx: &Secp256k1, unblinded_path: &Vec, destination: &Destination, session_priv: &SecretKey +) -> Result<(Vec, Vec), secp256k1::Error> { + // calls construct_keys_callback +} + /// Useful for simplifying the parameters of [`SimpleArcChannelManager`] and /// [`SimpleArcPeerManager`]. See their docs for more details. pub type SimpleArcOnionMessenger = OnionMessenger, Arc>; diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index 6ebb5665e6e..f6523ab5f82 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -214,6 +214,9 @@ pub(super) fn construct_onion_packet(payloads: Vec, onion_ke payloads, onion_keys, PacketData::Payment(packet_data), Some(associated_data)).try_into().unwrap() } +pub(super) fn construct_onion_message_packet(payloads: Vec<(onion_message::Payload, SharedSecret)>, onion_keys: Vec, prng_seed: [u8; 32]) -> onion_message::Packet { +} + #[cfg(test)] // Used in testing to write bogus OnionHopDatas, which is otherwise not representable in // msgs::OnionHopData. From 6916934bc25a8657f6aee45af4e988aa0353e297 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Sat, 4 Jun 2022 14:46:13 -0700 Subject: [PATCH 6/6] Implement receiving and forwarding onion messages This required adapting `onion_utils::decode_next_hop` to work for both payments and onion messages. Currently we just print out the path_id of any onion messages we receive. In the future, these received onion messages will be redirected to their respective handlers: i.e. an invoice_request will go to an InvoiceHandler, custom onion messages will go to a custom handler, etc. --- lightning/src/ln/channelmanager.rs | 4 +-- lightning/src/ln/onion_message.rs | 7 ++++++ lightning/src/ln/onion_utils.rs | 40 ++++++++++++++++++++++++++++-- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index c787cedfad3..781b8e25910 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -2139,7 +2139,7 @@ impl ChannelMana } } - let next_hop = match onion_utils::decode_next_hop(shared_secret, &msg.onion_routing_packet.hop_data[..], msg.onion_routing_packet.hmac, msg.payment_hash) { + let next_hop = match onion_utils::decode_next_payment_hop(shared_secret, &msg.onion_routing_packet.hop_data[..], msg.onion_routing_packet.hmac, msg.payment_hash) { Ok(res) => res, Err(onion_utils::OnionDecodeErr::Malformed { err_msg, err_code }) => { return_malformed_err!(err_msg, err_code); @@ -2967,7 +2967,7 @@ impl ChannelMana let phantom_secret_res = self.keys_manager.get_node_secret(Recipient::PhantomNode); if phantom_secret_res.is_ok() && fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, short_chan_id) { let phantom_shared_secret = SharedSecret::new(&onion_packet.public_key.unwrap(), &phantom_secret_res.unwrap()).secret_bytes(); - let next_hop = match onion_utils::decode_next_hop(phantom_shared_secret, &onion_packet.hop_data, onion_packet.hmac, payment_hash) { + let next_hop = match onion_utils::decode_next_payment_hop(phantom_shared_secret, &onion_packet.hop_data, onion_packet.hmac, payment_hash) { Ok(res) => res, Err(onion_utils::OnionDecodeErr::Malformed { err_msg, err_code }) => { let sha256_of_onion = Sha256::hash(&onion_packet.hop_data).into_inner(); diff --git a/lightning/src/ln/onion_message.rs b/lightning/src/ln/onion_message.rs index 41575536bea..966d2f6b5db 100644 --- a/lightning/src/ln/onion_message.rs +++ b/lightning/src/ln/onion_message.rs @@ -64,6 +64,13 @@ impl Writeable for (Payload, SharedSecret) { } } +/// Reads of `Payload`s are parameterized by the `rho` of a `SharedSecret`, which is used to decrypt +/// the onion message payload's `encrypted_data` field. +impl ReadableArgs for Payload { + fn read(mut r: &mut R, encrypted_tlvs_ss: SharedSecret) -> Result { + } +} + /// Onion messages contain an encrypted TLV stream. This can be supplied by someone else, in the /// case that we're sending to a blinded route, or created by us if we're constructing payloads for /// unblinded hops in the onion message's path. diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index f6523ab5f82..eb2b3824db3 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -558,7 +558,37 @@ pub(super) fn process_onion_failure(secp_ctx: & } else { unreachable!(); } } -/// Data decrypted from the onion payload. +/// Used in the decoding of inbound payments' and onion messages' routing packets. This enum allows +/// us to use `decode_next_hop` to return the payloads and next hop packet bytes of both payments +/// and onion messages. +enum Payload { + /// This payload was for an incoming payment. + Payment(msgs::OnionHopData), + /// This payload was for an incoming onion message. + Message(onion_message::Payload), +} + +enum NextPacketBytes { + Payment([u8; 20*65]), + Message(Vec), +} + +/// Data decrypted from an onion message's onion payload. +pub(crate) enum MessageHop { + /// This onion payload was for us, not for forwarding to a next-hop. + Receive(onion_message::Payload), + /// This onion payload needs to be forwarded to a next-hop. + Forward { + /// Onion payload data used in forwarding the onion message. + next_hop_data: onion_message::Payload, + /// HMAC of the next hop's onion packet. + next_hop_hmac: [u8; 32], + /// Bytes of the onion packet we're forwarding. + new_packet_bytes: Vec, + }, +} + +/// Data decrypted from a payment's onion payload. pub(crate) enum Hop { /// This onion payload was for us, not for forwarding to a next-hop. Contains information for /// verifying the incoming payment. @@ -588,7 +618,13 @@ pub(crate) enum OnionDecodeErr { }, } -pub(crate) fn decode_next_hop(shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], payment_hash: PaymentHash) -> Result { +pub(crate) fn decode_next_message_hop(shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], encrypted_tlvs_ss: SharedSecret) -> Result { +} + +pub(crate) fn decode_next_payment_hop(shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], payment_hash: PaymentHash) -> Result { +} + +fn decode_next_hop(shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], payment_hash: Option, encrypted_tlv_ss: Option) -> Result<(Payload, Option<([u8; 32], NextPacketBytes)>), OnionDecodeErr> { let (rho, mu) = gen_rho_mu_from_shared_secret(&shared_secret); let mut hmac = HmacEngine::::new(&mu); hmac.input(hop_data);