Skip to content

Commit 9625d82

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 269a85a commit 9625d82

File tree

1 file changed

+50
-43
lines changed

1 file changed

+50
-43
lines changed

lightning/src/routing/scorer.rs

Lines changed: 50 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ use routing::network_graph::NodeId;
5050
use routing::router::RouteHop;
5151

5252
use prelude::*;
53-
#[cfg(not(feature = "no-std"))]
5453
use core::time::Duration;
5554
#[cfg(not(feature = "no-std"))]
5655
use std::time::Instant;
@@ -65,10 +64,7 @@ use std::time::Instant;
6564
/// [module-level documentation]: crate::routing::scorer
6665
pub struct Scorer {
6766
params: ScoringParameters,
68-
#[cfg(not(feature = "no-std"))]
69-
channel_failures: HashMap<u64, (u64, Instant)>,
70-
#[cfg(feature = "no-std")]
71-
channel_failures: HashMap<u64, u64>,
67+
channel_failures: HashMap<u64, ChannelFailure>,
7268
}
7369

7470
/// Parameters for configuring [`Scorer`].
@@ -84,10 +80,21 @@ pub struct ScoringParameters {
8480
pub failure_penalty_msat: u64,
8581

8682
/// The time needed before any accumulated channel failure penalties are cut in half.
87-
#[cfg(not(feature = "no-std"))]
8883
pub failure_penalty_half_life: Duration,
8984
}
9085

86+
/// Accounting for penalties from channel failures.
87+
///
88+
/// Penalties decay over time, though accumulated as more failures occur.
89+
struct ChannelFailure {
90+
/// Accumulated penalty in msats for the channel as of `last_failed`.
91+
undecayed_penalty_msat: u64,
92+
93+
/// Last time the channel failed. Used to decay `undecayed_penalty_msat`.
94+
#[cfg(not(feature = "no-std"))]
95+
last_failed: Instant,
96+
}
97+
9198
impl Scorer {
9299
/// Creates a new scorer using the given scoring parameters.
93100
pub fn new(params: ScoringParameters) -> Self {
@@ -103,14 +110,41 @@ impl Scorer {
103110
Self::new(ScoringParameters {
104111
base_penalty_msat: penalty_msat,
105112
failure_penalty_msat: 0,
106-
#[cfg(not(feature = "no-std"))]
107113
failure_penalty_half_life: Duration::from_secs(0),
108114
})
109115
}
116+
}
117+
118+
impl ChannelFailure {
119+
fn new(failure_penalty_msat: u64) -> Self {
120+
Self {
121+
undecayed_penalty_msat: failure_penalty_msat,
122+
#[cfg(not(feature = "no-std"))]
123+
last_failed: Instant::now(),
124+
}
125+
}
110126

111-
#[cfg(not(feature = "no-std"))]
112-
fn decay_from(&self, penalty_msat: u64, last_failure: &Instant) -> u64 {
113-
decay_from(penalty_msat, last_failure, self.params.failure_penalty_half_life)
127+
fn add_penalty(&mut self, failure_penalty_msat: u64, half_life: Duration) {
128+
self.undecayed_penalty_msat = self.decayed_penalty(half_life) + failure_penalty_msat;
129+
#[cfg(not(feature = "no-std"))]
130+
{
131+
self.last_failed = Instant::now();
132+
}
133+
}
134+
135+
fn decayed_penalty(&self, half_life: Duration) -> u64 {
136+
let decays = self.elapsed().as_secs().checked_div(half_life.as_secs());
137+
match decays {
138+
Some(decays) => self.undecayed_penalty_msat >> decays,
139+
None => 0,
140+
}
141+
}
142+
143+
fn elapsed(&self) -> Duration {
144+
#[cfg(not(feature = "no-std"))]
145+
return self.last_failed.elapsed();
146+
#[cfg(feature = "no-std")]
147+
return Duration::from_secs(0);
114148
}
115149
}
116150

@@ -125,7 +159,6 @@ impl Default for ScoringParameters {
125159
Self {
126160
base_penalty_msat: 500,
127161
failure_penalty_msat: 1024 * 1000,
128-
#[cfg(not(feature = "no-std"))]
129162
failure_penalty_half_life: Duration::from_secs(3600),
130163
}
131164
}
@@ -135,45 +168,19 @@ impl routing::Score for Scorer {
135168
fn channel_penalty_msat(
136169
&self, short_channel_id: u64, _source: &NodeId, _target: &NodeId
137170
) -> u64 {
138-
#[cfg(not(feature = "no-std"))]
139-
let failure_penalty_msat = match self.channel_failures.get(&short_channel_id) {
140-
Some((penalty_msat, last_failure)) => self.decay_from(*penalty_msat, last_failure),
141-
None => 0,
142-
};
143-
#[cfg(feature = "no-std")]
144-
let failure_penalty_msat =
145-
self.channel_failures.get(&short_channel_id).copied().unwrap_or(0);
171+
let failure_penalty_msat = self.channel_failures
172+
.get(&short_channel_id)
173+
.map_or(0,|failure| failure.decayed_penalty(self.params.failure_penalty_half_life));
146174

147175
self.params.base_penalty_msat + failure_penalty_msat
148176
}
149177

150178
fn payment_path_failed(&mut self, _path: &Vec<RouteHop>, short_channel_id: u64) {
151179
let failure_penalty_msat = self.params.failure_penalty_msat;
152-
#[cfg(not(feature = "no-std"))]
153-
{
154-
let half_life = self.params.failure_penalty_half_life;
155-
self.channel_failures
156-
.entry(short_channel_id)
157-
.and_modify(|(penalty_msat, last_failure)| {
158-
let decayed_penalty = decay_from(*penalty_msat, last_failure, half_life);
159-
*penalty_msat = decayed_penalty + failure_penalty_msat;
160-
*last_failure = Instant::now();
161-
})
162-
.or_insert_with(|| (failure_penalty_msat, Instant::now()));
163-
}
164-
#[cfg(feature = "no-std")]
180+
let half_life = self.params.failure_penalty_half_life;
165181
self.channel_failures
166182
.entry(short_channel_id)
167-
.and_modify(|penalty_msat| *penalty_msat += failure_penalty_msat)
168-
.or_insert(failure_penalty_msat);
169-
}
170-
}
171-
172-
#[cfg(not(feature = "no-std"))]
173-
fn decay_from(penalty_msat: u64, last_failure: &Instant, half_life: Duration) -> u64 {
174-
let decays = last_failure.elapsed().as_secs().checked_div(half_life.as_secs());
175-
match decays {
176-
Some(decays) => penalty_msat >> decays,
177-
None => 0,
183+
.and_modify(|failure| failure.add_penalty(failure_penalty_msat, half_life))
184+
.or_insert_with(|| ChannelFailure::new(failure_penalty_msat));
178185
}
179186
}

0 commit comments

Comments
 (0)