Skip to content

Commit cf13f78

Browse files
Blinded paths: support constructing onion keys + handling onion errors
We don't bother actually parsing errors from within a blinded path, since all errors should be wiped by the introduction node by the time it gets back to us (the sender).
1 parent 6144e30 commit cf13f78

File tree

1 file changed

+37
-10
lines changed

1 file changed

+37
-10
lines changed

lightning/src/ln/onion_utils.rs

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,23 @@ pub(crate) fn next_hop_packet_pubkey<T: secp256k1::Signing + secp256k1::Verifica
104104

105105
// can only fail if an intermediary hop has an invalid public key or session_priv is invalid
106106
#[inline]
107-
pub(super) fn construct_onion_keys_callback<T: secp256k1::Signing, FType: FnMut(SharedSecret, [u8; 32], PublicKey, &RouteHop, usize)> (secp_ctx: &Secp256k1<T>, path: &Vec<RouteHop>, session_priv: &SecretKey, mut callback: FType) -> Result<(), secp256k1::Error> {
107+
pub(super) fn construct_onion_keys_callback<T, FType>(
108+
secp_ctx: &Secp256k1<T>, path: &Path, session_priv: &SecretKey, mut callback: FType
109+
) -> Result<(), secp256k1::Error>
110+
where
111+
T: secp256k1::Signing,
112+
FType: FnMut(SharedSecret, [u8; 32], PublicKey, Option<&RouteHop>, usize)
113+
{
108114
let mut blinded_priv = session_priv.clone();
109115
let mut blinded_pub = PublicKey::from_secret_key(secp_ctx, &blinded_priv);
110116

111-
for (idx, hop) in path.iter().enumerate() {
112-
let shared_secret = SharedSecret::new(&hop.pubkey, &blinded_priv);
117+
let unblinded_hops_iter = path.hops.iter().map(|h| (&h.pubkey, Some(h)));
118+
let blinded_pks_iter = path.blinded_tail.as_ref()
119+
.map(|t| t.hops.iter()).unwrap_or([].iter())
120+
.skip(1) // Skip the intro node because it's included in the unblinded hops
121+
.map(|h| (&h.blinded_node_id, None));
122+
for (idx, (pubkey, route_hop_opt)) in unblinded_hops_iter.chain(blinded_pks_iter).enumerate() {
123+
let shared_secret = SharedSecret::new(pubkey, &blinded_priv);
113124

114125
let mut sha = Sha256::engine();
115126
sha.input(&blinded_pub.serialize()[..]);
@@ -121,7 +132,7 @@ pub(super) fn construct_onion_keys_callback<T: secp256k1::Signing, FType: FnMut(
121132
blinded_priv = blinded_priv.mul_tweak(&Scalar::from_be_bytes(blinding_factor).unwrap())?;
122133
blinded_pub = PublicKey::from_secret_key(secp_ctx, &blinded_priv);
123134

124-
callback(shared_secret, blinding_factor, ephemeral_pubkey, hop, idx);
135+
callback(shared_secret, blinding_factor, ephemeral_pubkey, route_hop_opt, idx);
125136
}
126137

127138
Ok(())
@@ -131,7 +142,9 @@ pub(super) fn construct_onion_keys_callback<T: secp256k1::Signing, FType: FnMut(
131142
pub(super) fn construct_onion_keys<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, path: &Path, session_priv: &SecretKey) -> Result<Vec<OnionKeys>, secp256k1::Error> {
132143
let mut res = Vec::with_capacity(path.hops.len());
133144

134-
construct_onion_keys_callback(secp_ctx, &path.hops, session_priv, |shared_secret, _blinding_factor, ephemeral_pubkey, _, _| {
145+
construct_onion_keys_callback(secp_ctx, &path, session_priv,
146+
|shared_secret, _blinding_factor, ephemeral_pubkey, _, _|
147+
{
135148
let (rho, mu) = gen_rho_mu_from_shared_secret(shared_secret.as_ref());
136149

137150
res.push(OnionKeys {
@@ -400,10 +413,28 @@ where L::Target: Logger {
400413
let mut error_packet_ret = None;
401414
let mut is_from_final_node = false;
402415

416+
const BADONION: u16 = 0x8000;
417+
const PERM: u16 = 0x4000;
418+
const NODE: u16 = 0x2000;
419+
const UPDATE: u16 = 0x1000;
420+
403421
// Handle packed channel/node updates for passing back for the route handler
404-
construct_onion_keys_callback(secp_ctx, &path.hops, session_priv, |shared_secret, _, _, route_hop, route_hop_idx| {
422+
construct_onion_keys_callback(secp_ctx, &path, session_priv,
423+
|shared_secret, _, _, route_hop_opt, route_hop_idx|
424+
{
405425
if res.is_some() { return; }
406426

427+
let route_hop = match route_hop_opt {
428+
Some(hop) => hop,
429+
None => {
430+
// Got an error from within a blinded route.
431+
error_code_ret = Some(BADONION | PERM | 24); // invalid_onion_blinding
432+
error_packet_ret = Some(vec![0; 32]);
433+
is_from_final_node = false;
434+
return
435+
},
436+
};
437+
407438
let amt_to_forward = htlc_msat - route_hop.fee_msat;
408439
htlc_msat = amt_to_forward;
409440

@@ -443,10 +474,6 @@ where L::Target: Logger {
443474
return
444475
}
445476
};
446-
const BADONION: u16 = 0x8000;
447-
const PERM: u16 = 0x4000;
448-
const NODE: u16 = 0x2000;
449-
const UPDATE: u16 = 0x1000;
450477

451478
let error_code = u16::from_be_bytes(error_code_slice.try_into().expect("len is 2"));
452479
error_code_ret = Some(error_code);

0 commit comments

Comments
 (0)