Skip to content

Commit 25236b0

Browse files
authored
Merge pull request #4269 from TheBlueMatt/2025-12-dont-seal-structs-0.2-bindings
[0.2-bindings] Make `AttributionData` actually pub since its used in the public API
2 parents c46aa23 + 7be45d5 commit 25236b0

File tree

5 files changed

+112
-114
lines changed

5 files changed

+112
-114
lines changed

fuzz/src/process_onion_failure.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ use lightning::{
99
ln::{
1010
channelmanager::{HTLCSource, PaymentId},
1111
msgs::OnionErrorPacket,
12+
onion_utils,
1213
},
1314
routing::router::{BlindedTail, Path, RouteHop, TrampolineHop},
1415
types::features::{ChannelFeatures, NodeFeatures},
1516
util::logger::Logger,
17+
util::ser::Readable,
1618
};
1719

1820
// Imports that need to be added manually
@@ -126,19 +128,18 @@ fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
126128
let failure_data = get_slice!(failure_len);
127129

128130
let attribution_data = if get_bool!() {
129-
Some(lightning::ln::AttributionData {
130-
hold_times: get_slice!(80).try_into().unwrap(),
131-
hmacs: get_slice!(840).try_into().unwrap(),
132-
})
131+
let mut bytes = get_slice!(80 + 840);
132+
let data: onion_utils::AttributionData = Readable::read(&mut bytes).unwrap();
133+
Some(data)
133134
} else {
134135
None
135136
};
136137
let encrypted_packet =
137138
OnionErrorPacket { data: failure_data.into(), attribution_data: attribution_data.clone() };
138-
lightning::ln::process_onion_failure(&secp_ctx, &logger, &htlc_source, encrypted_packet);
139+
onion_utils::process_onion_failure(&secp_ctx, &logger, &htlc_source, encrypted_packet);
139140

