Skip to content

Commit 88cf9b3

Browse files
committed
Refactor channel failure penalty logic
Move channel failure penalty logic into a ChannelFailure abstraction. This encapsulates the logic for accumulating penalties and decaying them over time. It also is responsible for the no-std behavior. This cleans up Scorer and will make it easier to serialize it.
1 parent 1c2b3c2 commit 88cf9b3

File tree

1 file changed

+58
-46
lines changed

1 file changed

+58
-46
lines changed

lightning/src/routing/scorer.rs

+58-46
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ use routing::network_graph::NodeId;
5252
use routing::router::RouteHop;
5353

5454
use prelude::*;
55-
#[cfg(not(feature = "no-std"))]
5655
use core::time::Duration;
5756
#[cfg(not(feature = "no-std"))]
5857
use std::time::Instant;
@@ -67,29 +66,42 @@ use std::time::Instant;
6766
/// [module-level documentation]: crate::routing::scorer
6867
pub struct Scorer {
6968
params: ScoringParameters,
70-
#[cfg(not(feature = "no-std"))]
71-
channel_failures: HashMap<u64, (u64, Instant)>,
72-
#[cfg(feature = "no-std")]
73-
channel_failures: HashMap<u64, u64>,
69+
// TODO: Remove entries of closed channels.
70+
channel_failures: HashMap<u64, ChannelFailure>,
7471
}
7572

7673
/// Parameters for configuring [`Scorer`].
7774
pub struct ScoringParameters {
7875
/// A fixed penalty in msats to apply to each channel.
7976
pub base_penalty_msat: u64,
8077

81-
/// A penalty in msats to apply to a channel upon failure.
78+
/// A penalty in msats to apply to a channel upon failing to relay a payment.
8279
///
83-
/// This may be reduced over time based on [`failure_penalty_half_life`].
80+
/// This accumulates for each failure but may be reduced over time based on
81+
/// [`failure_penalty_half_life`].
8482
///
8583
/// [`failure_penalty_half_life`]: Self::failure_penalty_half_life
8684
pub failure_penalty_msat: u64,
8785

88-
/// The time needed before any accumulated channel failure penalties are cut in half.
89-
#[cfg(not(feature = "no-std"))]
86+
/// The time required to elapse before any accumulated [`failure_penalty_msat`] penalties are
87+
/// cut in half.
88+
///
89+
/// [`failure_penalty_msat`]: Self::failure_penalty_msat
9090
pub failure_penalty_half_life: Duration,
9191
}
9292

