Blame outbound channel on UPDATE onion failure with 0-len update
authorValentine Wallace <vwallace@protonmail.com>
Fri, 15 Sep 2023 20:55:12 +0000 (16:55 -0400)
committerValentine Wallace <vwallace@protonmail.com>
Fri, 22 Sep 2023 19:56:28 +0000 (15:56 -0400)
We've run into this several times in the wild, likely due to
https://github.com/ElementsProject/lightning/issues/6200 wherein a node on the
path will error with 0x1000 but not provide a channel update (a spec
violation).

Previously, we would blame the inbound edge even though the buggy peer wanted
us to blame the outbound edge. Since this issue seems to be recurring and our
blaming the inbound edge is causing us to punish innocent channels, trust the
peer that the outbound edge is the one to blame.

lightning/src/ln/onion_route_tests.rs
lightning/src/ln/onion_utils.rs

index 3731c31c5b42c372663f29e31d30b6f6207ea811..c124360d1ee47f2112c03fe9227bab4ec4455d07 100644 (file)
@@ -645,8 +645,34 @@ fn test_onion_failure() {
                }, || nodes[2].node.fail_htlc_backwards(&payment_hash), false, None,
                Some(NetworkUpdate::NodeFailure { node_id: route.paths[0].hops[1].pubkey, is_permanent: true }),
                Some(channels[1].0.contents.short_channel_id));
-       run_onion_failure_test_with_fail_intercept("0-length channel update in UPDATE onion failure", 200, &nodes,
-               &route, &payment_hash, &payment_secret, |_msg| {}, |msg| {
+       run_onion_failure_test_with_fail_intercept("0-length channel update in intermediate node UPDATE onion failure",
+               100, &nodes, &route, &payment_hash, &payment_secret, |msg| {
+                       msg.amount_msat -= 1;
+               }, |msg| {
+                       let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
+                       let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
+                       let mut decoded_err_packet = msgs::DecodedOnionErrorPacket {
+                               failuremsg: vec![
+                                       0x10, 0x7, // UPDATE|7
+                                       0x0, 0x0 // 0-len channel update
+                               ],
+                               pad: vec![0; 255 - 4 /* 4-byte error message */],
+                               hmac: [0; 32],
+                       };
+                       let um = onion_utils::gen_um_from_shared_secret(&onion_keys[0].shared_secret.as_ref());
+                       let mut hmac = HmacEngine::<Sha256>::new(&um);
+                       hmac.input(&decoded_err_packet.encode()[32..]);
+                       decoded_err_packet.hmac = Hmac::from_engine(hmac).into_inner();
+                       msg.reason = onion_utils::encrypt_failure_packet(
+                               &onion_keys[0].shared_secret.as_ref(), &decoded_err_packet.encode()[..])
+               }, || {}, true, Some(0x1000|7),
+               Some(NetworkUpdate::ChannelFailure {
+                       short_channel_id: channels[1].0.contents.short_channel_id,
+                       is_permanent: false,
+               }),
+               Some(channels[1].0.contents.short_channel_id));
+       run_onion_failure_test_with_fail_intercept("0-length channel update in final node UPDATE onion failure",
+               200, &nodes, &route, &payment_hash, &payment_secret, |_msg| {}, |msg| {
                        let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
                        let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
                        let mut decoded_err_packet = msgs::DecodedOnionErrorPacket {
index f3f8608604d3230ee113996a21f64d36720cd49a..390826e4f3d2938b32a69855b8b70eab2d476e3e 100644 (file)
@@ -641,8 +641,9 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
                                                        } else {
                                                                // The node in question intentionally encoded a 0-length channel update. This is
                                                                // likely due to https://github.com/ElementsProject/lightning/issues/6200.
+                                                               short_channel_id = Some(failing_route_hop.short_channel_id);
                                                                network_update = Some(NetworkUpdate::ChannelFailure {
-                                                                       short_channel_id: route_hop.short_channel_id,
+                                                                       short_channel_id: failing_route_hop.short_channel_id,
                                                                        is_permanent: false,
                                                                });
                                                        }