|
7 | 7 | // You may not use this file except in accordance with one or both of these |
8 | 8 | // licenses. |
9 | 9 |
|
| 10 | +//! Low-level onion manipulation logic and fields |
| 11 | +
|
10 | 12 | use super::msgs::OnionErrorPacket; |
11 | 13 | use crate::blinded_path::BlindedHop; |
12 | 14 | use crate::crypto::chacha20::ChaCha20; |
@@ -979,27 +981,79 @@ mod fuzzy_onion_utils { |
979 | 981 | #[cfg(test)] |
980 | 982 | pub(crate) attribution_failed_channel: Option<u64>, |
981 | 983 | } |
| 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, session_priv) = match htlc_source { |
| 993 | + HTLCSource::OutboundRoute { ref path, ref session_priv, .. } => (path, session_priv), |
| 994 | + _ => unreachable!(), |
| 995 | + }; |
| 996 | + |
| 997 | + process_onion_failure_inner(secp_ctx, logger, path, &session_priv, None, encrypted_packet) |
| 998 | + } |
| 999 | + |
| 1000 | + /// Decodes the attribution data that we got back from upstream on a payment we sent. |
| 1001 | + pub fn decode_fulfill_attribution_data<T: secp256k1::Signing, L: Deref>( |
| 1002 | + secp_ctx: &Secp256k1<T>, logger: &L, path: &Path, outer_session_priv: &SecretKey, |
| 1003 | + mut attribution_data: AttributionData, |
| 1004 | + ) -> Vec<u32> |
| 1005 | + where |
| 1006 | + L::Target: Logger, |
| 1007 | + { |
| 1008 | + let mut hold_times = Vec::new(); |
| 1009 | + |
| 1010 | + // Only consider hops in the regular path for attribution data. Blinded path attribution data isn't accessible. |
| 1011 | + let shared_secrets = |
| 1012 | + construct_onion_keys_generic(secp_ctx, &path.hops, None, outer_session_priv) |
| 1013 | + .map(|(shared_secret, _, _, _, _)| shared_secret); |
| 1014 | + |
| 1015 | + // Path length can reach 27 hops, but attribution data can only be conveyed back to the sender from the first 20 |
| 1016 | + // hops. Determine the number of hops to be used for attribution data. |
| 1017 | + let attributable_hop_count = usize::min(path.hops.len(), MAX_HOPS); |
| 1018 | + |
| 1019 | + for (route_hop_idx, shared_secret) in |
| 1020 | + shared_secrets.enumerate().take(attributable_hop_count) |
| 1021 | + { |
| 1022 | + attribution_data.crypt(shared_secret.as_ref()); |
| 1023 | + |
| 1024 | + // Calculate position relative to the last attributable hop. The last attributable hop is at position 0. We need |
| 1025 | + // to look at the chain of HMACs that does include all data up to the last attributable hop. Hold times beyond |
| 1026 | + // the last attributable hop will not be available. |
| 1027 | + let position = attributable_hop_count - route_hop_idx - 1; |
| 1028 | + let res = attribution_data.verify(&Vec::new(), shared_secret.as_ref(), position); |
| 1029 | + match res { |
| 1030 | + Ok(hold_time) => { |
| 1031 | + hold_times.push(hold_time); |
| 1032 | + |
| 1033 | + // Shift attribution data to prepare for processing the next hop. |
| 1034 | + attribution_data.shift_left(); |
| 1035 | + }, |
| 1036 | + Err(()) => { |
| 1037 | + // We will hit this if there is a node on the path that does not support fulfill attribution data. |
| 1038 | + log_debug!( |
| 1039 | + logger, |
| 1040 | + "Invalid fulfill HMAC in attribution data for node at pos {}", |
| 1041 | + route_hop_idx |
| 1042 | + ); |
| 1043 | + |
| 1044 | + break; |
| 1045 | + }, |
| 1046 | + } |
| 1047 | + } |
| 1048 | + |
| 1049 | + hold_times |
| 1050 | + } |
982 | 1051 | } |
983 | 1052 | #[cfg(fuzzing)] |
984 | 1053 | pub use self::fuzzy_onion_utils::*; |
985 | 1054 | #[cfg(not(fuzzing))] |
986 | 1055 | pub(crate) use self::fuzzy_onion_utils::*; |
987 | 1056 |
|
988 | | -pub fn process_onion_failure<T: secp256k1::Signing, L: Deref>( |
989 | | - secp_ctx: &Secp256k1<T>, logger: &L, htlc_source: &HTLCSource, |
990 | | - encrypted_packet: OnionErrorPacket, |
991 | | -) -> DecodedOnionFailure |
992 | | -where |
993 | | - L::Target: Logger, |
994 | | -{ |
995 | | - let (path, session_priv) = match htlc_source { |
996 | | - HTLCSource::OutboundRoute { ref path, ref session_priv, .. } => (path, session_priv), |
997 | | - _ => unreachable!(), |
998 | | - }; |
999 | | - |
1000 | | - process_onion_failure_inner(secp_ctx, logger, path, &session_priv, None, encrypted_packet) |
1001 | | -} |
1002 | | - |
1003 | 1057 | /// Process failure we got back from upstream on a payment we sent (implying htlc_source is an |
1004 | 1058 | /// OutboundRoute). |
1005 | 1059 | fn process_onion_failure_inner<T: secp256k1::Signing, L: Deref>( |
@@ -1449,56 +1503,6 @@ where |
1449 | 1503 | } |
1450 | 1504 | } |
1451 | 1505 |
|
1452 | | -/// Decodes the attribution data that we got back from upstream on a payment we sent. |
1453 | | -pub fn decode_fulfill_attribution_data<T: secp256k1::Signing, L: Deref>( |
1454 | | - secp_ctx: &Secp256k1<T>, logger: &L, path: &Path, outer_session_priv: &SecretKey, |
1455 | | - mut attribution_data: AttributionData, |
1456 | | -) -> Vec<u32> |
1457 | | -where |
1458 | | - L::Target: Logger, |
1459 | | -{ |
1460 | | - let mut hold_times = Vec::new(); |
1461 | | - |
1462 | | - // Only consider hops in the regular path for attribution data. Blinded path attribution data isn't accessible. |
1463 | | - let shared_secrets = |
1464 | | - construct_onion_keys_generic(secp_ctx, &path.hops, None, outer_session_priv) |
1465 | | - .map(|(shared_secret, _, _, _, _)| shared_secret); |
1466 | | - |
1467 | | - // Path length can reach 27 hops, but attribution data can only be conveyed back to the sender from the first 20 |
1468 | | - // hops. Determine the number of hops to be used for attribution data. |
1469 | | - let attributable_hop_count = usize::min(path.hops.len(), MAX_HOPS); |
1470 | | - |
1471 | | - for (route_hop_idx, shared_secret) in shared_secrets.enumerate().take(attributable_hop_count) { |
1472 | | - attribution_data.crypt(shared_secret.as_ref()); |
1473 | | - |
1474 | | - // Calculate position relative to the last attributable hop. The last attributable hop is at position 0. We need |
1475 | | - // to look at the chain of HMACs that does include all data up to the last attributable hop. Hold times beyond |
1476 | | - // the last attributable hop will not be available. |
1477 | | - let position = attributable_hop_count - route_hop_idx - 1; |
1478 | | - let res = attribution_data.verify(&Vec::new(), shared_secret.as_ref(), position); |
1479 | | - match res { |
1480 | | - Ok(hold_time) => { |
1481 | | - hold_times.push(hold_time); |
1482 | | - |
1483 | | - // Shift attribution data to prepare for processing the next hop. |
1484 | | - attribution_data.shift_left(); |
1485 | | - }, |
1486 | | - Err(()) => { |
1487 | | - // We will hit this if there is a node on the path that does not support fulfill attribution data. |
1488 | | - log_debug!( |
1489 | | - logger, |
1490 | | - "Invalid fulfill HMAC in attribution data for node at pos {}", |
1491 | | - route_hop_idx |
1492 | | - ); |
1493 | | - |
1494 | | - break; |
1495 | | - }, |
1496 | | - } |
1497 | | - } |
1498 | | - |
1499 | | - hold_times |
1500 | | -} |
1501 | | - |
1502 | 1506 | const BADONION: u16 = 0x8000; |
1503 | 1507 | const PERM: u16 = 0x4000; |
1504 | 1508 | const NODE: u16 = 0x2000; |
@@ -2522,6 +2526,7 @@ where |
2522 | 2526 | } |
2523 | 2527 |
|
2524 | 2528 | /// Build a payment onion, returning the first hop msat and cltv values as well. |
| 2529 | +/// |
2525 | 2530 | /// `cur_block_height` should be set to the best known block height + 1. |
2526 | 2531 | pub fn create_payment_onion<T: secp256k1::Signing>( |
2527 | 2532 | secp_ctx: &Secp256k1<T>, path: &Path, session_priv: &SecretKey, total_msat: u64, |
@@ -2711,22 +2716,28 @@ fn decode_next_hop<T, R: ReadableArgs<T>, N: NextPacketBytes>( |
2711 | 2716 | } |
2712 | 2717 | } |
2713 | 2718 |
|
2714 | | -pub const HOLD_TIME_LEN: usize = 4; |
2715 | | -pub const MAX_HOPS: usize = 20; |
2716 | | -pub const HMAC_LEN: usize = 4; |
| 2719 | +pub(crate) const HOLD_TIME_LEN: usize = 4; |
| 2720 | +pub(crate) const MAX_HOPS: usize = 20; |
| 2721 | +pub(crate) const HMAC_LEN: usize = 4; |
2717 | 2722 |
|
2718 | 2723 | // Define the number of HMACs in the attributable data block. For the first node, there are 20 HMACs, and then for every |
2719 | 2724 | // subsequent node, the number of HMACs decreases by 1. 20 + 19 + 18 + ... + 1 = 20 * 21 / 2 = 210. |
2720 | | -pub const HMAC_COUNT: usize = MAX_HOPS * (MAX_HOPS + 1) / 2; |
| 2725 | +pub(crate) const HMAC_COUNT: usize = MAX_HOPS * (MAX_HOPS + 1) / 2; |
2721 | 2726 |
|
2722 | 2727 | #[derive(Clone, Debug, Hash, PartialEq, Eq)] |
| 2728 | +/// Attribution data allows the sender of an HTLC to identify which hop failed an HTLC robustly, |
| 2729 | +/// preventing earlier hops from corrupting the HTLC failure information (or at least allowing the |
| 2730 | +/// sender to identify the earliest hop which corrupted HTLC failure information). |
| 2731 | +/// |
| 2732 | +/// Additionally, it allows a sender to identify how long each hop along a path held an HTLC, with |
| 2733 | +/// 100ms granularity. |
2723 | 2734 | pub struct AttributionData { |
2724 | | - pub hold_times: [u8; MAX_HOPS * HOLD_TIME_LEN], |
2725 | | - pub hmacs: [u8; HMAC_LEN * HMAC_COUNT], |
| 2735 | + hold_times: [u8; MAX_HOPS * HOLD_TIME_LEN], |
| 2736 | + hmacs: [u8; HMAC_LEN * HMAC_COUNT], |
2726 | 2737 | } |
2727 | 2738 |
|
2728 | 2739 | impl AttributionData { |
2729 | | - pub fn new() -> Self { |
| 2740 | + pub(crate) fn new() -> Self { |
2730 | 2741 | Self { hold_times: [0; MAX_HOPS * HOLD_TIME_LEN], hmacs: [0; HMAC_LEN * HMAC_COUNT] } |
2731 | 2742 | } |
2732 | 2743 | } |
@@ -2775,7 +2786,7 @@ impl AttributionData { |
2775 | 2786 |
|
2776 | 2787 | /// Writes the HMACs corresponding to the given position that have been added already by downstream hops. Position is |
2777 | 2788 | /// relative to the final node. The final node is at position 0. |
2778 | | - pub fn write_downstream_hmacs(&self, position: usize, w: &mut HmacEngine<Sha256>) { |
| 2789 | + pub(crate) fn write_downstream_hmacs(&self, position: usize, w: &mut HmacEngine<Sha256>) { |
2779 | 2790 | // Set the index to the first downstream HMAC that we need to include. Note that we skip the first MAX_HOPS HMACs |
2780 | 2791 | // because this is space reserved for the HMACs that we are producing for the current node. |
2781 | 2792 | let mut hmac_idx = MAX_HOPS + MAX_HOPS - position - 1; |
|
0 commit comments