From ae210e7d09758560b0e418b939222e7bc26ee009 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Thu, 28 Oct 2021 23:44:26 -0500 Subject: [PATCH] Implement (de)serialization for Scorer Scorer should be serialized to retain penalty data between restarts. Implement (de)serialization for Scorer by serializing last failure times as duration since the UNIX epoch. For no-std, the zero-Duration is used. --- lightning/src/routing/scorer.rs | 80 ++++++++++++++++++++++++++++++++- lightning/src/util/ser.rs | 17 +++++++ 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/lightning/src/routing/scorer.rs b/lightning/src/routing/scorer.rs index d9b8da2dc..2aa0b4c14 100644 --- a/lightning/src/routing/scorer.rs +++ b/lightning/src/routing/scorer.rs @@ -44,15 +44,25 @@ //! # } //! ``` //! +//! # Note +//! +//! If persisting [`Scorer`], it must be restored using the same [`Time`] parameterization. Using a +//! different type results in undefined behavior. Specifically, persisting when built with feature +//! `no-std` and restoring without it, or vice versa, uses different types and thus is undefined. +//! //! [`find_route`]: crate::routing::router::find_route use routing; +use ln::msgs::DecodeError; use routing::network_graph::NodeId; use routing::router::RouteHop; +use util::ser::{Readable, Writeable, Writer}; use prelude::*; +use core::ops::Sub; use core::time::Duration; +use io::{self, Read}; /// [`routing::Score`] implementation that provides reasonable default behavior. /// @@ -75,6 +85,10 @@ pub type DefaultTime = Eternity; /// [`routing::Score`] implementation parameterized by [`Time`]. /// /// See [`Scorer`] for details. +/// +/// # Note +/// +/// Mixing [`Time`] types between serialization and deserialization results in undefined behavior. pub struct ScorerUsingTime { params: ScoringParameters, // TODO: Remove entries of closed channels. @@ -106,6 +120,12 @@ pub struct ScoringParameters { pub failure_penalty_half_life: Duration, } +impl_writeable_tlv_based!(ScoringParameters, { + (0, base_penalty_msat, required), + (2, failure_penalty_msat, required), + (4, failure_penalty_half_life, required), +}); + /// Accounting for penalties against a channel for failing to relay any payments. /// /// Penalties decay over time, though accumulate as more failures occur. @@ -118,12 +138,17 @@ struct ChannelFailure { } /// A measurement of time. -pub trait Time { +pub trait Time: Sub where Self: Sized { /// Returns an instance corresponding to the current moment. fn now() -> Self; /// Returns the amount of time elapsed since `self` was created. fn elapsed(&self) -> Duration; + + /// Returns the amount of time passed since the beginning of [`Time`]. + /// + /// Used during (de-)serialization. + fn duration_since_epoch() -> Duration; } impl ScorerUsingTime { @@ -211,6 +236,11 @@ impl Time for std::time::Instant { std::time::Instant::now() } + fn duration_since_epoch() -> Duration { + use std::time::SystemTime; + SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap() + } + fn elapsed(&self) -> Duration { std::time::Instant::elapsed(self) } @@ -224,7 +254,55 @@ impl Time for Eternity { Self } + fn duration_since_epoch() -> Duration { + Duration::from_secs(0) + } + fn elapsed(&self) -> Duration { Duration::from_secs(0) } } + +impl Sub for Eternity { + type Output = Self; + + fn sub(self, _other: Duration) -> Self { + self + } +} + +impl Writeable for ScorerUsingTime { + #[inline] + fn write(&self, w: &mut W) -> Result<(), io::Error> { + self.params.write(w)?; + self.channel_failures.write(w) + } +} + +impl Readable for ScorerUsingTime { + #[inline] + fn read(r: &mut R) -> Result { + Ok(Self { + params: Readable::read(r)?, + channel_failures: Readable::read(r)?, + }) + } +} + +impl Writeable for ChannelFailure { + #[inline] + fn write(&self, w: &mut W) -> Result<(), io::Error> { + self.undecayed_penalty_msat.write(w)?; + (T::duration_since_epoch() - self.last_failed.elapsed()).write(w) + } +} + +impl Readable for ChannelFailure { + #[inline] + fn read(r: &mut R) -> Result { + Ok(Self { + undecayed_penalty_msat: Readable::read(r)?, + last_failed: T::now() - (T::duration_since_epoch() - Readable::read(r)?), + }) + } +} diff --git a/lightning/src/util/ser.rs b/lightning/src/util/ser.rs index ba8a9fd0d..8fb72a8f9 100644 --- a/lightning/src/util/ser.rs +++ b/lightning/src/util/ser.rs @@ -27,6 +27,7 @@ use bitcoin::consensus::Encodable; use bitcoin::hashes::sha256d::Hash as Sha256dHash; use bitcoin::hash_types::{Txid, BlockHash}; use core::marker::Sized; +use core::time::Duration; use ln::msgs::DecodeError; use ln::{PaymentPreimage, PaymentHash, PaymentSecret}; @@ -911,3 +912,19 @@ impl Readable for String { Ok(ret) } } + +impl Writeable for Duration { + #[inline] + fn write(&self, w: &mut W) -> Result<(), io::Error> { + self.as_secs().write(w)?; + self.subsec_nanos().write(w) + } +} +impl Readable for Duration { + #[inline] + fn read(r: &mut R) -> Result { + let secs = Readable::read(r)?; + let nanos = Readable::read(r)?; + Ok(Duration::new(secs, nanos)) + } +} -- 2.39.5