From: Jeffrey Czyz Date: Sat, 2 Mar 2024 18:28:20 +0000 (-0600) Subject: Generalize onion message ForwardTlvs::next_node_id X-Git-Tag: v0.0.123-beta~10^2~9 X-Git-Url: http://git.bitcoin.ninja/index.cgi?p=rust-lightning;a=commitdiff_plain;h=d85e0d619a5225cbf73bdf7cac7f400529cb6a73 Generalize onion message ForwardTlvs::next_node_id Allow either using a node id or a short channel id when forwarding an onion message. This allows for a more compact representation of blinded paths, which is advantageous for reducing offer QR code size. Follow-up commits will implement handling the short channel id case as it requires looking up the destination node id. --- diff --git a/lightning/src/blinded_path/message.rs b/lightning/src/blinded_path/message.rs index bdcbd772..967ef612 100644 --- a/lightning/src/blinded_path/message.rs +++ b/lightning/src/blinded_path/message.rs @@ -19,8 +19,8 @@ use core::ops::Deref; /// TLVs to encode in an intermediate onion message packet's hop data. When provided in a blinded /// route, they are encoded into [`BlindedHop::encrypted_payload`]. pub(crate) struct ForwardTlvs { - /// The node id of the next hop in the onion message's path. - pub(crate) next_node_id: PublicKey, + /// The next hop in the onion message's path. + pub(crate) next_hop: NextHop, /// Senders to a blinded path use this value to concatenate the route they find to the /// introduction node with the blinded path. pub(crate) next_blinding_override: Option, @@ -34,11 +34,25 @@ pub(crate) struct ReceiveTlvs { pub(crate) path_id: Option<[u8; 32]>, } +/// The next hop to forward the onion message along its path. +#[derive(Debug)] +pub enum NextHop { + /// The node id of the next hop. + NodeId(PublicKey), + /// The short channel id leading to the next hop. + ShortChannelId(u64), +} + impl Writeable for ForwardTlvs { fn write(&self, writer: &mut W) -> Result<(), io::Error> { + let (next_node_id, short_channel_id) = match self.next_hop { + NextHop::NodeId(pubkey) => (Some(pubkey), None), + NextHop::ShortChannelId(scid) => (None, Some(scid)), + }; // TODO: write padding encode_tlv_stream!(writer, { - (4, self.next_node_id, required), + (2, short_channel_id, option), + (4, next_node_id, option), (8, self.next_blinding_override, option) }); Ok(()) @@ -61,9 +75,8 @@ pub(super) fn blinded_hops( ) -> Result, secp256k1::Error> { let blinded_tlvs = unblinded_path.iter() .skip(1) // The first node's TLVs contains the next node's pubkey - .map(|pk| { - ControlTlvs::Forward(ForwardTlvs { next_node_id: *pk, next_blinding_override: None }) - }) + .map(|pk| ForwardTlvs { next_hop: NextHop::NodeId(*pk), next_blinding_override: None }) + .map(|tlvs| ControlTlvs::Forward(tlvs)) .chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs { path_id: None }))); utils::construct_blinded_hops(secp_ctx, unblinded_path.iter(), blinded_tlvs, session_priv) @@ -80,9 +93,13 @@ pub(crate) fn advance_path_by_one { + Ok(ChaChaPolyReadAdapter { + readable: ControlTlvs::Forward(ForwardTlvs { next_hop, next_blinding_override }) + }) => { + let mut next_node_id = match next_hop { + NextHop::NodeId(pubkey) => pubkey, + NextHop::ShortChannelId(_) => todo!(), + }; let mut new_blinding_point = match next_blinding_override { Some(blinding_point) => blinding_point, None => { diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index e213bcbb..85e19cc5 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -16,7 +16,7 @@ use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey}; use crate::blinded_path::BlindedPath; -use crate::blinded_path::message::{advance_path_by_one, ForwardTlvs, ReceiveTlvs}; +use crate::blinded_path::message::{advance_path_by_one, ForwardTlvs, NextHop, ReceiveTlvs}; use crate::blinded_path::utils; use crate::events::{Event, EventHandler, EventsProvider}; use crate::sign::{EntropySource, NodeSigner, Recipient}; @@ -538,7 +538,7 @@ pub trait CustomOnionMessageHandler { #[derive(Debug)] pub enum PeeledOnion { /// Forwarded onion, with the next node id and a new onion - Forward(PublicKey, OnionMessage), + Forward(NextHop, OnionMessage), /// Received onion message, with decrypted contents, path_id, and reply path Receive(ParsedOnionMessageContents, Option<[u8; 32]>, Option) } @@ -647,9 +647,9 @@ where Ok(PeeledOnion::Receive(message, path_id, reply_path)) }, Ok((Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs { - next_node_id, next_blinding_override + next_hop, next_blinding_override })), Some((next_hop_hmac, new_packet_bytes)))) => { - // TODO: we need to check whether `next_node_id` is our node, in which case this is a dummy + // TODO: we need to check whether `next_hop` is our node, in which case this is a dummy // blinded hop and this onion message is destined for us. In this situation, we should keep // unwrapping the onion layers to get to the final payload. Since we don't have the option // of creating blinded paths with dummy hops currently, we should be ok to not handle this @@ -685,7 +685,7 @@ where onion_routing_packet: outgoing_packet, }; - Ok(PeeledOnion::Forward(next_node_id, onion_message)) + Ok(PeeledOnion::Forward(next_hop, onion_message)) }, Err(e) => { log_trace!(logger, "Errored decoding onion message packet: {:?}", e); @@ -961,7 +961,12 @@ where }, } }, - Ok(PeeledOnion::Forward(next_node_id, onion_message)) => { + Ok(PeeledOnion::Forward(next_hop, onion_message)) => { + let next_node_id = match next_hop { + NextHop::NodeId(pubkey) => pubkey, + NextHop::ShortChannelId(_) => todo!(), + }; + let mut message_recipients = self.message_recipients.lock().unwrap(); if outbound_buffer_full(&next_node_id, &message_recipients) { log_trace!( @@ -1146,7 +1151,7 @@ fn packet_payloads_and_keys(r: &mut R) -> Result { _init_and_read_tlv_stream!(r, { (1, _padding, option), - (2, _short_channel_id, option), + (2, short_channel_id, option), (4, next_node_id, option), (6, path_id, option), (8, next_blinding_override, option), }); let _padding: Option = _padding; - let _short_channel_id: Option = _short_channel_id; - let valid_fwd_fmt = next_node_id.is_some() && path_id.is_none(); - let valid_recv_fmt = next_node_id.is_none() && next_blinding_override.is_none(); + let next_hop = match (short_channel_id, next_node_id) { + (Some(_), Some(_)) => return Err(DecodeError::InvalidValue), + (Some(scid), None) => Some(NextHop::ShortChannelId(scid)), + (None, Some(pubkey)) => Some(NextHop::NodeId(pubkey)), + (None, None) => None, + }; + + let valid_fwd_fmt = next_hop.is_some() && path_id.is_none(); + let valid_recv_fmt = next_hop.is_none() && next_blinding_override.is_none(); let payload_fmt = if valid_fwd_fmt { ControlTlvs::Forward(ForwardTlvs { - next_node_id: next_node_id.unwrap(), + next_hop: next_hop.unwrap(), next_blinding_override, }) } else if valid_recv_fmt {