Skip to content

Commit f2ece97

Browse files
authored
Merge pull request #1652 from valentinewallace/2022-08-reply-paths
Onion messages: support reply paths
2 parents 17ae116 + 950b7d7 commit f2ece97

File tree

5 files changed

+110
-43
lines changed

5 files changed

+110
-43
lines changed

fuzz/src/onion_message.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ mod tests {
122122
super::do_test(&::hex::decode(one_hop_om).unwrap(), &logger);
123123
{
124124
let log_entries = logger.lines.lock().unwrap();
125-
assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), "Received an onion message with path_id: None".to_string())), Some(&1));
125+
assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), "Received an onion message with path_id: None and no reply_path".to_string())), Some(&1));
126126
}
127127

128128
let two_unblinded_hops_om = "020000000000000000000000000000000000000000000000000000000000000e01055600020000000000000000000000000000000000000000000000000000000000000e0135043304210200000000000000000000000000000000000000000000000000000000000000039500000000000000000000000000000058000000000000000000000000000000000000000000000000000000000000001204105e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b300000000000000000000000000000000000000000000000000000000000000";

lightning/src/onion_message/blinded_route.rs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
1313

1414
use chain::keysinterface::{KeysInterface, Sign};
1515
use super::utils;
16+
use ln::msgs::DecodeError;
1617
use util::chacha20poly1305rfc::ChaChaPolyWriteAdapter;
17-
use util::ser::{VecWriter, Writeable, Writer};
18+
use util::ser::{Readable, VecWriter, Writeable, Writer};
1819

19-
use core::iter::FromIterator;
2020
use io;
2121
use prelude::*;
2222

@@ -113,6 +113,41 @@ fn encrypt_payload<P: Writeable>(payload: P, encrypted_tlvs_ss: [u8; 32]) -> Vec
113113
writer.0
114114
}
115115

116+
impl Writeable for BlindedRoute {
117+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
118+
self.introduction_node_id.write(w)?;
119+
self.blinding_point.write(w)?;
120+
(self.blinded_hops.len() as u8).write(w)?;
121+
for hop in &self.blinded_hops {
122+
hop.write(w)?;
123+
}
124+
Ok(())
125+
}
126+
}
127+
128+
impl Readable for BlindedRoute {
129+
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
130+
let introduction_node_id = Readable::read(r)?;
131+
let blinding_point = Readable::read(r)?;
132+
let num_hops: u8 = Readable::read(r)?;
133+
if num_hops == 0 { return Err(DecodeError::InvalidValue) }
134+
let mut blinded_hops: Vec<BlindedHop> = Vec::with_capacity(num_hops.into());
135+
for _ in 0..num_hops {
136+
blinded_hops.push(Readable::read(r)?);
137+
}
138+
Ok(BlindedRoute {
139+
introduction_node_id,
140+
blinding_point,
141+
blinded_hops,
142+
})
143+
}
144+
}
145+
146+
impl_writeable!(BlindedHop, {
147+
blinded_node_id,
148+
encrypted_payload
149+
});
150+
116151
/// TLVs to encode in an intermediate onion message packet's hop data. When provided in a blinded
117152
/// route, they are encoded into [`BlindedHop::encrypted_payload`].
118153
pub(crate) struct ForwardTlvs {

lightning/src/onion_message/functional_tests.rs

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ fn create_nodes(num_messengers: u8) -> Vec<MessengerNode> {
4747
res
4848
}
4949

50-
fn pass_along_path(mut path: Vec<MessengerNode>, expected_path_id: Option<[u8; 32]>) {
51-
let mut prev_node = path.remove(0);
50+
fn pass_along_path(path: &Vec<MessengerNode>, expected_path_id: Option<[u8; 32]>) {
51+
let mut prev_node = &path[0];
5252
let num_nodes = path.len();
53-
for (idx, node) in path.into_iter().enumerate() {
53+
for (idx, node) in path.into_iter().skip(1).enumerate() {
5454
let events = prev_node.messenger.release_pending_msgs();
5555
assert_eq!(events.len(), 1);
5656
let onion_msg = {
@@ -72,16 +72,16 @@ fn pass_along_path(mut path: Vec<MessengerNode>, expected_path_id: Option<[u8; 3
7272
fn one_hop() {
7373
let nodes = create_nodes(2);
7474

75-
nodes[0].messenger.send_onion_message(&[], Destination::Node(nodes[1].get_node_pk())).unwrap();
76-
pass_along_path(nodes, None);
75+
nodes[0].messenger.send_onion_message(&[], Destination::Node(nodes[1].get_node_pk()), None).unwrap();
76+
pass_along_path(&nodes, None);
7777
}
7878

7979
#[test]
8080
fn two_unblinded_hops() {
8181
let nodes = create_nodes(3);
8282

83-
nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk()], Destination::Node(nodes[2].get_node_pk())).unwrap();
84-
pass_along_path(nodes, None);
83+
nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk()], Destination::Node(nodes[2].get_node_pk()), None).unwrap();
84+
pass_along_path(&nodes, None);
8585
}
8686

8787
#[test]
@@ -91,8 +91,8 @@ fn two_unblinded_two_blinded() {
9191
let secp_ctx = Secp256k1::new();
9292
let blinded_route = BlindedRoute::new::<EnforcingSigner, _, _>(&[nodes[3].get_node_pk(), nodes[4].get_node_pk()], &*nodes[4].keys_manager, &secp_ctx).unwrap();
9393

94-
nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], Destination::BlindedRoute(blinded_route)).unwrap();
95-
pass_along_path(nodes, None);
94+
nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], Destination::BlindedRoute(blinded_route), None).unwrap();
95+
pass_along_path(&nodes, None);
9696
}
9797

