From 8ff16046475b39c280b85dede5f0912269aed28c Mon Sep 17 00:00:00 2001 From: Alec Chen Date: Wed, 7 Jun 2023 23:17:09 -0500 Subject: [PATCH] Add `FailureCode::InvalidOnionPayload` variant When a user decodes custom TLVs, if they fail to recognize even type numbers they should fail back with the correct failure code and fail data. This new variant adds the proper failure variant for the user to pass into `ChannelManager::fail_htlc_backwards_with_reason`. Note that the enum discriminants were removed because when adding a struct variant we can no longer make use of the discriminant through casting like we previously did, and instead have to manually define the associated failure code anyway. --- lightning/src/ln/channelmanager.rs | 36 ++++++++++++++++++++++----- lightning/src/ln/onion_route_tests.rs | 12 +++++++-- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index d2a62b897..68e5451d8 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -359,15 +359,32 @@ struct InboundOnionErr { pub enum FailureCode { /// We had a temporary error processing the payment. Useful if no other error codes fit /// and you want to indicate that the payer may want to retry. - TemporaryNodeFailure = 0x2000 | 2, + TemporaryNodeFailure, /// We have a required feature which was not in this onion. For example, you may require /// some additional metadata that was not provided with this payment. - RequiredNodeFeatureMissing = 0x4000 | 0x2000 | 3, + RequiredNodeFeatureMissing, /// You may wish to use this when a `payment_preimage` is unknown, or the CLTV expiry of /// the HTLC is too close to the current block height for safe handling. /// Using this failure code in [`ChannelManager::fail_htlc_backwards_with_reason`] is /// equivalent to calling [`ChannelManager::fail_htlc_backwards`]. - IncorrectOrUnknownPaymentDetails = 0x4000 | 15, + IncorrectOrUnknownPaymentDetails, + /// We failed to process the payload after the onion was decrypted. You may wish to + /// use this when receiving custom HTLC TLVs with even type numbers that you don't recognize. + /// + /// If available, the tuple data may include the type number and byte offset in the + /// decrypted byte stream where the failure occurred. + InvalidOnionPayload(Option<(u64, u16)>), +} + +impl Into for FailureCode { + fn into(self) -> u16 { + match self { + FailureCode::TemporaryNodeFailure => 0x2000 | 2, + FailureCode::RequiredNodeFeatureMissing => 0x4000 | 0x2000 | 3, + FailureCode::IncorrectOrUnknownPaymentDetails => 0x4000 | 15, + FailureCode::InvalidOnionPayload(_) => 0x4000 | 22, + } + } } /// Error type returned across the peer_state mutex boundary. When an Err is generated for a @@ -4583,12 +4600,19 @@ where /// Gets error data to form an [`HTLCFailReason`] given a [`FailureCode`] and [`ClaimableHTLC`]. fn get_htlc_fail_reason_from_failure_code(&self, failure_code: FailureCode, htlc: &ClaimableHTLC) -> HTLCFailReason { match failure_code { - FailureCode::TemporaryNodeFailure => HTLCFailReason::from_failure_code(failure_code as u16), - FailureCode::RequiredNodeFeatureMissing => HTLCFailReason::from_failure_code(failure_code as u16), + FailureCode::TemporaryNodeFailure => HTLCFailReason::from_failure_code(failure_code.into()), + FailureCode::RequiredNodeFeatureMissing => HTLCFailReason::from_failure_code(failure_code.into()), FailureCode::IncorrectOrUnknownPaymentDetails => { let mut htlc_msat_height_data = htlc.value.to_be_bytes().to_vec(); htlc_msat_height_data.extend_from_slice(&self.best_block.read().unwrap().height().to_be_bytes()); - HTLCFailReason::reason(failure_code as u16, htlc_msat_height_data) + HTLCFailReason::reason(failure_code.into(), htlc_msat_height_data) + }, + FailureCode::InvalidOnionPayload(data) => { + let fail_data = match data { + Some((typ, offset)) => [BigSize(typ).encode(), offset.encode()].concat(), + None => Vec::new(), + }; + HTLCFailReason::reason(failure_code.into(), fail_data) } } } diff --git a/lightning/src/ln/onion_route_tests.rs b/lightning/src/ln/onion_route_tests.rs index 888ac7dca..c6ac77ac3 100644 --- a/lightning/src/ln/onion_route_tests.rs +++ b/lightning/src/ln/onion_route_tests.rs @@ -24,7 +24,7 @@ use crate::ln::features::{InitFeatures, Bolt11InvoiceFeatures}; use crate::ln::msgs; use crate::ln::msgs::{ChannelMessageHandler, ChannelUpdate}; use crate::ln::wire::Encode; -use crate::util::ser::{Writeable, Writer}; +use crate::util::ser::{Writeable, Writer, BigSize}; use crate::util::test_utils; use crate::util::config::{UserConfig, ChannelConfig, MaxDustHTLCExposure}; use crate::util::errors::APIError; @@ -942,10 +942,16 @@ fn do_test_fail_htlc_backwards_with_reason(failure_code: FailureCode) { let mut htlc_msat_height_data = (payment_amount as u64).to_be_bytes().to_vec(); htlc_msat_height_data.extend_from_slice(&CHAN_CONFIRM_DEPTH.to_be_bytes()); htlc_msat_height_data + }, + FailureCode::InvalidOnionPayload(data) => { + match data { + Some((typ, offset)) => [BigSize(typ).encode(), offset.encode()].concat(), + None => Vec::new(), + } } }; - let failure_code = failure_code as u16; + let failure_code = failure_code.into(); let permanent_flag = 0x4000; let permanent_fail = (failure_code & permanent_flag) != 0; expect_payment_failed!(nodes[0], payment_hash, permanent_fail, failure_code, failure_data); @@ -957,6 +963,8 @@ fn test_fail_htlc_backwards_with_reason() { do_test_fail_htlc_backwards_with_reason(FailureCode::TemporaryNodeFailure); do_test_fail_htlc_backwards_with_reason(FailureCode::RequiredNodeFeatureMissing); do_test_fail_htlc_backwards_with_reason(FailureCode::IncorrectOrUnknownPaymentDetails); + do_test_fail_htlc_backwards_with_reason(FailureCode::InvalidOnionPayload(Some((1 << 16, 42)))); + do_test_fail_htlc_backwards_with_reason(FailureCode::InvalidOnionPayload(None)); } macro_rules! get_phantom_route { -- 2.39.5