Skip to content

Commit 8fbd174

Browse files
committed
Attributable failures
This commit extends the generation, forwarding and interpretation of htlc fail messages with attribution data. This allows senders to identify the failing node even when this node does not want to be identified and is generating a failure message without a valid hmac. For more information, see the bolt spec.
1 parent a9a59f8 commit 8fbd174

File tree

3 files changed

+429
-71
lines changed

3 files changed

+429
-71
lines changed

lightning/src/ln/functional_tests.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ use crate::util::errors::APIError;
3838
use crate::util::ser::{Writeable, ReadableArgs};
3939
use crate::util::string::UntrustedString;
4040
use crate::util::config::{ChannelConfigOverrides, ChannelHandshakeConfigUpdate, ChannelConfigUpdate, MaxDustHTLCExposure, UserConfig};
41+
use crate::ln::onion_utils::AttributionData;
4142

4243
use bitcoin::hash_types::BlockHash;
4344
use bitcoin::locktime::absolute::LockTime;
@@ -7060,7 +7061,7 @@ pub fn test_update_fulfill_htlc_bolt2_update_fail_htlc_before_commitment() {
70607061
channel_id: chan.2,
70617062
htlc_id: 0,
70627063
reason: Vec::new(),
7063-
attribution_data: None,
7064+
attribution_data: Some(AttributionData::new())
70647065
};
70657066

70667067
nodes[0].node.handle_update_fail_htlc(nodes[1].node.get_our_node_id(), &update_msg);

lightning/src/ln/onion_route_tests.rs

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ use crate::ln::functional_test_utils::*;
5050
use crate::ln::onion_utils::{construct_trampoline_onion_keys, construct_trampoline_onion_packet};
5151

5252
use super::msgs::OnionErrorPacket;
53+
use super::onion_utils::{add_hmacs, AttributionData};
5354

5455
fn run_onion_failure_test<F1,F2>(_name: &str, test_case: u8, nodes: &Vec<Node>, route: &Route, payment_hash: &PaymentHash, payment_secret: &PaymentSecret, callback_msg: F1, callback_node: F2, expected_retryable: bool, expected_error_code: Option<u16>, expected_channel_update: Option<NetworkUpdate>, expected_short_channel_id: Option<u64>, expected_htlc_destination: Option<HTLCDestination>)
5556
where F1: for <'a> FnMut(&'a mut msgs::UpdateAddHTLC),
@@ -411,17 +412,19 @@ fn test_onion_failure() {
411412
// and tamper returning error message
412413
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
413414
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
414-
let failure = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), NODE|2, &[0;0]);
415+
let failure = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), NODE|2, &[0;0], 0);
415416
msg.reason = failure.data;
417+
msg.attribution_data = failure.attribution_data;
416418
}, ||{}, true, Some(NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0].hops[0].pubkey, is_permanent: false}), Some(route.paths[0].hops[0].short_channel_id), Some(next_hop_failure.clone()));
417419

418420
// final node failure
419421
run_onion_failure_test_with_fail_intercept("temporary_node_failure", 200, &nodes, &route, &payment_hash, &payment_secret, |_msg| {}, |msg| {
420422
// and tamper returning error message
421423
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
422424
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
423-
let failure = onion_utils::build_failure_packet(onion_keys[1].shared_secret.as_ref(), NODE|2, &[0;0]);
425+
let failure = onion_utils::build_failure_packet(onion_keys[1].shared_secret.as_ref(), NODE|2, &[0;0], 0);
424426
msg.reason = failure.data;
427+
msg.attribution_data = failure.attribution_data;
425428
}, ||{
426429
nodes[2].node.fail_htlc_backwards(&payment_hash);
427430
}, true, Some(NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0].hops[1].pubkey, is_permanent: false}), Some(route.paths[0].hops[1].short_channel_id), None);
@@ -433,16 +436,18 @@ fn test_onion_failure() {
433436
}, |msg| {
434437
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
435438
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
436-
let failure = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|NODE|2, &[0;0]);
439+
let failure = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|NODE|2, &[0;0], 0);
437440
msg.reason = failure.data;
441+
msg.attribution_data = failure.attribution_data;
438442
}, ||{}, true, Some(PERM|NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0].hops[0].pubkey, is_permanent: true}), Some(route.paths[0].hops[0].short_channel_id), Some(next_hop_failure.clone()));
439443

