Skip to content

Commit 47caa1e

Browse files
committed
Log cases where an onion failure cannot be attributed or interpreted
Create more visibility into these edge cases. The non-attributable failure in particular can be used to disrupt sender operation and it is therefore good to at least log these cases clearly.
1 parent 447148d commit 47caa1e

File tree

1 file changed

+104
-7
lines changed

1 file changed

+104
-7
lines changed

lightning/src/ln/onion_utils.rs

Lines changed: 104 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,22 +1044,43 @@ where
10441044
let amt_to_forward = htlc_msat - route_hop.fee_msat;
10451045
htlc_msat = amt_to_forward;
10461046

1047-
let err_packet = match decrypt_onion_error_packet(&mut encrypted_packet, shared_secret) {
1048-
Ok(p) => p,
1049-
Err(_) => return,
1050-
};
1047+
let decrypt_result = decrypt_onion_error_packet(&mut encrypted_packet, shared_secret);
1048+
10511049
let um = gen_um_from_shared_secret(shared_secret.as_ref());
10521050
let mut hmac = HmacEngine::<Sha256>::new(&um);
1053-
hmac.input(&err_packet.encode()[32..]);
1051+
hmac.input(&encrypted_packet[32..]);
10541052

1055-
if !fixed_time_eq(&Hmac::from_engine(hmac).to_byte_array(), &err_packet.hmac) {
1053+
if !fixed_time_eq(&Hmac::from_engine(hmac).to_byte_array(), &encrypted_packet[..32]) {
10561054
return;
10571055
}
1056+
1057+
let err_packet = match decrypt_result {
1058+
Ok(p) => p,
1059+
Err(_) => {
1060+
log_warn!(logger, "Unreadable failure from {}", route_hop.pubkey);
1061+
1062+
let network_update = Some(NetworkUpdate::NodeFailure {
1063+
node_id: route_hop.pubkey,
1064+
is_permanent: true,
1065+
});
1066+
let short_channel_id = Some(route_hop.short_channel_id);
1067+
res = Some(FailureLearnings {
1068+
network_update,
1069+
short_channel_id,
1070+
payment_failed_permanently: is_from_final_node,
1071+
failed_within_blinded_path: false,
1072+
});
1073+
return;
1074+
},
1075+
};
1076+
10581077
let error_code_slice = match err_packet.failuremsg.get(0..2) {
10591078
Some(s) => s,
10601079
None => {
10611080
// Useless packet that we can't use but it passed HMAC, so it definitely came from the peer
10621081
// in question
1082+
log_warn!(logger, "Missing error code in failure from {}", route_hop.pubkey);
1083+
10631084
let network_update = Some(NetworkUpdate::NodeFailure {
10641085
node_id: route_hop.pubkey,
10651086
is_permanent: true,
@@ -1219,6 +1240,12 @@ where
12191240
} else {
12201241
// only not set either packet unparseable or hmac does not match with any
12211242
// payment not retryable only when garbage is from the final node
1243+
log_warn!(
1244+
logger,
1245+
"Non-attributable failure encountered on route {}",
1246+
path.hops.iter().map(|h| h.pubkey.to_string()).collect::<Vec<_>>().join("->")
1247+
);
1248+
12221249
DecodedOnionFailure {
12231250
network_update: None,
12241251
short_channel_id: None,
@@ -1768,7 +1795,7 @@ mod tests {
17681795

17691796
use crate::io;
17701797
use crate::ln::channelmanager::PaymentId;
1771-
use crate::ln::msgs;
1798+
use crate::ln::msgs::{self, OnionErrorPacket};
17721799
use crate::routing::router::{Path, PaymentParameters, Route, RouteHop};
17731800
use crate::types::features::{ChannelFeatures, NodeFeatures};
17741801
use crate::types::payment::PaymentHash;
@@ -2155,6 +2182,76 @@ mod tests {
21552182
assert_eq!(decrypted_failure.onion_error_code, Some(0x2002));
21562183
}
21572184

2185+
#[test]
2186+
fn test_non_attributable_failure_packet_onion() {
2187+
// Create a failure packet with bogus data.
2188+
let packet = vec![1u8; 292];
2189+
2190+
// In the current protocol, it is unfortunately not possible to identify the failure source.
2191+
let decrypted_failure = test_failure_attribution(&packet);
2192+
assert_eq!(decrypted_failure.short_channel_id, None);
2193+
}
2194+
2195+
#[test]
2196+
fn test_unreadable_failure_packet_onion() {
2197+
// Create a failure packet with a valid hmac but unreadable failure message.
2198+
let onion_keys: Vec<OnionKeys> = build_test_onion_keys();
2199+
let shared_secret = onion_keys[0].shared_secret.as_ref();
2200+
let um = gen_um_from_shared_secret(&shared_secret);
2201+
2202+
// The failure message is a single 0 byte.
2203+
let mut packet = [0u8; 33];
2204+
2205+
let mut hmac = HmacEngine::<Sha256>::new(&um);
2206+
hmac.input(&packet[32..]);
2207+
let hmac = Hmac::from_engine(hmac).to_byte_array();
2208+
packet[..32].copy_from_slice(&hmac);
2209+
2210+
let packet = encrypt_failure_packet(shared_secret, &packet);
2211+
2212+
// For the unreadable failure, it is still expected that the failing channel can be identified.
2213+
let decrypted_failure = test_failure_attribution(&packet.data);
2214+
assert_eq!(decrypted_failure.short_channel_id, Some(0));
2215+
}
2216+
2217+
#[test]
2218+
fn test_missing_error_code() {
2219+
// Create a failure packet with a valid hmac and structure, but no error code.
2220+
let onion_keys: Vec<OnionKeys> = build_test_onion_keys();
2221+
let shared_secret = onion_keys[0].shared_secret.as_ref();
2222+
let um = gen_um_from_shared_secret(&shared_secret);
2223+
2224+
let failuremsg = vec![1];
2225+
let pad = Vec::new();
2226+
let mut packet = msgs::DecodedOnionErrorPacket { hmac: [0; 32], failuremsg, pad };
2227+
2228+
let mut hmac = HmacEngine::<Sha256>::new(&um);
2229+
hmac.input(&packet.encode()[32..]);
2230+
packet.hmac = Hmac::from_engine(hmac).to_byte_array();
2231+
2232+
let packet = encrypt_failure_packet(shared_secret, &packet.encode()[..]);
2233+
2234+
let decrypted_failure = test_failure_attribution(&packet.data);
2235+
assert_eq!(decrypted_failure.short_channel_id, Some(0));
2236+
}
2237+
2238+
fn test_failure_attribution(packet: &[u8]) -> DecodedOnionFailure {
2239+
let logger: Arc<TestLogger> = Arc::new(TestLogger::new());
2240+
let ctx_full = Secp256k1::new();
2241+
let path = build_test_path();
2242+
let htlc_source = HTLCSource::OutboundRoute {
2243+
path,
2244+
session_priv: get_test_session_key(),
2245+
first_hop_htlc_msat: 0,
2246+
payment_id: PaymentId([1; 32]),
2247+
};
2248+
2249+
let decrypted_failure =
2250+
process_onion_failure(&ctx_full, &logger, &htlc_source, packet.into());
2251+
2252+
decrypted_failure
2253+
}
2254+
21582255
struct RawOnionHopData {
21592256
data: Vec<u8>,
21602257
}

0 commit comments

Comments
 (0)