93+
/// Accounting for penalties against a channel for failing to relay any payments.
94+
///
95+
/// Penalties decay over time, though accumulate as more failures occur.
96+
struct ChannelFailure {
97+
/// Accumulated penalty in msats for the channel as of `last_failed`.
98+
undecayed_penalty_msat: u64,
99+
100+
/// Last time the channel failed. Used to decay `undecayed_penalty_msat`.
101+
#[cfg(not(feature = "no-std"))]
102+
last_failed: Instant,
103+
}
104+
93105
impl Scorer {
94106
/// Creates a new scorer using the given scoring parameters.
95107
pub fn new(params: ScoringParameters) -> Self {
@@ -105,14 +117,41 @@ impl Scorer {
105117
Self::new(ScoringParameters {
106118
base_penalty_msat: penalty_msat,
107119
failure_penalty_msat: 0,
108-
#[cfg(not(feature = "no-std"))]
109120
failure_penalty_half_life: Duration::from_secs(0),
110121
})
111122
}
123+
}
112124

113-
#[cfg(not(feature = "no-std"))]
114-
fn decay_from(&self, penalty_msat: u64, last_failure: &Instant) -> u64 {
115-
decay_from(penalty_msat, last_failure, self.params.failure_penalty_half_life)
125+
impl ChannelFailure {
126+
fn new(failure_penalty_msat: u64) -> Self {
127+
Self {
128+
undecayed_penalty_msat: failure_penalty_msat,
129+
#[cfg(not(feature = "no-std"))]
130+
last_failed: Instant::now(),
131+
}
132+
}
133+
134+
fn add_penalty(&mut self, failure_penalty_msat: u64, half_life: Duration) {
135+
self.undecayed_penalty_msat = self.decayed_penalty_msat(half_life) + failure_penalty_msat;
136+
#[cfg(not(feature = "no-std"))]
137+
{
138+
self.last_failed = Instant::now();
139+
}
140+
}
141+
142+
fn decayed_penalty_msat(&self, half_life: Duration) -> u64 {
143+
let decays = self.elapsed().as_secs().checked_div(half_life.as_secs());
144+
match decays {
145+
Some(decays) => self.undecayed_penalty_msat >> decays,
146+
None => 0,
147+
}
148+
}
149+
150+
fn elapsed(&self) -> Duration {
151+
#[cfg(not(feature = "no-std"))]
152+
return self.last_failed.elapsed();
153+
#[cfg(feature = "no-std")]
154+
return Duration::from_secs(0);
116155
}
117156
}
118157

@@ -127,7 +166,6 @@ impl Default for ScoringParameters {
127166
Self {
128167
base_penalty_msat: 500,
129168
failure_penalty_msat: 1024 * 1000,
130-
#[cfg(not(feature = "no-std"))]
131169
failure_penalty_half_life: Duration::from_secs(3600),
132170
}
133171
}
@@ -137,45 +175,19 @@ impl routing::Score for Scorer {
137175
fn channel_penalty_msat(
138176
&self, short_channel_id: u64, _source: &NodeId, _target: &NodeId
139177
) -> u64 {
140-
#[cfg(not(feature = "no-std"))]
141-
let failure_penalty_msat = match self.channel_failures.get(&short_channel_id) {
142-
Some((penalty_msat, last_failure)) => self.decay_from(*penalty_msat, last_failure),
143-
None => 0,
144-
};
145-
#[cfg(feature = "no-std")]
146-
let failure_penalty_msat =
147-
self.channel_failures.get(&short_channel_id).copied().unwrap_or(0);
178+
let failure_penalty_msat = self.channel_failures
179+
.get(&short_channel_id)
180+
.map_or(0, |value| value.decayed_penalty_msat(self.params.failure_penalty_half_life));
148181

149182
self.params.base_penalty_msat + failure_penalty_msat
150183
}
151184

152185
fn payment_path_failed(&mut self, _path: &Vec<RouteHop>, short_channel_id: u64) {
153186
let failure_penalty_msat = self.params.failure_penalty_msat;
154-
#[cfg(not(feature = "no-std"))]
155-
{
156-
let half_life = self.params.failure_penalty_half_life;
157-
self.channel_failures
158-
.entry(short_channel_id)
159-
.and_modify(|(penalty_msat, last_failure)| {
160-
let decayed_penalty = decay_from(*penalty_msat, last_failure, half_life);
161-
*penalty_msat = decayed_penalty + failure_penalty_msat;
162-
*last_failure = Instant::now();
163-
})
164-
.or_insert_with(|| (failure_penalty_msat, Instant::now()));
165-
}
166-
#[cfg(feature = "no-std")]
187+
let half_life = self.params.failure_penalty_half_life;
167188
self.channel_failures
168189
.entry(short_channel_id)
169-
.and_modify(|penalty_msat| *penalty_msat += failure_penalty_msat)
170-
.or_insert(failure_penalty_msat);
171-
}
172-
}
173-
174-
#[cfg(not(feature = "no-std"))]
175-
fn decay_from(penalty_msat: u64, last_failure: &Instant, half_life: Duration) -> u64 {
176-
let decays = last_failure.elapsed().as_secs().checked_div(half_life.as_secs());
177-
match decays {
178-
Some(decays) => penalty_msat >> decays,
179-
None => 0,
190+
.and_modify(|failure| failure.add_penalty(failure_penalty_msat, half_life))
191+
.or_insert_with(|| ChannelFailure::new(failure_penalty_msat));
180192
}
181193
}

0 commit comments

Comments
 (0)