Skip to content

Commit 960854c

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 3f1ad58 commit 960854c

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.
@@ -103,6 +117,12 @@ pub struct ScoringParameters {
103117
pub failure_penalty_half_life: Duration,
104118
}
105119

120+
impl_writeable_tlv_based!(ScoringParameters, {
121+
(0, base_penalty_msat, required),
122+
(2, failure_penalty_msat, required),
123+
(4, failure_penalty_half_life, required),
124+
});
125+
106126
/// Accounting for penalties from channel failures.
107127
///
108128
/// Penalties decay over time, though accumulate as more failures occur.
@@ -115,12 +135,17 @@ struct ChannelFailure<T: Time> {
115135
}
116136

117137
/// A measurement of time.
118-
pub trait Time {
138+
pub trait Time: Sub<Duration, Output = Self> where Self: Sized {
119139
/// Returns an instance corresponding to the current moment.
120140
fn now() -> Self;
121141

122142
/// Returns the amount of time elapsed since created.
123143
fn elapsed(&self) -> Duration;
144+
145+
/// Returns the amount of time passed since the beginning of [`Time`].
146+
///
147+
/// Used during (de-)serialization.
148+
fn duration_since_epoch() -> Duration;
124149
}
125150

126151
impl<T: Time> ScorerUsingTime<T> {
@@ -208,6 +233,11 @@ impl Time for std::time::Instant {
208233
std::time::Instant::now()
209234
}
210235

236+
fn duration_since_epoch() -> Duration {
237+
use std::time::SystemTime;
238+
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap()
239+
}
240+
211241
fn elapsed(&self) -> Duration {
212242
std::time::Instant::elapsed(self)
213243
}
@@ -221,7 +251,55 @@ impl Time for Eternity {
221251
Self
222252
}
223253

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

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)