Skip to content

Commit f891dd0

Browse files
committed
ln/refactor: use LocalHTLCFailureCodes in onion error processing
1 parent bc54c89 commit f891dd0

File tree

3 files changed

+70
-87
lines changed

3 files changed

+70
-87
lines changed

lightning/src/ln/onion_utils.rs

Lines changed: 69 additions & 39 deletions
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,27 @@ 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
13921392
);
13931393
} else {
13941394
log_info!(
13951395
logger,
1396-
"Onion Error[from {}: {}({:#x})] {}",
1396+
"Onion Error[from {}: {:?}({:#x})]",
13971397
route_hop.pubkey(),
1398-
title,
13991398
error_code,
1400-
description
1399+
error_code.failure_code(),
14011400
);
14021401
}
14031402

@@ -1651,14 +1650,45 @@ impl LocalHTLCFailureReason {
16511650
}
16521651
}
16531652

1653+
/// Returns the name of an error's data field and its expected length.
1654+
fn get_onion_debug_field(&self) -> (&'static str, usize) {
1655+
match self {
1656+
Self::InvalidOnionVersion | Self::InvalidOnionHMAC | Self::InvalidOnionKey => {
1657+
("sha256_of_onion", 32)
1658+
},
1659+
Self::AmountBelowMinimum | Self::FeeInsufficient => ("htlc_msat", 8),
1660+
Self::IncorrectCLTVExpiry | Self::FinalIncorrectCLTVExpiry => ("cltv_expiry", 4),
1661+
Self::FinalIncorrectHTLCAmount => ("incoming_htlc_msat", 8),
1662+
Self::ChannelDisabled => ("flags", 2),
1663+
_ => ("", 0),
1664+
}
1665+
}
1666+
16541667
pub(super) fn is_temporary(&self) -> bool {
16551668
self.failure_code() & UPDATE == UPDATE
16561669
}
16571670

1658-
#[cfg(test)]
16591671
pub(super) fn is_permanent(&self) -> bool {
16601672
self.failure_code() & PERM == PERM
16611673
}
1674+
1675+
fn is_badonion(&self) -> bool {
1676+
self.failure_code() & BADONION == BADONION
1677+
}
1678+
1679+
fn is_node(&self) -> bool {
1680+
self.failure_code() & NODE == NODE
1681+
}
1682+
1683+
/// Returns true if the failure is only sent by the final recipient. Note that this function
1684+
/// only checks [`LocalHTLCFailureReason`] variants that represent bolt 04 errors directly,
1685+
/// as it's intended to analyze errors we've received as a sender.
1686+
fn is_recipient_failure(&self) -> bool {
1687+
self.failure_code() == LocalHTLCFailureReason::IncorrectPaymentDetails.failure_code()
1688+
|| *self == LocalHTLCFailureReason::FinalIncorrectCLTVExpiry
1689+
|| *self == LocalHTLCFailureReason::FinalIncorrectHTLCAmount
1690+
|| *self == LocalHTLCFailureReason::MPPTimeout
1691+
}
16621692
}
16631693

16641694
impl From<u16> for LocalHTLCFailureReason {
@@ -2006,7 +2036,7 @@ impl HTLCFailReason {
20062036
failed_within_blinded_path: false,
20072037
hold_times: Vec::new(),
20082038
#[cfg(any(test, feature = "_test_utils"))]
2009-
onion_error_code: Some(failure_reason.failure_code()),
2039+
onion_error_code: Some(*failure_reason),
20102040
#[cfg(any(test, feature = "_test_utils"))]
20112041
onion_error_data: Some(data.clone()),
20122042
}
@@ -3093,7 +3123,7 @@ mod tests {
30933123
test_attributable_failure_packet_onion_with_mutation(Some(mutation));
30943124

30953125
if decrypted_failure.onion_error_code
3096-
== Some(LocalHTLCFailureReason::IncorrectPaymentDetails.failure_code())
3126+
== Some(LocalHTLCFailureReason::IncorrectPaymentDetails)
30973127
{
30983128
continue;
30993129
}
@@ -3109,7 +3139,7 @@ mod tests {
31093139
let decrypted_failure = test_attributable_failure_packet_onion_with_mutation(None);
31103140
assert_eq!(
31113141
decrypted_failure.onion_error_code,
3112-
Some(LocalHTLCFailureReason::IncorrectPaymentDetails.failure_code())
3142+
Some(LocalHTLCFailureReason::IncorrectPaymentDetails)
31133143
);
31143144
assert_eq!(decrypted_failure.hold_times, [5, 4, 3, 2, 1]);
31153145
}
@@ -3334,7 +3364,7 @@ mod tests {
33343364
);
33353365
assert_eq!(
33363366
decrypted_failure.onion_error_code,
3337-
Some(LocalHTLCFailureReason::IncorrectPaymentDetails.failure_code())
3367+
Some(LocalHTLCFailureReason::IncorrectPaymentDetails),
33383368
);
33393369
}
33403370

@@ -3380,7 +3410,7 @@ mod tests {
33803410

33813411
let decrypted_failure =
33823412
process_onion_failure(&secp_ctx, &logger, &htlc_source, first_hop_error_packet);
3383-
assert_eq!(decrypted_failure.onion_error_code, Some(error_code.failure_code()));
3413+
assert_eq!(decrypted_failure.onion_error_code, Some(error_code));
33843414
};
33853415

33863416
{
@@ -3411,7 +3441,7 @@ mod tests {
34113441
&htlc_source,
34123442
trampoline_outer_hop_error_packet,
34133443
);
3414-
assert_eq!(decrypted_failure.onion_error_code, Some(error_code.failure_code()));
3444+
assert_eq!(decrypted_failure.onion_error_code, Some(error_code));
34153445
};
34163446

34173447
{
@@ -3447,7 +3477,7 @@ mod tests {
34473477
&htlc_source,
34483478
trampoline_inner_hop_error_packet,
34493479
);
3450-
assert_eq!(decrypted_failure.onion_error_code, Some(error_code.failure_code()));
3480+
assert_eq!(decrypted_failure.onion_error_code, Some(error_code));
34513481
}
34523482

34533483
{
@@ -3488,7 +3518,7 @@ mod tests {
34883518
&htlc_source,
34893519
trampoline_second_hop_error_packet,
34903520
);
3491-
assert_eq!(decrypted_failure.onion_error_code, Some(error_code.failure_code()));
3521+
assert_eq!(decrypted_failure.onion_error_code, Some(error_code));
34923522
}
34933523
}
34943524
}

lightning/src/ln/outbound_payment.rs

Lines changed: 1 addition & 1 deletion
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

Lines changed: 0 additions & 47 deletions
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)