9898
#[test]
@@ -102,8 +102,8 @@ fn three_blinded_hops() {
102102
let secp_ctx = Secp256k1::new();
103103
let blinded_route = BlindedRoute::new::<EnforcingSigner, _, _>(&[nodes[1].get_node_pk(), nodes[2].get_node_pk(), nodes[3].get_node_pk()], &*nodes[3].keys_manager, &secp_ctx).unwrap();
104104

105-
nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route)).unwrap();
106-
pass_along_path(nodes, None);
105+
nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route), None).unwrap();
106+
pass_along_path(&nodes, None);
107107
}
108108

109109
#[test]
@@ -116,27 +116,51 @@ fn too_big_packet_error() {
116116
let hop_node_id = PublicKey::from_secret_key(&secp_ctx, &hop_secret);
117117

118118
let hops = [hop_node_id; 400];
119-
let err = nodes[0].messenger.send_onion_message(&hops, Destination::Node(hop_node_id)).unwrap_err();
119+
let err = nodes[0].messenger.send_onion_message(&hops, Destination::Node(hop_node_id), None).unwrap_err();
120120
assert_eq!(err, SendError::TooBigPacket);
121121
}
122122

123123
#[test]
124124
fn invalid_blinded_route_error() {
125125
// Make sure we error as expected if a provided blinded route has 0 or 1 hops.
126126
let mut nodes = create_nodes(3);
127-
let (node1, node2, node3) = (nodes.remove(0), nodes.remove(0), nodes.remove(0));
128127

129128
// 0 hops
130129
let secp_ctx = Secp256k1::new();
131-
let mut blinded_route = BlindedRoute::new::<EnforcingSigner, _, _>(&[node2.get_node_pk(), node3.get_node_pk()], &*node3.keys_manager, &secp_ctx).unwrap();
130+
let mut blinded_route = BlindedRoute::new::<EnforcingSigner, _, _>(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], &*nodes[2].keys_manager, &secp_ctx).unwrap();
132131
blinded_route.blinded_hops.clear();
133-
let err = node1.messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route)).unwrap_err();
132+
let err = nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route), None).unwrap_err();
134133
assert_eq!(err, SendError::TooFewBlindedHops);
135134

