Skip to content

Commit d85e0d6

Browse files
committed
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.
1 parent eaf76f6 commit d85e0d6

File tree

3 files changed

+51
-23
lines changed

3 files changed

+51
-23
lines changed

lightning/src/blinded_path/message.rs

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ use core::ops::Deref;
1919
/// TLVs to encode in an intermediate onion message packet's hop data. When provided in a blinded
2020
/// route, they are encoded into [`BlindedHop::encrypted_payload`].
2121
pub(crate) struct ForwardTlvs {
22-
/// The node id of the next hop in the onion message's path.
23-
pub(crate) next_node_id: PublicKey,
22+
/// The next hop in the onion message's path.
23+
pub(crate) next_hop: NextHop,
2424
/// Senders to a blinded path use this value to concatenate the route they find to the
2525
/// introduction node with the blinded path.
2626
pub(crate) next_blinding_override: Option<PublicKey>,
@@ -34,11 +34,25 @@ pub(crate) struct ReceiveTlvs {
3434
pub(crate) path_id: Option<[u8; 32]>,
3535
}
3636

37+
/// The next hop to forward the onion message along its path.
38+
#[derive(Debug)]
39+
pub enum NextHop {
40+
/// The node id of the next hop.
41+
NodeId(PublicKey),
42+
/// The short channel id leading to the next hop.
43+
ShortChannelId(u64),
44+
}
45+
3746
impl Writeable for ForwardTlvs {
3847
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
48+
let (next_node_id, short_channel_id) = match self.next_hop {
49+
NextHop::NodeId(pubkey) => (Some(pubkey), None),
50+
NextHop::ShortChannelId(scid) => (None, Some(scid)),
51+
};
3952
// TODO: write padding
4053
encode_tlv_stream!(writer, {
41-
(4, self.next_node_id, required),
54+
(2, short_channel_id, option),
55+
(4, next_node_id, option),
4256
(8, self.next_blinding_override, option)
4357
});
4458
Ok(())
@@ -61,9 +75,8 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
6175
) -> Result<Vec<BlindedHop>, secp256k1::Error> {
6276
let blinded_tlvs = unblinded_path.iter()
6377
.skip(1) // The first node's TLVs contains the next node's pubkey
64-
.map(|pk| {
65-
ControlTlvs::Forward(ForwardTlvs { next_node_id: *pk, next_blinding_override: None })
66-
})
78+
.map(|pk| ForwardTlvs { next_hop: NextHop::NodeId(*pk), next_blinding_override: None })
79+
.map(|tlvs| ControlTlvs::Forward(tlvs))
6780
.chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs { path_id: None })));
6881

