Skip to content

Commit ae210e7

Browse files
committed
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.
1 parent a8d3b5a commit ae210e7

File tree

2 files changed

+96
-1
lines changed

2 files changed

+96
-1
lines changed

lightning/src/routing/scorer.rs

+79-1
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,25 @@
4444
//! # }
4545
//! ```
4646
//!
47+
//! # Note
48+
//!
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.
52+
//!
4753
//! [`find_route`]: crate::routing::router::find_route
4854
4955
use routing;
5056

57+
use ln::msgs::DecodeError;
5158
use routing::network_graph::NodeId;
5259
use routing::router::RouteHop;
60+
use util::ser::{Readable, Writeable, Writer};
5361

5462
use prelude::*;
63+
use core::ops::Sub;
5564
use core::time::Duration;
65+
use io::{self, Read};
5666

5767
/// [`routing::Score`] implementation that provides reasonable default behavior.
5868
///
@@ -75,6 +85,10 @@ pub type DefaultTime = Eternity;
7585
/// [`routing::Score`] implementation parameterized by [`Time`].
7686
///
7787
/// See [`Scorer`] for details.
88+
///
89+
/// # Note
90+
///
91+
/// Mixing [`Time`] types between serialization and deserialization results in undefined behavior.
7892
pub struct ScorerUsingTime<T: Time> {
7993
params: ScoringParameters,
8094
// TODO: Remove entries of closed channels.
@@ -106,6 +120,12 @@ pub struct ScoringParameters {
106120
pub failure_penalty_half_life: Duration,
107121
}
108122

123+
impl_writeable_tlv_based!(ScoringParameters, {
124+
(0, base_penalty_msat, required),
125+
(2, failure_penalty_msat, required),
126+
(4, failure_penalty_half_life, required),
127+
});
128+
109129
/// Accounting for penalties against a channel for failing to relay any payments.
110130
///
111131
/// Penalties decay over time, though accumulate as more failures occur.
@@ -118,12 +138,17 @@ struct ChannelFailure<T: Time> {
118138
}
119139

120140
/// A measurement of time.
121-
pub trait Time {
141+
pub trait Time: Sub<Duration, Output = Self> where Self: Sized {
122142
/// Returns an instance corresponding to the current moment.
123143
fn now() -> Self;
124144

125145
/// Returns the amount of time elapsed since `self` was created.
126146
fn elapsed(&self) -> Duration;
147+
148+
/// Returns the amount of time passed since the beginning of [`Time`].
149+
///
150+
/// Used during (de-)serialization.
151+
fn duration_since_epoch() -> Duration;
127152
}
128153

129154
impl<T: Time> ScorerUsingTime<T> {
@@ -211,6 +236,11 @@ impl Time for std::time::Instant {
211236
std::time::Instant::now()
212237
}
213238

239+
fn duration_since_epoch() -> Duration {
240+
use std::time::SystemTime;
241+
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap()
242+
}
243+
214244
fn elapsed(&self) -> Duration {
215245
std::time::Instant::elapsed(self)
216246
}
@@ -224,7 +254,55 @@ impl Time for Eternity {
224254
Self
225255
}
226256

257+
fn duration_since_epoch() -> Duration {
258+
Duration::from_secs(0)
259+
}
260+
227261
fn elapsed(&self) -> Duration {
228262
Duration::from_secs(0)
229263
}
230264
}
265+
266+
impl Sub<Duration> for Eternity {
267+
type Output = Self;
268+
269+
fn sub(self, _other: Duration) -> Self {
270+
self
271+
}
272+
}
273+
274+
impl<T: Time> Writeable for ScorerUsingTime<T> {
275+
#[inline]
276+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
277+
self.params.write(w)?;
278+
self.channel_failures.write(w)
279+
}
280+
}
281+
282+
impl<T: Time> Readable for ScorerUsingTime<T> {
283+
#[inline]
284+
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
285+
Ok(Self {
286+
params: Readable::read(r)?,
287+
channel_failures: Readable::read(r)?,
288+
})
289+
}
290+
}
291+
292+
impl<T: Time> Writeable for ChannelFailure<T> {
293+
#[inline]
294+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
295+
self.undecayed_penalty_msat.write(w)?;
296+
(T::duration_since_epoch() - self.last_failed.elapsed()).write(w)
297+
}
298+
}
299+
300+
impl<T: Time> Readable for ChannelFailure<T> {
301+
#[inline]
302+
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
303+
Ok(Self {
304+
undecayed_penalty_msat: Readable::read(r)?,
305+
last_failed: T::now() - (T::duration_since_epoch() - Readable::read(r)?),
306+
})
307+
}
308+
}

lightning/src/util/ser.rs

+17
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use bitcoin::consensus::Encodable;
2727
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
2828
use bitcoin::hash_types::{Txid, BlockHash};
2929
use core::marker::Sized;
30+
use core::time::Duration;
3031
use ln::msgs::DecodeError;
3132
use ln::{PaymentPreimage, PaymentHash, PaymentSecret};
3233

@@ -911,3 +912,19 @@ impl Readable for String {
911912
Ok(ret)
912913
}
913914
}
915+
916+
impl Writeable for Duration {
917+
#[inline]
918+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
919+
self.as_secs().write(w)?;
920+
self.subsec_nanos().write(w)
921+
}
922+
}
923+
impl Readable for Duration {
924+
#[inline]
925+
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
926+
let secs = Readable::read(r)?;
927+
let nanos = Readable::read(r)?;
928+
Ok(Duration::new(secs, nanos))
929+
}
930+
}

0 commit comments

Comments
 (0)