136135
// 1 hop
137-
let mut blinded_route = BlindedRoute::new::<EnforcingSigner, _, _>(&[node2.get_node_pk(), node3.get_node_pk()], &*node3.keys_manager, &secp_ctx).unwrap();
136+
let mut blinded_route = BlindedRoute::new::<EnforcingSigner, _, _>(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], &*nodes[2].keys_manager, &secp_ctx).unwrap();
138137
blinded_route.blinded_hops.remove(0);
139138
assert_eq!(blinded_route.blinded_hops.len(), 1);
140-
let err = node1.messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route)).unwrap_err();
139+
let err = nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route), None).unwrap_err();
141140
assert_eq!(err, SendError::TooFewBlindedHops);
142141
}
142+
143+
#[test]
144+
fn reply_path() {
145+
let mut nodes = create_nodes(4);
146+
let secp_ctx = Secp256k1::new();
147+
148+
// Destination::Node
149+
let reply_path = BlindedRoute::new::<EnforcingSigner, _, _>(&[nodes[2].get_node_pk(), nodes[1].get_node_pk(), nodes[0].get_node_pk()], &*nodes[0].keys_manager, &secp_ctx).unwrap();
150+
nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], Destination::Node(nodes[3].get_node_pk()), Some(reply_path)).unwrap();
151+
pass_along_path(&nodes, None);
152+
// Make sure the last node successfully decoded the reply path.
153+
nodes[3].logger.assert_log_contains(
154+
"lightning::onion_message::messenger".to_string(),
155+
format!("Received an onion message with path_id: None and reply_path").to_string(), 1);
156+
157+
// Destination::BlindedRoute
158+
let blinded_route = BlindedRoute::new::<EnforcingSigner, _, _>(&[nodes[1].get_node_pk(), nodes[2].get_node_pk(), nodes[3].get_node_pk()], &*nodes[3].keys_manager, &secp_ctx).unwrap();
159+
let reply_path = BlindedRoute::new::<EnforcingSigner, _, _>(&[nodes[2].get_node_pk(), nodes[1].get_node_pk(), nodes[0].get_node_pk()], &*nodes[0].keys_manager, &secp_ctx).unwrap();
160+
161+
nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route), Some(reply_path)).unwrap();
162+
pass_along_path(&nodes, None);
163+
nodes[3].logger.assert_log_contains(
164+
"lightning::onion_message::messenger".to_string(),
165+
format!("Received an onion message with path_id: None and reply_path").to_string(), 2);
166+
}

lightning/src/onion_message/messenger.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ impl<Signer: Sign, K: Deref, L: Deref> OnionMessenger<Signer, K, L>
142142

