diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index a09b3bfa77a..8625eb6914a 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -35,7 +35,7 @@ use crate::ln::interactivetxs::{ TX_COMMON_FIELDS_WEIGHT, }; use crate::ln::msgs; -use crate::ln::msgs::{ClosingSigned, ClosingSignedFeeRange, DecodeError}; +use crate::ln::msgs::{ClosingSigned, ClosingSignedFeeRange, DecodeError, OnionErrorPacket}; use crate::ln::script::{self, ShutdownScript}; use crate::ln::channel_state::{ChannelShutdownState, CounterpartyForwardingInfo, InboundHTLCDetails, InboundHTLCStateDetails, OutboundHTLCDetails, OutboundHTLCStateDetails}; use crate::ln::channelmanager::{self, OpenChannelMessage, PendingHTLCStatus, HTLCSource, SentHTLCId, HTLCFailureMsg, PendingHTLCInfo, RAACommitmentOrder, PaymentClaimDetails, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA, MAX_LOCAL_BREAKDOWN_TIMEOUT}; @@ -50,7 +50,7 @@ use crate::ln::chan_utils::{ #[cfg(splicing)] use crate::ln::chan_utils::FUNDING_TRANSACTION_WITNESS_WEIGHT; use crate::ln::chan_utils; -use crate::ln::onion_utils::HTLCFailReason; +use crate::ln::onion_utils::{HTLCFailReason}; use crate::chain::BestBlock; use crate::chain::chaininterface::{FeeEstimator, ConfirmationTarget, LowerBoundedFeeEstimator, fee_for_weight}; use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, LATENCY_GRACE_PERIOD_BLOCKS}; @@ -4933,7 +4933,7 @@ trait FailHTLCContents { impl FailHTLCContents for msgs::OnionErrorPacket { type Message = msgs::UpdateFailHTLC; fn to_message(self, htlc_id: u64, channel_id: ChannelId) -> Self::Message { - msgs::UpdateFailHTLC { htlc_id, channel_id, reason: self } + msgs::UpdateFailHTLC { htlc_id, channel_id, reason: self.data } } fn to_inbound_htlc_state(self) -> InboundHTLCState { InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailRelay(self)) @@ -6136,7 +6136,7 @@ impl FundedChannel where require_commitment = true; match fail_msg { HTLCFailureMsg::Relay(msg) => { - htlc.state = InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailRelay(msg.reason.clone())); + htlc.state = InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailRelay(msg.clone().into())); update_fail_htlcs.push(msg) }, HTLCFailureMsg::Malformed(msg) => { @@ -6844,7 +6844,7 @@ impl FundedChannel where update_fail_htlcs.push(msgs::UpdateFailHTLC { channel_id: self.context.channel_id(), htlc_id: htlc.htlc_id, - reason: err_packet.clone() + reason: err_packet.data.clone(), }); }, &InboundHTLCRemovalReason::FailMalformed((ref sha256_of_onion, ref failure_code)) => { @@ -10142,11 +10142,6 @@ fn get_initial_channel_type(config: &UserConfig, their_features: &InitFeatures) const SERIALIZATION_VERSION: u8 = 4; const MIN_SERIALIZATION_VERSION: u8 = 4; -impl_writeable_tlv_based_enum_legacy!(InboundHTLCRemovalReason,; - (0, FailRelay), - (1, FailMalformed), - (2, Fulfill), -); impl Writeable for ChannelUpdateStatus { fn write(&self, writer: &mut W) -> Result<(), io::Error> { @@ -10276,7 +10271,20 @@ impl Writeable for FundedChannel where SP::Target: SignerProvider }, &InboundHTLCState::LocalRemoved(ref removal_reason) => { 4u8.write(writer)?; - removal_reason.write(writer)?; + match removal_reason { + InboundHTLCRemovalReason::FailRelay(msgs::OnionErrorPacket { data }) => { + 0u8.write(writer)?; + data.write(writer)?; + }, + InboundHTLCRemovalReason::FailMalformed((hash, code)) => { + 1u8.write(writer)?; + (hash, code).write(writer)?; + }, + InboundHTLCRemovalReason::Fulfill(preimage) => { + 2u8.write(writer)?; + preimage.write(writer)?; + }, + } }, } } @@ -10355,7 +10363,7 @@ impl Writeable for FundedChannel where SP::Target: SignerProvider &HTLCUpdateAwaitingACK::FailHTLC { ref htlc_id, ref err_packet } => { 2u8.write(writer)?; htlc_id.write(writer)?; - err_packet.write(writer)?; + err_packet.data.write(writer)?; } &HTLCUpdateAwaitingACK::FailMalformedHTLC { htlc_id, failure_code, sha256_of_onion @@ -10364,10 +10372,9 @@ impl Writeable for FundedChannel where SP::Target: SignerProvider // `::FailHTLC` variant and write the real malformed error as an optional TLV. malformed_htlcs.push((htlc_id, failure_code, sha256_of_onion)); - let dummy_err_packet = msgs::OnionErrorPacket { data: Vec::new() }; 2u8.write(writer)?; htlc_id.write(writer)?; - dummy_err_packet.write(writer)?; + Vec::::new().write(writer)?; } } } @@ -10613,7 +10620,17 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel InboundHTLCState::AwaitingAnnouncedRemoteRevoke(resolution) }, 3 => InboundHTLCState::Committed, - 4 => InboundHTLCState::LocalRemoved(Readable::read(reader)?), + 4 => { + let reason = match ::read(reader)? { + 0 => InboundHTLCRemovalReason::FailRelay(msgs::OnionErrorPacket { + data: Readable::read(reader)?, + }), + 1 => InboundHTLCRemovalReason::FailMalformed(Readable::read(reader)?), + 2 => InboundHTLCRemovalReason::Fulfill(Readable::read(reader)?), + _ => return Err(DecodeError::InvalidValue), + }; + InboundHTLCState::LocalRemoved(reason) + }, _ => return Err(DecodeError::InvalidValue), }, }); @@ -10669,7 +10686,9 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel }, 2 => HTLCUpdateAwaitingACK::FailHTLC { htlc_id: Readable::read(reader)?, - err_packet: Readable::read(reader)?, + err_packet: OnionErrorPacket { + data: Readable::read(reader)?, + }, }, _ => return Err(DecodeError::InvalidValue), }); diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index c69e6c6b02b..4574b2e7f8a 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -4470,11 +4470,12 @@ where } else { (err_code, &res.0[..]) }; + let failure = HTLCFailReason::reason(err_code, err_data.to_vec()) + .get_encrypted_failure_packet(shared_secret, &None); HTLCFailureMsg::Relay(msgs::UpdateFailHTLC { channel_id: msg.channel_id, htlc_id: msg.htlc_id, - reason: HTLCFailReason::reason(err_code, err_data.to_vec()) - .get_encrypted_failure_packet(shared_secret, &None), + reason: failure.data, }) } @@ -4498,11 +4499,12 @@ where } )) } + let failure = HTLCFailReason::reason($err_code, $data.to_vec()) + .get_encrypted_failure_packet(&shared_secret, &None); return PendingHTLCStatus::Fail(HTLCFailureMsg::Relay(msgs::UpdateFailHTLC { channel_id: msg.channel_id, htlc_id: msg.htlc_id, - reason: HTLCFailReason::reason($err_code, $data.to_vec()) - .get_encrypted_failure_packet(&shared_secret, &None), + reason: failure.data, })); } } @@ -5881,7 +5883,7 @@ where let failure = match htlc_fail { HTLCFailureMsg::Relay(fail_htlc) => HTLCForwardInfo::FailHTLC { htlc_id: fail_htlc.htlc_id, - err_packet: fail_htlc.reason, + err_packet: fail_htlc.into(), }, HTLCFailureMsg::Malformed(fail_malformed_htlc) => HTLCForwardInfo::FailMalformedHTLC { htlc_id: fail_malformed_htlc.htlc_id, @@ -13224,7 +13226,7 @@ impl Writeable for HTLCForwardInfo { FAIL_HTLC_VARIANT_ID.write(w)?; write_tlv_fields!(w, { (0, htlc_id, required), - (2, err_packet, required), + (2, err_packet.data, required), }); }, Self::FailMalformedHTLC { htlc_id, failure_code, sha256_of_onion } => { @@ -13232,11 +13234,10 @@ impl Writeable for HTLCForwardInfo { // packet so older versions have something to fail back with, but serialize the real data as // optional TLVs for the benefit of newer versions. FAIL_HTLC_VARIANT_ID.write(w)?; - let dummy_err_packet = msgs::OnionErrorPacket { data: Vec::new() }; write_tlv_fields!(w, { (0, htlc_id, required), (1, failure_code, required), - (2, dummy_err_packet, required), + (2, Vec::::new(), required), (3, sha256_of_onion, required), }); }, @@ -13266,7 +13267,9 @@ impl Readable for HTLCForwardInfo { } else { Self::FailHTLC { htlc_id: _init_tlv_based_struct_field!(htlc_id, required), - err_packet: _init_tlv_based_struct_field!(err_packet, required), + err_packet: crate::ln::msgs::OnionErrorPacket { + data: _init_tlv_based_struct_field!(err_packet, required), + }, } } }, @@ -16273,7 +16276,7 @@ mod tests { let mut nodes = create_network(1, &node_cfg, &chanmgrs); let dummy_failed_htlc = |htlc_id| { - HTLCForwardInfo::FailHTLC { htlc_id, err_packet: msgs::OnionErrorPacket { data: vec![42] }, } + HTLCForwardInfo::FailHTLC { htlc_id, err_packet: msgs::OnionErrorPacket { data: vec![42] } } }; let dummy_malformed_htlc = |htlc_id| { HTLCForwardInfo::FailMalformedHTLC { htlc_id, failure_code: 0x4000, sha256_of_onion: [0; 32] } diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index d46fc721c13..a65646e9b61 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -7057,7 +7057,7 @@ pub fn test_update_fulfill_htlc_bolt2_update_fail_htlc_before_commitment() { let update_msg = msgs::UpdateFailHTLC{ channel_id: chan.2, htlc_id: 0, - reason: msgs::OnionErrorPacket { data: Vec::new()}, + reason: Vec::new(), }; nodes[0].node.handle_update_fail_htlc(nodes[1].node.get_our_node_id(), &update_msg); diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 85cbd3b8b1b..4d2102171eb 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -766,7 +766,7 @@ pub struct UpdateFailHTLC { pub channel_id: ChannelId, /// The HTLC ID pub htlc_id: u64, - pub(crate) reason: OnionErrorPacket, + pub(crate) reason: Vec, } /// An [`update_fail_malformed_htlc`] message to be sent to or received from a peer. @@ -2355,6 +2355,14 @@ pub(crate) struct OnionErrorPacket { pub(crate) data: Vec, } +impl From for OnionErrorPacket { + fn from(msg: UpdateFailHTLC) -> Self { + OnionErrorPacket { + data: msg.reason, + } + } +} + impl fmt::Display for DecodeError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { @@ -3001,13 +3009,6 @@ impl_writeable_msg!(PeerStorageRetrieval, { data }, {}); -// Note that this is written as a part of ChannelManager objects, and thus cannot change its -// serialization format in a way which assumes we know the total serialized length/message end -// position. -impl_writeable!(OnionErrorPacket, { - data -}); - // Note that this is written as a part of ChannelManager objects, and thus cannot change its // serialization format in a way which assumes we know the total serialized length/message end // position. @@ -3933,7 +3934,7 @@ mod tests { use crate::ln::types::ChannelId; use crate::types::payment::{PaymentPreimage, PaymentHash, PaymentSecret}; use crate::types::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures}; - use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket, CommonOpenChannelFields, CommonAcceptChannelFields, OutboundTrampolinePayload, TrampolineOnionPacket, InboundOnionForwardPayload, InboundOnionReceivePayload}; + use crate::ln::msgs::{self, FinalOnionHopData, CommonOpenChannelFields, CommonAcceptChannelFields, OutboundTrampolinePayload, TrampolineOnionPacket, InboundOnionForwardPayload, InboundOnionReceivePayload}; use crate::ln::msgs::SocketAddress; use crate::routing::gossip::{NodeAlias, NodeId}; use crate::util::ser::{BigSize, FixedLengthReader, Hostname, LengthReadable, Readable, ReadableArgs, TransactionU16LenLimited, Writeable}; @@ -4932,13 +4933,10 @@ mod tests { #[test] fn encoding_update_fail_htlc() { - let reason = OnionErrorPacket { - data: [1; 32].to_vec(), - }; let update_fail_htlc = msgs::UpdateFailHTLC { channel_id: ChannelId::from_bytes([2; 32]), htlc_id: 2316138423780173, - reason + reason: [1; 32].to_vec() }; let encoded_value = update_fail_htlc.encode(); let target_value = >::from_hex("020202020202020202020202020202020202020202020202020202020202020200083a840000034d00200101010101010101010101010101010101010101010101010101010101010101").unwrap(); diff --git a/lightning/src/ln/onion_payment.rs b/lightning/src/ln/onion_payment.rs index c3594a9b0d1..46661df6807 100644 --- a/lightning/src/ln/onion_payment.rs +++ b/lightning/src/ln/onion_payment.rs @@ -407,7 +407,7 @@ where ).map_err(|e| { let (err_code, err_data) = match e { HTLCFailureMsg::Malformed(m) => (m.failure_code, Vec::new()), - HTLCFailureMsg::Relay(r) => (0x4000 | 22, r.reason.data), + HTLCFailureMsg::Relay(r) => (0x4000 | 22, r.reason), }; let msg = "Failed to decode update add htlc onion"; InboundHTLCErr { msg, err_code, err_data } @@ -512,11 +512,12 @@ where } log_info!(logger, "Failed to accept/forward incoming HTLC: {}", message); + let failure = HTLCFailReason::reason(err_code, data.to_vec()) + .get_encrypted_failure_packet(&shared_secret, &trampoline_shared_secret); return Err(HTLCFailureMsg::Relay(msgs::UpdateFailHTLC { channel_id: msg.channel_id, htlc_id: msg.htlc_id, - reason: HTLCFailReason::reason(err_code, data.to_vec()) - .get_encrypted_failure_packet(&shared_secret, &trampoline_shared_secret), + reason: failure.data, })); }; diff --git a/lightning/src/ln/onion_route_tests.rs b/lightning/src/ln/onion_route_tests.rs index f23cfaacdb0..127d4a45588 100644 --- a/lightning/src/ln/onion_route_tests.rs +++ b/lightning/src/ln/onion_route_tests.rs @@ -28,7 +28,7 @@ use crate::ln::msgs::{ OutboundOnionPayload, OutboundTrampolinePayload, MessageSendEvent, }; use crate::ln::wire::Encode; -use crate::util::ser::{Writeable, Writer, BigSize}; +use crate::util::ser::{BigSize, Writeable, Writer}; use crate::util::test_utils; use crate::util::config::{UserConfig, ChannelConfig, MaxDustHTLCExposure}; use crate::util::errors::APIError; @@ -49,6 +49,8 @@ use crate::blinded_path::BlindedHop; use crate::ln::functional_test_utils::*; use crate::ln::onion_utils::{construct_trampoline_onion_keys, construct_trampoline_onion_packet}; +use super::msgs::OnionErrorPacket; + fn run_onion_failure_test(_name: &str, test_case: u8, nodes: &Vec, route: &Route, payment_hash: &PaymentHash, payment_secret: &PaymentSecret, callback_msg: F1, callback_node: F2, expected_retryable: bool, expected_error_code: Option, expected_channel_update: Option, expected_short_channel_id: Option, expected_htlc_destination: Option) where F1: for <'a> FnMut(&'a mut msgs::UpdateAddHTLC), F2: FnMut(), @@ -409,7 +411,8 @@ fn test_onion_failure() { // and tamper returning error message let session_priv = SecretKey::from_slice(&[3; 32]).unwrap(); let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap(); - msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[0].shared_secret.as_ref(), NODE|2, &[0;0]); + let failure = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), NODE|2, &[0;0]); + msg.reason = failure.data; }, ||{}, 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())); // final node failure @@ -417,7 +420,8 @@ fn test_onion_failure() { // and tamper returning error message let session_priv = SecretKey::from_slice(&[3; 32]).unwrap(); let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap(); - msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[1].shared_secret.as_ref(), NODE|2, &[0;0]); + let failure = onion_utils::build_failure_packet(onion_keys[1].shared_secret.as_ref(), NODE|2, &[0;0]); + msg.reason = failure.data; }, ||{ nodes[2].node.fail_htlc_backwards(&payment_hash); }, 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); @@ -429,14 +433,16 @@ fn test_onion_failure() { }, |msg| { let session_priv = SecretKey::from_slice(&[3; 32]).unwrap(); let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap(); - msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|NODE|2, &[0;0]); + let failure = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|NODE|2, &[0;0]); + msg.reason = failure.data; }, ||{}, 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())); // final node failure run_onion_failure_test_with_fail_intercept("permanent_node_failure", 200, &nodes, &route, &payment_hash, &payment_secret, |_msg| {}, |msg| { let session_priv = SecretKey::from_slice(&[3; 32]).unwrap(); let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap(); - msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[1].shared_secret.as_ref(), PERM|NODE|2, &[0;0]); + let failure = onion_utils::build_failure_packet(onion_keys[1].shared_secret.as_ref(), PERM|NODE|2, &[0;0]); + msg.reason = failure.data; }, ||{ nodes[2].node.fail_htlc_backwards(&payment_hash); }, 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); @@ -448,7 +454,8 @@ fn test_onion_failure() { }, |msg| { let session_priv = SecretKey::from_slice(&[3; 32]).unwrap(); let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap(); - msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|NODE|3, &[0;0]); + let failure = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|NODE|3, &[0;0]); + msg.reason = failure.data; }, ||{ nodes[2].node.fail_htlc_backwards(&payment_hash); }, 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())); @@ -457,7 +464,8 @@ fn test_onion_failure() { run_onion_failure_test_with_fail_intercept("required_node_feature_missing", 200, &nodes, &route, &payment_hash, &payment_secret, |_msg| {}, |msg| { let session_priv = SecretKey::from_slice(&[3; 32]).unwrap(); let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap(); - msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[1].shared_secret.as_ref(), PERM|NODE|3, &[0;0]); + let failure = onion_utils::build_failure_packet(onion_keys[1].shared_secret.as_ref(), PERM|NODE|3, &[0;0]); + msg.reason = failure.data; }, ||{ nodes[2].node.fail_htlc_backwards(&payment_hash); }, 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); @@ -487,7 +495,8 @@ fn test_onion_failure() { }, |msg| { let session_priv = SecretKey::from_slice(&[3; 32]).unwrap(); let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap(); - msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[0].shared_secret.as_ref(), UPDATE|7, &err_data); + let failure = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), UPDATE|7, &err_data); + msg.reason = failure.data; }, ||{}, true, Some(UPDATE|7), Some(NetworkUpdate::ChannelFailure { short_channel_id, is_permanent: false }), Some(short_channel_id), Some(next_hop_failure.clone())); @@ -499,7 +508,8 @@ fn test_onion_failure() { }, |msg| { let session_priv = SecretKey::from_slice(&[3; 32]).unwrap(); let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap(); - msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[0].shared_secret.as_ref(), UPDATE|7, &err_data_without_type); + let failure = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), UPDATE|7, &err_data_without_type); + msg.reason = failure.data; }, ||{}, true, Some(UPDATE|7), Some(NetworkUpdate::ChannelFailure { short_channel_id, is_permanent: false }), Some(short_channel_id), Some(next_hop_failure.clone())); @@ -510,7 +520,8 @@ fn test_onion_failure() { }, |msg| { let session_priv = SecretKey::from_slice(&[3; 32]).unwrap(); let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap(); - msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|8, &[0;0]); + let failure = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|8, &[0;0]); + msg.reason = failure.data; // short_channel_id from the processing node }, ||{}, true, Some(PERM|8), Some(NetworkUpdate::ChannelFailure{short_channel_id, is_permanent: true}), Some(short_channel_id), Some(next_hop_failure.clone())); @@ -520,7 +531,8 @@ fn test_onion_failure() { }, |msg| { let session_priv = SecretKey::from_slice(&[3; 32]).unwrap(); let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap(); - msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|9, &[0;0]); + let failure = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|9, &[0;0]); + msg.reason = failure.data; // short_channel_id from the processing node }, ||{}, true, Some(PERM|9), Some(NetworkUpdate::ChannelFailure{short_channel_id, is_permanent: true}), Some(short_channel_id), Some(next_hop_failure.clone())); @@ -652,7 +664,8 @@ fn test_onion_failure() { // Tamper returning error message let session_priv = SecretKey::from_slice(&[3; 32]).unwrap(); let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap(); - msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[1].shared_secret.as_ref(), 23, &[0;0]); + let failure = onion_utils::build_failure_packet(onion_keys[1].shared_secret.as_ref(), 23, &[0;0]); + msg.reason = failure.data; }, ||{ nodes[2].node.fail_htlc_backwards(&payment_hash); }, true, Some(23), None, None, None); @@ -670,8 +683,12 @@ fn test_onion_failure() { let mut hmac = HmacEngine::::new(&um); hmac.input(&decoded_err_packet.encode()[32..]); decoded_err_packet.hmac = Hmac::from_engine(hmac).to_byte_array(); - msg.reason = onion_utils::encrypt_failure_packet( - &onion_keys[1].shared_secret.as_ref(), &decoded_err_packet.encode()[..]) + let mut onion_error = OnionErrorPacket { + data: decoded_err_packet.encode(), + }; + onion_utils::test_crypt_failure_packet( + &onion_keys[1].shared_secret.as_ref(), &mut onion_error); + msg.reason = onion_error.data; }, || nodes[2].node.fail_htlc_backwards(&payment_hash), false, None, Some(NetworkUpdate::NodeFailure { node_id: route.paths[0].hops[1].pubkey, is_permanent: true }), Some(channels[1].0.contents.short_channel_id), None); @@ -693,8 +710,12 @@ fn test_onion_failure() { let mut hmac = HmacEngine::::new(&um); hmac.input(&decoded_err_packet.encode()[32..]); decoded_err_packet.hmac = Hmac::from_engine(hmac).to_byte_array(); - msg.reason = onion_utils::encrypt_failure_packet( - &onion_keys[0].shared_secret.as_ref(), &decoded_err_packet.encode()[..]) + let mut onion_error = OnionErrorPacket{ + data: decoded_err_packet.encode(), + }; + onion_utils::test_crypt_failure_packet( + &onion_keys[0].shared_secret.as_ref(), &mut onion_error); + msg.reason = onion_error.data; }, || {}, true, Some(0x1000|7), Some(NetworkUpdate::ChannelFailure { short_channel_id: channels[1].0.contents.short_channel_id, @@ -717,8 +738,12 @@ fn test_onion_failure() { let mut hmac = HmacEngine::::new(&um); hmac.input(&decoded_err_packet.encode()[32..]); decoded_err_packet.hmac = Hmac::from_engine(hmac).to_byte_array(); - msg.reason = onion_utils::encrypt_failure_packet( - &onion_keys[1].shared_secret.as_ref(), &decoded_err_packet.encode()[..]) + let mut onion_error = OnionErrorPacket{ + data: decoded_err_packet.encode(), + }; + onion_utils::test_crypt_failure_packet( + &onion_keys[1].shared_secret.as_ref(), &mut onion_error); + msg.reason = onion_error.data; }, || nodes[2].node.fail_htlc_backwards(&payment_hash), true, Some(0x1000|7), Some(NetworkUpdate::ChannelFailure { short_channel_id: channels[1].0.contents.short_channel_id, diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index bc4121cfd3d..41701b1ade3 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -7,6 +7,7 @@ // You may not use this file except in accordance with one or both of these // licenses. +use super::msgs::OnionErrorPacket; use crate::blinded_path::BlindedHop; use crate::crypto::chacha20::ChaCha20; use crate::crypto::streams::ChaChaReader; @@ -869,23 +870,25 @@ fn construct_onion_packet_with_init_noise( Ok(P::new(onion_keys.first().unwrap().ephemeral_pubkey, packet_data, hmac_res)) } -/// Encrypts a failure packet. raw_packet can either be a -/// msgs::DecodedOnionErrorPacket.encode() result or a msgs::OnionErrorPacket.data element. -pub(super) fn encrypt_failure_packet( - shared_secret: &[u8], raw_packet: &[u8], -) -> msgs::OnionErrorPacket { +/// Encrypts/decrypts a failure packet. +fn crypt_failure_packet(shared_secret: &[u8], packet: &mut OnionErrorPacket) { let ammag = gen_ammag_from_shared_secret(&shared_secret); + process_chacha(&ammag, &mut packet.data); +} - let mut packet_crypted = Vec::with_capacity(raw_packet.len()); - packet_crypted.resize(raw_packet.len(), 0); - let mut chacha = ChaCha20::new(&ammag, &[0u8; 8]); - chacha.process(&raw_packet, &mut packet_crypted[..]); - msgs::OnionErrorPacket { data: packet_crypted } +#[cfg(test)] +pub(super) fn test_crypt_failure_packet(shared_secret: &[u8], packet: &mut OnionErrorPacket) { + crypt_failure_packet(shared_secret, packet) } -pub(super) fn build_failure_packet( +fn process_chacha(key: &[u8; 32], packet: &mut [u8]) { + let mut chacha = ChaCha20::new(key, &[0u8; 8]); + chacha.process_in_place(packet); +} + +fn build_unencrypted_failure_packet( shared_secret: &[u8], failure_type: u16, failure_data: &[u8], -) -> msgs::DecodedOnionErrorPacket { +) -> OnionErrorPacket { assert_eq!(shared_secret.len(), 32); assert!(failure_data.len() <= 256 - 2); @@ -909,15 +912,18 @@ pub(super) fn build_failure_packet( hmac.input(&packet.encode()[32..]); packet.hmac = Hmac::from_engine(hmac).to_byte_array(); - packet + OnionErrorPacket { data: packet.encode() } } -#[cfg(test)] -pub(super) fn build_first_hop_failure_packet( +pub(super) fn build_failure_packet( shared_secret: &[u8], failure_type: u16, failure_data: &[u8], -) -> msgs::OnionErrorPacket { - let failure_packet = build_failure_packet(shared_secret, failure_type, failure_data); - encrypt_failure_packet(shared_secret, &failure_packet.encode()[..]) +) -> OnionErrorPacket { + let mut onion_error_packet = + build_unencrypted_failure_packet(shared_secret, failure_type, failure_data); + + crypt_failure_packet(shared_secret, &mut onion_error_packet); + + onion_error_packet } pub(crate) struct DecodedOnionFailure { @@ -931,18 +937,12 @@ pub(crate) struct DecodedOnionFailure { pub(crate) onion_error_data: Option>, } -/// Decrypt the error packet in-place. -fn decrypt_onion_error_packet(packet: &mut Vec, shared_secret: SharedSecret) { - let ammag = gen_ammag_from_shared_secret(shared_secret.as_ref()); - let mut chacha = ChaCha20::new(&ammag, &[0u8; 8]); - chacha.process_in_place(packet); -} - /// Process failure we got back from upstream on a payment we sent (implying htlc_source is an /// OutboundRoute). #[inline] pub(super) fn process_onion_failure( - secp_ctx: &Secp256k1, logger: &L, htlc_source: &HTLCSource, mut encrypted_packet: Vec, + secp_ctx: &Secp256k1, logger: &L, htlc_source: &HTLCSource, + mut encrypted_packet: OnionErrorPacket, ) -> DecodedOnionFailure where L::Target: Logger, @@ -975,7 +975,11 @@ where const UPDATE: u16 = 0x1000; // Handle packed channel/node updates for passing back for the route handler - let callback = |shared_secret, _, _, route_hop_opt: Option<&RouteHop>, route_hop_idx| { + let callback = |shared_secret: SharedSecret, + _, + _, + route_hop_opt: Option<&RouteHop>, + route_hop_idx| { if res.is_some() { return; } @@ -1017,9 +1021,9 @@ where { // Actually parse the onion error data in tests so we can check that blinded hops fail // back correctly. - decrypt_onion_error_packet(&mut encrypted_packet, shared_secret); + crypt_failure_packet(shared_secret.as_ref(), &mut encrypted_packet); let err_packet = msgs::DecodedOnionErrorPacket::read(&mut Cursor::new( - &encrypted_packet, + &encrypted_packet.data, )) .unwrap(); error_code_ret = Some(u16::from_be_bytes( @@ -1042,18 +1046,18 @@ where let amt_to_forward = htlc_msat - route_hop.fee_msat; htlc_msat = amt_to_forward; - decrypt_onion_error_packet(&mut encrypted_packet, shared_secret); + crypt_failure_packet(shared_secret.as_ref(), &mut encrypted_packet); let um = gen_um_from_shared_secret(shared_secret.as_ref()); let mut hmac = HmacEngine::::new(&um); - hmac.input(&encrypted_packet[32..]); + hmac.input(&encrypted_packet.data[32..]); - if !fixed_time_eq(&Hmac::from_engine(hmac).to_byte_array(), &encrypted_packet[..32]) { + if !fixed_time_eq(&Hmac::from_engine(hmac).to_byte_array(), &encrypted_packet.data[..32]) { return; } let err_packet = - match msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(&encrypted_packet)) { + match msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(&encrypted_packet.data)) { Ok(p) => p, Err(_) => { log_warn!(logger, "Unreadable failure from {}", route_hop.pubkey); @@ -1295,7 +1299,14 @@ impl Readable for HTLCFailReason { impl_writeable_tlv_based_enum!(HTLCFailReasonRepr, (0, LightningError) => { - (0, err, required), + (0, data, (legacy, Vec, |us| + if let &HTLCFailReasonRepr::LightningError { err: msgs::OnionErrorPacket { ref data, .. } } = us { + Some(data) + } else { + None + }) + ), + (_unused, err, (static_value, msgs::OnionErrorPacket { data: data.ok_or(DecodeError::InvalidValue)? })), }, (1, Reason) => { (0, failure_code, required), @@ -1352,7 +1363,9 @@ impl HTLCFailReason { } pub(super) fn from_msg(msg: &msgs::UpdateFailHTLC) -> Self { - Self(HTLCFailReasonRepr::LightningError { err: msg.reason.clone() }) + Self(HTLCFailReasonRepr::LightningError { + err: OnionErrorPacket { data: msg.reason.clone() }, + }) } /// Encrypted a failure packet using a shared secret. @@ -1366,27 +1379,22 @@ impl HTLCFailReason { match self.0 { HTLCFailReasonRepr::Reason { ref failure_code, ref data } => { if let Some(secondary_shared_secret) = secondary_shared_secret { - let inner_packet = - build_failure_packet(secondary_shared_secret, *failure_code, &data[..]) - .encode(); - let encrypted_inner_packet = - encrypt_failure_packet(secondary_shared_secret, &inner_packet); - encrypt_failure_packet( - incoming_packet_shared_secret, - &encrypted_inner_packet.data[..], - ) + let mut packet = + build_failure_packet(secondary_shared_secret, *failure_code, &data[..]); + + crypt_failure_packet(incoming_packet_shared_secret, &mut packet); + + packet } else { - let packet = build_failure_packet( - incoming_packet_shared_secret, - *failure_code, - &data[..], - ) - .encode(); - encrypt_failure_packet(incoming_packet_shared_secret, &packet) + build_failure_packet(incoming_packet_shared_secret, *failure_code, &data[..]) } }, HTLCFailReasonRepr::LightningError { ref err } => { - encrypt_failure_packet(incoming_packet_shared_secret, &err.data) + let mut err = err.clone(); + + crypt_failure_packet(incoming_packet_shared_secret, &mut err); + + err }, } } @@ -1399,7 +1407,7 @@ impl HTLCFailReason { { match self.0 { HTLCFailReasonRepr::LightningError { ref err } => { - process_onion_failure(secp_ctx, logger, &htlc_source, err.data.clone()) + process_onion_failure(secp_ctx, logger, &htlc_source, err.clone()) }, #[allow(unused)] HTLCFailReasonRepr::Reason { ref failure_code, ref data, .. } => { @@ -2287,45 +2295,33 @@ mod tests { // Returning Errors test vectors from BOLT 4 let onion_keys = build_test_onion_keys(); - let onion_error = - super::build_failure_packet(onion_keys[4].shared_secret.as_ref(), 0x2002, &[0; 0]); - let hex = "4c2fc8bc08510334b6833ad9c3e79cd1b52ae59dfe5c2a4b23ead50f09f7ee0b0002200200fe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; - assert_eq!(onion_error.encode(), >::from_hex(hex).unwrap()); - - let onion_packet_1 = super::encrypt_failure_packet( + let mut onion_error = super::build_unencrypted_failure_packet( onion_keys[4].shared_secret.as_ref(), - &onion_error.encode()[..], + 0x2002, + &[0; 0], ); + let hex = "4c2fc8bc08510334b6833ad9c3e79cd1b52ae59dfe5c2a4b23ead50f09f7ee0b0002200200fe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + assert_eq!(onion_error.data, >::from_hex(hex).unwrap()); + + super::crypt_failure_packet(onion_keys[4].shared_secret.as_ref(), &mut onion_error); let hex = "a5e6bd0c74cb347f10cce367f949098f2457d14c046fd8a22cb96efb30b0fdcda8cb9168b50f2fd45edd73c1b0c8b33002df376801ff58aaa94000bf8a86f92620f343baef38a580102395ae3abf9128d1047a0736ff9b83d456740ebbb4aeb3aa9737f18fb4afb4aa074fb26c4d702f42968888550a3bded8c05247e045b866baef0499f079fdaeef6538f31d44deafffdfd3afa2fb4ca9082b8f1c465371a9894dd8c243fb4847e004f5256b3e90e2edde4c9fb3082ddfe4d1e734cacd96ef0706bf63c9984e22dc98851bcccd1c3494351feb458c9c6af41c0044bea3c47552b1d992ae542b17a2d0bba1a096c78d169034ecb55b6e3a7263c26017f033031228833c1daefc0dedb8cf7c3e37c9c37ebfe42f3225c326e8bcfd338804c145b16e34e4"; - assert_eq!(onion_packet_1.data, >::from_hex(hex).unwrap()); + assert_eq!(onion_error.data, >::from_hex(hex).unwrap()); - let onion_packet_2 = super::encrypt_failure_packet( - onion_keys[3].shared_secret.as_ref(), - &onion_packet_1.data[..], - ); + super::crypt_failure_packet(onion_keys[3].shared_secret.as_ref(), &mut onion_error); let hex = "c49a1ce81680f78f5f2000cda36268de34a3f0a0662f55b4e837c83a8773c22aa081bab1616a0011585323930fa5b9fae0c85770a2279ff59ec427ad1bbff9001c0cd1497004bd2a0f68b50704cf6d6a4bf3c8b6a0833399a24b3456961ba00736785112594f65b6b2d44d9f5ea4e49b5e1ec2af978cbe31c67114440ac51a62081df0ed46d4a3df295da0b0fe25c0115019f03f15ec86fabb4c852f83449e812f141a9395b3f70b766ebbd4ec2fae2b6955bd8f32684c15abfe8fd3a6261e52650e8807a92158d9f1463261a925e4bfba44bd20b166d532f0017185c3a6ac7957adefe45559e3072c8dc35abeba835a8cb01a71a15c736911126f27d46a36168ca5ef7dccd4e2886212602b181463e0dd30185c96348f9743a02aca8ec27c0b90dca270"; - assert_eq!(onion_packet_2.data, >::from_hex(hex).unwrap()); + assert_eq!(onion_error.data, >::from_hex(hex).unwrap()); - let onion_packet_3 = super::encrypt_failure_packet( - onion_keys[2].shared_secret.as_ref(), - &onion_packet_2.data[..], - ); + super::crypt_failure_packet(onion_keys[2].shared_secret.as_ref(), &mut onion_error); let hex = "a5d3e8634cfe78b2307d87c6d90be6fe7855b4f2cc9b1dfb19e92e4b79103f61ff9ac25f412ddfb7466e74f81b3e545563cdd8f5524dae873de61d7bdfccd496af2584930d2b566b4f8d3881f8c043df92224f38cf094cfc09d92655989531524593ec6d6caec1863bdfaa79229b5020acc034cd6deeea1021c50586947b9b8e6faa83b81fbfa6133c0af5d6b07c017f7158fa94f0d206baf12dda6b68f785b773b360fd0497e16cc402d779c8d48d0fa6315536ef0660f3f4e1865f5b38ea49c7da4fd959de4e83ff3ab686f059a45c65ba2af4a6a79166aa0f496bf04d06987b6d2ea205bdb0d347718b9aeff5b61dfff344993a275b79717cd815b6ad4c0beb568c4ac9c36ff1c315ec1119a1993c4b61e6eaa0375e0aaf738ac691abd3263bf937e3"; - assert_eq!(onion_packet_3.data, >::from_hex(hex).unwrap()); + assert_eq!(onion_error.data, >::from_hex(hex).unwrap()); - let onion_packet_4 = super::encrypt_failure_packet( - onion_keys[1].shared_secret.as_ref(), - &onion_packet_3.data[..], - ); + super::crypt_failure_packet(onion_keys[1].shared_secret.as_ref(), &mut onion_error); let hex = "aac3200c4968f56b21f53e5e374e3a2383ad2b1b6501bbcc45abc31e59b26881b7dfadbb56ec8dae8857add94e6702fb4c3a4de22e2e669e1ed926b04447fc73034bb730f4932acd62727b75348a648a1128744657ca6a4e713b9b646c3ca66cac02cdab44dd3439890ef3aaf61708714f7375349b8da541b2548d452d84de7084bb95b3ac2345201d624d31f4d52078aa0fa05a88b4e20202bd2b86ac5b52919ea305a8949de95e935eed0319cf3cf19ebea61d76ba92532497fcdc9411d06bcd4275094d0a4a3c5d3a945e43305a5a9256e333e1f64dbca5fcd4e03a39b9012d197506e06f29339dfee3331995b21615337ae060233d39befea925cc262873e0530408e6990f1cbd233a150ef7b004ff6166c70c68d9f8c853c1abca640b8660db2921"; - assert_eq!(onion_packet_4.data, >::from_hex(hex).unwrap()); + assert_eq!(onion_error.data, >::from_hex(hex).unwrap()); - let onion_packet_5 = super::encrypt_failure_packet( - onion_keys[0].shared_secret.as_ref(), - &onion_packet_4.data[..], - ); + super::crypt_failure_packet(onion_keys[0].shared_secret.as_ref(), &mut onion_error); let hex = "9c5add3963fc7f6ed7f148623c84134b5647e1306419dbe2174e523fa9e2fbed3a06a19f899145610741c83ad40b7712aefaddec8c6baf7325d92ea4ca4d1df8bce517f7e54554608bf2bd8071a4f52a7a2f7ffbb1413edad81eeea5785aa9d990f2865dc23b4bc3c301a94eec4eabebca66be5cf638f693ec256aec514620cc28ee4a94bd9565bc4d4962b9d3641d4278fb319ed2b84de5b665f307a2db0f7fbb757366067d88c50f7e829138fde4f78d39b5b5802f1b92a8a820865af5cc79f9f30bc3f461c66af95d13e5e1f0381c184572a91dee1c849048a647a1158cf884064deddbf1b0b88dfe2f791428d0ba0f6fb2f04e14081f69165ae66d9297c118f0907705c9c4954a199bae0bb96fad763d690e7daa6cfda59ba7f2c8d11448b604d12d"; - assert_eq!(onion_packet_5.data, >::from_hex(hex).unwrap()); + assert_eq!(onion_error.data, >::from_hex(hex).unwrap()); let logger: Arc = Arc::new(TestLogger::new()); let ctx_full = Secp256k1::new(); @@ -2339,7 +2335,7 @@ mod tests { // Assert that the original failure can be retrieved and that all hmacs check out. let decrypted_failure = - process_onion_failure(&ctx_full, &logger, &htlc_source, onion_packet_5.data); + process_onion_failure(&ctx_full, &logger, &htlc_source, onion_error); assert_eq!(decrypted_failure.onion_error_code, Some(0x2002)); } @@ -2348,10 +2344,11 @@ mod tests { fn test_non_attributable_failure_packet_onion() { // Create a failure packet with bogus data. let packet = vec![1u8; 292]; + let onion_error_packet = OnionErrorPacket { data: packet }; // In the current protocol, it is unfortunately not possible to identify the failure source. let logger: TestLogger = TestLogger::new(); - let decrypted_failure = test_failure_attribution(&logger, &packet); + let decrypted_failure = test_failure_attribution(&logger, onion_error_packet); assert_eq!(decrypted_failure.short_channel_id, None); logger.assert_log_contains( @@ -2376,11 +2373,12 @@ mod tests { let hmac = Hmac::from_engine(hmac).to_byte_array(); packet[..32].copy_from_slice(&hmac); - let packet = encrypt_failure_packet(shared_secret, &packet); + let mut onion_error_packet = OnionErrorPacket { data: packet.to_vec() }; + crypt_failure_packet(shared_secret, &mut onion_error_packet); // For the unreadable failure, it is still expected that the failing channel can be identified. let logger: TestLogger = TestLogger::new(); - let decrypted_failure = test_failure_attribution(&logger, &packet.data); + let decrypted_failure = test_failure_attribution(&logger, onion_error_packet); assert_eq!(decrypted_failure.short_channel_id, Some(0)); logger.assert_log_contains("lightning::ln::onion_utils", "Unreadable failure", 1); @@ -2401,10 +2399,11 @@ mod tests { hmac.input(&packet.encode()[32..]); packet.hmac = Hmac::from_engine(hmac).to_byte_array(); - let packet = encrypt_failure_packet(shared_secret, &packet.encode()[..]); + let mut onion_error_packet = OnionErrorPacket { data: packet.encode() }; + crypt_failure_packet(shared_secret, &mut onion_error_packet); let logger = TestLogger::new(); - let decrypted_failure = test_failure_attribution(&logger, &packet.data); + let decrypted_failure = test_failure_attribution(&logger, onion_error_packet); assert_eq!(decrypted_failure.short_channel_id, Some(0)); logger.assert_log_contains( @@ -2414,7 +2413,9 @@ mod tests { ); } - fn test_failure_attribution(logger: &TestLogger, packet: &[u8]) -> DecodedOnionFailure { + fn test_failure_attribution( + logger: &TestLogger, packet: OnionErrorPacket, + ) -> DecodedOnionFailure { let ctx_full = Secp256k1::new(); let path = build_test_path(); let htlc_source = HTLCSource::OutboundRoute { @@ -2424,8 +2425,7 @@ mod tests { payment_id: PaymentId([1; 32]), }; - let decrypted_failure = - process_onion_failure(&ctx_full, &logger, &htlc_source, packet.into()); + let decrypted_failure = process_onion_failure(&ctx_full, &logger, &htlc_source, packet); decrypted_failure }