Skip to content

Commit f9b8d63

Browse files
authored
Merge pull request #2256 from joostjager/attr-errs
Attributable failures
2 parents d9c20be + 07d4336 commit f9b8d63

11 files changed

+754
-115
lines changed

fuzz/src/process_onion_failure.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,17 @@ fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
118118
HTLCSource::OutboundRoute { path, session_priv, first_hop_htlc_msat: 0, payment_id };
119119

120120
let failure_len = get_u16!();
121-
let encrypted_packet = OnionErrorPacket { data: get_slice!(failure_len).into() };
121+
let failure_data = get_slice!(failure_len);
122122

123+
let attribution_data = if get_bool!() {
124+
Some(lightning::ln::AttributionData {
125+
hold_times: get_slice!(80).try_into().unwrap(),
126+
hmacs: get_slice!(840).try_into().unwrap(),
127+
})
128+
} else {
129+
None
130+
};
131+
let encrypted_packet = OnionErrorPacket { data: failure_data.into(), attribution_data };
123132
lightning::ln::process_onion_failure(&secp_ctx, &logger, &htlc_source, encrypted_packet);
124133
}
125134

lightning/src/ln/channel.rs

+97-9
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ use crate::ln::chan_utils::{
5050
#[cfg(splicing)]
5151
use crate::ln::chan_utils::FUNDING_TRANSACTION_WITNESS_WEIGHT;
5252
use crate::ln::chan_utils;
53-
use crate::ln::onion_utils::{HTLCFailReason};
53+
use crate::ln::onion_utils::{HTLCFailReason, AttributionData};
5454
use crate::chain::BestBlock;
5555
use crate::chain::chaininterface::{FeeEstimator, ConfirmationTarget, LowerBoundedFeeEstimator, fee_for_weight};
5656
use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, LATENCY_GRACE_PERIOD_BLOCKS};
@@ -68,6 +68,7 @@ use crate::util::scid_utils::scid_from_parts;
6868

6969
use crate::io;
7070
use crate::prelude::*;
71+
use core::time::Duration;
7172
use core::{cmp,mem,fmt};
7273
use core::ops::Deref;
7374
#[cfg(any(test, fuzzing, debug_assertions))]
@@ -323,6 +324,7 @@ struct OutboundHTLCOutput {
323324
source: HTLCSource,
324325
blinding_point: Option<PublicKey>,
325326
skimmed_fee_msat: Option<u64>,
327+
send_timestamp: Option<Duration>,
326328
}
327329

