Skip to content

Commit f4a22e2

Browse files
committed
Use impl Iterator rather than a callback when building onion keys
The onion keys building logic is rather ancient and predates the `-> impl Trait` syntax in Rust, and thus used a callback. Here we move it to an `impl Iterator` which is much Rustier and cleans up the code a bit.
1 parent 6ea3576 commit f4a22e2

File tree

1 file changed

+62
-80
lines changed

1 file changed

+62
-80
lines changed

lightning/src/ln/onion_utils.rs

+62-80
Original file line numberDiff line numberDiff line change
@@ -308,26 +308,25 @@ impl<'a, 'b> OnionPayload<'a, 'b> for msgs::OutboundTrampolinePayload<'a> {
308308
}
309309
}
310310

311-
#[inline]
312-
fn construct_onion_keys_generic_callback<'a, T, H, FType>(
313-
secp_ctx: &Secp256k1<T>, hops: &'a [H], blinded_tail: Option<&BlindedTail>,
314-
session_priv: &SecretKey, mut callback: FType,
315-
) where
311+
fn construct_onion_keys_generic<'a, T, H>(
312+
secp_ctx: &'a Secp256k1<T>, hops: &'a [H], blinded_tail: Option<&'a BlindedTail>,
313+
session_priv: &SecretKey,
314+
) -> impl Iterator<Item = (SharedSecret, [u8; 32], PublicKey, Option<&'a H>, usize)> + 'a
315+
where
316316
T: secp256k1::Signing,
317317
H: HopInfo,
318-
FType: FnMut(SharedSecret, [u8; 32], PublicKey, Option<&'a H>, usize),
319318
{
320319
let mut blinded_priv = session_priv.clone();
321320
let mut blinded_pub = PublicKey::from_secret_key(secp_ctx, &blinded_priv);
322321

323-
let unblinded_hops_iter = hops.iter().map(|h| (h.node_pubkey(), Some(h)));
324-
let blinded_pks_iter = blinded_tail
322+
let unblinded_hops = hops.iter().map(|h| (h.node_pubkey(), Some(h)));
323+
let blinded_pubkeys = blinded_tail
325324
.map(|t| t.hops.iter())
326325
.unwrap_or([].iter())
327326
.skip(1) // Skip the intro node because it's included in the unblinded hops
328327
.map(|h| (&h.blinded_node_id, None));
329328

330-
for (idx, (pubkey, route_hop_opt)) in unblinded_hops_iter.chain(blinded_pks_iter).enumerate() {
329+
unblinded_hops.chain(blinded_pubkeys).enumerate().map(move |(idx, (pubkey, route_hop_opt))| {
331330
let shared_secret = SharedSecret::new(pubkey, &blinded_priv);
332331

333332
let mut sha = Sha256::engine();
@@ -342,8 +341,8 @@ fn construct_onion_keys_generic_callback<'a, T, H, FType>(
342341
.expect("Blinding are never invalid as we picked the starting private key randomly");
343342
blinded_pub = PublicKey::from_secret_key(secp_ctx, &blinded_priv);
344343

345-
callback(shared_secret, blinding_factor, ephemeral_pubkey, route_hop_opt, idx);
346-
}
344+
(shared_secret, blinding_factor, ephemeral_pubkey, route_hop_opt, idx)
345+
})
347346
}
348347

349348
// can only fail if an intermediary hop has an invalid public key or session_priv is invalid
@@ -358,25 +357,20 @@ pub(super) fn construct_onion_keys<T: secp256k1::Signing>(
358357
}
359358
Some(t)
360359
});
361-
construct_onion_keys_generic_callback(
362-
secp_ctx,
363-
&path.hops,
364-
blinded_tail,
365-
session_priv,
366-
|shared_secret, _blinding_factor, ephemeral_pubkey, _, _| {
367-
let (rho, mu) = gen_rho_mu_from_shared_secret(shared_secret.as_ref());
368-
369-
res.push(OnionKeys {
370-
#[cfg(test)]
371-
shared_secret,
372-
#[cfg(test)]
373-
blinding_factor: _blinding_factor,
374-
ephemeral_pubkey,
375-
rho,
376-
mu,
377-
});
378-
},
379-
);
360+
let iter = construct_onion_keys_generic(secp_ctx, &path.hops, blinded_tail, session_priv);
361+
for (shared_secret, _blinding_factor, ephemeral_pubkey, _, _) in iter {
362+
let (rho, mu) = gen_rho_mu_from_shared_secret(shared_secret.as_ref());
363+
364+
res.push(OnionKeys {
365+
#[cfg(test)]
366+
shared_secret,
367+
#[cfg(test)]
368+
blinding_factor: _blinding_factor,
369+
ephemeral_pubkey,
370+
rho,
371+
mu,
372+
});
373+
}
380374

