Skip to content

Commit 718e045

Browse files
committed
Track htlc hold time
Record a timestamp when the htlc is sent out and record the hold duration alongside the failure reason.
1 parent 35bbc8a commit 718e045

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

@@ -11195,6 +11212,18 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1119511212
}
1119611213
}
1119711214

11215+
fn duration_since_epoch() -> Option<Duration> {
11216+
#[cfg(not(feature = "std"))]
11217+
let now = None;
11218+
11219+
#[cfg(feature = "std")]
11220+
let now = Some(std::time::SystemTime::now()
11221+
.duration_since(std::time::SystemTime::UNIX_EPOCH)
11222+
.expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH"));
11223+
11224+
now
11225+
}
11226+
1119811227
#[cfg(test)]
1119911228
mod tests {
1120011229
use std::cmp;
@@ -11430,6 +11459,7 @@ mod tests {
1143011459
},
1143111460
skimmed_fee_msat: None,
1143211461
blinding_point: None,
11462+
send_timestamp: None,
1143311463
});
1143411464

1143511465
// Make sure when Node A calculates their local commitment transaction, none of the HTLCs pass
@@ -11814,6 +11844,7 @@ mod tests {
1181411844
source: dummy_htlc_source.clone(),
1181511845
skimmed_fee_msat: None,
1181611846
blinding_point: None,
11847+
send_timestamp: None,
1181711848
};
1181811849
let mut pending_outbound_htlcs = vec![dummy_outbound_output.clone(); 10];
1181911850
for (idx, htlc) in pending_outbound_htlcs.iter_mut().enumerate() {
@@ -12136,6 +12167,7 @@ mod tests {
1213612167
source: HTLCSource::dummy(),
1213712168
skimmed_fee_msat: None,
1213812169
blinding_point: None,
12170+
send_timestamp: None,
1213912171
};
1214012172
out.payment_hash.0 = Sha256::hash(&<Vec<u8>>::from_hex("0202020202020202020202020202020202020202020202020202020202020202").unwrap()).to_byte_array();
1214112173
out
@@ -12150,6 +12182,7 @@ mod tests {
1215012182
source: HTLCSource::dummy(),
1215112183
skimmed_fee_msat: None,
1215212184
blinding_point: None,
12185+
send_timestamp: None,
1215312186
};
1215412187
out.payment_hash.0 = Sha256::hash(&<Vec<u8>>::from_hex("0303030303030303030303030303030303030303030303030303030303030303").unwrap()).to_byte_array();
1215512188
out
@@ -12562,6 +12595,7 @@ mod tests {
1256212595
source: HTLCSource::dummy(),
1256312596
skimmed_fee_msat: None,
1256412597
blinding_point: None,
12598+
send_timestamp: None,
1256512599
};
1256612600
out.payment_hash.0 = Sha256::hash(&<Vec<u8>>::from_hex("0505050505050505050505050505050505050505050505050505050505050505").unwrap()).to_byte_array();
1256712601
out
@@ -12576,6 +12610,7 @@ mod tests {
1257612610
source: HTLCSource::dummy(),
1257712611
skimmed_fee_msat: None,
1257812612
blinding_point: None,
12613+
send_timestamp: None,
1257912614
};
1258012615
out.payment_hash.0 = Sha256::hash(&<Vec<u8>>::from_hex("0505050505050505050505050505050505050505050505050505050505050505").unwrap()).to_byte_array();
1258112616
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.clone(),
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)