Skip to content

Commit 593f0d1

Browse files
committed
Track htlc hold time
Record a timestamp when the htlc is sent out and record the hold duration along side the failure reason.
1 parent c738213 commit 593f0d1

File tree

2 files changed

+56
-5
lines changed

2 files changed

+56
-5
lines changed

lightning/src/ln/channel.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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
@@ -6095,10 +6097,16 @@ impl<SP: Deref> FundedChannel<SP> where
60956097
false
60966098
} else { true }
60976099
});
6100+
let now = duration_since_epoch();
60986101
pending_outbound_htlcs.retain(|htlc| {
60996102
if let &OutboundHTLCState::AwaitingRemovedRemoteRevoke(ref outcome) = &htlc.state {
61006103
log_trace!(logger, " ...removing outbound AwaitingRemovedRemoteRevoke {}", &htlc.payment_hash);
6101-
if let OutboundHTLCOutcome::Failure(reason) = outcome.clone() { // We really want take() here, but, again, non-mut ref :(
6104+
if let OutboundHTLCOutcome::Failure(mut reason) = outcome.clone() { // We really want take() here, but, again, non-mut ref :(
6105+
if let (Some(timestamp), Some(now)) = (htlc.send_timestamp, now) {
6106+
let hold_time = u32::try_from((now - timestamp).as_millis()).unwrap_or(u32::MAX);
6107+
reason.set_hold_time(hold_time);
6108+
}
6109+
61026110
revoked_htlcs.push((htlc.source.clone(), htlc.payment_hash, reason));
61036111
} else {
61046112
finalized_claimed_htlcs.push(htlc.source.clone());
@@ -8667,6 +8675,13 @@ impl<SP: Deref> FundedChannel<SP> where
86678675
return Ok(None);
86688676
}
86698677

8678+
// Record the approximate time when the HTLC is sent to the peer. This timestamp is later used to calculate the
8679+
// htlc hold time for reporting back to the sender. There is some freedom to report a time including or
8680+
// excluding our own processing time. What we choose here doesn't matter all that much, because it will probably
8681+
// just shift sender-applied penalties between our incoming and outgoing side. So we choose measuring points
8682+
// that are simple to implement, and we do it on the outgoing side because then the failure message that encodes
8683+
// the hold time still needs to be built in channel manager.
8684+
let timestamp = duration_since_epoch();
86708685
self.context.pending_outbound_htlcs.push(OutboundHTLCOutput {
86718686
htlc_id: self.context.next_holder_htlc_id,
86728687
amount_msat,
@@ -8676,6 +8691,7 @@ impl<SP: Deref> FundedChannel<SP> where
86768691
source,
86778692
blinding_point,
86788693
skimmed_fee_msat,
8694+
send_timestamp: timestamp,
86798695
});
86808696

86818697
let res = msgs::UpdateAddHTLC {
@@ -10673,6 +10689,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1067310689
},
1067410690
skimmed_fee_msat: None,
1067510691
blinding_point: None,
10692+
send_timestamp: None,
1067610693
});
1067710694
}
1067810695

@@ -11191,6 +11208,18 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1119111208
}
1119211209
}
1119311210

11211+
fn duration_since_epoch() -> Option<Duration> {
11212+
#[cfg(not(feature = "std"))]
11213+
let now = None;
11214+
11215+
#[cfg(feature = "std")]
11216+
let now = Some(std::time::SystemTime::now()
11217+
.duration_since(std::time::SystemTime::UNIX_EPOCH)
11218+
.expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH"));
11219+
11220+
now
11221+
}
11222+
1119411223
#[cfg(test)]
1119511224
mod tests {
1119611225
use std::cmp;
@@ -11426,6 +11455,7 @@ mod tests {
1142611455
},
1142711456
skimmed_fee_msat: None,
1142811457
blinding_point: None,
11458+
send_timestamp: None,
1142911459
});
1143011460