328330
/// See AwaitingRemoteRevoke ChannelState for more info
@@ -4982,7 +4984,7 @@ trait FailHTLCContents {
49824984
impl FailHTLCContents for msgs::OnionErrorPacket {
49834985
type Message = msgs::UpdateFailHTLC;
49844986
fn to_message(self, htlc_id: u64, channel_id: ChannelId) -> Self::Message {
4985-
msgs::UpdateFailHTLC { htlc_id, channel_id, reason: self.data }
4987+
msgs::UpdateFailHTLC { htlc_id, channel_id, reason: self.data, attribution_data: self.attribution_data }
49864988
}
49874989
fn to_inbound_htlc_state(self) -> InboundHTLCState {
49884990
InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailRelay(self))
@@ -6148,10 +6150,16 @@ impl<SP: Deref> FundedChannel<SP> where
61486150
false
61496151
} else { true }
61506152
});
6153+
let now = duration_since_epoch();
61516154
pending_outbound_htlcs.retain(|htlc| {
61526155
if let &OutboundHTLCState::AwaitingRemovedRemoteRevoke(ref outcome) = &htlc.state {
61536156
log_trace!(logger, " ...removing outbound AwaitingRemovedRemoteRevoke {}", &htlc.payment_hash);
6154-
if let OutboundHTLCOutcome::Failure(reason) = outcome.clone() { // We really want take() here, but, again, non-mut ref :(
6157+
if let OutboundHTLCOutcome::Failure(mut reason) = outcome.clone() { // We really want take() here, but, again, non-mut ref :(
6158+
if let (Some(timestamp), Some(now)) = (htlc.send_timestamp, now) {
6159+
let hold_time = u32::try_from(now.saturating_sub(timestamp).as_millis()).unwrap_or(u32::MAX);
6160+
reason.set_hold_time(hold_time);
6161+
}
6162+
61556163
revoked_htlcs.push((htlc.source.clone(), htlc.payment_hash, reason));
61566164
} else {
61576165
finalized_claimed_htlcs.push(htlc.source.clone());
@@ -6897,6 +6905,7 @@ impl<SP: Deref> FundedChannel<SP> where
68976905
channel_id: self.context.channel_id(),
68986906
htlc_id: htlc.htlc_id,
68996907
reason: err_packet.data.clone(),
6908+
attribution_data: err_packet.attribution_data.clone(),
69006909
});
69016910
},
69026911
&InboundHTLCRemovalReason::FailMalformed((ref sha256_of_onion, ref failure_code)) => {
@@ -8723,6 +8732,13 @@ impl<SP: Deref> FundedChannel<SP> where
87238732
return Ok(None);
87248733
}
87258734

8735+
// Record the approximate time when the HTLC is sent to the peer. This timestamp is later used to calculate the
8736+
// htlc hold time for reporting back to the sender. There is some freedom to report a time including or
8737+
// excluding our own processing time. What we choose here doesn't matter all that much, because it will probably
8738+
// just shift sender-applied penalties between our incoming and outgoing side. So we choose measuring points
8739+
// that are simple to implement, and we do it on the outgoing side because then the failure message that encodes
8740+
// the hold time still needs to be built in channel manager.
8741+
let send_timestamp = duration_since_epoch();
87268742
self.context.pending_outbound_htlcs.push(OutboundHTLCOutput {
87278743
htlc_id: self.context.next_holder_htlc_id,
87288744
amount_msat,
@@ -8732,6 +8748,7 @@ impl<SP: Deref> FundedChannel<SP> where
87328748
source,
87338749
blinding_point,
87348750
skimmed_fee_msat,
8751+
send_timestamp,
87358752
});
87368753

87378754
let res = msgs::UpdateAddHTLC {
@@ -10305,6 +10322,7 @@ impl<SP: Deref> Writeable for FundedChannel<SP> where SP::Target: SignerProvider
1030510322
dropped_inbound_htlcs += 1;
1030610323
}
1030710324
}
10325+
let mut removed_htlc_failure_attribution_data: Vec<&Option<AttributionData>> = Vec::new();
1030810326
(self.context.pending_inbound_htlcs.len() as u64 - dropped_inbound_htlcs).write(writer)?;
1030910327
for htlc in self.context.pending_inbound_htlcs.iter() {
1031010328
if let &InboundHTLCState::RemoteAnnounced(_) = &htlc.state {
@@ -10330,9 +10348,10 @@ impl<SP: Deref> Writeable for FundedChannel<SP> where SP::Target: SignerProvider
1033010348
&InboundHTLCState::LocalRemoved(ref removal_reason) => {
1033110349
4u8.write(writer)?;
1033210350
match removal_reason {
10333-
InboundHTLCRemovalReason::FailRelay(msgs::OnionErrorPacket { data }) => {
10351+
InboundHTLCRemovalReason::FailRelay(msgs::OnionErrorPacket { data, attribution_data }) => {
1033410352
0u8.write(writer)?;
1033510353
data.write(writer)?;
10354+
removed_htlc_failure_attribution_data.push(&attribution_data);
1033610355
},
1033710356
InboundHTLCRemovalReason::FailMalformed((hash, code)) => {
1033810357
1u8.write(writer)?;
@@ -10392,11 +10411,13 @@ impl<SP: Deref> Writeable for FundedChannel<SP> where SP::Target: SignerProvider
1039210411
pending_outbound_blinding_points.push(htlc.blinding_point);
1039310412
}
1039410413

10395-
let mut holding_cell_skimmed_fees: Vec<Option<u64>> = Vec::new();
10396-
let mut holding_cell_blinding_points: Vec<Option<PublicKey>> = Vec::new();
10414+
let holding_cell_htlc_update_count = self.context.holding_cell_htlc_updates.len();
10415+
let mut holding_cell_skimmed_fees: Vec<Option<u64>> = Vec::with_capacity(holding_cell_htlc_update_count);
10416+
let mut holding_cell_blinding_points: Vec<Option<PublicKey>> = Vec::with_capacity(holding_cell_htlc_update_count);
10417+
let mut holding_cell_failure_attribution_data: Vec<Option<&AttributionData>> = Vec::with_capacity(holding_cell_htlc_update_count);
1039710418
// Vec of (htlc_id, failure_code, sha256_of_onion)
1039810419
let mut malformed_htlcs: Vec<(u64, u16, [u8; 32])> = Vec::new();
10399-
(self.context.holding_cell_htlc_updates.len() as u64).write(writer)?;
10420+
(holding_cell_htlc_update_count as u64).write(writer)?;
1040010421
for update in self.context.holding_cell_htlc_updates.iter() {
1040110422
match update {
1040210423
&HTLCUpdateAwaitingACK::AddHTLC {
@@ -10422,6 +10443,9 @@ impl<SP: Deref> Writeable for FundedChannel<SP> where SP::Target: SignerProvider
1042210443
2u8.write(writer)?;
1042310444
htlc_id.write(writer)?;
1042410445
err_packet.data.write(writer)?;
10446+
10447+
// Store the attribution data for later writing.
10448+
holding_cell_failure_attribution_data.push(err_packet.attribution_data.as_ref());
1042510449
}
1042610450
&HTLCUpdateAwaitingACK::FailMalformedHTLC {
1042710451
htlc_id, failure_code, sha256_of_onion
@@ -10433,6 +10457,10 @@ impl<SP: Deref> Writeable for FundedChannel<SP> where SP::Target: SignerProvider
1043310457
2u8.write(writer)?;
1043410458
htlc_id.write(writer)?;
1043510459
Vec::<u8>::new().write(writer)?;
10460+
10461+
// Push 'None' attribution data for FailMalformedHTLC, because FailMalformedHTLC uses the same
10462+
// type 2 and is deserialized as a FailHTLC.
10463+
holding_cell_failure_attribution_data.push(None);
1043610464
}
1043710465
}
1043810466
}
@@ -10605,6 +10633,8 @@ impl<SP: Deref> Writeable for FundedChannel<SP> where SP::Target: SignerProvider
1060510633
(49, self.context.local_initiated_shutdown, option), // Added in 0.0.122
1060610634
(51, is_manual_broadcast, option), // Added in 0.0.124
1060710635
(53, funding_tx_broadcast_safe_event_emitted, option), // Added in 0.0.124
10636+
(55, removed_htlc_failure_attribution_data, optional_vec), // Added in 0.2
10637+
(57, holding_cell_failure_attribution_data, optional_vec), // Added in 0.2
1060810638
});
1060910639

1061010640
Ok(())
@@ -10682,6 +10712,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1068210712
let reason = match <u8 as Readable>::read(reader)? {
1068310713
0 => InboundHTLCRemovalReason::FailRelay(msgs::OnionErrorPacket {
1068410714
data: Readable::read(reader)?,
10715+
attribution_data: None,
1068510716
}),
1068610717
1 => InboundHTLCRemovalReason::FailMalformed(Readable::read(reader)?),
1068710718
2 => InboundHTLCRemovalReason::Fulfill(Readable::read(reader)?),
@@ -10722,6 +10753,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1072210753
},
1072310754
skimmed_fee_msat: None,
1072410755
blinding_point: None,
10756+
send_timestamp: None,
1072510757
});
1072610758
}
1072710759

@@ -10746,6 +10778,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1074610778
htlc_id: Readable::read(reader)?,
1074710779
err_packet: OnionErrorPacket {
1074810780
data: Readable::read(reader)?,
10781+
attribution_data: None,
1074910782
},
1075010783
},
1075110784
_ => return Err(DecodeError::InvalidValue),
@@ -10889,6 +10922,9 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1088910922
let mut pending_outbound_blinding_points_opt: Option<Vec<Option<PublicKey>>> = None;
1089010923
let mut holding_cell_blinding_points_opt: Option<Vec<Option<PublicKey>>> = None;
1089110924