143143
/// Send an empty onion message to `destination`, routing it through `intermediate_nodes`.
144144
/// See [`OnionMessenger`] for example usage.
145-
pub fn send_onion_message(&self, intermediate_nodes: &[PublicKey], destination: Destination) -> Result<(), SendError> {
145+
pub fn send_onion_message(&self, intermediate_nodes: &[PublicKey], destination: Destination, reply_path: Option<BlindedRoute>) -> Result<(), SendError> {
146146
if let Destination::BlindedRoute(BlindedRoute { ref blinded_hops, .. }) = destination {
147147
if blinded_hops.len() < 2 {
148148
return Err(SendError::TooFewBlindedHops);
@@ -160,7 +160,7 @@ impl<Signer: Sign, K: Deref, L: Deref> OnionMessenger<Signer, K, L>
160160
}
161161
};
162162
let (packet_payloads, packet_keys) = packet_payloads_and_keys(
163-
&self.secp_ctx, intermediate_nodes, destination, &blinding_secret)
163+
&self.secp_ctx, intermediate_nodes, destination, reply_path, &blinding_secret)
164164
.map_err(|e| SendError::Secp256k1(e))?;
165165

166166
let prng_seed = self.keys_manager.get_secure_random_bytes();
@@ -209,9 +209,11 @@ impl<Signer: Sign, K: Deref, L: Deref> OnionMessenger<Signer, K, L>
209209
msg.onion_routing_packet.hmac, control_tlvs_ss)
210210
{
211211
Ok((Payload::Receive {
212-
control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id })
212+
control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id }), reply_path,
213213
}, None)) => {
214-
log_info!(self.logger, "Received an onion message with path_id: {:02x?}", path_id);
214+
log_info!(self.logger,
215+
"Received an onion message with path_id: {:02x?} and {}reply_path",
216+
path_id, if reply_path.is_some() { "" } else { "no " });
215217
},
216218
Ok((Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs {
217219
next_node_id, next_blinding_override
@@ -299,7 +301,8 @@ pub type SimpleRefOnionMessenger<'a, 'b, L> = OnionMessenger<InMemorySigner, &'a
299301
/// Construct onion packet payloads and keys for sending an onion message along the given
300302
/// `unblinded_path` to the given `destination`.
301303
fn packet_payloads_and_keys<T: secp256k1::Signing + secp256k1::Verification>(
302-
secp_ctx: &Secp256k1<T>, unblinded_path: &[PublicKey], destination: Destination, session_priv: &SecretKey
304+
secp_ctx: &Secp256k1<T>, unblinded_path: &[PublicKey], destination: Destination, mut reply_path:
305+
Option<BlindedRoute>, session_priv: &SecretKey
303306
) -> Result<(Vec<(Payload, [u8; 32])>, Vec<onion_utils::OnionKeys>), secp256k1::Error> {
304307
let num_hops = unblinded_path.len() + destination.num_hops();
305308
let mut payloads = Vec::with_capacity(num_hops);
@@ -344,6 +347,7 @@ fn packet_payloads_and_keys<T: secp256k1::Signing + secp256k1::Verification>(
344347
} else if let Some(encrypted_payload) = enc_payload_opt {
345348
payloads.push((Payload::Receive {
346349
control_tlvs: ReceiveControlTlvs::Blinded(encrypted_payload),
350+
reply_path: reply_path.take(),
347351
}, control_tlvs_ss));
348352
}
349353

@@ -361,7 +365,8 @@ fn packet_payloads_and_keys<T: secp256k1::Signing + secp256k1::Verification>(
361365

362366
if let Some(control_tlvs_ss) = prev_control_tlvs_ss {
363367
payloads.push((Payload::Receive {
364-
control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id: None, })
368+
control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id: None, }),
369+
reply_path: reply_path.take(),
365370
}, control_tlvs_ss));
366371
}
367372

lightning/src/onion_message/packet.rs

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ use bitcoin::secp256k1::ecdh::SharedSecret;
1414

1515
use ln::msgs::DecodeError;
1616
use ln::onion_utils;
17-
use super::blinded_route::{ForwardTlvs, ReceiveTlvs};
17+
use super::blinded_route::{BlindedRoute, ForwardTlvs, ReceiveTlvs};
1818
use util::chacha20poly1305rfc::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter};
19-
use util::ser::{FixedLengthReader, LengthRead, LengthReadable, LengthReadableArgs, Readable, ReadableArgs, Writeable, Writer};
19+
use util::ser::{BigSize, FixedLengthReader, LengthRead, LengthReadable, LengthReadableArgs, Readable, ReadableArgs, Writeable, Writer};
2020