1143111461
// Make sure when Node A calculates their local commitment transaction, none of the HTLCs pass
@@ -11810,6 +11840,7 @@ mod tests {
1181011840
source: dummy_htlc_source.clone(),
1181111841
skimmed_fee_msat: None,
1181211842
blinding_point: None,
11843+
send_timestamp: None,
1181311844
};
1181411845
let mut pending_outbound_htlcs = vec![dummy_outbound_output.clone(); 10];
1181511846
for (idx, htlc) in pending_outbound_htlcs.iter_mut().enumerate() {
@@ -12132,6 +12163,7 @@ mod tests {
1213212163
source: HTLCSource::dummy(),
1213312164
skimmed_fee_msat: None,
1213412165
blinding_point: None,
12166+
timestamp: None,
1213512167
};
1213612168
out.payment_hash.0 = Sha256::hash(&<Vec<u8>>::from_hex("0202020202020202020202020202020202020202020202020202020202020202").unwrap()).to_byte_array();
1213712169
out
@@ -12146,6 +12178,7 @@ mod tests {
1214612178
source: HTLCSource::dummy(),
1214712179
skimmed_fee_msat: None,
1214812180
blinding_point: None,
12181+
timestamp: None,
1214912182
};
1215012183
out.payment_hash.0 = Sha256::hash(&<Vec<u8>>::from_hex("0303030303030303030303030303030303030303030303030303030303030303").unwrap()).to_byte_array();
1215112184
out
@@ -12558,6 +12591,7 @@ mod tests {
1255812591
source: HTLCSource::dummy(),
1255912592
skimmed_fee_msat: None,
1256012593
blinding_point: None,
12594+
timestamp: None,
1256112595
};
1256212596
out.payment_hash.0 = Sha256::hash(&<Vec<u8>>::from_hex("0505050505050505050505050505050505050505050505050505050505050505").unwrap()).to_byte_array();
1256312597
out
@@ -12572,6 +12606,7 @@ mod tests {
1257212606
source: HTLCSource::dummy(),
1257312607
skimmed_fee_msat: None,
1257412608
blinding_point: None,
12609+
timestamp: None,
1257512610
};
1257612611
out.payment_hash.0 = Sha256::hash(&<Vec<u8>>::from_hex("0505050505050505050505050505050505050505050505050505050505050505").unwrap()).to_byte_array();
1257712612
out

lightning/src/ln/onion_utils.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,10 +1365,21 @@ pub(super) struct HTLCFailReason(HTLCFailReasonRepr);
13651365
#[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
13661366
#[cfg_attr(test, derive(PartialEq))]
13671367
enum HTLCFailReasonRepr {
1368-
LightningError { err: msgs::OnionErrorPacket },
1368+
LightningError { err: msgs::OnionErrorPacket, hold_time: Option<u32> },
13691369
Reason { failure_code: u16, data: Vec<u8> },
13701370
}
13711371

1372+
impl HTLCFailReason {
1373+
pub fn set_hold_time(&mut self, hold_time: u32) {
1374+
match self.0 {
1375+
HTLCFailReasonRepr::LightningError { hold_time: ref mut current_hold_time, .. } => {
1376+
*current_hold_time = Some(hold_time);
1377+
},
1378+
_ => {},
1379+
}
1380+
}
1381+
}
1382+
13721383
impl core::fmt::Debug for HTLCFailReason {
13731384
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
13741385
match self.0 {
@@ -1409,6 +1420,7 @@ impl_writeable_tlv_based_enum!(HTLCFailReasonRepr,
14091420
None
14101421
})
14111422
),
1423+
(2, hold_time, option),
14121424
(_unused, err, (static_value, msgs::OnionErrorPacket { data: data.ok_or(DecodeError::InvalidValue)?, attribution_data })),
14131425
},
14141426
(1, Reason) => {
@@ -1467,7 +1479,11 @@ impl HTLCFailReason {
14671479

14681480
pub(super) fn from_msg(msg: &msgs::UpdateFailHTLC) -> Self {
14691481
Self(HTLCFailReasonRepr::LightningError {
1470-
err: OnionErrorPacket { data: msg.reason.clone(), attribution_data: None },
1482+
err: OnionErrorPacket {
1483+
data: msg.reason.clone(),
1484+
attribution_data: msg.attribution_data,
1485+
},
1486+
hold_time: None,
14711487
})
14721488
}
14731489

@@ -1492,7 +1508,7 @@ impl HTLCFailReason {
14921508
build_failure_packet(incoming_packet_shared_secret, *failure_code, &data[..])
14931509
}
14941510
},
1495-
HTLCFailReasonRepr::LightningError { ref err } => {
1511+
HTLCFailReasonRepr::LightningError { ref err, .. } => {
14961512
let mut err = err.clone();
14971513

14981514
crypt_failure_packet(incoming_packet_shared_secret, &mut err);
@@ -1509,7 +1525,7 @@ impl HTLCFailReason {
15091525
L::Target: Logger,
15101526
{
15111527
match self.0 {
1512-
HTLCFailReasonRepr::LightningError { ref err } => {
1528+
HTLCFailReasonRepr::LightningError { ref err, .. } => {
15131529
process_onion_failure(secp_ctx, logger, &htlc_source, err.clone())
15141530
},
15151531
#[allow(unused)]

0 commit comments

Comments
 (0)