Add a scoring decay method to the `ScoreUpdate` trait
authorMatt Corallo <git@bluematt.me>
Mon, 2 Oct 2023 19:14:26 +0000 (19:14 +0000)
committerMatt Corallo <git@bluematt.me>
Wed, 13 Dec 2023 23:26:09 +0000 (23:26 +0000)
Rather than relying on fetching the current time during
routefinding, here we introduce a new trait method to `ScoreUpdate`
to do so. This largely mirrors what we do with the `NetworkGraph`,
and allows us to take on much more expensive operations (floating
point exponentiation) in our decaying.

lightning-background-processor/src/lib.rs
lightning/src/routing/scoring.rs
lightning/src/util/test_utils.rs

index 416f8d7b6d0fdbaa4fdd13e3e664bf56fc7c2351..1d5899682b3f491e8fa00d174554c77e16745dc0 100644 (file)
@@ -294,6 +294,7 @@ macro_rules! define_run_body {
                let mut last_scorer_persist_call = $get_timer(SCORER_PERSIST_TIMER);
                let mut last_rebroadcast_call = $get_timer(REBROADCAST_TIMER);
                let mut have_pruned = false;
+               let mut have_decayed_scorer = false;
 
                loop {
                        $process_channel_manager_events;
@@ -401,9 +402,24 @@ macro_rules! define_run_body {
                                last_prune_call = $get_timer(prune_timer);
                        }
 
+                       if !have_decayed_scorer {
+                               if let Some(ref scorer) = $scorer {
+                                       if let Some(duration_since_epoch) = $time_fetch() {
+                                               log_trace!($logger, "Calling time_passed on scorer at startup");
+                                               scorer.write_lock().time_passed(duration_since_epoch);
+                                       }
+                               }
+                               have_decayed_scorer = true;
+                       }
+
                        if $timer_elapsed(&mut last_scorer_persist_call, SCORER_PERSIST_TIMER) {
                                if let Some(ref scorer) = $scorer {
-                                       log_trace!($logger, "Persisting scorer");
+                                       if let Some(duration_since_epoch) = $time_fetch() {
+                                               log_trace!($logger, "Calling time_passed and persisting scorer");
+                                               scorer.write_lock().time_passed(duration_since_epoch);
+                                       } else {
+                                               log_trace!($logger, "Persisting scorer");
+                                       }
                                        if let Err(e) = $persister.persist_scorer(&scorer) {
                                                log_error!($logger, "Error: Failed to persist scorer, check your disk and permissions {}", e)
                                        }
@@ -1208,6 +1224,7 @@ mod tests {
                                }
                        }
                }
+               fn time_passed(&mut self, _: Duration) {}
        }
 
        #[cfg(c_bindings)]
@@ -1616,7 +1633,7 @@ mod tests {
 
                loop {
                        let log_entries = nodes[0].logger.lines.lock().unwrap();
-                       let expected_log = "Persisting scorer".to_string();
+                       let expected_log = "Calling time_passed and persisting scorer".to_string();
                        if log_entries.get(&("lightning_background_processor", expected_log)).is_some() {
                                break
                        }
index 92ebb979cf737712ce26d2dcd26499a1f38a0ff1..ee6d515bc0eeaf242558d294fbcae6979f6012a8 100644 (file)
@@ -120,6 +120,12 @@ pub trait ScoreUpdate {
 
        /// Handles updating channel penalties after a probe over the given path succeeded.
        fn probe_successful(&mut self, path: &Path, duration_since_epoch: Duration);
+
+       /// Scorers may wish to reduce their certainty of channel liquidity information over time.
+       /// Thus, this method is provided to allow scorers to observe the passage of time - the holder
+       /// of this object should call this method regularly (generally via the
+       /// `lightning-background-processor` crate).
+       fn time_passed(&mut self, duration_since_epoch: Duration);
 }
 
 /// A trait which can both lookup and update routing channel penalty scores.
@@ -160,6 +166,10 @@ impl<S: ScoreUpdate, T: DerefMut<Target=S>> ScoreUpdate for T {
        fn probe_successful(&mut self, path: &Path, duration_since_epoch: Duration) {
                self.deref_mut().probe_successful(path, duration_since_epoch)
        }
+
+       fn time_passed(&mut self, duration_since_epoch: Duration) {
+               self.deref_mut().time_passed(duration_since_epoch)
+       }
 }
 } }
 
@@ -361,6 +371,10 @@ impl<'a, T: Score> ScoreUpdate for MultiThreadedScoreLockWrite<'a, T> {
        fn probe_successful(&mut self, path: &Path, duration_since_epoch: Duration) {
                self.0.probe_successful(path, duration_since_epoch)
        }
+
+       fn time_passed(&mut self, duration_since_epoch: Duration) {
+               self.0.time_passed(duration_since_epoch)
+       }
 }
 
 
@@ -406,6 +420,8 @@ impl ScoreUpdate for FixedPenaltyScorer {
        fn probe_failed(&mut self, _path: &Path, _short_channel_id: u64, _duration_since_epoch: Duration) {}
 
        fn probe_successful(&mut self, _path: &Path, _duration_since_epoch: Duration) {}
+
+       fn time_passed(&mut self, _duration_since_epoch: Duration) {}
 }
 
 impl Writeable for FixedPenaltyScorer {
@@ -1463,6 +1479,8 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ScoreUpdate for Prob
        fn probe_successful(&mut self, path: &Path, duration_since_epoch: Duration) {
                self.payment_path_failed(path, u64::max_value(), duration_since_epoch)
        }
+
+       fn time_passed(&mut self, _duration_since_epoch: Duration) {}
 }
 
 #[cfg(c_bindings)]
index c6561863e090415e61dd5060d0f5d2aacb06ee69..805806dc34645f765ab9b616c283164a273eeb1e 100644 (file)
@@ -1357,6 +1357,8 @@ impl ScoreUpdate for TestScorer {
        fn probe_failed(&mut self, _actual_path: &Path, _: u64, _duration_since_epoch: Duration) {}
 
        fn probe_successful(&mut self, _actual_path: &Path, _duration_since_epoch: Duration) {}
+
+       fn time_passed(&mut self, _duration_since_epoch: Duration) {}
 }
 
 impl Drop for TestScorer {