140141
if let Some(attribution_data) = attribution_data {
141-
lightning::ln::decode_fulfill_attribution_data(
142+
onion_utils::decode_fulfill_attribution_data(
142143
&secp_ctx,
143144
&logger,
144145
&path,

lightning/src/events/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ use crate::blinded_path::payment::{
2525
use crate::chain::transaction;
2626
use crate::ln::channel::FUNDING_CONF_DEADLINE_BLOCKS;
2727
use crate::ln::channelmanager::{InterceptId, PaymentId, RecipientOnionFields};
28-
use crate::ln::types::ChannelId;
2928
use crate::ln::onion_utils::LocalHTLCFailureReason;
29+
use crate::ln::types::ChannelId;
3030
use crate::ln::msgs;
3131
use crate::offers::invoice::Bolt12Invoice;
3232
use crate::offers::invoice_request::InvoiceRequest;

lightning/src/ln/mod.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,6 @@ pub(crate) mod interactivetxs;
5252
// without the node parameter being mut. This is incorrect, and thus newer rustcs will complain
5353
// about an unnecessary mut. Thus, we silence the unused_mut warning in two test modules below.
5454

55-
#[cfg(fuzzing)]
56-
pub use onion_utils::decode_fulfill_attribution_data;
57-
#[cfg(fuzzing)]
58-
pub use onion_utils::process_onion_failure;
59-
60-
#[cfg(fuzzing)]
61-
pub use onion_utils::AttributionData;
62-
6355
#[cfg(test)]
6456
#[allow(unused_mut)]
6557
mod async_payments_tests;

lightning/src/ln/msgs.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4354,7 +4354,7 @@ mod tests {
43544354
InboundOnionForwardPayload, InboundOnionReceivePayload, OutboundTrampolinePayload,
43554355
TrampolineOnionPacket,
43564356
};
4357-
use crate::ln::onion_utils::{AttributionData, HMAC_COUNT, HMAC_LEN, HOLD_TIME_LEN, MAX_HOPS};
4357+
use crate::ln::onion_utils::AttributionData;
43584358
use crate::ln::types::ChannelId;
43594359
use crate::routing::gossip::{NodeAlias, NodeId};
43604360
use crate::types::features::{
@@ -5887,13 +5887,10 @@ mod tests {
58875887
channel_id: ChannelId::from_bytes([2; 32]),
58885888
htlc_id: 2316138423780173,
58895889
reason: [1; 32].to_vec(),
5890-
attribution_data: Some(AttributionData {
5891-
hold_times: [3; MAX_HOPS * HOLD_TIME_LEN],
5892-
hmacs: [3; HMAC_LEN * HMAC_COUNT],
5893-
}),
5890+
attribution_data: Some(AttributionData::new()),
58945891
};
58955892
let encoded_value = update_fail_htlc.encode();
5896-
let target_value = <Vec<u8>>::from_hex("020202020202020202020202020202020202020202020202020202020202020200083a840000034d0020010101010101010101010101010101010101010101010101010101010101010101fd03980303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303").unwrap();
5893+
let target_value = <Vec<u8>>::from_hex("020202020202020202020202020202020202020202020202020202020202020200083a840000034d0020010101010101010101010101010101010101010101010101010101010101010101fd03980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap();
58975894
assert_eq!(encoded_value, target_value);
58985895
}
58995896

lightning/src/ln/onion_utils.rs

Lines changed: 101 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// You may not use this file except in accordance with one or both of these
88
// licenses.
99

10-
//! Utilities for handling and manipulating onions
10+
//! Low-level onion manipulation logic and fields
1111
1212
use super::msgs::OnionErrorPacket;
1313
use crate::blinded_path::BlindedHop;
@@ -981,49 +981,100 @@ mod fuzzy_onion_utils {
981981
#[cfg(test)]
982982
pub(crate) attribution_failed_channel: Option<u64>,
983983
}
984+
985+
pub fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
986+
secp_ctx: &Secp256k1<T>, logger: &L, htlc_source: &HTLCSource,
987+
encrypted_packet: OnionErrorPacket,
988+
) -> DecodedOnionFailure
989+
where
990+
L::Target: Logger,
991+
{
992+
let (path, primary_session_priv) = match htlc_source {
993+
HTLCSource::OutboundRoute { ref path, ref session_priv, .. } => (path, session_priv),
994+
_ => unreachable!(),
995+
};
996+
997+
if path.has_trampoline_hops() {
998+
// If we have Trampoline hops, the outer onion session_priv is a hash of the inner one.
999+
let session_priv_hash =
1000+
Sha256::hash(&primary_session_priv.secret_bytes()).to_byte_array();
1001+
let outer_session_priv =
1002+
SecretKey::from_slice(&session_priv_hash[..]).expect("You broke SHA-256!");
1003+
process_onion_failure_inner(
1004+
secp_ctx,
1005+
logger,
1006+
path,
1007+
&outer_session_priv,
1008+
Some(primary_session_priv),
1009+
encrypted_packet,
1010+
)
1011+
} else {
1012+
process_onion_failure_inner(
1013+
secp_ctx,
1014+
logger,
1015+
path,
1016+
primary_session_priv,
1017+
None,
1018+
encrypted_packet,
1019+
)
1020+
}
1021+
}
1022+
1023+
/// Decodes the attribution data that we got back from upstream on a payment we sent.
1024+
pub fn decode_fulfill_attribution_data<T: secp256k1::Signing, L: Deref>(
1025+
secp_ctx: &Secp256k1<T>, logger: &L, path: &Path, outer_session_priv: &SecretKey,
1026+
mut attribution_data: AttributionData,
1027+
) -> Vec<u32>
1028+
where
1029+
L::Target: Logger,
1030+
{
1031+
let mut hold_times = Vec::new();
1032+
1033+
// Only consider hops in the regular path for attribution data. Blinded path attribution data isn't accessible.
1034+
let shared_secrets =
1035+
construct_onion_keys_generic(secp_ctx, &path.hops, None, outer_session_priv)
1036+
.map(|(shared_secret, _, _, _, _)| shared_secret);
1037+
1038+
// Path length can reach 27 hops, but attribution data can only be conveyed back to the sender from the first 20
1039+
// hops. Determine the number of hops to be used for attribution data.
1040+
let attributable_hop_count = usize::min(path.hops.len(), MAX_HOPS);
1041+
1042+
for (route_hop_idx, shared_secret) in shared_secrets.enumerate().take(attributable_hop_count) {
1043+
attribution_data.crypt(shared_secret.as_ref());
1044+
1045+
// Calculate position relative to the last attributable hop. The last attributable hop is at position 0. We need
1046+
// to look at the chain of HMACs that does include all data up to the last attributable hop. Hold times beyond
1047+
// the last attributable hop will not be available.
1048+
let position = attributable_hop_count - route_hop_idx - 1;
1049+
let res = attribution_data.verify(&Vec::new(), shared_secret.as_ref(), position);
1050+
match res {
1051+
Ok(hold_time) => {
1052+
hold_times.push(hold_time);
1053+
1054+
// Shift attribution data to prepare for processing the next hop.
1055+
attribution_data.shift_left();
1056+
},
1057+
Err(()) => {
1058+
// We will hit this if there is a node on the path that does not support fulfill attribution data.
1059+
log_debug!(
1060+
logger,
1061+
"Invalid fulfill HMAC in attribution data for node at pos {}",
1062+
route_hop_idx
1063+
);
1064+
1065+
break;
1066+
},
1067+
}
1068+
}
1069+
1070+
hold_times
1071+
}
9841072
}
9851073
#[cfg(fuzzing)]
9861074
pub use self::fuzzy_onion_utils::*;
9871075
#[cfg(not(fuzzing))]
9881076
pub(crate) use self::fuzzy_onion_utils::*;
9891077

990-
pub(crate) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
991-
secp_ctx: &Secp256k1<T>, logger: &L, htlc_source: &HTLCSource,
992-
encrypted_packet: OnionErrorPacket,
993-
) -> DecodedOnionFailure
994-
where
995-
L::Target: Logger,
996-
{
997-
let (path, primary_session_priv) = match htlc_source {
998-
HTLCSource::OutboundRoute { ref path, ref session_priv, .. } => (path, session_priv),
999-
_ => unreachable!(),
1000-
};
1001-
1002-
if path.has_trampoline_hops() {
1003-
// If we have Trampoline hops, the outer onion session_priv is a hash of the inner one.
1004-
let session_priv_hash = Sha256::hash(&primary_session_priv.secret_bytes()).to_byte_array();
1005-
let outer_session_priv =
1006-
SecretKey::from_slice(&session_priv_hash[..]).expect("You broke SHA-256!");
1007-
process_onion_failure_inner(
1008-
secp_ctx,
1009-
logger,
1010-
path,
1011-
&outer_session_priv,
1012-
Some(primary_session_priv),
1013-
encrypted_packet,
1014-
)
1015-
} else {
1016-
process_onion_failure_inner(
1017-
secp_ctx,
1018-
logger,
1019-
path,
1020-
primary_session_priv,
1021-
None,
1022-
encrypted_packet,
1023-
)
1024-
}
1025-
}
1026-
10271078
/// Process failure we got back from upstream on a payment we sent (implying htlc_source is an
10281079
/// OutboundRoute).
10291080
fn process_onion_failure_inner<T: secp256k1::Signing, L: Deref>(
@@ -1468,56 +1519,6 @@ where
14681519
}
14691520
}
14701521

1471-
/// Decodes the attribution data that we got back from upstream on a payment we sent.
1472-
pub(crate) fn decode_fulfill_attribution_data<T: secp256k1::Signing, L: Deref>(
1473-
secp_ctx: &Secp256k1<T>, logger: &L, path: &Path, outer_session_priv: &SecretKey,
1474-
mut attribution_data: AttributionData,
1475-
) -> Vec<u32>
1476-
where
1477-
L::Target: Logger,
1478-
{
1479-
let mut hold_times = Vec::new();
1480-
1481-
// Only consider hops in the regular path for attribution data. Blinded path attribution data isn't accessible.
1482-
let shared_secrets =
1483-
construct_onion_keys_generic(secp_ctx, &path.hops, None, outer_session_priv)
1484-
.map(|(shared_secret, _, _, _, _)| shared_secret);
1485-
1486-
// Path length can reach 27 hops, but attribution data can only be conveyed back to the sender from the first 20
1487-
// hops. Determine the number of hops to be used for attribution data.
1488-
let attributable_hop_count = usize::min(path.hops.len(), MAX_HOPS);
1489-
1490-
for (route_hop_idx, shared_secret) in shared_secrets.enumerate().take(attributable_hop_count) {
1491-
attribution_data.crypt(shared_secret.as_ref());
1492-
1493-
// Calculate position relative to the last attributable hop. The last attributable hop is at position 0. We need
1494-
// to look at the chain of HMACs that does include all data up to the last attributable hop. Hold times beyond
1495-
// the last attributable hop will not be available.
1496-
let position = attributable_hop_count - route_hop_idx - 1;
1497-
let res = attribution_data.verify(&Vec::new(), shared_secret.as_ref(), position);
1498-
match res {
1499-
Ok(hold_time) => {
1500-
hold_times.push(hold_time);
1501-
1502-
// Shift attribution data to prepare for processing the next hop.
1503-
attribution_data.shift_left();
1504-
},
1505-
Err(()) => {
1506-
// We will hit this if there is a node on the path that does not support fulfill attribution data.
1507-
log_debug!(
1508-
logger,
1509-
"Invalid fulfill HMAC in attribution data for node at pos {}",
1510-
route_hop_idx
1511-
);
1512-
1513-
break;
1514-
},
1515-
}
1516-
}
1517-
1518-
hold_times
1519-
}
1520-
15211522
const BADONION: u16 = 0x8000;
15221523
const PERM: u16 = 0x4000;
15231524
const NODE: u16 = 0x2000;
@@ -2522,6 +2523,7 @@ where
25222523
}
25232524

