Attributable failures pre-factor

This commits prepares the persistence layer for the addition of
attribution data. Changes:

- Expand InboundHTLCRemovalReason serialization instead of using the
macro. When attribution data is added, it can't just be serialized
along with the existing fields because it would break backwards
compatibility. Instead the new field needs to go into the tlv block.

- Stop using OnionErrorPacket in the UpdateFailHTLC message. When
attribution data is added to OnionErrorPacket, it would not be
serialized for the wire properly because also here the new field needs
to go in the tlv extension of the message.

- Prepare HTLCFailReasonRepr serialization for that addition of
  attribution data.

Co-authored-by: Matt Corallo <git@bluematt.me>
This commit is contained in:
Joost Jager 2025-03-05 09:12:20 +01:00
parent ea0f099ddb
commit 1426197828
7 changed files with 100 additions and 59 deletions

View file

@ -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<SP: Deref> FundedChannel<SP> 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<SP: Deref> FundedChannel<SP> 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<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
@ -10276,7 +10271,20 @@ impl<SP: Deref> Writeable for FundedChannel<SP> 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<SP: Deref> Writeable for FundedChannel<SP> 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<SP: Deref> Writeable for FundedChannel<SP> 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::<u8>::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 <u8 as Readable>::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),
});

View file

@ -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::<u8>::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] }

View file

@ -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);

View file

@ -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<u8>,
}
/// 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<u8>,
}
impl From<UpdateFailHTLC> 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 = <Vec<u8>>::from_hex("020202020202020202020202020202020202020202020202020202020202020200083a840000034d00200101010101010101010101010101010101010101010101010101010101010101").unwrap();

View file

@ -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,
}));
};

View file

@ -411,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_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
@ -419,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_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);
@ -431,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_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_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);
@ -450,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_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()));
@ -459,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_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);
@ -489,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_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()));
@ -501,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_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()));
@ -512,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_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()));
@ -522,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_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()));
@ -654,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_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);
@ -677,7 +688,7 @@ fn test_onion_failure() {
};
onion_utils::test_crypt_failure_packet(
&onion_keys[1].shared_secret.as_ref(), &mut onion_error);
msg.reason = 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);
@ -704,7 +715,7 @@ fn test_onion_failure() {
};
onion_utils::test_crypt_failure_packet(
&onion_keys[0].shared_secret.as_ref(), &mut onion_error);
msg.reason = onion_error;
msg.reason = onion_error.data;
}, || {}, true, Some(0x1000|7),
Some(NetworkUpdate::ChannelFailure {
short_channel_id: channels[1].0.contents.short_channel_id,
@ -732,7 +743,7 @@ fn test_onion_failure() {
};
onion_utils::test_crypt_failure_packet(
&onion_keys[1].shared_secret.as_ref(), &mut onion_error);
msg.reason = 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,

View file

@ -1299,7 +1299,14 @@ impl Readable for HTLCFailReason {
impl_writeable_tlv_based_enum!(HTLCFailReasonRepr,
(0, LightningError) => {
(0, err, required),
(0, data, (legacy, Vec<u8>, |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),
@ -1356,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.