Skip to content

Commit 85951fc

Browse files
committed
Use Duration based time info in scoring rather than Time
In the coming commits, the `T: Time` bound on `ProbabilisticScorer` will be removed. In order to enable that, we need to switch over to using the `ScoreUpdate`-provided current time (as a `Duration` since the unix epoch), making the `T` bound entirely unused.
1 parent 54f2266 commit 85951fc

File tree

1 file changed

+61
-84
lines changed

1 file changed

+61
-84
lines changed

lightning/src/routing/scoring.rs

Lines changed: 61 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,8 @@ where L::Target: Logger {
494494
decay_params: ProbabilisticScoringDecayParameters,
495495
network_graph: G,
496496
logger: L,
497-
channel_liquidities: HashMap<u64, ChannelLiquidity<T>>,
497+
channel_liquidities: HashMap<u64, ChannelLiquidity>,
498+
_unused_time: core::marker::PhantomData<T>,
498499
}
499500

500501
/// Parameters for configuring [`ProbabilisticScorer`].
@@ -798,7 +799,7 @@ impl ProbabilisticScoringDecayParameters {
798799
/// Direction is defined in terms of [`NodeId`] partial ordering, where the source node is the
799800
/// first node in the ordering of the channel's counterparties. Thus, swapping the two liquidity
800801
/// offset fields gives the opposite direction.
801-
struct ChannelLiquidity<T: Time> {
802+
struct ChannelLiquidity {
802803
/// Lower channel liquidity bound in terms of an offset from zero.
803804
min_liquidity_offset_msat: u64,
804805

@@ -808,23 +809,23 @@ struct ChannelLiquidity<T: Time> {
808809
min_liquidity_offset_history: HistoricalBucketRangeTracker,
809810
max_liquidity_offset_history: HistoricalBucketRangeTracker,
810811

811-
/// Time when the liquidity bounds were last modified.
812-
last_updated: T,
812+
/// Time when the liquidity bounds were last modified as an offset since the unix epoch.
813+
last_updated: Duration,
813814

814-
/// Time when the historical liquidity bounds were last modified.
815-
offset_history_last_updated: T,
815+
/// Time when the historical liquidity bounds were last modified as an offset against the unix
816+
/// epoch.
817+
offset_history_last_updated: Duration,
816818
}
817819

818820
/// A snapshot of [`ChannelLiquidity`] in one direction assuming a certain channel capacity and
819821
/// decayed with a given half life.
820-
struct DirectedChannelLiquidity<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>, T: Time, U: Deref<Target = T>> {
822+
struct DirectedChannelLiquidity<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>, T: Deref<Target = Duration>> {
821823
min_liquidity_offset_msat: L,
822824
max_liquidity_offset_msat: L,
823825
liquidity_history: HistoricalMinMaxBuckets<BRT>,
824826
capacity_msat: u64,
825-
last_updated: U,
826-
offset_history_last_updated: U,
827-
now: T,
827+
last_updated: T,
828+
offset_history_last_updated: T,
828829
decay_params: ProbabilisticScoringDecayParameters,
829830
}
830831

@@ -837,11 +838,12 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
837838
network_graph,
838839
logger,
839840
channel_liquidities: HashMap::new(),
841+
_unused_time: core::marker::PhantomData,
840842
}
841843
}
842844

843845
#[cfg(test)]
844-
fn with_channel(mut self, short_channel_id: u64, liquidity: ChannelLiquidity<T>) -> Self {
846+
fn with_channel(mut self, short_channel_id: u64, liquidity: ChannelLiquidity) -> Self {
845847
assert!(self.channel_liquidities.insert(short_channel_id, liquidity).is_none());
846848
self
847849
}
@@ -994,24 +996,23 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
994996
}
995997
}
996998

997-
impl<T: Time> ChannelLiquidity<T> {
998-
#[inline]
999-
fn new() -> Self {
999+
impl ChannelLiquidity {
1000+
fn new(last_updated: Duration) -> Self {
10001001
Self {
10011002
min_liquidity_offset_msat: 0,
10021003
max_liquidity_offset_msat: 0,
10031004
min_liquidity_offset_history: HistoricalBucketRangeTracker::new(),
10041005
max_liquidity_offset_history: HistoricalBucketRangeTracker::new(),
1005-
last_updated: T::now(),
1006-
offset_history_last_updated: T::now(),
1006+
last_updated,
1007+
offset_history_last_updated: last_updated,
10071008
}
10081009
}
10091010

10101011
/// Returns a view of the channel liquidity directed from `source` to `target` assuming
10111012
/// `capacity_msat`.
10121013
fn as_directed(
10131014
&self, source: &NodeId, target: &NodeId, capacity_msat: u64, decay_params: ProbabilisticScoringDecayParameters
1014-
) -> DirectedChannelLiquidity<&u64, &HistoricalBucketRangeTracker, T, &T> {
1015+
) -> DirectedChannelLiquidity<&u64, &HistoricalBucketRangeTracker, &Duration> {
10151016
let (min_liquidity_offset_msat, max_liquidity_offset_msat, min_liquidity_offset_history, max_liquidity_offset_history) =
10161017
if source < target {
10171018
(&self.min_liquidity_offset_msat, &self.max_liquidity_offset_msat,
@@ -1031,7 +1032,6 @@ impl<T: Time> ChannelLiquidity<T> {
10311032
capacity_msat,
10321033
last_updated: &self.last_updated,
10331034
offset_history_last_updated: &self.offset_history_last_updated,
1034-
now: T::now(),
10351035
decay_params: decay_params,
10361036
}
10371037
}
@@ -1040,7 +1040,7 @@ impl<T: Time> ChannelLiquidity<T> {
10401040
/// `capacity_msat`.
10411041
fn as_directed_mut(
10421042
&mut self, source: &NodeId, target: &NodeId, capacity_msat: u64, decay_params: ProbabilisticScoringDecayParameters
1043-
) -> DirectedChannelLiquidity<&mut u64, &mut HistoricalBucketRangeTracker, T, &mut T> {
1043+
) -> DirectedChannelLiquidity<&mut u64, &mut HistoricalBucketRangeTracker, &mut Duration> {
10441044
let (min_liquidity_offset_msat, max_liquidity_offset_msat, min_liquidity_offset_history, max_liquidity_offset_history) =
10451045
if source < target {
10461046
(&mut self.min_liquidity_offset_msat, &mut self.max_liquidity_offset_msat,
@@ -1060,7 +1060,6 @@ impl<T: Time> ChannelLiquidity<T> {
10601060
capacity_msat,
10611061
last_updated: &mut self.last_updated,
10621062
offset_history_last_updated: &mut self.offset_history_last_updated,
1063-
now: T::now(),
10641063
decay_params: decay_params,
10651064
}
10661065
}
@@ -1070,7 +1069,7 @@ impl<T: Time> ChannelLiquidity<T> {
10701069
) -> u64 {
10711070
let half_life = decay_params.liquidity_offset_half_life.as_secs();
10721071
if half_life != 0 {
1073-
let elapsed_time = T::now().duration_since(self.last_updated).as_secs() as f64;
1072+
let elapsed_time = duration_since_epoch.saturating_sub(self.last_updated).as_secs() as f64;
10741073
((offset as f64) * powf64(0.5, elapsed_time / (half_life as f64))) as u64
10751074
} else {
10761075
0
@@ -1159,7 +1158,8 @@ fn success_probability(
11591158
(numerator, denominator)
11601159
}
11611160

1162-
impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>, T: Time, U: Deref<Target = T>> DirectedChannelLiquidity< L, BRT, T, U> {
1161+
impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>, T: Deref<Target = Duration>>
1162+
DirectedChannelLiquidity< L, BRT, T> {
11631163
/// Returns a liquidity penalty for routing the given HTLC `amount_msat` through the channel in
11641164
/// this direction.
11651165
fn penalty_msat(&self, amount_msat: u64, score_params: &ProbabilisticScoringFeeParameters) -> u64 {
@@ -1267,7 +1267,8 @@ impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>,
12671267
}
12681268
}
12691269

1270-
impl<L: DerefMut<Target = u64>, BRT: DerefMut<Target = HistoricalBucketRangeTracker>, T: Time, U: DerefMut<Target = T>> DirectedChannelLiquidity<L, BRT, T, U> {
1270+
impl<L: DerefMut<Target = u64>, BRT: DerefMut<Target = HistoricalBucketRangeTracker>, T: DerefMut<Target = Duration>>
1271+
DirectedChannelLiquidity<L, BRT, T> {
12711272
/// Adjusts the channel liquidity balance bounds when failing to route `amount_msat`.
12721273
fn failed_at_channel<Log: Deref>(
12731274
&mut self, amount_msat: u64, duration_since_epoch: Duration, chan_descr: fmt::Arguments, logger: &Log
@@ -1313,7 +1314,9 @@ impl<L: DerefMut<Target = u64>, BRT: DerefMut<Target = HistoricalBucketRangeTrac
13131314
/// state"), we allow the caller to set an offset applied to our liquidity bounds which
13141315
/// represents the amount of the successful payment we just made.
13151316
fn update_history_buckets(&mut self, bucket_offset_msat: u64, duration_since_epoch: Duration) {
1316-
let half_lives = self.now.duration_since(*self.offset_history_last_updated).as_secs()
1317+
let half_lives =
1318+
duration_since_epoch.checked_sub(*self.offset_history_last_updated)
1319+
.unwrap_or(Duration::ZERO).as_secs()
13171320
.checked_div(self.decay_params.historical_no_updates_half_life.as_secs())
13181321
.map(|v| v.try_into().unwrap_or(u32::max_value())).unwrap_or(u32::max_value());
13191322
self.liquidity_history.min_liquidity_offset_history.time_decay_data(half_lives);
@@ -1327,29 +1330,25 @@ impl<L: DerefMut<Target = u64>, BRT: DerefMut<Target = HistoricalBucketRangeTrac
13271330
self.liquidity_history.max_liquidity_offset_history.track_datapoint(
13281331
max_liquidity_offset_msat.saturating_sub(bucket_offset_msat), self.capacity_msat
13291332
);
1330-
*self.offset_history_last_updated = self.now;
1333+
*self.offset_history_last_updated = duration_since_epoch;
13311334
}
13321335

13331336
/// Adjusts the lower bound of the channel liquidity balance in this direction.
13341337
fn set_min_liquidity_msat(&mut self, amount_msat: u64, duration_since_epoch: Duration) {
13351338
*self.min_liquidity_offset_msat = amount_msat;
1336-
*self.max_liquidity_offset_msat = if amount_msat > self.max_liquidity_msat() {
1337-
0
1338-
} else {
1339-
self.decayed_offset_msat(*self.max_liquidity_offset_msat)
1340-
};
1341-
*self.last_updated = self.now;
1339+
if amount_msat > self.max_liquidity_msat() {
1340+
*self.max_liquidity_offset_msat = 0;
1341+
}
1342+
*self.last_updated = duration_since_epoch;
13421343
}
13431344

13441345
/// Adjusts the upper bound of the channel liquidity balance in this direction.
13451346
fn set_max_liquidity_msat(&mut self, amount_msat: u64, duration_since_epoch: Duration) {
13461347
*self.max_liquidity_offset_msat = self.capacity_msat.checked_sub(amount_msat).unwrap_or(0);
1347-
*self.min_liquidity_offset_msat = if amount_msat < self.min_liquidity_msat() {
1348-
0
1349-
} else {
1350-
self.decayed_offset_msat(*self.min_liquidity_offset_msat)
1351-
};
1352-
*self.last_updated = self.now;
1348+
if amount_msat < *self.min_liquidity_offset_msat {
1349+
*self.min_liquidity_offset_msat = 0;
1350+
}
1351+
*self.last_updated = duration_since_epoch;
13531352
}
13541353
}
13551354

@@ -1389,7 +1388,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ScoreLookUp for Prob
13891388
let capacity_msat = usage.effective_capacity.as_msat();
13901389
self.channel_liquidities
13911390
.get(&short_channel_id)
1392-
.unwrap_or(&ChannelLiquidity::new())
1391+
.unwrap_or(&ChannelLiquidity::new(Duration::ZERO))
13931392
.as_directed(source, target, capacity_msat, self.decay_params)
13941393
.penalty_msat(amount_msat, score_params)
13951394
.saturating_add(anti_probing_penalty_msat)
@@ -1419,14 +1418,14 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ScoreUpdate for Prob
14191418
if at_failed_channel {
14201419
self.channel_liquidities
14211420
.entry(hop.short_channel_id)
1422-
.or_insert_with(ChannelLiquidity::new)
1421+
.or_insert_with(|| ChannelLiquidity::new(duration_since_epoch))
14231422
.as_directed_mut(source, &target, capacity_msat, self.decay_params)
14241423
.failed_at_channel(amount_msat, duration_since_epoch,
14251424
format_args!("SCID {}, towards {:?}", hop.short_channel_id, target), &self.logger);
14261425
} else {
14271426
self.channel_liquidities
14281427
.entry(hop.short_channel_id)
1429-
.or_insert_with(ChannelLiquidity::new)
1428+
.or_insert_with(|| ChannelLiquidity::new(duration_since_epoch))
14301429
.as_directed_mut(source, &target, capacity_msat, self.decay_params)
14311430
.failed_downstream(amount_msat, duration_since_epoch,
14321431
format_args!("SCID {}, towards {:?}", hop.short_channel_id, target), &self.logger);
@@ -1455,7 +1454,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ScoreUpdate for Prob
14551454
let capacity_msat = channel.effective_capacity().as_msat();
14561455
self.channel_liquidities
14571456
.entry(hop.short_channel_id)
1458-
.or_insert_with(ChannelLiquidity::new)
1457+
.or_insert_with(|| ChannelLiquidity::new(duration_since_epoch))
14591458
.as_directed_mut(source, &target, capacity_msat, self.decay_params)
14601459
.successful(amount_msat, duration_since_epoch,
14611460
format_args!("SCID {}, towards {:?}", hop.short_channel_id, target), &self.logger);
@@ -1481,10 +1480,10 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ScoreUpdate for Prob
14811480
liquidity.decayed_offset(liquidity.min_liquidity_offset_msat, duration_since_epoch, decay_params);
14821481
liquidity.max_liquidity_offset_msat =
14831482
liquidity.decayed_offset(liquidity.max_liquidity_offset_msat, duration_since_epoch, decay_params);
1484-
liquidity.last_updated = T::now();
1483+
liquidity.last_updated = duration_since_epoch;
14851484

14861485
let elapsed_time =
1487-
T::now().duration_since(liquidity.offset_history_last_updated);
1486+
duration_since_epoch.saturating_sub(liquidity.offset_history_last_updated);
14881487
if elapsed_time > decay_params.historical_no_updates_half_life {
14891488
let half_life = decay_params.historical_no_updates_half_life.as_secs() as f64;
14901489
let divisor = powf64(2048.0, (elapsed_time.as_secs() as f64) / half_life) as u64;
@@ -1494,7 +1493,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ScoreUpdate for Prob
14941493
for bucket in liquidity.max_liquidity_offset_history.buckets.iter_mut() {
14951494
*bucket = ((*bucket as u64) * 1024 / divisor) as u16;
14961495
}
1497-
liquidity.offset_history_last_updated = T::now();
1496+
liquidity.offset_history_last_updated = duration_since_epoch;
14981497
}
14991498
liquidity.min_liquidity_offset_msat != 0 || liquidity.max_liquidity_offset_msat != 0 ||
15001499
liquidity.min_liquidity_offset_history.buckets != [0; 32] ||
@@ -2116,31 +2115,29 @@ ReadableArgs<(ProbabilisticScoringDecayParameters, G, L)> for ProbabilisticScore
21162115
network_graph,
21172116
logger,
21182117
channel_liquidities,
2118+
_unused_time: core::marker::PhantomData,
21192119
})
21202120
}
21212121
}
21222122

2123-
impl<T: Time> Writeable for ChannelLiquidity<T> {
2123+
impl Writeable for ChannelLiquidity {
21242124
#[inline]
21252125
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
2126-
let offset_history_duration_since_epoch =
2127-
T::duration_since_epoch() - self.offset_history_last_updated.elapsed();
2128-
let duration_since_epoch = T::duration_since_epoch() - self.last_updated.elapsed();
21292126
write_tlv_fields!(w, {
21302127
(0, self.min_liquidity_offset_msat, required),
21312128
// 1 was the min_liquidity_offset_history in octile form
21322129
(2, self.max_liquidity_offset_msat, required),
21332130
// 3 was the max_liquidity_offset_history in octile form
2134-
(4, duration_since_epoch, required),
2131+
(4, self.last_updated, required),
21352132
(5, Some(self.min_liquidity_offset_history), option),
21362133
(7, Some(self.max_liquidity_offset_history), option),
2137-
(9, offset_history_duration_since_epoch, required),
2134+
(9, self.offset_history_last_updated, required),
21382135
});
21392136
Ok(())
21402137
}
21412138
}
21422139

2143-
impl<T: Time> Readable for ChannelLiquidity<T> {
2140+
impl Readable for ChannelLiquidity {
21442141
#[inline]
21452142
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
21462143
let mut min_liquidity_offset_msat = 0;
@@ -2149,37 +2146,18 @@ impl<T: Time> Readable for ChannelLiquidity<T> {
21492146
let mut legacy_max_liq_offset_history: Option<LegacyHistoricalBucketRangeTracker> = None;
21502147
let mut min_liquidity_offset_history: Option<HistoricalBucketRangeTracker> = None;
21512148
let mut max_liquidity_offset_history: Option<HistoricalBucketRangeTracker> = None;
2152-
let mut duration_since_epoch = Duration::from_secs(0);
2153-
let mut offset_history_duration_since_epoch = None;
2149+
let mut last_updated = Duration::from_secs(0);
2150+
let mut offset_history_last_updated = None;
21542151
read_tlv_fields!(r, {
21552152
(0, min_liquidity_offset_msat, required),
21562153
(1, legacy_min_liq_offset_history, option),
21572154
(2, max_liquidity_offset_msat, required),
21582155
(3, legacy_max_liq_offset_history, option),
2159-
(4, duration_since_epoch, required),
2156+
(4, last_updated, required),
21602157
(5, min_liquidity_offset_history, option),
21612158
(7, max_liquidity_offset_history, option),
2162-
(9, offset_history_duration_since_epoch, option),
2159+
(9, offset_history_last_updated, option),
21632160
});
2164-
// On rust prior to 1.60 `Instant::duration_since` will panic if time goes backwards.
2165-
// We write `last_updated` as wallclock time even though its ultimately an `Instant` (which
2166-
// is a time from a monotonic clock usually represented as an offset against boot time).
2167-
// Thus, we have to construct an `Instant` by subtracting the difference in wallclock time
2168-
// from the one that was written. However, because `Instant` can panic if we construct one
2169-
// in the future, we must handle wallclock time jumping backwards, which we do by simply
2170-
// using `Instant::now()` in that case.
2171-
let wall_clock_now = T::duration_since_epoch();
2172-
let now = T::now();
2173-
let last_updated = if wall_clock_now > duration_since_epoch {
2174-
now - (wall_clock_now - duration_since_epoch)
2175-
} else { now };
2176-
2177-
let offset_history_duration_since_epoch =
2178-
offset_history_duration_since_epoch.unwrap_or(duration_since_epoch);
2179-
let offset_history_last_updated = if wall_clock_now > offset_history_duration_since_epoch {
2180-
now - (wall_clock_now - duration_since_epoch)
2181-
} else { now };
2182-
21832161
if min_liquidity_offset_history.is_none() {
21842162
if let Some(legacy_buckets) = legacy_min_liq_offset_history {
21852163
min_liquidity_offset_history = Some(legacy_buckets.into_current());
@@ -2200,7 +2178,7 @@ impl<T: Time> Readable for ChannelLiquidity<T> {
22002178
min_liquidity_offset_history: min_liquidity_offset_history.unwrap(),
22012179
max_liquidity_offset_history: max_liquidity_offset_history.unwrap(),
22022180
last_updated,
2203-
offset_history_last_updated,
2181+
offset_history_last_updated: offset_history_last_updated.unwrap_or(last_updated),
22042182
})
22052183
}
22062184
}
@@ -2210,7 +2188,6 @@ mod tests {
22102188
use super::{ChannelLiquidity, HistoricalBucketRangeTracker, ProbabilisticScoringFeeParameters, ProbabilisticScoringDecayParameters, ProbabilisticScorerUsingTime};
22112189
use crate::blinded_path::{BlindedHop, BlindedPath};
22122190
use crate::util::config::UserConfig;
2213-
use crate::util::time::Time;
22142191
use crate::util::time::tests::SinceEpoch;
22152192

22162193
use crate::ln::channelmanager;
@@ -2379,8 +2356,8 @@ mod tests {
23792356
#[test]
23802357
fn liquidity_bounds_directed_from_lowest_node_id() {
23812358
let logger = TestLogger::new();
2382-
let last_updated = SinceEpoch::now();
2383-
let offset_history_last_updated = SinceEpoch::now();
2359+
let last_updated = Duration::ZERO;
2360+
let offset_history_last_updated = Duration::ZERO;
23842361
let network_graph = network_graph(&logger);
23852362
let decay_params = ProbabilisticScoringDecayParameters::default();
23862363
let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger)
@@ -2460,8 +2437,8 @@ mod tests {
24602437
#[test]
24612438
fn resets_liquidity_upper_bound_when_crossed_by_lower_bound() {
24622439
let logger = TestLogger::new();
2463-
let last_updated = SinceEpoch::now();
2464-
let offset_history_last_updated = SinceEpoch::now();
2440+
let last_updated = Duration::ZERO;
2441+
let offset_history_last_updated = Duration::ZERO;
24652442
let network_graph = network_graph(&logger);
24662443
let decay_params = ProbabilisticScoringDecayParameters::default();
24672444
let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger)
@@ -2521,8 +2498,8 @@ mod tests {
25212498
#[test]
25222499
fn resets_liquidity_lower_bound_when_crossed_by_upper_bound() {
25232500
let logger = TestLogger::new();
2524-
let last_updated = SinceEpoch::now();
2525-
let offset_history_last_updated = SinceEpoch::now();
2501+
let last_updated = Duration::ZERO;
2502+
let offset_history_last_updated = Duration::ZERO;
25262503
let network_graph = network_graph(&logger);
25272504
let decay_params = ProbabilisticScoringDecayParameters::default();
25282505
let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger)
@@ -2628,8 +2605,8 @@ mod tests {
26282605
#[test]
26292606
fn constant_penalty_outside_liquidity_bounds() {
26302607
let logger = TestLogger::new();
2631-
let last_updated = SinceEpoch::now();
2632-
let offset_history_last_updated = SinceEpoch::now();
2608+
let last_updated = Duration::ZERO;
2609+
let offset_history_last_updated = Duration::ZERO;
26332610
let network_graph = network_graph(&logger);
26342611
let params = ProbabilisticScoringFeeParameters {
26352612
liquidity_penalty_multiplier_msat: 1_000,

0 commit comments

Comments
 (0)