25242525
/// Build a payment onion, returning the first hop msat and cltv values as well.
2526+
///
25252527
/// `cur_block_height` should be set to the best known block height + 1.
25262528
pub fn create_payment_onion<T: secp256k1::Signing>(
25272529
secp_ctx: &Secp256k1<T>, path: &Path, session_priv: &SecretKey, total_msat: u64,
@@ -2719,13 +2721,19 @@ pub(crate) const HMAC_LEN: usize = 4;
27192721
pub(crate) const HMAC_COUNT: usize = MAX_HOPS * (MAX_HOPS + 1) / 2;
27202722

27212723
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
2722-
pub(crate) struct AttributionData {
2723-
pub hold_times: [u8; MAX_HOPS * HOLD_TIME_LEN],
2724-
pub hmacs: [u8; HMAC_LEN * HMAC_COUNT],
2724+
/// Attribution data allows the sender of an HTLC to identify which hop failed an HTLC robustly,
2725+
/// preventing earlier hops from corrupting the HTLC failure information (or at least allowing the
2726+
/// sender to identify the earliest hop which corrupted HTLC failure information).
2727+
///
2728+
/// Additionally, it allows a sender to identify how long each hop along a path held an HTLC, with
2729+
/// 100ms granularity.
2730+
pub struct AttributionData {
2731+
hold_times: [u8; MAX_HOPS * HOLD_TIME_LEN],
2732+
hmacs: [u8; HMAC_LEN * HMAC_COUNT],
27252733
}
27262734

27272735
impl AttributionData {
2728-
pub fn new() -> Self {
2736+
pub(crate) fn new() -> Self {
27292737
Self { hold_times: [0; MAX_HOPS * HOLD_TIME_LEN], hmacs: [0; HMAC_LEN * HMAC_COUNT] }
27302738
}
27312739
}
@@ -2774,7 +2782,7 @@ impl AttributionData {
27742782

27752783
/// Writes the HMACs corresponding to the given position that have been added already by downstream hops. Position is
27762784
/// relative to the final node. The final node is at position 0.
2777-
pub fn write_downstream_hmacs(&self, position: usize, w: &mut HmacEngine<Sha256>) {
2785+
pub(crate) fn write_downstream_hmacs(&self, position: usize, w: &mut HmacEngine<Sha256>) {
27782786
// Set the index to the first downstream HMAC that we need to include. Note that we skip the first MAX_HOPS HMACs
27792787
// because this is space reserved for the HMACs that we are producing for the current node.
27802788
let mut hmac_idx = MAX_HOPS + MAX_HOPS - position - 1;

0 commit comments

Comments
 (0)