Skip to content

Commit 2c07c7b

Browse files
committed
Attributable failures
1 parent 8d01eb9 commit 2c07c7b

File tree

8 files changed

+503
-66
lines changed

8 files changed

+503
-66
lines changed

Diff for: lightning/src/ln/channel.rs

+58-6
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ use crate::ln::chan_utils::{
4949
ClosingTransaction, commit_tx_fee_sat,
5050
};
5151
use crate::ln::chan_utils;
52-
use crate::ln::onion_utils::{HTLCFailReason};
52+
use crate::ln::onion_utils::{HTLCFailReason, ATTRIBUTION_DATA_LEN};
5353
use crate::chain::BestBlock;
5454
use crate::chain::chaininterface::{FeeEstimator, ConfirmationTarget, LowerBoundedFeeEstimator, fee_for_weight};
5555
use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, LATENCY_GRACE_PERIOD_BLOCKS};
@@ -4856,7 +4856,7 @@ trait FailHTLCContents {
48564856
impl FailHTLCContents for msgs::OnionErrorPacket {
48574857
type Message = msgs::UpdateFailHTLC;
48584858
fn to_message(self, htlc_id: u64, channel_id: ChannelId) -> Self::Message {
4859-
msgs::UpdateFailHTLC { htlc_id, channel_id, reason: self.data }
4859+
msgs::UpdateFailHTLC { htlc_id, channel_id, reason: self.data, attribution_data: self.attribution_data }
48604860
}
48614861
fn to_inbound_htlc_state(self) -> InboundHTLCState {
48624862
InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailRelay(self))
@@ -6779,6 +6779,7 @@ impl<SP: Deref> FundedChannel<SP> where
67796779
channel_id: self.context.channel_id(),
67806780
htlc_id: htlc.htlc_id,
67816781
reason: err_packet.data.clone(),
6782+
attribution_data: err_packet.attribution_data,
67826783
});
67836784
},
67846785
&InboundHTLCRemovalReason::FailMalformed((ref sha256_of_onion, ref failure_code)) => {
@@ -10037,6 +10038,7 @@ impl<SP: Deref> Writeable for FundedChannel<SP> where SP::Target: SignerProvider
1003710038
dropped_inbound_htlcs += 1;
1003810039
}
1003910040
}
10041+
let mut removed_htlc_failure_attribution_data: Vec<&Option<[u8; ATTRIBUTION_DATA_LEN]>> = Vec::new();
1004010042
(self.context.pending_inbound_htlcs.len() as u64 - dropped_inbound_htlcs).write(writer)?;
1004110043
for htlc in self.context.pending_inbound_htlcs.iter() {
1004210044
if let &InboundHTLCState::RemoteAnnounced(_) = &htlc.state {
@@ -10062,9 +10064,10 @@ impl<SP: Deref> Writeable for FundedChannel<SP> where SP::Target: SignerProvider
1006210064
&InboundHTLCState::LocalRemoved(ref removal_reason) => {
1006310065
4u8.write(writer)?;
1006410066
match removal_reason {
10065-
InboundHTLCRemovalReason::FailRelay(msgs::OnionErrorPacket { data }) => {
10067+
InboundHTLCRemovalReason::FailRelay(msgs::OnionErrorPacket { data, attribution_data }) => {
1006610068
0u8.write(writer)?;
1006710069
data.write(writer)?;
10070+
removed_htlc_failure_attribution_data.push(&attribution_data);
1006810071
},
1006910072
InboundHTLCRemovalReason::FailMalformed((hash, code)) => {
1007010073
1u8.write(writer)?;
@@ -10126,10 +10129,11 @@ impl<SP: Deref> Writeable for FundedChannel<SP> where SP::Target: SignerProvider
1012610129

1012710130
let mut holding_cell_skimmed_fees: Vec<Option<u64>> = Vec::new();
1012810131
let mut holding_cell_blinding_points: Vec<Option<PublicKey>> = Vec::new();
10132+
let mut holding_cell_failure_attribution_data: Vec<(u32, [u8; ATTRIBUTION_DATA_LEN])> = Vec::new();
1012910133
// Vec of (htlc_id, failure_code, sha256_of_onion)
1013010134
let mut malformed_htlcs: Vec<(u64, u16, [u8; 32])> = Vec::new();
1013110135
(self.context.holding_cell_htlc_updates.len() as u64).write(writer)?;
10132-
for update in self.context.holding_cell_htlc_updates.iter() {
10136+
for (i, update) in self.context.holding_cell_htlc_updates.iter().enumerate() {
1013310137
match update {
1013410138
&HTLCUpdateAwaitingACK::AddHTLC {
1013510139
ref amount_msat, ref cltv_expiry, ref payment_hash, ref source, ref onion_routing_packet,
@@ -10154,6 +10158,13 @@ impl<SP: Deref> Writeable for FundedChannel<SP> where SP::Target: SignerProvider
1015410158
2u8.write(writer)?;
1015510159
htlc_id.write(writer)?;
1015610160
err_packet.data.write(writer)?;
10161+
10162+
// Store the attribution data for later writing. Include the holding cell htlc update index because
10163+
// FailMalformedHTLC is stored with the same type 2 and we wouldn't be able to distinguish the two
10164+
// when reading back in.
10165+
if let Some(attribution_data ) = err_packet.attribution_data {
10166+
holding_cell_failure_attribution_data.push((i as u32, attribution_data));
10167+
}
1015710168
}
1015810169
&HTLCUpdateAwaitingACK::FailMalformedHTLC {
1015910170
htlc_id, failure_code, sha256_of_onion
@@ -10337,6 +10348,8 @@ impl<SP: Deref> Writeable for FundedChannel<SP> where SP::Target: SignerProvider
1033710348
(49, self.context.local_initiated_shutdown, option), // Added in 0.0.122
1033810349
(51, is_manual_broadcast, option), // Added in 0.0.124
1033910350
(53, funding_tx_broadcast_safe_event_emitted, option), // Added in 0.0.124
10351+
(55, removed_htlc_failure_attribution_data, optional_vec), // Added in 0.2
10352+
(57, holding_cell_failure_attribution_data, optional_vec), // Added in 0.2
1034010353
});
1034110354

1034210355
Ok(())
@@ -10414,6 +10427,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1041410427
let reason = match <u8 as Readable>::read(reader)? {
1041510428
0 => InboundHTLCRemovalReason::FailRelay(msgs::OnionErrorPacket {
1041610429
data: Readable::read(reader)?,
10430+
attribution_data: None,
1041710431
}),
1041810432
1 => InboundHTLCRemovalReason::FailMalformed(Readable::read(reader)?),
1041910433
2 => InboundHTLCRemovalReason::Fulfill(Readable::read(reader)?),
@@ -10478,6 +10492,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1047810492
htlc_id: Readable::read(reader)?,
1047910493
err_packet: OnionErrorPacket {
1048010494
data: Readable::read(reader)?,
10495+
attribution_data: None,
1048110496
},
1048210497
},
1048310498
_ => return Err(DecodeError::InvalidValue),
@@ -10621,6 +10636,9 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1062110636
let mut pending_outbound_blinding_points_opt: Option<Vec<Option<PublicKey>>> = None;
1062210637
let mut holding_cell_blinding_points_opt: Option<Vec<Option<PublicKey>>> = None;
1062310638

10639+
let mut removed_htlc_failure_attribution_data: Option<Vec<Option<[u8; ATTRIBUTION_DATA_LEN]>>> = None;
10640+
let mut holding_cell_failure_attribution_data: Option<Vec<(u32, [u8; ATTRIBUTION_DATA_LEN])>> = None;
10641+
1062410642
let mut malformed_htlcs: Option<Vec<(u64, u16, [u8; 32])>> = None;
1062510643
let mut monitor_pending_update_adds: Option<Vec<msgs::UpdateAddHTLC>> = None;
1062610644

@@ -10663,6 +10681,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1066310681
(49, local_initiated_shutdown, option),
1066410682
(51, is_manual_broadcast, option),
1066510683
(53, funding_tx_broadcast_safe_event_emitted, option),
10684+
(55, removed_htlc_failure_attribution_data, optional_vec),
10685+
(57, holding_cell_failure_attribution_data, optional_vec),
1066610686
});
1066710687

1066810688
let holder_signer = signer_provider.derive_channel_signer(channel_keys_id);
@@ -10744,6 +10764,38 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1074410764
if iter.next().is_some() { return Err(DecodeError::InvalidValue) }
1074510765
}
1074610766

10767+
if let Some(attribution_datas) = removed_htlc_failure_attribution_data {
10768+
let mut removed_htlc_relay_failures =
10769+
pending_inbound_htlcs.iter_mut().filter_map(|status|
10770+
if let InboundHTLCState::LocalRemoved(ref mut reason) = &mut status.state {
10771+
if let InboundHTLCRemovalReason::FailRelay(ref mut packet) = reason {
10772+
Some(&mut packet.attribution_data)
10773+
} else {
10774+
None
10775+
}
10776+
} else {
10777+
None
10778+
}
10779+
);
10780+
10781+
for attribution_data in attribution_datas {
10782+
*removed_htlc_relay_failures.next().ok_or(DecodeError::InvalidValue)? = attribution_data;
10783+
}
10784+
if removed_htlc_relay_failures.next().is_some() { return Err(DecodeError::InvalidValue); }
10785+
}
10786+
10787+
if let Some(attribution_datas) = holding_cell_failure_attribution_data {
10788+
for (i, attribution_data) in attribution_datas {
10789+
let update = holding_cell_htlc_updates.get_mut(i as usize).ok_or(DecodeError::InvalidValue)?;
10790+
10791+
if let HTLCUpdateAwaitingACK::FailHTLC { htlc_id: _, ref mut err_packet } = update {
10792+
err_packet.attribution_data = Some(attribution_data);
10793+
} else {
10794+
return Err(DecodeError::InvalidValue);
10795+
}
10796+
}
10797+
}
10798+
1074710799
if let Some(malformed_htlcs) = malformed_htlcs {
1074810800
for (malformed_htlc_id, failure_code, sha256_of_onion) in malformed_htlcs {
1074910801
let htlc_idx = holding_cell_htlc_updates.iter().position(|htlc| {
@@ -10941,7 +10993,7 @@ mod tests {
1094110993
use bitcoin::transaction::{Transaction, TxOut, Version};
1094210994
use bitcoin::opcodes;
1094310995
use bitcoin::network::Network;
10944-
use crate::ln::onion_utils::INVALID_ONION_BLINDING;
10996+
use crate::ln::onion_utils::{ATTRIBUTION_DATA_LEN, INVALID_ONION_BLINDING};
1094510997
use crate::types::payment::{PaymentHash, PaymentPreimage};
1094610998
use crate::ln::channel_keys::{RevocationKey, RevocationBasepoint};
1094710999
use crate::ln::channelmanager::{self, HTLCSource, PaymentId};
@@ -11577,7 +11629,7 @@ mod tests {
1157711629
htlc_id: 0,
1157811630
};
1157911631
let dummy_holding_cell_failed_htlc = |htlc_id| HTLCUpdateAwaitingACK::FailHTLC {
11580-
htlc_id, err_packet: msgs::OnionErrorPacket { data: vec![42] }
11632+
htlc_id, err_packet: msgs::OnionErrorPacket { data: vec![42], attribution_data: Some([1; ATTRIBUTION_DATA_LEN]) }
1158111633
};
1158211634
let dummy_holding_cell_malformed_htlc = |htlc_id| HTLCUpdateAwaitingACK::FailMalformedHTLC {
1158311635
htlc_id, failure_code: INVALID_ONION_BLINDING, sha256_of_onion: [0; 32],

Diff for: lightning/src/ln/channelmanager.rs

+18-3
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ use crate::types::features::Bolt11InvoiceFeatures;
5757
use crate::routing::router::{BlindedTail, InFlightHtlcs, Path, Payee, PaymentParameters, RouteParameters, RouteParametersConfig, Router, FixedRouter, Route};
5858
use crate::ln::onion_payment::{check_incoming_htlc_cltv, create_recv_pending_htlc_info, create_fwd_pending_htlc_info, decode_incoming_update_add_htlc_onion, HopConnector, InboundHTLCErr, NextPacketDetails};
5959
use crate::ln::msgs;
60-
use crate::ln::onion_utils;
60+
use crate::ln::onion_utils::{self, ATTRIBUTION_DATA_LEN};
6161
use crate::ln::onion_utils::{HTLCFailReason, INVALID_ONION_BLINDING};
6262
use crate::ln::msgs::{BaseMessageHandler, ChannelMessageHandler, CommitmentUpdate, DecodeError, LightningError, MessageSendEvent};
6363
#[cfg(test)]
@@ -4441,6 +4441,7 @@ where
44414441
channel_id: msg.channel_id,
44424442
htlc_id: msg.htlc_id,
44434443
reason: failure.data.clone(),
4444+
attribution_data: failure.attribution_data,
44444445
})
44454446
}
44464447

@@ -4466,10 +4467,12 @@ where
44664467
}
44674468
let failure = HTLCFailReason::reason($err_code, $data.to_vec())
44684469
.get_encrypted_failure_packet(&shared_secret, &None);
4470+
44694471
return PendingHTLCStatus::Fail(HTLCFailureMsg::Relay(msgs::UpdateFailHTLC {
44704472
channel_id: msg.channel_id,
44714473
htlc_id: msg.htlc_id,
44724474
reason: failure.data,
4475+
attribution_data: failure.attribution_data,
44734476
}));
44744477
}
44754478
}
@@ -12871,11 +12874,15 @@ impl_writeable_tlv_based!(PendingHTLCInfo, {
1287112874
impl Writeable for HTLCFailureMsg {
1287212875
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
1287312876
match self {
12874-
HTLCFailureMsg::Relay(msgs::UpdateFailHTLC { channel_id, htlc_id, reason }) => {
12877+
HTLCFailureMsg::Relay(msgs::UpdateFailHTLC { channel_id, htlc_id, reason, attribution_data }) => {
1287512878
0u8.write(writer)?;
1287612879
channel_id.write(writer)?;
1287712880
htlc_id.write(writer)?;
1287812881
reason.write(writer)?;
12882+
12883+
// This code will only ever be hit for legacy data that is re-serialized. It isn't necessary to try
12884+
// writing out attribution data, because it can never be present.
12885+
debug_assert!(attribution_data.is_none());
1287912886
},
1288012887
HTLCFailureMsg::Malformed(msgs::UpdateFailMalformedHTLC {
1288112888
channel_id, htlc_id, sha256_of_onion, failure_code
@@ -12900,6 +12907,7 @@ impl Readable for HTLCFailureMsg {
1290012907
channel_id: Readable::read(reader)?,
1290112908
htlc_id: Readable::read(reader)?,
1290212909
reason: Readable::read(reader)?,
12910+
attribution_data: None,
1290312911
}))
1290412912
},
1290512913
1 => {
@@ -13130,6 +13138,7 @@ impl Writeable for HTLCForwardInfo {
1313013138
write_tlv_fields!(w, {
1313113139
(0, htlc_id, required),
1313213140
(2, err_packet.data, required),
13141+
(5, err_packet.attribution_data, option),
1313313142
});
1313413143
},
1313513144
Self::FailMalformedHTLC { htlc_id, failure_code, sha256_of_onion } => {
@@ -13160,8 +13169,12 @@ impl Readable for HTLCForwardInfo {
1316013169
(1, malformed_htlc_failure_code, option),
1316113170
(2, err_packet, required),
1316213171
(3, sha256_of_onion, option),
13172+
(5, attribution_data, option),
1316313173
});
1316413174
if let Some(failure_code) = malformed_htlc_failure_code {
13175+
if attribution_data.is_some() {
13176+
return Err(DecodeError::InvalidValue);
13177+
}
1316513178
Self::FailMalformedHTLC {
1316613179
htlc_id: _init_tlv_based_struct_field!(htlc_id, required),
1316713180
failure_code,
@@ -13172,6 +13185,7 @@ impl Readable for HTLCForwardInfo {
1317213185
htlc_id: _init_tlv_based_struct_field!(htlc_id, required),
1317313186
err_packet: crate::ln::msgs::OnionErrorPacket {
1317413187
data: _init_tlv_based_struct_field!(err_packet, required),
13188+
attribution_data: _init_tlv_based_struct_field!(attribution_data, option),
1317513189
},
1317613190
}
1317713191
}
@@ -14872,6 +14886,7 @@ mod tests {
1487214886
use bitcoin::secp256k1::ecdh::SharedSecret;
1487314887
use core::sync::atomic::Ordering;
1487414888
use crate::events::{Event, HTLCDestination, ClosureReason};
14889+
use crate::ln::onion_utils::ATTRIBUTION_DATA_LEN;
1487514890
use crate::ln::types::ChannelId;
1487614891
use crate::types::payment::{PaymentPreimage, PaymentHash, PaymentSecret};
1487714892
use crate::ln::channelmanager::{create_recv_pending_htlc_info, inbound_payment, ChannelConfigOverrides, HTLCForwardInfo, InterceptId, PaymentId, RecipientOnionFields};
@@ -16313,7 +16328,7 @@ mod tests {
1631316328
let mut nodes = create_network(1, &node_cfg, &chanmgrs);
1631416329

1631516330
let dummy_failed_htlc = |htlc_id| {
16316-
HTLCForwardInfo::FailHTLC { htlc_id, err_packet: msgs::OnionErrorPacket { data: vec![42] } }
16331+
HTLCForwardInfo::FailHTLC { htlc_id, err_packet: msgs::OnionErrorPacket { data: vec![42], attribution_data: Some([0; ATTRIBUTION_DATA_LEN]) } }
1631716332
};
1631816333
let dummy_malformed_htlc = |htlc_id| {
1631916334
HTLCForwardInfo::FailMalformedHTLC { htlc_id, failure_code: 0x4000, sha256_of_onion: [0; 32] }

Diff for: lightning/src/ln/functional_tests.rs

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use crate::chain::chaininterface::LowerBoundedFeeEstimator;
1717
use crate::chain::channelmonitor;
1818
use crate::chain::channelmonitor::{Balance, ChannelMonitorUpdateStep, CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY, COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE};
1919
use crate::chain::transaction::OutPoint;
20+
use crate::ln::onion_utils::ATTRIBUTION_DATA_LEN;
2021
use crate::sign::{ecdsa::EcdsaChannelSigner, EntropySource, OutputSpender, SignerProvider};
2122
use crate::events::bump_transaction::WalletSource;
2223
use crate::events::{Event, FundingInfo, PathFailure, PaymentPurpose, ClosureReason, HTLCDestination, PaymentFailureReason};
@@ -7058,6 +7059,7 @@ pub fn test_update_fulfill_htlc_bolt2_update_fail_htlc_before_commitment() {
70587059
channel_id: chan.2,
70597060
htlc_id: 0,
70607061
reason: Vec::new(),
7062+
attribution_data: Some([0; ATTRIBUTION_DATA_LEN])
70617063
};
70627064

70637065
nodes[0].node.handle_update_fail_htlc(nodes[1].node.get_our_node_id(), &update_msg);

Diff for: lightning/src/ln/msgs.rs

+17-5
Original file line numberDiff line numberDiff line change
@@ -767,8 +767,10 @@ pub struct UpdateFailHTLC {
767767
/// The HTLC ID
768768
pub htlc_id: u64,
769769
pub(crate) reason: Vec<u8>,
770-
}
771770

771+
/// Optional field for the attribution data that allows the sender to pinpoint the failing node under all conditions
772+
pub attribution_data: Option<[u8; ATTRIBUTION_DATA_LEN]>
773+
}
772774
/// An [`update_fail_malformed_htlc`] message to be sent to or received from a peer.
773775
///
774776
/// [`update_fail_malformed_htlc`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#removing-an-htlc-update_fulfill_htlc-update_fail_htlc-and-update_fail_malformed_htlc
@@ -2247,6 +2249,8 @@ pub use self::fuzzy_internal_msgs::*;
22472249
#[cfg(not(fuzzing))]
22482250
pub(crate) use self::fuzzy_internal_msgs::*;
22492251

2252+
use super::onion_utils::ATTRIBUTION_DATA_LEN;
2253+
22502254
/// BOLT 4 onion packet including hop data for the next peer.
22512255
#[derive(Clone, Hash, PartialEq, Eq)]
22522256
pub struct OnionPacket {
@@ -2353,12 +2357,14 @@ pub(crate) struct OnionErrorPacket {
23532357
// This really should be a constant size slice, but the spec lets these things be up to 128KB?
23542358
// (TODO) We limit it in decode to much lower...
23552359
pub(crate) data: Vec<u8>,
2360+
pub(crate) attribution_data: Option<[u8; ATTRIBUTION_DATA_LEN]>,
23562361
}
23572362

23582363
impl From<&UpdateFailHTLC> for OnionErrorPacket {
23592364
fn from(msg: &UpdateFailHTLC) -> Self {
23602365
OnionErrorPacket {
23612366
data: msg.reason.clone(),
2367+
attribution_data: msg.attribution_data,
23622368
}
23632369
}
23642370
}
@@ -2699,6 +2705,7 @@ impl_writeable!(DecodedOnionErrorPacket, {
26992705
pad
27002706
});
27012707

2708+
27022709
#[cfg(not(taproot))]
27032710
impl_writeable_msg!(FundingCreated, {
27042711
temporary_channel_id,
@@ -2981,7 +2988,9 @@ impl_writeable_msg!(UpdateFailHTLC, {
29812988
channel_id,
29822989
htlc_id,
29832990
reason
2984-
}, {});
2991+
}, {
2992+
(1, attribution_data, option)
2993+
});
29852994

29862995
impl_writeable_msg!(UpdateFailMalformedHTLC, {
29872996
channel_id,
@@ -3931,7 +3940,8 @@ impl_writeable_msg!(GossipTimestampFilter, {
39313940
mod tests {
39323941
use bitcoin::{Amount, Transaction, TxIn, ScriptBuf, Sequence, Witness, TxOut};
39333942
use bitcoin::hex::DisplayHex;
3934-
use crate::ln::types::ChannelId;
3943+
use crate::ln::onion_utils::ATTRIBUTION_DATA_LEN;
3944+
use crate::ln::types::ChannelId;
39353945
use crate::types::payment::{PaymentPreimage, PaymentHash, PaymentSecret};
39363946
use crate::types::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
39373947
use crate::ln::msgs::{self, FinalOnionHopData, CommonOpenChannelFields, CommonAcceptChannelFields, OutboundTrampolinePayload, TrampolineOnionPacket, InboundOnionForwardPayload, InboundOnionReceivePayload};
@@ -4936,10 +4946,12 @@ mod tests {
49364946
let update_fail_htlc = msgs::UpdateFailHTLC {
49374947
channel_id: ChannelId::from_bytes([2; 32]),
49384948
htlc_id: 2316138423780173,
4939-
reason: [1; 32].to_vec()
4949+
reason: [1; 32].to_vec(),
4950+
attribution_data: Some([3; ATTRIBUTION_DATA_LEN])
49404951
};
49414952
let encoded_value = update_fail_htlc.encode();
4942-
let target_value = <Vec<u8>>::from_hex("020202020202020202020202020202020202020202020202020202020202020200083a840000034d00200101010101010101010101010101010101010101010101010101010101010101").unwrap();
4953+
4954+
let target_value = <Vec<u8>>::from_hex("020202020202020202020202020202020202020202020202020202020202020200083a840000034d0020010101010101010101010101010101010101010101010101010101010101010101fd03980303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303").unwrap();
49434955
assert_eq!(encoded_value, target_value);
49444956
}
49454957

Diff for: lightning/src/ln/onion_payment.rs

+1
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,7 @@ where
518518
channel_id: msg.channel_id,
519519
htlc_id: msg.htlc_id,
520520
reason: failure.data,
521+
attribution_data: failure.attribution_data,
521522
}));
522523
};
523524

0 commit comments

Comments
 (0)