2121
use core::cmp;
2222
use io::{self, Read};
@@ -98,8 +98,8 @@ pub(super) enum Payload {
9898
/// This payload is for the final hop.
9999
Receive {
100100
control_tlvs: ReceiveControlTlvs,
101+
reply_path: Option<BlindedRoute>,
101102
// Coming soon:
102-
// reply_path: Option<BlindedRoute>,
103103
// message: Message,
104104
}
105105
}
@@ -135,21 +135,31 @@ pub(super) enum ReceiveControlTlvs {
135135
impl Writeable for (Payload, [u8; 32]) {
136136
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
137137
match &self.0 {
138-
Payload::Forward(ForwardControlTlvs::Blinded(encrypted_bytes)) |
139-
Payload::Receive { control_tlvs: ReceiveControlTlvs::Blinded(encrypted_bytes)} => {
138+
Payload::Forward(ForwardControlTlvs::Blinded(encrypted_bytes)) => {
140139
encode_varint_length_prefixed_tlv!(w, {
141140
(4, encrypted_bytes, vec_type)
142141
})
143142
},
143+
Payload::Receive {
144+
control_tlvs: ReceiveControlTlvs::Blinded(encrypted_bytes), reply_path
145+
} => {
146+
encode_varint_length_prefixed_tlv!(w, {
147+
(2, reply_path, option),
148+
(4, encrypted_bytes, vec_type)
149+
})
150+
},
144151
Payload::Forward(ForwardControlTlvs::Unblinded(control_tlvs)) => {
145152
let write_adapter = ChaChaPolyWriteAdapter::new(self.1, &control_tlvs);
146153
encode_varint_length_prefixed_tlv!(w, {
147154
(4, write_adapter, required)
148155
})
149156
},
150-
Payload::Receive { control_tlvs: ReceiveControlTlvs::Unblinded(control_tlvs)} => {
157+
Payload::Receive {
158+
control_tlvs: ReceiveControlTlvs::Unblinded(control_tlvs), reply_path,
159+
} => {
151160
let write_adapter = ChaChaPolyWriteAdapter::new(self.1, &control_tlvs);
152161
encode_varint_length_prefixed_tlv!(w, {
162+
(2, reply_path, option),
153163
(4, write_adapter, required)
154164
})
155165
},
@@ -161,20 +171,13 @@ impl Writeable for (Payload, [u8; 32]) {
161171
// Uses the provided secret to simultaneously decode and decrypt the control TLVs.
162172
impl ReadableArgs<SharedSecret> for Payload {
163173
fn read<R: Read>(mut r: &mut R, encrypted_tlvs_ss: SharedSecret) -> Result<Self, DecodeError> {
164-
use bitcoin::consensus::encode::{Decodable, Error, VarInt};
165-
let v: VarInt = Decodable::consensus_decode(&mut r)
166-
.map_err(|e| match e {
167-
Error::Io(ioe) => DecodeError::from(ioe),
168-
_ => DecodeError::InvalidValue
169-
})?;
170-
174+
let v: BigSize = Readable::read(r)?;
171175
let mut rd = FixedLengthReader::new(r, v.0);
172-
// TODO: support reply paths
173-
let mut _reply_path_bytes: Option<Vec<u8>> = Some(Vec::new());
176+
let mut reply_path: Option<BlindedRoute> = None;
174177
let mut read_adapter: Option<ChaChaPolyReadAdapter<ControlTlvs>> = None;
175178
let rho = onion_utils::gen_rho_from_shared_secret(&encrypted_tlvs_ss.secret_bytes());
176179
decode_tlv_stream!(&mut rd, {
177-
(2, _reply_path_bytes, vec_type),
180+
(2, reply_path, option),
178181
(4, read_adapter, (option: LengthReadableArgs, rho))
179182
});
180183
rd.eat_remaining().map_err(|_| DecodeError::ShortRead)?;
@@ -185,7 +188,7 @@ impl ReadableArgs<SharedSecret> for Payload {
185188
Ok(Payload::Forward(ForwardControlTlvs::Unblinded(tlvs)))
186189
},
187190
Some(ChaChaPolyReadAdapter { readable: ControlTlvs::Receive(tlvs)}) => {
188-
Ok(Payload::Receive { control_tlvs: ReceiveControlTlvs::Unblinded(tlvs)})
191+
Ok(Payload::Receive { control_tlvs: ReceiveControlTlvs::Unblinded(tlvs), reply_path })
189192
},
190193
}
191194
}

0 commit comments

Comments
 (0)