Skip to content

Commit 2cc8799

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

File tree

3 files changed

+72
-88
lines changed

3 files changed

+72
-88
lines changed

lightning/src/ln/onion_utils.rs

+71-40
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,9 +1358,12 @@ 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(),
1362-
_ => None,
1361+
short_channel_id = if error_code == LocalHTLCFailureReason::FinalIncorrectCLTVExpiry
1362+
|| error_code == LocalHTLCFailureReason::FinalIncorrectHTLCAmount
1363+
{
1364+
route_hop.short_channel_id()
1365+
} else {
1366+
None
13631367
};
13641368
} else {
13651369
// We can't understand their error messages and they failed to forward...they probably can't
@@ -1374,30 +1378,29 @@ where
13741378
res = Some(FailureLearnings {
13751379
network_update,
13761380
short_channel_id,
1377-
payment_failed_permanently: error_code & PERM == PERM && is_from_final_non_blinded_node,
1381+
payment_failed_permanently: error_code.is_permanent() && is_from_final_non_blinded_node,
13781382
failed_within_blinded_path: false,
13791383
});
13801384

1381-
let (description, title) = errors::get_onion_error_description(error_code);
13821385
if debug_field_size > 0 && err_packet.failuremsg.len() >= 4 + debug_field_size {
13831386
log_info!(
13841387
logger,
1385-
"Onion Error[from {}: {}({:#x}) {}({})] {}",
1388+
"Onion Error[from {}: {:?}({:#x}) {}({})] {}",
13861389
route_hop.pubkey(),
1387-
title,
13881390
error_code,
1391+
error_code.failure_code(),
13891392
debug_field,
13901393
log_bytes!(&err_packet.failuremsg[4..4 + debug_field_size]),
1391-
description
1394+
error_code
13921395
);
13931396
} else {
13941397
log_info!(
13951398
logger,
1396-
"Onion Error[from {}: {}({:#x})] {}",
1399+
"Onion Error[from {}: {:?}({:#x})] {}",
13971400
route_hop.pubkey(),
1398-
title,
13991401
error_code,
1400-
description
1402+
error_code.failure_code(),
1403+
error_code
14011404
);
14021405
}
14031406

@@ -1651,14 +1654,42 @@ impl LocalHTLCFailureReason {
16511654
}
16521655
}
16531656

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

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

16641695
impl From<u16> for LocalHTLCFailureReason {
@@ -2086,7 +2117,7 @@ impl HTLCFailReason {
20862117
failed_within_blinded_path: false,
20872118
hold_times: Vec::new(),
20882119
#[cfg(any(test, feature = "_test_utils"))]
2089-
onion_error_code: Some(failure_reason.failure_code()),
2120+
onion_error_code: Some(*failure_reason),
20902121
#[cfg(any(test, feature = "_test_utils"))]
20912122
onion_error_data: Some(data.clone()),
20922123
}
@@ -3173,7 +3204,7 @@ mod tests {
31733204
test_attributable_failure_packet_onion_with_mutation(Some(mutation));
31743205

31753206
if decrypted_failure.onion_error_code
3176-
== Some(LocalHTLCFailureReason::IncorrectPaymentDetails.failure_code())
3207+
== Some(LocalHTLCFailureReason::IncorrectPaymentDetails)
31773208
{
31783209
continue;
31793210
}
@@ -3189,7 +3220,7 @@ mod tests {
31893220
let decrypted_failure = test_attributable_failure_packet_onion_with_mutation(None);
31903221
assert_eq!(
31913222
decrypted_failure.onion_error_code,
3192-
Some(LocalHTLCFailureReason::IncorrectPaymentDetails.failure_code())
3223+
Some(LocalHTLCFailureReason::IncorrectPaymentDetails)
31933224
);
31943225
assert_eq!(decrypted_failure.hold_times, [5, 4, 3, 2, 1]);
31953226
}
@@ -3414,7 +3445,7 @@ mod tests {
34143445
);
34153446
assert_eq!(
34163447
decrypted_failure.onion_error_code,
3417-
Some(LocalHTLCFailureReason::IncorrectPaymentDetails.failure_code())
3448+
Some(LocalHTLCFailureReason::IncorrectPaymentDetails),
34183449
);
34193450
}
34203451

@@ -3460,7 +3491,7 @@ mod tests {
34603491

34613492
let decrypted_failure =
34623493
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()));
3494+
assert_eq!(decrypted_failure.onion_error_code, Some(error_code));
34643495
};
34653496

34663497
{
@@ -3491,7 +3522,7 @@ mod tests {
34913522
&htlc_source,
34923523
trampoline_outer_hop_error_packet,
34933524
);
3494-
assert_eq!(decrypted_failure.onion_error_code, Some(error_code.failure_code()));
3525+
assert_eq!(decrypted_failure.onion_error_code, Some(error_code));
34953526
};
34963527

34973528
{
@@ -3527,7 +3558,7 @@ mod tests {
35273558
&htlc_source,
35283559
trampoline_inner_hop_error_packet,
35293560
);
3530-
assert_eq!(decrypted_failure.onion_error_code, Some(error_code.failure_code()));
3561+
assert_eq!(decrypted_failure.onion_error_code, Some(error_code));
35313562
}
35323563

35333564
{
@@ -3568,7 +3599,7 @@ mod tests {
35683599
&htlc_source,
35693600
trampoline_second_hop_error_packet,
35703601
);
3571-
assert_eq!(decrypted_failure.onion_error_code, Some(error_code.failure_code()));
3602+
assert_eq!(decrypted_failure.onion_error_code, Some(error_code));
35723603
}
35733604
}
35743605
}

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)