440444
// final node failure
441445
run_onion_failure_test_with_fail_intercept("permanent_node_failure", 200, &nodes, &route, &payment_hash, &payment_secret, |_msg| {}, |msg| {
442446
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
443447
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
444-
let failure = onion_utils::build_failure_packet(onion_keys[1].shared_secret.as_ref(), PERM|NODE|2, &[0;0]);
448+
let failure = onion_utils::build_failure_packet(onion_keys[1].shared_secret.as_ref(), PERM|NODE|2, &[0;0], 0);
445449
msg.reason = failure.data;
450+
msg.attribution_data = failure.attribution_data;
446451
}, ||{
447452
nodes[2].node.fail_htlc_backwards(&payment_hash);
448453
}, false, Some(PERM|NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0].hops[1].pubkey, is_permanent: true}), Some(route.paths[0].hops[1].short_channel_id), None);
@@ -454,8 +459,9 @@ fn test_onion_failure() {
454459
}, |msg| {
455460
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
456461
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
457-
let failure = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|NODE|3, &[0;0]);
462+
let failure = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|NODE|3, &[0;0], 0);
458463
msg.reason = failure.data;
464+
msg.attribution_data = failure.attribution_data;
459465
}, ||{
460466
nodes[2].node.fail_htlc_backwards(&payment_hash);
461467
}, true, Some(PERM|NODE|3), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0].hops[0].pubkey, is_permanent: true}), Some(route.paths[0].hops[0].short_channel_id), Some(next_hop_failure.clone()));
@@ -464,8 +470,9 @@ fn test_onion_failure() {
464470
run_onion_failure_test_with_fail_intercept("required_node_feature_missing", 200, &nodes, &route, &payment_hash, &payment_secret, |_msg| {}, |msg| {
465471
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
466472
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
467-
let failure = onion_utils::build_failure_packet(onion_keys[1].shared_secret.as_ref(), PERM|NODE|3, &[0;0]);
473+
let failure = onion_utils::build_failure_packet(onion_keys[1].shared_secret.as_ref(), PERM|NODE|3, &[0;0], 0);
468474
msg.reason = failure.data;
475+
msg.attribution_data = failure.attribution_data;
469476
}, ||{
470477
nodes[2].node.fail_htlc_backwards(&payment_hash);
471478
}, false, Some(PERM|NODE|3), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0].hops[1].pubkey, is_permanent: true}), Some(route.paths[0].hops[1].short_channel_id), None);
@@ -495,8 +502,9 @@ fn test_onion_failure() {
495502
}, |msg| {
496503
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
497504
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
498-
let failure = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), UPDATE|7, &err_data);
505+
let failure = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), UPDATE|7, &err_data, 0);
499506
msg.reason = failure.data;
507+
msg.attribution_data = failure.attribution_data;
500508
}, ||{}, true, Some(UPDATE|7),
501509
Some(NetworkUpdate::ChannelFailure { short_channel_id, is_permanent: false }),
502510
Some(short_channel_id), Some(next_hop_failure.clone()));
@@ -508,8 +516,9 @@ fn test_onion_failure() {
508516
}, |msg| {
509517
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
510518
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
511-
let failure = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), UPDATE|7, &err_data_without_type);
519+
let failure = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), UPDATE|7, &err_data_without_type, 0);
512520
msg.reason = failure.data;
521+
msg.attribution_data = failure.attribution_data;
513522
}, ||{}, true, Some(UPDATE|7),
514523
Some(NetworkUpdate::ChannelFailure { short_channel_id, is_permanent: false }),
515524
Some(short_channel_id), Some(next_hop_failure.clone()));
@@ -520,8 +529,9 @@ fn test_onion_failure() {
520529
}, |msg| {
521530
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
522531
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
523-
let failure = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|8, &[0;0]);
532+
let failure = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|8, &[0;0], 0);
524533
msg.reason = failure.data;
534+
msg.attribution_data = failure.attribution_data;
525535
// short_channel_id from the processing node
526536
}, ||{}, true, Some(PERM|8), Some(NetworkUpdate::ChannelFailure{short_channel_id, is_permanent: true}), Some(short_channel_id), Some(next_hop_failure.clone()));
527537

