Skip to content

Commit f89ba52

Browse files
committed
ln/refactor: use LocalHTLCFailureCodes in onion error processing
1 parent 54f18b8 commit f89ba52

File tree

3 files changed

+69
-87
lines changed

3 files changed

+69
-87
lines changed

lightning/src/ln/onion_utils.rs

+68-39
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::routing::router::{BlindedTail, Path, RouteHop, RouteParameters, Tramp
2020
use crate::sign::{NodeSigner, Recipient};
2121
use crate::types::features::{ChannelFeatures, NodeFeatures};
2222
use crate::types::payment::{PaymentHash, PaymentPreimage};
23-
use crate::util::errors::{self, APIError};
23+
use crate::util::errors::APIError;
2424
use crate::util::logger::Logger;
2525
use crate::util::ser::{
2626
LengthCalculatingWriter, Readable, ReadableArgs, VecWriter, Writeable, Writer,
@@ -975,7 +975,7 @@ mod fuzzy_onion_utils {
975975
#[allow(dead_code)]
976976
pub(crate) hold_times: Vec<u32>,
977977
#[cfg(any(test, feature = "_test_utils"))]
978-
pub(crate) onion_error_code: Option<u16>,
978+
pub(crate) onion_error_code: Option<LocalHTLCFailureReason>,
979979
#[cfg(any(test, feature = "_test_utils"))]
980980
pub(crate) onion_error_data: Option<Vec<u8>>,
981981
}
@@ -1126,7 +1126,7 @@ where
11261126
Some(hop) => hop,
11271127
None => {
11281128
// Got an error from within a blinded route.
1129-
_error_code_ret = Some(BADONION | PERM | 24); // invalid_onion_blinding
1129+
_error_code_ret = Some(LocalHTLCFailureReason::InvalidOnionBlinding);
11301130
_error_packet_ret = Some(vec![0; 32]);
11311131
res = Some(FailureLearnings {
11321132
network_update: None,
@@ -1154,7 +1154,7 @@ where
11541154
// The failing hop is within a multi-hop blinded path.
11551155
#[cfg(not(test))]
11561156
{
1157-
_error_code_ret = Some(BADONION | PERM | 24); // invalid_onion_blinding
1157+
_error_code_ret = Some(LocalHTLCFailureReason::InvalidOnionBlinding);
11581158
_error_packet_ret = Some(vec![0; 32]);
11591159
}
11601160
#[cfg(test)]
@@ -1166,9 +1166,12 @@ where
11661166
&encrypted_packet.data,
11671167
))
11681168
.unwrap();
1169-
_error_code_ret = Some(u16::from_be_bytes(
1170-
err_packet.failuremsg.get(0..2).unwrap().try_into().unwrap(),
1171-
));
1169+
_error_code_ret = Some(
1170+
u16::from_be_bytes(
1171+
err_packet.failuremsg.get(0..2).unwrap().try_into().unwrap(),
1172+
)
1173+
.into(),
1174+
);
11721175
_error_packet_ret = Some(err_packet.failuremsg[2..].to_vec());
11731176
}
11741177

@@ -1279,22 +1282,19 @@ where
12791282
},
12801283
};
12811284

1282-
let error_code = u16::from_be_bytes(error_code_slice.try_into().expect("len is 2"));
1285+
let error_code = u16::from_be_bytes(error_code_slice.try_into().expect("len is 2")).into();
12831286
_error_code_ret = Some(error_code);
12841287
_error_packet_ret = Some(err_packet.failuremsg[2..].to_vec());
12851288

1286-
let (debug_field, debug_field_size) = errors::get_onion_debug_field(error_code);
1289+
let (debug_field, debug_field_size) = error_code.get_onion_debug_field();
12871290

12881291
// indicate that payment parameter has failed and no need to update Route object
1289-
let payment_failed = match error_code & 0xff {
1290-
15 | 18 | 19 | 23 => true,
1291-
_ => false,
1292-
} && is_from_final_non_blinded_node; // PERM bit observed below even if this error is from the intermediate nodes
1292+
let payment_failed = error_code.is_recipient_failure() && is_from_final_non_blinded_node;
12931293

12941294
let mut network_update = None;
12951295
let mut short_channel_id = None;
12961296