6982
utils::construct_blinded_hops(secp_ctx, unblinded_path.iter(), blinded_tlvs, session_priv)
@@ -80,9 +93,13 @@ pub(crate) fn advance_path_by_one<NS: Deref, T: secp256k1::Signing + secp256k1::
8093
let mut s = Cursor::new(&encrypted_control_tlvs);
8194
let mut reader = FixedLengthReader::new(&mut s, encrypted_control_tlvs.len() as u64);
8295
match ChaChaPolyReadAdapter::read(&mut reader, rho) {
83-
Ok(ChaChaPolyReadAdapter { readable: ControlTlvs::Forward(ForwardTlvs {
84-
mut next_node_id, next_blinding_override,
85-
})}) => {
96+
Ok(ChaChaPolyReadAdapter {
97+
readable: ControlTlvs::Forward(ForwardTlvs { next_hop, next_blinding_override })
98+
}) => {
99+
let mut next_node_id = match next_hop {
100+
NextHop::NodeId(pubkey) => pubkey,
101+
NextHop::ShortChannelId(_) => todo!(),
102+
};
86103
let mut new_blinding_point = match next_blinding_override {
87104
Some(blinding_point) => blinding_point,
88105
None => {

lightning/src/onion_message/messenger.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use bitcoin::hashes::sha256::Hash as Sha256;
1616
use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey};
1717

1818
use crate::blinded_path::BlindedPath;
19-
use crate::blinded_path::message::{advance_path_by_one, ForwardTlvs, ReceiveTlvs};
19+
use crate::blinded_path::message::{advance_path_by_one, ForwardTlvs, NextHop, ReceiveTlvs};
2020
use crate::blinded_path::utils;
2121
use crate::events::{Event, EventHandler, EventsProvider};
2222
use crate::sign::{EntropySource, NodeSigner, Recipient};
@@ -538,7 +538,7 @@ pub trait CustomOnionMessageHandler {
538538
#[derive(Debug)]
539539
pub enum PeeledOnion<T: OnionMessageContents> {
540540
/// Forwarded onion, with the next node id and a new onion
541-
Forward(PublicKey, OnionMessage),
541+
Forward(NextHop, OnionMessage),
542542
/// Received onion message, with decrypted contents, path_id, and reply path
543543
Receive(ParsedOnionMessageContents<T>, Option<[u8; 32]>, Option<BlindedPath>)
544544
}
@@ -647,9 +647,9 @@ where
647647
Ok(PeeledOnion::Receive(message, path_id, reply_path))
648648
},
649649
Ok((Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs {
650-
next_node_id, next_blinding_override
650+
next_hop, next_blinding_override
651651
})), Some((next_hop_hmac, new_packet_bytes)))) => {
652-
// TODO: we need to check whether `next_node_id` is our node, in which case this is a dummy
652+
// TODO: we need to check whether `next_hop` is our node, in which case this is a dummy
653653
// blinded hop and this onion message is destined for us. In this situation, we should keep
654654
// unwrapping the onion layers to get to the final payload. Since we don't have the option
655655
// of creating blinded paths with dummy hops currently, we should be ok to not handle this
@@ -685,7 +685,7 @@ where
685685
onion_routing_packet: outgoing_packet,
686686
};
687687

688-
Ok(PeeledOnion::Forward(next_node_id, onion_message))
688+
Ok(PeeledOnion::Forward(next_hop, onion_message))
689689
},
690690
Err(e) => {
691691
log_trace!(logger, "Errored decoding onion message packet: {:?}", e);
@@ -961,7 +961,12 @@ where
961961
},
962962
}
963963
},
964-
Ok(PeeledOnion::Forward(next_node_id, onion_message)) => {
964+
Ok(PeeledOnion::Forward(next_hop, onion_message)) => {
965+
let next_node_id = match next_hop {
966+
NextHop::NodeId(pubkey) => pubkey,
967+
NextHop::ShortChannelId(_) => todo!(),
968+
};
969+
965970
let mut message_recipients = self.message_recipients.lock().unwrap();
966971
if outbound_buffer_full(&next_node_id, &message_recipients) {
967972
log_trace!(
@@ -1146,7 +1151,7 @@ fn packet_payloads_and_keys<T: OnionMessageContents, S: secp256k1::Signing + sec
11461151
if let Some(ss) = prev_control_tlvs_ss.take() {
11471152
payloads.push((Payload::Forward(ForwardControlTlvs::Unblinded(
11481153
ForwardTlvs {
1149-
next_node_id: unblinded_pk_opt.unwrap(),
1154+
next_hop: NextHop::NodeId(unblinded_pk_opt.unwrap()),
11501155
next_blinding_override: None,
11511156
}
11521157
)), ss));
@@ -1156,7 +1161,7 @@ fn packet_payloads_and_keys<T: OnionMessageContents, S: secp256k1::Signing + sec
11561161
} else if let Some((intro_node_id, blinding_pt)) = intro_node_id_blinding_pt.take() {
11571162
if let Some(control_tlvs_ss) = prev_control_tlvs_ss.take() {
11581163
payloads.push((Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs {
1159-
next_node_id: intro_node_id,
1164+
next_hop: NextHop::NodeId(intro_node_id),
11601165
next_blinding_override: Some(blinding_pt),
11611166
})), control_tlvs_ss));
11621167
}

lightning/src/onion_message/packet.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use bitcoin::secp256k1::PublicKey;
1313
use bitcoin::secp256k1::ecdh::SharedSecret;
1414

1515
use crate::blinded_path::BlindedPath;
16-
use crate::blinded_path::message::{ForwardTlvs, ReceiveTlvs};
16+
use crate::blinded_path::message::{ForwardTlvs, NextHop, ReceiveTlvs};
1717
use crate::blinded_path::utils::Padding;
1818
use crate::ln::msgs::DecodeError;
1919
use crate::ln::onion_utils;
@@ -284,20 +284,26 @@ impl Readable for ControlTlvs {
284284
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
285285
_init_and_read_tlv_stream!(r, {
286286
(1, _padding, option),
287-
(2, _short_channel_id, option),
287+
(2, short_channel_id, option),
288288
(4, next_node_id, option),
289289
(6, path_id, option),
290290
(8, next_blinding_override, option),
291291
});
292292
let _padding: Option<Padding> = _padding;
293-
let _short_channel_id: Option<u64> = _short_channel_id;
294293

295-
let valid_fwd_fmt = next_node_id.is_some() && path_id.is_none();
296-
let valid_recv_fmt = next_node_id.is_none() && next_blinding_override.is_none();
294+
let next_hop = match (short_channel_id, next_node_id) {
295+
(Some(_), Some(_)) => return Err(DecodeError::InvalidValue),
296+
(Some(scid), None) => Some(NextHop::ShortChannelId(scid)),
297+
(None, Some(pubkey)) => Some(NextHop::NodeId(pubkey)),
298+
(None, None) => None,
299+
};
300+
301+
let valid_fwd_fmt = next_hop.is_some() && path_id.is_none();
302+
let valid_recv_fmt = next_hop.is_none() && next_blinding_override.is_none();
297303

298304
let payload_fmt = if valid_fwd_fmt {
299305
ControlTlvs::Forward(ForwardTlvs {
300-
next_node_id: next_node_id.unwrap(),
306+
next_hop: next_hop.unwrap(),
301307
next_blinding_override,
302308
})
303309
} else if valid_recv_fmt {

0 commit comments

Comments
 (0)