Pass the current time through ScoreUpDate methods

In the coming commits, we'll stop relying on fetching the time
during routefetching, preferring to decay score data in the
background instead.

The first step towards this - passing the current time through into
the scorer when updating.
This commit is contained in:
Matt Corallo 2023-10-02 18:33:08 +00:00
parent 6471eb050e
commit 6c366cf35f
4 changed files with 105 additions and 88 deletions

View file

@ -244,30 +244,30 @@ fn handle_network_graph_update<L: Deref>(
/// Updates scorer based on event and returns whether an update occurred so we can decide whether /// Updates scorer based on event and returns whether an update occurred so we can decide whether
/// to persist. /// to persist.
fn update_scorer<'a, S: 'static + Deref<Target = SC> + Send + Sync, SC: 'a + WriteableScore<'a>>( fn update_scorer<'a, S: 'static + Deref<Target = SC> + Send + Sync, SC: 'a + WriteableScore<'a>>(
scorer: &'a S, event: &Event scorer: &'a S, event: &Event, duration_since_epoch: Duration,
) -> bool { ) -> bool {
match event { match event {
Event::PaymentPathFailed { ref path, short_channel_id: Some(scid), .. } => { Event::PaymentPathFailed { ref path, short_channel_id: Some(scid), .. } => {
let mut score = scorer.write_lock(); let mut score = scorer.write_lock();
score.payment_path_failed(path, *scid); score.payment_path_failed(path, *scid, duration_since_epoch);
}, },
Event::PaymentPathFailed { ref path, payment_failed_permanently: true, .. } => { Event::PaymentPathFailed { ref path, payment_failed_permanently: true, .. } => {
// Reached if the destination explicitly failed it back. We treat this as a successful probe // Reached if the destination explicitly failed it back. We treat this as a successful probe
// because the payment made it all the way to the destination with sufficient liquidity. // because the payment made it all the way to the destination with sufficient liquidity.
let mut score = scorer.write_lock(); let mut score = scorer.write_lock();
score.probe_successful(path); score.probe_successful(path, duration_since_epoch);
}, },
Event::PaymentPathSuccessful { path, .. } => { Event::PaymentPathSuccessful { path, .. } => {
let mut score = scorer.write_lock(); let mut score = scorer.write_lock();
score.payment_path_successful(path); score.payment_path_successful(path, duration_since_epoch);
}, },
Event::ProbeSuccessful { path, .. } => { Event::ProbeSuccessful { path, .. } => {
let mut score = scorer.write_lock(); let mut score = scorer.write_lock();
score.probe_successful(path); score.probe_successful(path, duration_since_epoch);
}, },
Event::ProbeFailed { path, short_channel_id: Some(scid), .. } => { Event::ProbeFailed { path, short_channel_id: Some(scid), .. } => {
let mut score = scorer.write_lock(); let mut score = scorer.write_lock();
score.probe_failed(path, *scid); score.probe_failed(path, *scid, duration_since_epoch);
}, },
_ => return false, _ => return false,
} }
@ -280,7 +280,7 @@ macro_rules! define_run_body {
$channel_manager: ident, $process_channel_manager_events: expr, $channel_manager: ident, $process_channel_manager_events: expr,
$peer_manager: ident, $process_onion_message_handler_events: expr, $gossip_sync: ident, $peer_manager: ident, $process_onion_message_handler_events: expr, $gossip_sync: ident,
$logger: ident, $scorer: ident, $loop_exit_check: expr, $await: expr, $get_timer: expr, $logger: ident, $scorer: ident, $loop_exit_check: expr, $await: expr, $get_timer: expr,
$timer_elapsed: expr, $check_slow_await: expr $timer_elapsed: expr, $check_slow_await: expr, $time_fetch: expr,
) => { { ) => { {
log_trace!($logger, "Calling ChannelManager's timer_tick_occurred on startup"); log_trace!($logger, "Calling ChannelManager's timer_tick_occurred on startup");
$channel_manager.timer_tick_occurred(); $channel_manager.timer_tick_occurred();
@ -383,11 +383,10 @@ macro_rules! define_run_body {
if should_prune { if should_prune {
// The network graph must not be pruned while rapid sync completion is pending // The network graph must not be pruned while rapid sync completion is pending
if let Some(network_graph) = $gossip_sync.prunable_network_graph() { if let Some(network_graph) = $gossip_sync.prunable_network_graph() {
#[cfg(feature = "std")] { if let Some(duration_since_epoch) = $time_fetch() {
log_trace!($logger, "Pruning and persisting network graph."); log_trace!($logger, "Pruning and persisting network graph.");
network_graph.remove_stale_channels_and_tracking(); network_graph.remove_stale_channels_and_tracking_with_time(duration_since_epoch.as_secs());
} } else {
#[cfg(not(feature = "std"))] {
log_warn!($logger, "Not pruning network graph, consider enabling `std` or doing so manually with remove_stale_channels_and_tracking_with_time."); log_warn!($logger, "Not pruning network graph, consider enabling `std` or doing so manually with remove_stale_channels_and_tracking_with_time.");
log_trace!($logger, "Persisting network graph."); log_trace!($logger, "Persisting network graph.");
} }
@ -510,12 +509,16 @@ use core::task;
/// are unsure, you should set the flag, as the performance impact of it is minimal unless there /// are unsure, you should set the flag, as the performance impact of it is minimal unless there
/// are hundreds or thousands of simultaneous process calls running. /// are hundreds or thousands of simultaneous process calls running.
/// ///
/// The `fetch_time` parameter should return the current wall clock time, if one is available. If
/// no time is available, some features may be disabled, however the node will still operate fine.
///
/// For example, in order to process background events in a [Tokio](https://tokio.rs/) task, you /// For example, in order to process background events in a [Tokio](https://tokio.rs/) task, you
/// could setup `process_events_async` like this: /// could setup `process_events_async` like this:
/// ``` /// ```
/// # use lightning::io; /// # use lightning::io;
/// # use std::sync::{Arc, RwLock}; /// # use std::sync::{Arc, RwLock};
/// # use std::sync::atomic::{AtomicBool, Ordering}; /// # use std::sync::atomic::{AtomicBool, Ordering};
/// # use std::time::SystemTime;
/// # use lightning_background_processor::{process_events_async, GossipSync}; /// # use lightning_background_processor::{process_events_async, GossipSync};
/// # struct MyStore {} /// # struct MyStore {}
/// # impl lightning::util::persist::KVStore for MyStore { /// # impl lightning::util::persist::KVStore for MyStore {
@ -584,6 +587,7 @@ use core::task;
/// Some(background_scorer), /// Some(background_scorer),
/// sleeper, /// sleeper,
/// mobile_interruptable_platform, /// mobile_interruptable_platform,
/// || Some(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap())
/// ) /// )
/// .await /// .await
/// .expect("Failed to process events"); /// .expect("Failed to process events");
@ -620,11 +624,12 @@ pub async fn process_events_async<
S: 'static + Deref<Target = SC> + Send + Sync, S: 'static + Deref<Target = SC> + Send + Sync,
SC: for<'b> WriteableScore<'b>, SC: for<'b> WriteableScore<'b>,
SleepFuture: core::future::Future<Output = bool> + core::marker::Unpin, SleepFuture: core::future::Future<Output = bool> + core::marker::Unpin,
Sleeper: Fn(Duration) -> SleepFuture Sleeper: Fn(Duration) -> SleepFuture,
FetchTime: Fn() -> Option<Duration>,
>( >(
persister: PS, event_handler: EventHandler, chain_monitor: M, channel_manager: CM, persister: PS, event_handler: EventHandler, chain_monitor: M, channel_manager: CM,
gossip_sync: GossipSync<PGS, RGS, G, UL, L>, peer_manager: PM, logger: L, scorer: Option<S>, gossip_sync: GossipSync<PGS, RGS, G, UL, L>, peer_manager: PM, logger: L, scorer: Option<S>,
sleeper: Sleeper, mobile_interruptable_platform: bool, sleeper: Sleeper, mobile_interruptable_platform: bool, fetch_time: FetchTime,
) -> Result<(), lightning::io::Error> ) -> Result<(), lightning::io::Error>
where where
UL::Target: 'static + UtxoLookup, UL::Target: 'static + UtxoLookup,
@ -648,15 +653,18 @@ where
let scorer = &scorer; let scorer = &scorer;
let logger = &logger; let logger = &logger;
let persister = &persister; let persister = &persister;
let fetch_time = &fetch_time;
async move { async move {
if let Some(network_graph) = network_graph { if let Some(network_graph) = network_graph {
handle_network_graph_update(network_graph, &event) handle_network_graph_update(network_graph, &event)
} }
if let Some(ref scorer) = scorer { if let Some(ref scorer) = scorer {
if update_scorer(scorer, &event) { if let Some(duration_since_epoch) = fetch_time() {
log_trace!(logger, "Persisting scorer after update"); if update_scorer(scorer, &event, duration_since_epoch) {
if let Err(e) = persister.persist_scorer(&scorer) { log_trace!(logger, "Persisting scorer after update");
log_error!(logger, "Error: Failed to persist scorer, check your disk and permissions {}", e) if let Err(e) = persister.persist_scorer(&scorer) {
log_error!(logger, "Error: Failed to persist scorer, check your disk and permissions {}", e)
}
} }
} }
} }
@ -688,7 +696,7 @@ where
task::Poll::Ready(exit) => { should_break = exit; true }, task::Poll::Ready(exit) => { should_break = exit; true },
task::Poll::Pending => false, task::Poll::Pending => false,
} }
}, mobile_interruptable_platform }, mobile_interruptable_platform, fetch_time,
) )
} }
@ -810,7 +818,10 @@ impl BackgroundProcessor {
handle_network_graph_update(network_graph, &event) handle_network_graph_update(network_graph, &event)
} }
if let Some(ref scorer) = scorer { if let Some(ref scorer) = scorer {
if update_scorer(scorer, &event) { use std::time::SystemTime;
let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)
.expect("Time should be sometime after 1970");
if update_scorer(scorer, &event, duration_since_epoch) {
log_trace!(logger, "Persisting scorer after update"); log_trace!(logger, "Persisting scorer after update");
if let Err(e) = persister.persist_scorer(&scorer) { if let Err(e) = persister.persist_scorer(&scorer) {
log_error!(logger, "Error: Failed to persist scorer, check your disk and permissions {}", e) log_error!(logger, "Error: Failed to persist scorer, check your disk and permissions {}", e)
@ -829,7 +840,12 @@ impl BackgroundProcessor {
channel_manager.get_event_or_persistence_needed_future(), channel_manager.get_event_or_persistence_needed_future(),
chain_monitor.get_update_future() chain_monitor.get_update_future()
).wait_timeout(Duration::from_millis(100)); }, ).wait_timeout(Duration::from_millis(100)); },
|_| Instant::now(), |time: &Instant, dur| time.elapsed().as_secs() > dur, false |_| Instant::now(), |time: &Instant, dur| time.elapsed().as_secs() > dur, false,
|| {
use std::time::SystemTime;
Some(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)
.expect("Time should be sometime after 1970"))
},
) )
}); });
Self { stop_thread: stop_thread_clone, thread_handle: Some(handle) } Self { stop_thread: stop_thread_clone, thread_handle: Some(handle) }
@ -1117,7 +1133,7 @@ mod tests {
} }
impl ScoreUpdate for TestScorer { impl ScoreUpdate for TestScorer {
fn payment_path_failed(&mut self, actual_path: &Path, actual_short_channel_id: u64) { fn payment_path_failed(&mut self, actual_path: &Path, actual_short_channel_id: u64, _: Duration) {
if let Some(expectations) = &mut self.event_expectations { if let Some(expectations) = &mut self.event_expectations {
match expectations.pop_front().unwrap() { match expectations.pop_front().unwrap() {
TestResult::PaymentFailure { path, short_channel_id } => { TestResult::PaymentFailure { path, short_channel_id } => {
@ -1137,7 +1153,7 @@ mod tests {
} }
} }
fn payment_path_successful(&mut self, actual_path: &Path) { fn payment_path_successful(&mut self, actual_path: &Path, _: Duration) {
if let Some(expectations) = &mut self.event_expectations { if let Some(expectations) = &mut self.event_expectations {
match expectations.pop_front().unwrap() { match expectations.pop_front().unwrap() {
TestResult::PaymentFailure { path, .. } => { TestResult::PaymentFailure { path, .. } => {
@ -1156,7 +1172,7 @@ mod tests {
} }
} }
fn probe_failed(&mut self, actual_path: &Path, _: u64) { fn probe_failed(&mut self, actual_path: &Path, _: u64, _: Duration) {
if let Some(expectations) = &mut self.event_expectations { if let Some(expectations) = &mut self.event_expectations {
match expectations.pop_front().unwrap() { match expectations.pop_front().unwrap() {
TestResult::PaymentFailure { path, .. } => { TestResult::PaymentFailure { path, .. } => {
@ -1174,7 +1190,7 @@ mod tests {
} }
} }
} }
fn probe_successful(&mut self, actual_path: &Path) { fn probe_successful(&mut self, actual_path: &Path, _: Duration) {
if let Some(expectations) = &mut self.event_expectations { if let Some(expectations) = &mut self.event_expectations {
match expectations.pop_front().unwrap() { match expectations.pop_front().unwrap() {
TestResult::PaymentFailure { path, .. } => { TestResult::PaymentFailure { path, .. } => {
@ -1469,7 +1485,7 @@ mod tests {
tokio::time::sleep(dur).await; tokio::time::sleep(dur).await;
false // Never exit false // Never exit
}) })
}, false, }, false, || Some(Duration::ZERO),
); );
match bp_future.await { match bp_future.await {
Ok(_) => panic!("Expected error persisting manager"), Ok(_) => panic!("Expected error persisting manager"),
@ -1699,7 +1715,7 @@ mod tests {
_ = exit_receiver.changed() => true, _ = exit_receiver.changed() => true,
} }
}) })
}, false, }, false, || Some(Duration::from_secs(1696300000)),
); );
let t1 = tokio::spawn(bp_future); let t1 = tokio::spawn(bp_future);
@ -1874,7 +1890,7 @@ mod tests {
_ = exit_receiver.changed() => true, _ = exit_receiver.changed() => true,
} }
}) })
}, false, }, false, || Some(Duration::ZERO),
); );
let t1 = tokio::spawn(bp_future); let t1 = tokio::spawn(bp_future);
let t2 = tokio::spawn(async move { let t2 = tokio::spawn(async move {

View file

@ -8160,6 +8160,7 @@ mod tests {
pub(crate) mod bench_utils { pub(crate) mod bench_utils {
use super::*; use super::*;
use std::fs::File; use std::fs::File;
use std::time::Duration;
use bitcoin::hashes::Hash; use bitcoin::hashes::Hash;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
@ -8308,10 +8309,10 @@ pub(crate) mod bench_utils {
if let Ok(route) = route_res { if let Ok(route) = route_res {
for path in route.paths { for path in route.paths {
if seed & 0x80 == 0 { if seed & 0x80 == 0 {
scorer.payment_path_successful(&path); scorer.payment_path_successful(&path, Duration::ZERO);
} else { } else {
let short_channel_id = path.hops[path.hops.len() / 2].short_channel_id; let short_channel_id = path.hops[path.hops.len() / 2].short_channel_id;
scorer.payment_path_failed(&path, short_channel_id); scorer.payment_path_failed(&path, short_channel_id, Duration::ZERO);
} }
seed = seed.overflowing_mul(6364136223846793005).0.overflowing_add(1).0; seed = seed.overflowing_mul(6364136223846793005).0.overflowing_add(1).0;
} }

View file

@ -110,16 +110,16 @@ pub trait ScoreLookUp {
/// `ScoreUpdate` is used to update the scorer's internal state after a payment attempt. /// `ScoreUpdate` is used to update the scorer's internal state after a payment attempt.
pub trait ScoreUpdate { pub trait ScoreUpdate {
/// Handles updating channel penalties after failing to route through a channel. /// Handles updating channel penalties after failing to route through a channel.
fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64); fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration);
/// Handles updating channel penalties after successfully routing along a path. /// Handles updating channel penalties after successfully routing along a path.
fn payment_path_successful(&mut self, path: &Path); fn payment_path_successful(&mut self, path: &Path, duration_since_epoch: Duration);
/// Handles updating channel penalties after a probe over the given path failed. /// Handles updating channel penalties after a probe over the given path failed.
fn probe_failed(&mut self, path: &Path, short_channel_id: u64); fn probe_failed(&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration);
/// Handles updating channel penalties after a probe over the given path succeeded. /// Handles updating channel penalties after a probe over the given path succeeded.
fn probe_successful(&mut self, path: &Path); fn probe_successful(&mut self, path: &Path, duration_since_epoch: Duration);
} }
/// A trait which can both lookup and update routing channel penalty scores. /// A trait which can both lookup and update routing channel penalty scores.
@ -145,20 +145,20 @@ impl<S: ScoreLookUp, T: Deref<Target=S>> ScoreLookUp for T {
#[cfg(not(c_bindings))] #[cfg(not(c_bindings))]
impl<S: ScoreUpdate, T: DerefMut<Target=S>> ScoreUpdate for T { impl<S: ScoreUpdate, T: DerefMut<Target=S>> ScoreUpdate for T {
fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64) { fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration) {
self.deref_mut().payment_path_failed(path, short_channel_id) self.deref_mut().payment_path_failed(path, short_channel_id, duration_since_epoch)
} }
fn payment_path_successful(&mut self, path: &Path) { fn payment_path_successful(&mut self, path: &Path, duration_since_epoch: Duration) {
self.deref_mut().payment_path_successful(path) self.deref_mut().payment_path_successful(path, duration_since_epoch)
} }
fn probe_failed(&mut self, path: &Path, short_channel_id: u64) { fn probe_failed(&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration) {
self.deref_mut().probe_failed(path, short_channel_id) self.deref_mut().probe_failed(path, short_channel_id, duration_since_epoch)
} }
fn probe_successful(&mut self, path: &Path) { fn probe_successful(&mut self, path: &Path, duration_since_epoch: Duration) {
self.deref_mut().probe_successful(path) self.deref_mut().probe_successful(path, duration_since_epoch)
} }
} }
} } } }
@ -346,20 +346,20 @@ impl<'a, T: 'a + Score> DerefMut for MultiThreadedScoreLockWrite<'a, T> {
#[cfg(c_bindings)] #[cfg(c_bindings)]
impl<'a, T: Score> ScoreUpdate for MultiThreadedScoreLockWrite<'a, T> { impl<'a, T: Score> ScoreUpdate for MultiThreadedScoreLockWrite<'a, T> {
fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64) { fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration) {
self.0.payment_path_failed(path, short_channel_id) self.0.payment_path_failed(path, short_channel_id, duration_since_epoch)
} }
fn payment_path_successful(&mut self, path: &Path) { fn payment_path_successful(&mut self, path: &Path, duration_since_epoch: Duration) {
self.0.payment_path_successful(path) self.0.payment_path_successful(path, duration_since_epoch)
} }
fn probe_failed(&mut self, path: &Path, short_channel_id: u64) { fn probe_failed(&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration) {
self.0.probe_failed(path, short_channel_id) self.0.probe_failed(path, short_channel_id, duration_since_epoch)
} }
fn probe_successful(&mut self, path: &Path) { fn probe_successful(&mut self, path: &Path, duration_since_epoch: Duration) {
self.0.probe_successful(path) self.0.probe_successful(path, duration_since_epoch)
} }
} }
@ -399,13 +399,13 @@ impl ScoreLookUp for FixedPenaltyScorer {
} }
impl ScoreUpdate for FixedPenaltyScorer { impl ScoreUpdate for FixedPenaltyScorer {
fn payment_path_failed(&mut self, _path: &Path, _short_channel_id: u64) {} fn payment_path_failed(&mut self, _path: &Path, _short_channel_id: u64, _duration_since_epoch: Duration) {}
fn payment_path_successful(&mut self, _path: &Path) {} fn payment_path_successful(&mut self, _path: &Path, _duration_since_epoch: Duration) {}
fn probe_failed(&mut self, _path: &Path, _short_channel_id: u64) {} fn probe_failed(&mut self, _path: &Path, _short_channel_id: u64, _duration_since_epoch: Duration) {}
fn probe_successful(&mut self, _path: &Path) {} fn probe_successful(&mut self, _path: &Path, _duration_since_epoch: Duration) {}
} }
impl Writeable for FixedPenaltyScorer { impl Writeable for FixedPenaltyScorer {
@ -1391,7 +1391,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ScoreLookUp for Prob
} }
impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ScoreUpdate for ProbabilisticScorerUsingTime<G, L, T> where L::Target: Logger { impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ScoreUpdate for ProbabilisticScorerUsingTime<G, L, T> where L::Target: Logger {
fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64) { fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64, _duration_since_epoch: Duration) {
let amount_msat = path.final_value_msat(); let amount_msat = path.final_value_msat();
log_trace!(self.logger, "Scoring path through to SCID {} as having failed at {} msat", short_channel_id, amount_msat); log_trace!(self.logger, "Scoring path through to SCID {} as having failed at {} msat", short_channel_id, amount_msat);
let network_graph = self.network_graph.read_only(); let network_graph = self.network_graph.read_only();
@ -1430,7 +1430,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ScoreUpdate for Prob
} }
} }
fn payment_path_successful(&mut self, path: &Path) { fn payment_path_successful(&mut self, path: &Path, _duration_since_epoch: Duration) {
let amount_msat = path.final_value_msat(); let amount_msat = path.final_value_msat();
log_trace!(self.logger, "Scoring path through SCID {} as having succeeded at {} msat.", log_trace!(self.logger, "Scoring path through SCID {} as having succeeded at {} msat.",
path.hops.split_last().map(|(hop, _)| hop.short_channel_id).unwrap_or(0), amount_msat); path.hops.split_last().map(|(hop, _)| hop.short_channel_id).unwrap_or(0), amount_msat);
@ -1456,12 +1456,12 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ScoreUpdate for Prob
} }
} }
fn probe_failed(&mut self, path: &Path, short_channel_id: u64) { fn probe_failed(&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration) {
self.payment_path_failed(path, short_channel_id) self.payment_path_failed(path, short_channel_id, duration_since_epoch)
} }
fn probe_successful(&mut self, path: &Path) { fn probe_successful(&mut self, path: &Path, duration_since_epoch: Duration) {
self.payment_path_failed(path, u64::max_value()) self.payment_path_failed(path, u64::max_value(), duration_since_epoch)
} }
} }
@ -2661,10 +2661,10 @@ mod tests {
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 301); assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 301);
scorer.payment_path_failed(&failed_path, 41); scorer.payment_path_failed(&failed_path, 41, Duration::ZERO);
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 301); assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 301);
scorer.payment_path_successful(&successful_path); scorer.payment_path_successful(&successful_path, Duration::ZERO);
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 301); assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 301);
} }
@ -2697,7 +2697,7 @@ mod tests {
let usage = ChannelUsage { amount_msat: 750, ..usage }; let usage = ChannelUsage { amount_msat: 750, ..usage };
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 602); assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 602);
scorer.payment_path_failed(&path, 43); scorer.payment_path_failed(&path, 43, Duration::ZERO);
let usage = ChannelUsage { amount_msat: 250, ..usage }; let usage = ChannelUsage { amount_msat: 250, ..usage };
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0); assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
@ -2737,7 +2737,7 @@ mod tests {
let usage = ChannelUsage { amount_msat: 750, ..usage }; let usage = ChannelUsage { amount_msat: 750, ..usage };
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 602); assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 602);
scorer.payment_path_failed(&path, 42); scorer.payment_path_failed(&path, 42, Duration::ZERO);
let usage = ChannelUsage { amount_msat: 250, ..usage }; let usage = ChannelUsage { amount_msat: 250, ..usage };
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300); assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300);
@ -2814,7 +2814,7 @@ mod tests {
}; };
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 128); assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 128);
scorer.payment_path_failed(&Path { hops: path, blinded_tail: None }, 43); scorer.payment_path_failed(&Path { hops: path, blinded_tail: None }, 43, Duration::ZERO);
let channel = network_graph.read_only().channel(42).unwrap().to_owned(); let channel = network_graph.read_only().channel(42).unwrap().to_owned();
let (info, _) = channel.as_directed_from(&node_a).unwrap(); let (info, _) = channel.as_directed_from(&node_a).unwrap();
@ -2877,7 +2877,7 @@ mod tests {
assert_eq!(scorer.channel_penalty_msat(&candidate_42, usage, &params), 128); assert_eq!(scorer.channel_penalty_msat(&candidate_42, usage, &params), 128);
assert_eq!(scorer.channel_penalty_msat(&candidate_43, usage, &params), 128); assert_eq!(scorer.channel_penalty_msat(&candidate_43, usage, &params), 128);
scorer.payment_path_successful(&payment_path_for_amount(500)); scorer.payment_path_successful(&payment_path_for_amount(500), Duration::ZERO);
assert_eq!(scorer.channel_penalty_msat(&candidate_41, usage, &params), 128); assert_eq!(scorer.channel_penalty_msat(&candidate_41, usage, &params), 128);
assert_eq!(scorer.channel_penalty_msat(&candidate_42, usage, &params), 300); assert_eq!(scorer.channel_penalty_msat(&candidate_42, usage, &params), 300);
@ -2915,8 +2915,8 @@ mod tests {
let usage = ChannelUsage { amount_msat: 1_023, ..usage }; let usage = ChannelUsage { amount_msat: 1_023, ..usage };
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 2_000); assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 2_000);
scorer.payment_path_failed(&payment_path_for_amount(768), 42); scorer.payment_path_failed(&payment_path_for_amount(768), 42, Duration::ZERO);
scorer.payment_path_failed(&payment_path_for_amount(128), 43); scorer.payment_path_failed(&payment_path_for_amount(128), 43, Duration::ZERO);
// Initial penalties // Initial penalties
let usage = ChannelUsage { amount_msat: 128, ..usage }; let usage = ChannelUsage { amount_msat: 128, ..usage };
@ -3013,7 +3013,7 @@ mod tests {
}; };
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 125); assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 125);
scorer.payment_path_failed(&payment_path_for_amount(512), 42); scorer.payment_path_failed(&payment_path_for_amount(512), 42, Duration::ZERO);
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 281); assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 281);
// An unchecked right shift 64 bits or more in DirectedChannelLiquidity::decayed_offset_msat // An unchecked right shift 64 bits or more in DirectedChannelLiquidity::decayed_offset_msat
@ -3054,8 +3054,8 @@ mod tests {
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300); assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300);
// More knowledge gives higher confidence (256, 768), meaning a lower penalty. // More knowledge gives higher confidence (256, 768), meaning a lower penalty.
scorer.payment_path_failed(&payment_path_for_amount(768), 42); scorer.payment_path_failed(&payment_path_for_amount(768), 42, Duration::ZERO);
scorer.payment_path_failed(&payment_path_for_amount(256), 43); scorer.payment_path_failed(&payment_path_for_amount(256), 43, Duration::ZERO);
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 281); assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 281);
// Decaying knowledge gives less confidence (128, 896), meaning a higher penalty. // Decaying knowledge gives less confidence (128, 896), meaning a higher penalty.
@ -3064,12 +3064,12 @@ mod tests {
// Reducing the upper bound gives more confidence (128, 832) that the payment amount (512) // Reducing the upper bound gives more confidence (128, 832) that the payment amount (512)
// is closer to the upper bound, meaning a higher penalty. // is closer to the upper bound, meaning a higher penalty.
scorer.payment_path_successful(&payment_path_for_amount(64)); scorer.payment_path_successful(&payment_path_for_amount(64), Duration::from_secs(10));
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 331); assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 331);
// Increasing the lower bound gives more confidence (256, 832) that the payment amount (512) // Increasing the lower bound gives more confidence (256, 832) that the payment amount (512)
// is closer to the lower bound, meaning a lower penalty. // is closer to the lower bound, meaning a lower penalty.
scorer.payment_path_failed(&payment_path_for_amount(256), 43); scorer.payment_path_failed(&payment_path_for_amount(256), 43, Duration::from_secs(10));
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 245); assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 245);
// Further decaying affects the lower bound more than the upper bound (128, 928). // Further decaying affects the lower bound more than the upper bound (128, 928).
@ -3098,7 +3098,7 @@ mod tests {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 }, effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
}; };
scorer.payment_path_failed(&payment_path_for_amount(500), 42); scorer.payment_path_failed(&payment_path_for_amount(500), 42, Duration::ZERO);
let channel = network_graph.read_only().channel(42).unwrap().to_owned(); let channel = network_graph.read_only().channel(42).unwrap().to_owned();
let (info, _) = channel.as_directed_from(&source).unwrap(); let (info, _) = channel.as_directed_from(&source).unwrap();
let candidate = CandidateRouteHop::PublicHop { let candidate = CandidateRouteHop::PublicHop {
@ -3110,7 +3110,7 @@ mod tests {
SinceEpoch::advance(Duration::from_secs(10)); SinceEpoch::advance(Duration::from_secs(10));
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 473); assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 473);
scorer.payment_path_failed(&payment_path_for_amount(250), 43); scorer.payment_path_failed(&payment_path_for_amount(250), 43, Duration::from_secs(10));
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300); assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300);
let mut serialized_scorer = Vec::new(); let mut serialized_scorer = Vec::new();
@ -3143,7 +3143,7 @@ mod tests {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 }, effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
}; };
scorer.payment_path_failed(&payment_path_for_amount(500), 42); scorer.payment_path_failed(&payment_path_for_amount(500), 42, Duration::ZERO);
let channel = network_graph.read_only().channel(42).unwrap().to_owned(); let channel = network_graph.read_only().channel(42).unwrap().to_owned();
let (info, _) = channel.as_directed_from(&source).unwrap(); let (info, _) = channel.as_directed_from(&source).unwrap();
let candidate = CandidateRouteHop::PublicHop { let candidate = CandidateRouteHop::PublicHop {
@ -3162,7 +3162,7 @@ mod tests {
<ProbabilisticScorer>::read(&mut serialized_scorer, (decay_params, &network_graph, &logger)).unwrap(); <ProbabilisticScorer>::read(&mut serialized_scorer, (decay_params, &network_graph, &logger)).unwrap();
assert_eq!(deserialized_scorer.channel_penalty_msat(&candidate, usage, &params), 473); assert_eq!(deserialized_scorer.channel_penalty_msat(&candidate, usage, &params), 473);
scorer.payment_path_failed(&payment_path_for_amount(250), 43); scorer.payment_path_failed(&payment_path_for_amount(250), 43, Duration::from_secs(10));
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300); assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300);
SinceEpoch::advance(Duration::from_secs(10)); SinceEpoch::advance(Duration::from_secs(10));
@ -3437,7 +3437,7 @@ mod tests {
assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 42, &params), assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 42, &params),
None); None);
scorer.payment_path_failed(&payment_path_for_amount(1), 42); scorer.payment_path_failed(&payment_path_for_amount(1), 42, Duration::ZERO);
{ {
let network_graph = network_graph.read_only(); let network_graph = network_graph.read_only();
let channel = network_graph.channel(42).unwrap(); let channel = network_graph.channel(42).unwrap();
@ -3462,7 +3462,7 @@ mod tests {
// Even after we tell the scorer we definitely have enough available liquidity, it will // Even after we tell the scorer we definitely have enough available liquidity, it will
// still remember that there was some failure in the past, and assign a non-0 penalty. // still remember that there was some failure in the past, and assign a non-0 penalty.
scorer.payment_path_failed(&payment_path_for_amount(1000), 43); scorer.payment_path_failed(&payment_path_for_amount(1000), 43, Duration::ZERO);
{ {
let network_graph = network_graph.read_only(); let network_graph = network_graph.read_only();
let channel = network_graph.channel(42).unwrap(); let channel = network_graph.channel(42).unwrap();
@ -3515,7 +3515,7 @@ mod tests {
inflight_htlc_msat: 1024, inflight_htlc_msat: 1024,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_024 }, effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_024 },
}; };
scorer.payment_path_failed(&payment_path_for_amount(1), 42); scorer.payment_path_failed(&payment_path_for_amount(1), 42, Duration::from_secs(10 * 16));
{ {
let network_graph = network_graph.read_only(); let network_graph = network_graph.read_only();
let channel = network_graph.channel(42).unwrap(); let channel = network_graph.channel(42).unwrap();
@ -3547,7 +3547,7 @@ mod tests {
path_hop(source_pubkey(), 42, 1), path_hop(source_pubkey(), 42, 1),
path_hop(sender_pubkey(), 41, 0), path_hop(sender_pubkey(), 41, 0),
]; ];
scorer.payment_path_failed(&Path { hops: path, blinded_tail: None }, 42); scorer.payment_path_failed(&Path { hops: path, blinded_tail: None }, 42, Duration::from_secs(10 * (16 + 60 * 60)));
} }
#[test] #[test]
@ -3646,9 +3646,9 @@ mod tests {
// final value is taken into account. // final value is taken into account.
assert!(scorer.channel_liquidities.get(&42).is_none()); assert!(scorer.channel_liquidities.get(&42).is_none());
scorer.payment_path_failed(&path, 42); scorer.payment_path_failed(&path, 42, Duration::ZERO);
path.blinded_tail.as_mut().unwrap().final_value_msat = 256; path.blinded_tail.as_mut().unwrap().final_value_msat = 256;
scorer.payment_path_failed(&path, 43); scorer.payment_path_failed(&path, 43, Duration::ZERO);
let liquidity = scorer.channel_liquidities.get(&42).unwrap() let liquidity = scorer.channel_liquidities.get(&42).unwrap()
.as_directed(&source, &target, 1_000, decay_params); .as_directed(&source, &target, 1_000, decay_params);
@ -3702,7 +3702,7 @@ mod tests {
None); None);
// Fail to pay once, and then check the buckets and penalty. // Fail to pay once, and then check the buckets and penalty.
scorer.payment_path_failed(&payment_path_for_amount(amount_msat), 42); scorer.payment_path_failed(&payment_path_for_amount(amount_msat), 42, Duration::ZERO);
// The penalty should be the maximum penalty, as the payment we're scoring is now in the // The penalty should be the maximum penalty, as the payment we're scoring is now in the
// same bucket which is the only maximum datapoint. // same bucket which is the only maximum datapoint.
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params),
@ -3726,7 +3726,7 @@ mod tests {
// ...but once we see a failure, we consider the payment to be substantially less likely, // ...but once we see a failure, we consider the payment to be substantially less likely,
// even though not a probability of zero as we still look at the second max bucket which // even though not a probability of zero as we still look at the second max bucket which
// now shows 31. // now shows 31.
scorer.payment_path_failed(&payment_path_for_amount(amount_msat), 42); scorer.payment_path_failed(&payment_path_for_amount(amount_msat), 42, Duration::ZERO);
assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target), assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
Some(([63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], Some(([63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[32, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]))); [32, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])));

View file

@ -1350,13 +1350,13 @@ impl ScoreLookUp for TestScorer {
} }
impl ScoreUpdate for TestScorer { impl ScoreUpdate for TestScorer {
fn payment_path_failed(&mut self, _actual_path: &Path, _actual_short_channel_id: u64) {} fn payment_path_failed(&mut self, _actual_path: &Path, _actual_short_channel_id: u64, _duration_since_epoch: Duration) {}
fn payment_path_successful(&mut self, _actual_path: &Path) {} fn payment_path_successful(&mut self, _actual_path: &Path, _duration_since_epoch: Duration) {}
fn probe_failed(&mut self, _actual_path: &Path, _: u64) {} fn probe_failed(&mut self, _actual_path: &Path, _: u64, _duration_since_epoch: Duration) {}
fn probe_successful(&mut self, _actual_path: &Path) {} fn probe_successful(&mut self, _actual_path: &Path, _duration_since_epoch: Duration) {}
} }
impl Drop for TestScorer { impl Drop for TestScorer {