1297-
if error_code & BADONION == BADONION {
1297+
if error_code.is_badonion() {
12981298
// If the error code has the BADONION bit set, always blame the channel from the node
12991299
// "originating" the error to its next hop. The "originator" is ultimately actually claiming
13001300
// that its counterparty is the one who is failing the HTLC.
@@ -1308,12 +1308,13 @@ where
13081308
is_permanent: true,
13091309
});
13101310
}
1311-
} else if error_code & NODE == NODE {
1312-
let is_permanent = error_code & PERM == PERM;
1313-
network_update =
1314-
Some(NetworkUpdate::NodeFailure { node_id: *route_hop.pubkey(), is_permanent });
1311+
} else if error_code.is_node() {
1312+
network_update = Some(NetworkUpdate::NodeFailure {
1313+
node_id: *route_hop.pubkey(),
1314+
is_permanent: error_code.is_permanent(),
1315+
});
13151316
short_channel_id = route_hop.short_channel_id();
1316-
} else if error_code & PERM == PERM {
1317+
} else if error_code.is_permanent() {
13171318
if !payment_failed {
13181319
if let ErrorHop::RouteHop(failing_route_hop) = failing_route_hop {
13191320
network_update = Some(NetworkUpdate::ChannelFailure {
@@ -1323,7 +1324,7 @@ where
13231324
}
13241325
short_channel_id = failing_route_hop.short_channel_id();
13251326
}
1326-
} else if error_code & UPDATE == UPDATE {
1327+
} else if error_code.is_temporary() {
13271328
if let Some(update_len_slice) =
13281329
err_packet.failuremsg.get(debug_field_size + 2..debug_field_size + 4)
13291330
{
@@ -1357,8 +1358,9 @@ where
13571358
} else if payment_failed {
13581359
// Only blame the hop when a value in the HTLC doesn't match the corresponding value in the
13591360
// onion.
1360-
short_channel_id = match error_code & 0xff {
1361-
18 | 19 => route_hop.short_channel_id(),
1361+
short_channel_id = match error_code {
1362+
LocalHTLCFailureReason::FinalIncorrectCLTVExpiry
1363+
| LocalHTLCFailureReason::FinalIncorrectHTLCAmount => route_hop.short_channel_id(),
13621364
_ => None,
13631365
};
13641366
} else {
@@ -1374,30 +1376,29 @@ where
13741376
res = Some(FailureLearnings {
13751377
network_update,
13761378
short_channel_id,
1377-
payment_failed_permanently: error_code & PERM == PERM && is_from_final_non_blinded_node,
1379+
payment_failed_permanently: error_code.is_permanent() && is_from_final_non_blinded_node,
13781380
failed_within_blinded_path: false,
13791381
});
13801382

1381-
let (description, title) = errors::get_onion_error_description(error_code);
13821383
if debug_field_size > 0 && err_packet.failuremsg.len() >= 4 + debug_field_size {
13831384
log_info!(
13841385
logger,
1385-
"Onion Error[from {}: {}({:#x}) {}({})] {}",
1386+
"Onion Error[from {}: {:?}({:#x}) {}({})] {}",
13861387
route_hop.pubkey(),
1387-
title,
13881388
error_code,
1389+
error_code.failure_code(),
13891390
debug_field,
13901391
log_bytes!(&err_packet.failuremsg[4..4 + debug_field_size]),
1391-
description
1392+
error_code
13921393
);
13931394
} else {
13941395
log_info!(
13951396
logger,
1396-
"Onion Error[from {}: {}({:#x})] {}",
1397+
"Onion Error[from {}: {:?}({:#x})] {}",
13971398
route_hop.pubkey(),
1398-
title,
13991399
error_code,
1400-
description
1400+
error_code.failure_code(),
1401+
error_code
14011402
);
14021403
}
14031404

@@ -1651,14 +1652,42 @@ impl LocalHTLCFailureReason {
16511652
}
16521653
}
16531654

1655+
fn get_onion_debug_field(&self) -> (&'static str, usize) {
1656+
match self {
1657+
Self::InvalidOnionVersion | Self::InvalidOnionHMAC | Self::InvalidOnionKey => {
1658+
("sha256_of_onion", 32)
1659+
},
1660+
Self::AmountBelowMinimum | Self::FeeInsufficient => ("htlc_msat", 8),
1661+
Self::IncorrectCLTVExpiry | Self::FinalIncorrectCLTVExpiry => ("cltv_expiry", 4),
1662+
Self::FinalIncorrectHTLCAmount => ("incoming_htlc_msat", 8),
1663+
Self::ChannelDisabled => ("flags", 2),
1664+
_ => ("", 0),
1665+
}
1666+
}
1667+
16541668
pub(super) fn is_temporary(&self) -> bool {
16551669
self.failure_code() & UPDATE == UPDATE
16561670
}
16571671

1658-
#[cfg(test)]
16591672
pub(super) fn is_permanent(&self) -> bool {
16601673
self.failure_code() & PERM == PERM
16611674
}
1675+
1676+
fn is_badonion(&self) -> bool {
1677+
self.failure_code() & BADONION == BADONION
1678+
}
1679+
1680+
fn is_node(&self) -> bool {
1681+
self.failure_code() & NODE == NODE
1682+
}
1683+
1684+
/// Returns true if the failure is only sent by the final recipient.
1685+
fn is_recipient_failure(&self) -> bool {
1686+
self.failure_code() == LocalHTLCFailureReason::IncorrectPaymentDetails.failure_code()
1687+
|| *self == LocalHTLCFailureReason::FinalIncorrectCLTVExpiry
1688+
|| *self == LocalHTLCFailureReason::FinalIncorrectHTLCAmount
1689+
|| *self == LocalHTLCFailureReason::MPPTimeout
1690+
}
16621691
}
16631692

16641693
impl From<u16> for LocalHTLCFailureReason {
@@ -2086,7 +2115,7 @@ impl HTLCFailReason {
20862115
failed_within_blinded_path: false,
20872116
hold_times: Vec::new(),
20882117
#[cfg(any(test, feature = "_test_utils"))]
2089-
onion_error_code: Some(failure_reason.failure_code()),
2118+
onion_error_code: Some(*failure_reason),
20902119
#[cfg(any(test, feature = "_test_utils"))]
20912120
onion_error_data: Some(data.clone()),
20922121
}
@@ -3173,7 +3202,7 @@ mod tests {
31733202
test_attributable_failure_packet_onion_with_mutation(Some(mutation));
31743203

31753204
if decrypted_failure.onion_error_code
3176-
== Some(LocalHTLCFailureReason::IncorrectPaymentDetails.failure_code())
3205+
== Some(LocalHTLCFailureReason::IncorrectPaymentDetails)
31773206
{
31783207
continue;
31793208
}
@@ -3189,7 +3218,7 @@ mod tests {
31893218
let decrypted_failure = test_attributable_failure_packet_onion_with_mutation(None);
31903219
assert_eq!(
31913220
decrypted_failure.onion_error_code,
3192-
Some(LocalHTLCFailureReason::IncorrectPaymentDetails.failure_code())
3221+
Some(LocalHTLCFailureReason::IncorrectPaymentDetails)
31933222
);
31943223
assert_eq!(decrypted_failure.hold_times, [5, 4, 3, 2, 1]);
31953224
}
@@ -3414,7 +3443,7 @@ mod tests {
34143443
);
34153444
assert_eq!(
34163445
decrypted_failure.onion_error_code,
3417-
Some(LocalHTLCFailureReason::IncorrectPaymentDetails.failure_code())
3446+
Some(LocalHTLCFailureReason::IncorrectPaymentDetails),
34183447
);
34193448
}
34203449

@@ -3460,7 +3489,7 @@ mod tests {
34603489

34613490
let decrypted_failure =
34623491
process_onion_failure(&secp_ctx, &logger, &htlc_source, first_hop_error_packet);
3463-
assert_eq!(decrypted_failure.onion_error_code, Some(error_code.failure_code()));
3492+
assert_eq!(decrypted_failure.onion_error_code, Some(error_code));
34643493
};
34653494

34663495
{
@@ -3491,7 +3520,7 @@ mod tests {
34913520
&htlc_source,
34923521
trampoline_outer_hop_error_packet,
34933522
);
3494-
assert_eq!(decrypted_failure.onion_error_code, Some(error_code.failure_code()));
3523+
assert_eq!(decrypted_failure.onion_error_code, Some(error_code));
34953524
};
34963525

34973526
{
@@ -3527,7 +3556,7 @@ mod tests {
35273556
&htlc_source,
35283557
trampoline_inner_hop_error_packet,
35293558
);
3530-
assert_eq!(decrypted_failure.onion_error_code, Some(error_code.failure_code()));
3559+
assert_eq!(decrypted_failure.onion_error_code, Some(error_code));
35313560
}
35323561

35333562
{
@@ -3568,7 +3597,7 @@ mod tests {
35683597
&htlc_source,
35693598
trampoline_second_hop_error_packet,
35703599
);
3571-
assert_eq!(decrypted_failure.onion_error_code, Some(error_code.failure_code()));
3600+
assert_eq!(decrypted_failure.onion_error_code, Some(error_code));
35723601
}
35733602
}
35743603
}

lightning/src/ln/outbound_payment.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2288,7 +2288,7 @@ impl OutboundPayments {
22882288
path: path.clone(),
22892289
short_channel_id,
22902290
#[cfg(any(test, feature = "_test_utils"))]
2291-
error_code: onion_error_code,
2291+
error_code: onion_error_code.map(|f| f.failure_code()),
22922292
#[cfg(any(test, feature = "_test_utils"))]
22932293
error_data: onion_error_data
22942294
}

lightning/src/util/errors.rs

-47
Original file line numberDiff line numberDiff line change
@@ -101,50 +101,3 @@ impl_writeable_tlv_based_enum_upgradable!(APIError,
101101
(8, MonitorUpdateInProgress) => {},
102102
(10, IncompatibleShutdownScript) => { (0, script, required), },
103103
);
104-
105-
#[inline]
106-
pub(crate) fn get_onion_debug_field(error_code: u16) -> (&'static str, usize) {
107-
match error_code & 0xff {
108-
4 | 5 | 6 => ("sha256_of_onion", 32),
109-
11 | 12 => ("htlc_msat", 8),
110-
13 | 18 => ("cltv_expiry", 4),
111-
19 => ("incoming_htlc_msat", 8),
112-
20 => ("flags", 2),
113-
_ => ("", 0),
114-
}
115-
}
116-
117-
#[inline]
118-
pub(crate) fn get_onion_error_description(error_code: u16) -> (&'static str, &'static str) {
119-
const BADONION: u16 = 0x8000;
120-
const PERM: u16 = 0x4000;
121-
const NODE: u16 = 0x2000;
122-
const UPDATE: u16 = 0x1000;
123-
match error_code {
124-
_c if _c == PERM|1 => ("The realm byte was not understood by the processing node", "invalid_realm"),
125-
_c if _c == NODE|2 => ("Node indicated temporary node failure", "temporary_node_failure"),
126-
_c if _c == PERM|NODE|2 => ("Node indicated permanent node failure", "permanent_node_failure"),
127-
_c if _c == PERM|NODE|3 => ("Node indicated the required node feature is missing in the onion", "required_node_feature_missing"),
128-
_c if _c == BADONION|PERM|4 => ("Node indicated the version by is not understood", "invalid_onion_version"),
129-
_c if _c == BADONION|PERM|5 => ("Node indicated the HMAC of the onion is incorrect", "invalid_onion_hmac"),
130-
_c if _c == BADONION|PERM|6 => ("Node indicated the ephemeral public keys is not parseable", "invalid_onion_key"),
131-
_c if _c == UPDATE|7 => ("Node indicated the outgoing channel is unable to handle the HTLC temporarily", "temporary_channel_failure"),
132-
_c if _c == PERM|8 => ("Node indicated the outgoing channel is unable to handle the HTLC peramanently", "permanent_channel_failure"),
133-
_c if _c == PERM|9 => ("Node indicated the required feature for the outgoing channel is not satisfied", "required_channel_feature_missing"),
134-
_c if _c == PERM|10 => ("Node indicated the outbound channel is not found for the specified short_channel_id in the onion packet", "unknown_next_peer"),
135-
_c if _c == UPDATE|11 => ("Node indicated the HTLC amount was below the required minmum for the outbound channel", "amount_below_minimum"),
136-
_c if _c == UPDATE|12 => ("Node indicated the fee amount does not meet the required level", "fee_insufficient"),
137-
_c if _c == UPDATE|13 => ("Node indicated the cltv_expiry does not comply with the cltv_expiry_delta required by the outgoing channel", "incorrect_cltv_expiry"),
138-
_c if _c == UPDATE|14 => ("Node indicated the CLTV expiry too close to the current block height for safe handling", "expiry_too_soon"),
139-
_c if _c == PERM|15 => ("The final node indicated the payment hash is unknown or amount is incorrect", "incorrect_or_unknown_payment_details"),
140-
_c if _c == PERM|16 => ("The final node indicated the payment amount is incorrect", "incorrect_payment_amount"),
141-
_c if _c == 17 => ("The final node indicated the CLTV expiry is too close to the current block height for safe handling", "final_expiry_too_soon"),
142-
_c if _c == 18 => ("The final node indicated the CLTV expiry in the HTLC does not match the value in the onion", "final_incorrect_cltv_expiry"),
143-
_c if _c == 19 => ("The final node indicated the amount in the HTLC does not match the value in the onion", "final_incorrect_htlc_amount"),
144-
_c if _c == UPDATE|20 => ("Node indicated the outbound channel has been disabled", "channel_disabled"),
145-
_c if _c == 21 => ("Node indicated the CLTV expiry in the HTLC is too far in the future", "expiry_too_far"),
146-
_c if _c == PERM|22 => ("Node indicated that the decrypted onion per-hop payload was not understood by it or is incomplete", "invalid_onion_payload"),
147-
_c if _c == 23 => ("The final node indicated the complete amount of the multi-part payment was not received within a reasonable time", "mpp_timeout"),
148-
_ => ("Unknown", ""),
149-
}
150-
}

0 commit comments

Comments
 (0)