Skip to content

Commit 71fe4f5

Browse files
committed
Seal scoring::Time and only use Instant or Eternity publicly
`scoring::Time` exists in part to make testing the passage of time in `Scorer` practical. To allow no-std users to provide a time source it was exposed as a trait as well. However, it seems somewhat unlikely that a no-std user is going to have a use for providing their own time source (otherwise they wouldn't be a no-std user), and likely they won't have a graph in memory either. `scoring::Time` as currently written is also exceptionally hard to write C bindings for - the C bindings trait mappings relies on the ability to construct trait implementations at runtime with function pointers (i.e. `dyn Trait`s). `scoring::Time`, on the other hand, is a supertrait of `core::ops::Sub` which requires a `sub` method which takes a type parameter and returns a type parameter. Both of which aren't practical in bindings, especially given the `Sub::Output` associated type is not bound by any trait bounds at all (implying we cannot simply map the `sub` function to return an opaque trait object). Thus, for simplicity, we here simply seal `scoring::Time` and make it effectively-private, ensuring the bindings don't need to bother with it.
1 parent 020347a commit 71fe4f5

File tree

2 files changed

+92
-74
lines changed

2 files changed

+92
-74
lines changed

lightning/src/routing/scoring.rs

Lines changed: 90 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,8 @@
4646
//!
4747
//! # Note
4848
//!
49-
//! If persisting [`Scorer`], it must be restored using the same [`Time`] parameterization. Using a
50-
//! different type results in undefined behavior. Specifically, persisting when built with feature
51-
//! `no-std` and restoring without it, or vice versa, uses different types and thus is undefined.
49+
//! Persisting when built with feature `no-std` and restoring without it, or vice versa, uses
50+
//! different types and thus is undefined.
5251
//!
5352
//! [`find_route`]: crate::routing::router::find_route
5453
@@ -59,9 +58,10 @@ use util::ser::{Readable, Writeable, Writer};
5958

6059
use prelude::*;
6160
use core::cell::{RefCell, RefMut};
62-
use core::ops::{DerefMut, Sub};
61+
use core::ops::DerefMut;
6362
use core::time::Duration;
64-
use io::{self, Read}; use sync::{Mutex, MutexGuard};
63+
use io::{self, Read};
64+
use sync::{Mutex, MutexGuard};
6565

6666
/// We define Score ever-so-slightly differently based on whether we are being built for C bindings
6767
/// or not. For users, `LockableScore` must somehow be writeable to disk. For Rust users, this is
@@ -185,23 +185,33 @@ impl<'a, S: Writeable> Writeable for MutexGuard<'a, S> {
185185
/// See [module-level documentation] for usage.
186186
///
187187
/// [module-level documentation]: crate::routing::scoring
188-
pub type Scorer = ScorerUsingTime::<DefaultTime>;
189-
190-
/// Time used by [`Scorer`].
191188
#[cfg(not(feature = "no-std"))]
192-
pub type DefaultTime = std::time::Instant;
193-
194-
/// Time used by [`Scorer`].
189+
pub type Scorer = ScorerUsingTime::<std::time::Instant>;
190+
/// [`Score`] implementation that provides reasonable default behavior.
191+
///
192+
/// Used to apply a fixed penalty to each channel, thus avoiding long paths when shorter paths with
193+
/// slightly higher fees are available. Will further penalize channels that fail to relay payments.
194+
///
195+
/// See [module-level documentation] for usage.
196+
///
197+
/// [module-level documentation]: crate::routing::scoring
195198
#[cfg(feature = "no-std")]
196-
pub type DefaultTime = Eternity;
199+
pub type Scorer = ScorerUsingTime::<time::Eternity>;
200+
201+
// Note that ideally we'd hide ScorerUsingTime from public view by sealing it as well, but rustdoc
202+
// doesn't handle this well - instead exposing a `Scorer` which has no trait implementation(s) or
203+
// methods at all.
197204

198-
/// [`Score`] implementation parameterized by [`Time`].
205+
/// [`Score`] implementation.
199206
///
200207
/// See [`Scorer`] for details.
201208
///
202209
/// # Note
203210
///
204-
/// Mixing [`Time`] types between serialization and deserialization results in undefined behavior.
211+
/// Mixing the `no-std` feature between serialization and deserialization results in undefined
212+
/// behavior.
213+
///
214+
/// (C-not exported) generally all users should use the [`Scorer`] type alias.
205215
pub struct ScorerUsingTime<T: Time> {
206216
params: ScoringParameters,
207217
// TODO: Remove entries of closed channels.
@@ -247,8 +257,8 @@ pub struct ScoringParameters {
247257
///
248258
/// # Note
249259
///
250-
/// When time is an [`Eternity`], as is default when enabling feature `no-std`, it will never
251-
/// elapse. Therefore, this penalty will never decay.
260+
/// When built with the `no-std` feature, time will never elapse. Therefore, this penalty will
261+
/// never decay.
252262
///
253263
/// [`failure_penalty_msat`]: Self::failure_penalty_msat
254264
pub failure_penalty_half_life: Duration,
@@ -273,20 +283,6 @@ struct ChannelFailure<T: Time> {
273283
last_failed: T,
274284
}
275285

276-
/// A measurement of time.
277-
pub trait Time: Sub<Duration, Output = Self> where Self: Sized {
278-
/// Returns an instance corresponding to the current moment.
279-
fn now() -> Self;
280-
281-
/// Returns the amount of time elapsed since `self` was created.
282-
fn elapsed(&self) -> Duration;
283-
284-
/// Returns the amount of time passed since the beginning of [`Time`].
285-
///
286-
/// Used during (de-)serialization.
287-
fn duration_since_epoch() -> Duration;
288-
}
289-
290286
impl<T: Time> ScorerUsingTime<T> {
291287
/// Creates a new scorer using the given scoring parameters.
292288
pub fn new(params: ScoringParameters) -> Self {
@@ -383,48 +379,6 @@ impl<T: Time> Score for ScorerUsingTime<T> {
383379
}
384380
}
385381

386-
#[cfg(not(feature = "no-std"))]
387-
impl Time for std::time::Instant {
388-
fn now() -> Self {
389-
std::time::Instant::now()
390-
}
391-
392-
fn duration_since_epoch() -> Duration {
393-
use std::time::SystemTime;
394-
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap()
395-
}
396-
397-
fn elapsed(&self) -> Duration {
398-
std::time::Instant::elapsed(self)
399-
}
400-
}
401-
402-
/// A state in which time has no meaning.
403-
#[derive(Debug, PartialEq, Eq)]
404-
pub struct Eternity;
405-
406-
impl Time for Eternity {
407-
fn now() -> Self {
408-
Self
409-
}
410-
411-
fn duration_since_epoch() -> Duration {
412-
Duration::from_secs(0)
413-
}
414-
415-
fn elapsed(&self) -> Duration {
416-
Duration::from_secs(0)
417-
}
418-
}
419-
420-
impl Sub<Duration> for Eternity {
421-
type Output = Self;
422-
423-
fn sub(self, _other: Duration) -> Self {
424-
self
425-
}
426-
}
427-
428382
impl<T: Time> Writeable for ScorerUsingTime<T> {
429383
#[inline]
430384
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
@@ -475,9 +429,72 @@ impl<T: Time> Readable for ChannelFailure<T> {
475429
}
476430
}
477431

432+
pub(crate) mod time {
433+
use core::ops::Sub;
434+
use core::time::Duration;
435+
/// A measurement of time.
436+
pub trait Time: Sub<Duration, Output = Self> where Self: Sized {
437+
/// Returns an instance corresponding to the current moment.
438+
fn now() -> Self;
439+
440+
/// Returns the amount of time elapsed since `self` was created.
441+
fn elapsed(&self) -> Duration;
442+
443+
/// Returns the amount of time passed since the beginning of [`Time`].
444+
///
445+
/// Used during (de-)serialization.
446+
fn duration_since_epoch() -> Duration;
447+
}
448+
449+
/// A state in which time has no meaning.
450+
#[derive(Debug, PartialEq, Eq)]
451+
pub struct Eternity;
452+
453+
#[cfg(not(feature = "no-std"))]
454+
impl Time for std::time::Instant {
455+
fn now() -> Self {
456+
std::time::Instant::now()
457+
}
458+
459+
fn duration_since_epoch() -> Duration {
460+
use std::time::SystemTime;
461+
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap()
462+
}
463+
464+
fn elapsed(&self) -> Duration {
465+
std::time::Instant::elapsed(self)
466+
}
467+
}
468+
469+
impl Time for Eternity {
470+
fn now() -> Self {
471+
Self
472+
}
473+
474+
fn duration_since_epoch() -> Duration {
475+
Duration::from_secs(0)
476+
}
477+
478+
fn elapsed(&self) -> Duration {
479+
Duration::from_secs(0)
480+
}
481+
}
482+
483+
impl Sub<Duration> for Eternity {
484+
type Output = Self;
485+
486+
fn sub(self, _other: Duration) -> Self {
487+
self
488+
}
489+
}
490+
}
491+
492+
pub(crate) use self::time::Time;
493+
478494
#[cfg(test)]
479495
mod tests {
480-
use super::{Eternity, ScoringParameters, ScorerUsingTime, Time};
496+
use super::{ScoringParameters, ScorerUsingTime, Time};
497+
use super::time::Eternity;
481498

482499
use routing::scoring::Score;
483500
use routing::network_graph::NodeId;

lightning/src/util/test_utils.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ use ln::features::{ChannelFeatures, InitFeatures};
2121
use ln::msgs;
2222
use ln::msgs::OptionalField;
2323
use ln::script::ShutdownScript;
24-
use routing::scoring::{Eternity, ScorerUsingTime};
24+
use routing::scoring::ScorerUsingTime;
25+
use routing::scoring::time::Eternity;
2526
use util::enforcing_trait_impls::{EnforcingSigner, EnforcementState};
2627
use util::events;
2728
use util::logger::{Logger, Level, Record};

0 commit comments

Comments
 (0)