@@ -531,8 +541,9 @@ fn test_onion_failure() {
531541
}, |msg| {
532542
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
533543
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
534-
let failure = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|9, &[0;0]);
544+
let failure = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|9, &[0;0], 0);
535545
msg.reason = failure.data;
546+
msg.attribution_data = failure.attribution_data;
536547
// short_channel_id from the processing node
537548
}, ||{}, true, Some(PERM|9), Some(NetworkUpdate::ChannelFailure{short_channel_id, is_permanent: true}), Some(short_channel_id), Some(next_hop_failure.clone()));
538549

@@ -664,8 +675,9 @@ fn test_onion_failure() {
664675
// Tamper returning error message
665676
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
666677
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
667-
let failure = onion_utils::build_failure_packet(onion_keys[1].shared_secret.as_ref(), 23, &[0;0]);
678+
let failure = onion_utils::build_failure_packet(onion_keys[1].shared_secret.as_ref(), 23, &[0;0], 0);
668679
msg.reason = failure.data;
680+
msg.attribution_data = failure.attribution_data;
669681
}, ||{
670682
nodes[2].node.fail_htlc_backwards(&payment_hash);
671683
}, true, Some(23), None, None, None);
@@ -685,11 +697,13 @@ fn test_onion_failure() {
685697
decoded_err_packet.hmac = Hmac::from_engine(hmac).to_byte_array();
686698
let mut onion_error = OnionErrorPacket {
687699
data: decoded_err_packet.encode(),
688-
attribution_data: None,
700+
attribution_data: Some(AttributionData::new()),
689701
};
702+
add_hmacs(&onion_keys[1].shared_secret.as_ref(), &onion_error.data, onion_error.attribution_data.as_mut().unwrap());
690703
onion_utils::test_crypt_failure_packet(
691704
&onion_keys[1].shared_secret.as_ref(), &mut onion_error);
692705
msg.reason = onion_error.data;
706+
msg.attribution_data = onion_error.attribution_data;
693707
}, || nodes[2].node.fail_htlc_backwards(&payment_hash), false, None,
694708
Some(NetworkUpdate::NodeFailure { node_id: route.paths[0].hops[1].pubkey, is_permanent: true }),
695709
Some(channels[1].0.contents.short_channel_id), None);
@@ -713,11 +727,13 @@ fn test_onion_failure() {
713727
decoded_err_packet.hmac = Hmac::from_engine(hmac).to_byte_array();
714728
let mut onion_error = OnionErrorPacket{
715729
data: decoded_err_packet.encode(),
716-
attribution_data: None,
730+
attribution_data: Some(AttributionData::new()),
717731
};
732+
add_hmacs(&onion_keys[0].shared_secret.as_ref(), &onion_error.data, onion_error.attribution_data.as_mut().unwrap());
718733
onion_utils::test_crypt_failure_packet(
719734
&onion_keys[0].shared_secret.as_ref(), &mut onion_error);
720735
msg.reason = onion_error.data;
736+
msg.attribution_data = onion_error.attribution_data;
721737
}, || {}, true, Some(0x1000|7),
722738
Some(NetworkUpdate::ChannelFailure {
723739
short_channel_id: channels[1].0.contents.short_channel_id,
@@ -742,11 +758,13 @@ fn test_onion_failure() {
742758
decoded_err_packet.hmac = Hmac::from_engine(hmac).to_byte_array();
743759
let mut onion_error = OnionErrorPacket{
744760
data: decoded_err_packet.encode(),
745-
attribution_data: None,
761+
attribution_data: Some(AttributionData::new()),
746762
};
763+
add_hmacs(&onion_keys[1].shared_secret.as_ref(), &onion_error.data, onion_error.attribution_data.as_mut().unwrap());
747764
onion_utils::test_crypt_failure_packet(
748765
&onion_keys[1].shared_secret.as_ref(), &mut onion_error);
749766
msg.reason = onion_error.data;
767+
msg.attribution_data = onion_error.attribution_data;
750768
}, || nodes[2].node.fail_htlc_backwards(&payment_hash), true, Some(0x1000|7),
751769
Some(NetworkUpdate::ChannelFailure {
752770
short_channel_id: channels[1].0.contents.short_channel_id,

0 commit comments

Comments
 (0)