381375
res
382376
}
@@ -387,25 +381,21 @@ pub(super) fn construct_trampoline_onion_keys<T: secp256k1::Signing>(
387381
) -> Vec<OnionKeys> {
388382
let mut res = Vec::with_capacity(blinded_tail.trampoline_hops.len());
389383

390-
construct_onion_keys_generic_callback(
391-
secp_ctx,
392-
&blinded_tail.trampoline_hops,
393-
Some(blinded_tail),
394-
session_priv,
395-
|shared_secret, _blinding_factor, ephemeral_pubkey, _, _| {
396-
let (rho, mu) = gen_rho_mu_from_shared_secret(shared_secret.as_ref());
397-
398-
res.push(OnionKeys {
399-
#[cfg(test)]
400-
shared_secret,
401-
#[cfg(test)]
402-
blinding_factor: _blinding_factor,
403-
ephemeral_pubkey,
404-
rho,
405-
mu,
406-
});
407-
},
408-
);
384+
let hops = &blinded_tail.trampoline_hops;
385+
let iter = construct_onion_keys_generic(secp_ctx, &hops, Some(blinded_tail), session_priv);
386+
for (shared_secret, _blinding_factor, ephemeral_pubkey, _, _) in iter {
387+
let (rho, mu) = gen_rho_mu_from_shared_secret(shared_secret.as_ref());
388+
389+
res.push(OnionKeys {
390+
#[cfg(test)]
391+
shared_secret,
392+
#[cfg(test)]
393+
blinding_factor: _blinding_factor,
394+
ephemeral_pubkey,
395+
rho,
396+
mu,
397+
});
398+
}
409399

410400
res
411401
}
@@ -1113,31 +1103,27 @@ where
11131103
let mut onion_keys =
11141104
Vec::with_capacity(path.hops.len() + num_trampoline_hops + num_blinded_hops);
11151105

1116-
construct_onion_keys_generic_callback(
1117-
secp_ctx,
1118-
&path.hops,
1119-
// if we have Trampoline hops, the blinded hops are part of the inner Trampoline onion
1120-
if path.has_trampoline_hops() { None } else { path.blinded_tail.as_ref() },
1121-
outer_session_priv,
1122-
|shared_secret, _, _, route_hop_option: Option<&RouteHop>, _| {
1123-
onion_keys.push((route_hop_option.map(|rh| ErrorHop::RouteHop(rh)), shared_secret))
1124-
},
1125-
);
1106+
// if we have Trampoline hops, the blinded hops are part of the inner Trampoline onion
1107+
let nontrampoline_bp =
1108+
if path.has_trampoline_hops() { None } else { path.blinded_tail.as_ref() };
1109+
let nontrampoline_hops =
1110+
construct_onion_keys_generic(secp_ctx, &path.hops, nontrampoline_bp, outer_session_priv);
1111+
for (shared_secret, _, _, route_hop_option, _) in nontrampoline_hops {
1112+
onion_keys.push((route_hop_option.map(|rh| ErrorHop::RouteHop(rh)), shared_secret));
1113+
}
11261114

11271115
if path.has_trampoline_hops() {
1128-
construct_onion_keys_generic_callback(
1129-
secp_ctx,
1130-
// Trampoline hops are part of the blinded tail, so this can never panic
1131-
&path.blinded_tail.as_ref().unwrap().trampoline_hops,
1132-
path.blinded_tail.as_ref(),
1133-
inner_session_priv.expect("Trampoline hops always have an inner session priv"),
1134-
|shared_secret, _, _, trampoline_hop_option: Option<&TrampolineHop>, _| {
1135-
onion_keys.push((
1136-
trampoline_hop_option.map(|th| ErrorHop::TrampolineHop(th)),
1137-
shared_secret,
1138-
))
1139-
},
1140-
);
1116+
// Trampoline hops are part of the blinded tail, so this can never panic
1117+
let blinded_tail = path.blinded_tail.as_ref();
1118+
let hops = &blinded_tail.unwrap().trampoline_hops;
1119+
let inner_session_priv =
1120+
inner_session_priv.expect("Trampoline hops always have an inner session priv");
1121+
let trampoline_hops =
1122+
construct_onion_keys_generic(secp_ctx, hops, blinded_tail, inner_session_priv);
1123+
for (shared_secret, _, _, trampoline_hop_option, _) in trampoline_hops {
1124+
onion_keys
1125+
.push((trampoline_hop_option.map(|th| ErrorHop::TrampolineHop(th)), shared_secret));
1126+
}
11411127
}
11421128

11431129
// In the best case, paths can be up to 27 hops. But attribution data can only be conveyed back to the sender from
@@ -3191,15 +3177,11 @@ mod tests {
31913177
let path = Path { hops, blinded_tail: None };
31923178

31933179
// Calculate shared secrets.
3194-
let mut onion_keys = Vec::new();
31953180
let session_key = get_test_session_key();
3196-
construct_onion_keys_generic_callback(
3197-
&secp_ctx,
3198-
&path.hops,
3199-
None,
3200-
&session_key,
3201-
|shared_secret, _, _, _, _| onion_keys.push(shared_secret),
3202-
);
3181+
let onion_keys: Vec<_> =
3182+
construct_onion_keys_generic(&secp_ctx, &path.hops, None, &session_key)
3183+
.map(|(key, ..)| key)
3184+
.collect();
32033185

32043186
// Construct the htlc source.
32053187
let logger = TestLogger::new();

0 commit comments

Comments
 (0)