Skip to content

Commit 434e454

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 434e454

File tree

1 file changed

+51
-43
lines changed

1 file changed

+51
-43
lines changed

lightning/src/routing/scorer.rs

Lines changed: 51 additions & 43 deletions
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,10 +66,8 @@ 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`].
@@ -86,10 +83,21 @@ pub struct ScoringParameters {
8683
pub failure_penalty_msat: u64,
8784

8885
/// The time needed before any accumulated channel failure penalties are cut in half.
89-
#[cfg(not(feature = "no-std"))]
9086
pub failure_penalty_half_life: Duration,
9187
}
9288

89+
/// Accounting for penalties from channel failures.
90+
///
91+
/// Penalties decay over time, though accumulate as more failures occur.
92+
struct ChannelFailure {
93+
/// Accumulated penalty in msats for the channel as of `last_failed`.
94+
undecayed_penalty_msat: u64,
95+
96+
/// Last time the channel failed. Used to decay `undecayed_penalty_msat`.
97+
#[cfg(not(feature = "no-std"))]
98+
last_failed: Instant,
99+
}
100+
93101
impl Scorer {
94102
/// Creates a new scorer using the given scoring parameters.
95103
pub fn new(params: ScoringParameters) -> Self {
@@ -105,14 +113,41 @@ impl Scorer {
105113
Self::new(ScoringParameters {
106114
base_penalty_msat: penalty_msat,
107115
failure_penalty_msat: 0,
108-
#[cfg(not(feature = "no-std"))]
109116
failure_penalty_half_life: Duration::from_secs(0),
110117
})
111118
}
119+
}
120+
121+
impl ChannelFailure {
122+
fn new(failure_penalty_msat: u64) -> Self {
123+
Self {
124+
undecayed_penalty_msat: failure_penalty_msat,
125+
#[cfg(not(feature = "no-std"))]
126+
last_failed: Instant::now(),
127+
}
128+
}
112129

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)
130+
fn add_penalty(&mut self, failure_penalty_msat: u64, half_life: Duration) {
131+
self.undecayed_penalty_msat = self.decayed_penalty_msat(half_life) + failure_penalty_msat;
132+
#[cfg(not(feature = "no-std"))]
133+
{
134+
self.last_failed = Instant::now();
135+
}
136+
}
137+
138+
fn decayed_penalty_msat(&self, half_life: Duration) -> u64 {
139+
let decays = self.elapsed().as_secs().checked_div(half_life.as_secs());
140+
match decays {
141+
Some(decays) => self.undecayed_penalty_msat >> decays,
142+
None => 0,
143+
}
144+
}
145+
146+
fn elapsed(&self) -> Duration {
147+
#[cfg(not(feature = "no-std"))]
148+
return self.last_failed.elapsed();
149+
#[cfg(feature = "no-std")]
150+
return Duration::from_secs(0);
116151
}
117152
}
118153

@@ -127,7 +162,6 @@ impl Default for ScoringParameters {
127162
Self {
128163
base_penalty_msat: 500,
129164
failure_penalty_msat: 1024 * 1000,
130-
#[cfg(not(feature = "no-std"))]
131165
failure_penalty_half_life: Duration::from_secs(3600),
132166
}
133167
}
@@ -137,45 +171,19 @@ impl routing::Score for Scorer {
137171
fn channel_penalty_msat(
138172
&self, short_channel_id: u64, _source: &NodeId, _target: &NodeId
139173
) -> 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);
174+
let failure_penalty_msat = self.channel_failures
175+
.get(&short_channel_id)
176+
.map_or(0, |value| value.decayed_penalty_msat(self.params.failure_penalty_half_life));
148177

149178
self.params.base_penalty_msat + failure_penalty_msat
150179
}
151180

152181
fn payment_path_failed(&mut self, _path: &Vec<RouteHop>, short_channel_id: u64) {
153182
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")]
183+
let half_life = self.params.failure_penalty_half_life;
167184
self.channel_failures
168185
.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,
186+
.and_modify(|failure| failure.add_penalty(failure_penalty_msat, half_life))
187+
.or_insert_with(|| ChannelFailure::new(failure_penalty_msat));
180188
}
181189
}

0 commit comments

Comments
 (0)