10925+
let mut removed_htlc_failure_attribution_data: Option<Vec<Option<AttributionData>>> = None;
10926+
let mut holding_cell_failure_attribution_data: Option<Vec<Option<AttributionData>>> = None;
10927+
1089210928
let mut malformed_htlcs: Option<Vec<(u64, u16, [u8; 32])>> = None;
1089310929
let mut monitor_pending_update_adds: Option<Vec<msgs::UpdateAddHTLC>> = None;
1089410930

@@ -10931,6 +10967,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1093110967
(49, local_initiated_shutdown, option),
1093210968
(51, is_manual_broadcast, option),
1093310969
(53, funding_tx_broadcast_safe_event_emitted, option),
10970+
(55, removed_htlc_failure_attribution_data, optional_vec),
10971+
(57, holding_cell_failure_attribution_data, optional_vec),
1093410972
});
1093510973

1093610974
let holder_signer = signer_provider.derive_channel_signer(channel_keys_id);
@@ -11012,6 +11050,38 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1101211050
if iter.next().is_some() { return Err(DecodeError::InvalidValue) }
1101311051
}
1101411052

11053+
if let Some(attribution_data_list) = removed_htlc_failure_attribution_data {
11054+
let mut removed_htlc_relay_failures =
11055+
pending_inbound_htlcs.iter_mut().filter_map(|status|
11056+
if let InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailRelay(ref mut packet)) = &mut status.state {
11057+
Some(&mut packet.attribution_data)
11058+
} else {
11059+
None
11060+
}
11061+
);
11062+
11063+
for attribution_data in attribution_data_list {
11064+
*removed_htlc_relay_failures.next().ok_or(DecodeError::InvalidValue)? = attribution_data;
11065+
}
11066+
if removed_htlc_relay_failures.next().is_some() { return Err(DecodeError::InvalidValue); }
11067+
}
11068+
11069+
if let Some(attribution_data_list) = holding_cell_failure_attribution_data {
11070+
let mut holding_cell_failures =
11071+
holding_cell_htlc_updates.iter_mut().filter_map(|upd|
11072+
if let HTLCUpdateAwaitingACK::FailHTLC { err_packet: OnionErrorPacket { ref mut attribution_data, .. }, .. } = upd {
11073+
Some(attribution_data)
11074+
} else {
11075+
None
11076+
}
11077+
);
11078+
11079+
for attribution_data in attribution_data_list {
11080+
*holding_cell_failures.next().ok_or(DecodeError::InvalidValue)? = attribution_data;
11081+
}
11082+
if holding_cell_failures.next().is_some() { return Err(DecodeError::InvalidValue); }
11083+
}
11084+
1101511085
if let Some(malformed_htlcs) = malformed_htlcs {
1101611086
for (malformed_htlc_id, failure_code, sha256_of_onion) in malformed_htlcs {
1101711087
let htlc_idx = holding_cell_htlc_updates.iter().position(|htlc| {
@@ -11201,6 +11271,18 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1120111271
}
1120211272
}
1120311273

11274+
fn duration_since_epoch() -> Option<Duration> {
11275+
#[cfg(not(feature = "std"))]
11276+
let now = None;
11277+
11278+
#[cfg(feature = "std")]
11279+
let now = Some(std::time::SystemTime::now()
11280+
.duration_since(std::time::SystemTime::UNIX_EPOCH)
11281+
.expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH"));
11282+
11283+
now
11284+
}
11285+
1120411286
#[cfg(test)]
1120511287
mod tests {
1120611288
use std::cmp;
@@ -11214,7 +11296,7 @@ mod tests {
1121411296
use bitcoin::network::Network;
1121511297
#[cfg(splicing)]
1121611298
use bitcoin::Weight;
11217-
use crate::ln::onion_utils::INVALID_ONION_BLINDING;
11299+
use crate::ln::onion_utils::{AttributionData, INVALID_ONION_BLINDING};
1121811300
use crate::types::payment::{PaymentHash, PaymentPreimage};
1121911301
use crate::ln::channel_keys::{RevocationKey, RevocationBasepoint};
1122011302
use crate::ln::channelmanager::{self, HTLCSource, PaymentId};
@@ -11436,6 +11518,7 @@ mod tests {
1143611518
},
1143711519
skimmed_fee_msat: None,
1143811520
blinding_point: None,
11521+
send_timestamp: None,
1143911522
});
1144011523

1144111524
// Make sure when Node A calculates their local commitment transaction, none of the HTLCs pass
@@ -11820,6 +11903,7 @@ mod tests {
1182011903
source: dummy_htlc_source.clone(),
1182111904
skimmed_fee_msat: None,
1182211905
blinding_point: None,
11906+
send_timestamp: None,
1182311907
};
1182411908
let mut pending_outbound_htlcs = vec![dummy_outbound_output.clone(); 10];
1182511909
for (idx, htlc) in pending_outbound_htlcs.iter_mut().enumerate() {
@@ -11851,7 +11935,7 @@ mod tests {
1185111935
htlc_id: 0,
1185211936
};
1185311937
let dummy_holding_cell_failed_htlc = |htlc_id| HTLCUpdateAwaitingACK::FailHTLC {
11854-
htlc_id, err_packet: msgs::OnionErrorPacket { data: vec![42] }
11938+
htlc_id, err_packet: msgs::OnionErrorPacket { data: vec![42], attribution_data: Some(AttributionData::new()) }
1185511939
};
1185611940
let dummy_holding_cell_malformed_htlc = |htlc_id| HTLCUpdateAwaitingACK::FailMalformedHTLC {
1185711941
htlc_id, failure_code: INVALID_ONION_BLINDING, sha256_of_onion: [0; 32],
@@ -12133,6 +12217,7 @@ mod tests {
1213312217
source: HTLCSource::dummy(),
1213412218
skimmed_fee_msat: None,
1213512219
blinding_point: None,
12220+
send_timestamp: None,
1213612221
};
1213712222
out.payment_hash.0 = Sha256::hash(&<Vec<u8>>::from_hex("0202020202020202020202020202020202020202020202020202020202020202").unwrap()).to_byte_array();
1213812223
out
@@ -12147,6 +12232,7 @@ mod tests {
1214712232
source: HTLCSource::dummy(),
1214812233
skimmed_fee_msat: None,
1214912234
blinding_point: None,
12235+
send_timestamp: None,
1215012236
};
1215112237
out.payment_hash.0 = Sha256::hash(&<Vec<u8>>::from_hex("0303030303030303030303030303030303030303030303030303030303030303").unwrap()).to_byte_array();
1215212238
out
@@ -12559,6 +12645,7 @@ mod tests {
1255912645
source: HTLCSource::dummy(),
1256012646
skimmed_fee_msat: None,
1256112647
blinding_point: None,
12648+
send_timestamp: None,
1256212649
};
1256312650
out.payment_hash.0 = Sha256::hash(&<Vec<u8>>::from_hex("0505050505050505050505050505050505050505050505050505050505050505").unwrap()).to_byte_array();
1256412651
out
@@ -12573,6 +12660,7 @@ mod tests {
1257312660
source: HTLCSource::dummy(),
1257412661
skimmed_fee_msat: None,
1257512662
blinding_point: None,
12663+
send_timestamp: None,
1257612664
};
1257712665
out.payment_hash.0 = Sha256::hash(&<Vec<u8>>::from_hex("0505050505050505050505050505050505050505050505050505050505050505").unwrap()).to_byte_array();
1257812666
out

0 commit comments

Comments
 (0)