X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Frouting%2Fscoring.rs;h=ce1149e0f6460f618f6e5d2befcf6669a33a5edc;hb=5eeb254b8285adf4b741a7bced88956bcff208bc;hp=206cc0a83a5b0635acf3a98b48ba04fd4e0e4893;hpb=7181b53aa47b8c25aca01fda9661508d0ec7be59;p=rust-lightning diff --git a/lightning/src/routing/scoring.rs b/lightning/src/routing/scoring.rs index 206cc0a8..ce1149e0 100644 --- a/lightning/src/routing/scoring.rs +++ b/lightning/src/routing/scoring.rs @@ -33,14 +33,14 @@ //! # //! // Use the default channel penalties. //! let params = ProbabilisticScoringParameters::default(); -//! let scorer = ProbabilisticScorer::new(params, &network_graph); +//! let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); //! //! // Or use custom channel penalties. //! let params = ProbabilisticScoringParameters { //! liquidity_penalty_multiplier_msat: 2 * 1000, //! ..ProbabilisticScoringParameters::default() //! }; -//! let scorer = ProbabilisticScorer::new(params, &network_graph); +//! let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); //! # let random_seed_bytes = [42u8; 32]; //! //! let route = find_route(&payer, &route_params, &network_graph, None, &logger, &scorer, &random_seed_bytes); @@ -58,8 +58,10 @@ use ln::msgs::DecodeError; use routing::network_graph::{NetworkGraph, NodeId}; use routing::router::RouteHop; use util::ser::{Readable, ReadableArgs, Writeable, Writer}; +use util::logger::Logger; use prelude::*; +use core::fmt; use core::cell::{RefCell, RefMut}; use core::ops::{Deref, DerefMut}; use core::time::Duration; @@ -135,6 +137,14 @@ pub trait LockableScore<'a> { fn lock(&'a self) -> Self::Locked; } +/// Refers to a scorer that is accessible under lock and also writeable to disk +/// +/// We need this trait to be able to pass in a scorer to `lightning-background-processor` that will enable us to +/// use the Persister to persist it. +pub trait WriteableScore<'a>: LockableScore<'a> + Writeable {} + +impl<'a, T> WriteableScore<'a> for T where T: LockableScore<'a> + Writeable {} + /// (C-not exported) impl<'a, T: 'a + Score> LockableScore<'a> for Mutex { type Locked = MutexGuard<'a, T>; @@ -503,14 +513,15 @@ impl Readable for ChannelFailure { /// behavior. /// /// [1]: https://arxiv.org/abs/2107.05322 -pub type ProbabilisticScorer = ProbabilisticScorerUsingTime::; +pub type ProbabilisticScorer = ProbabilisticScorerUsingTime::; /// Probabilistic [`Score`] implementation. /// /// (C-not exported) generally all users should use the [`ProbabilisticScorer`] type alias. -pub struct ProbabilisticScorerUsingTime, T: Time> { +pub struct ProbabilisticScorerUsingTime, L: Deref, T: Time> where L::Target: Logger { params: ProbabilisticScoringParameters, network_graph: G, + logger: L, // TODO: Remove entries of closed channels. channel_liquidities: HashMap>, } @@ -603,13 +614,14 @@ struct DirectedChannelLiquidity, T: Time, U: Deref, T: Time> ProbabilisticScorerUsingTime { +impl, L: Deref, T: Time> ProbabilisticScorerUsingTime where L::Target: Logger { /// Creates a new scorer using the given scoring parameters for sending payments from a node /// through a network graph. - pub fn new(params: ProbabilisticScoringParameters, network_graph: G) -> Self { + pub fn new(params: ProbabilisticScoringParameters, network_graph: G, logger: L) -> Self { Self { params, network_graph, + logger, channel_liquidities: HashMap::new(), } } @@ -787,22 +799,29 @@ impl, T: Time, U: Deref> DirectedChannelLiqui impl, T: Time, U: DerefMut> DirectedChannelLiquidity { /// Adjusts the channel liquidity balance bounds when failing to route `amount_msat`. - fn failed_at_channel(&mut self, amount_msat: u64) { + fn failed_at_channel(&mut self, amount_msat: u64, chan_descr: fmt::Arguments, logger: &Log) where Log::Target: Logger { if amount_msat < self.max_liquidity_msat() { + log_debug!(logger, "Setting max liquidity of {} to {}", chan_descr, amount_msat); self.set_max_liquidity_msat(amount_msat); + } else { + log_trace!(logger, "Max liquidity of {} already more than {}", chan_descr, amount_msat); } } /// Adjusts the channel liquidity balance bounds when failing to route `amount_msat` downstream. - fn failed_downstream(&mut self, amount_msat: u64) { + fn failed_downstream(&mut self, amount_msat: u64, chan_descr: fmt::Arguments, logger: &Log) where Log::Target: Logger { if amount_msat > self.min_liquidity_msat() { + log_debug!(logger, "Setting min liquidity of {} to {}", chan_descr, amount_msat); self.set_min_liquidity_msat(amount_msat); + } else { + log_trace!(logger, "Min liquidity of {} already less than {}", chan_descr, amount_msat); } } /// Adjusts the channel liquidity balance bounds when successfully routing `amount_msat`. - fn successful(&mut self, amount_msat: u64) { + fn successful(&mut self, amount_msat: u64, chan_descr: fmt::Arguments, logger: &Log) where Log::Target: Logger { let max_liquidity_msat = self.max_liquidity_msat().checked_sub(amount_msat).unwrap_or(0); + log_debug!(logger, "Subtracting {} from max liquidity of {} (setting it to {})", amount_msat, chan_descr, max_liquidity_msat); self.set_max_liquidity_msat(max_liquidity_msat); } @@ -829,7 +848,7 @@ impl, T: Time, U: DerefMut> DirectedChanne } } -impl, T: Time> Score for ProbabilisticScorerUsingTime { +impl, L: Deref, T: Time> Score for ProbabilisticScorerUsingTime where L::Target: Logger { fn channel_penalty_msat( &self, short_channel_id: u64, amount_msat: u64, capacity_msat: u64, source: &NodeId, target: &NodeId @@ -845,13 +864,18 @@ impl, T: Time> Score for ProbabilisticScorerUsin fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) { let amount_msat = path.split_last().map(|(hop, _)| hop.fee_msat).unwrap_or(0); let liquidity_offset_half_life = self.params.liquidity_offset_half_life; + 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(); - for hop in path { + for (hop_idx, hop) in path.iter().enumerate() { let target = NodeId::from_pubkey(&hop.pubkey); let channel_directed_from_source = network_graph.channels() .get(&hop.short_channel_id) .and_then(|channel| channel.as_directed_to(&target)); + if hop.short_channel_id == short_channel_id && hop_idx == 0 { + log_warn!(self.logger, "Payment failed at the first hop - we do not attempt to learn channel info in such cases as we can directly observe local state.\n\tBecause we know the local state, we should generally not see failures here - this may be an indication that your channel peer on channel {} is broken and you may wish to close the channel.", hop.short_channel_id); + } + // Only score announced channels. if let Some((channel, source)) = channel_directed_from_source { let capacity_msat = channel.effective_capacity().as_msat(); @@ -860,7 +884,7 @@ impl, T: Time> Score for ProbabilisticScorerUsin .entry(hop.short_channel_id) .or_insert_with(ChannelLiquidity::new) .as_directed_mut(source, &target, capacity_msat, liquidity_offset_half_life) - .failed_at_channel(amount_msat); + .failed_at_channel(amount_msat, format_args!("SCID {}, towards {:?}", hop.short_channel_id, target), &self.logger); break; } @@ -868,7 +892,10 @@ impl, T: Time> Score for ProbabilisticScorerUsin .entry(hop.short_channel_id) .or_insert_with(ChannelLiquidity::new) .as_directed_mut(source, &target, capacity_msat, liquidity_offset_half_life) - .failed_downstream(amount_msat); + .failed_downstream(amount_msat, format_args!("SCID {}, towards {:?}", hop.short_channel_id, target), &self.logger); + } else { + log_debug!(self.logger, "Not able to penalize channel with SCID {} as we do not have graph info for it (likely a route-hint last-hop).", + hop.short_channel_id); } } } @@ -876,6 +903,8 @@ impl, T: Time> Score for ProbabilisticScorerUsin fn payment_path_successful(&mut self, path: &[&RouteHop]) { let amount_msat = path.split_last().map(|(hop, _)| hop.fee_msat).unwrap_or(0); let liquidity_offset_half_life = self.params.liquidity_offset_half_life; + log_trace!(self.logger, "Scoring path through SCID {} as having succeeded at {} msat.", + path.split_last().map(|(hop, _)| hop.short_channel_id).unwrap_or(0), amount_msat); let network_graph = self.network_graph.read_only(); for hop in path { let target = NodeId::from_pubkey(&hop.pubkey); @@ -890,7 +919,10 @@ impl, T: Time> Score for ProbabilisticScorerUsin .entry(hop.short_channel_id) .or_insert_with(ChannelLiquidity::new) .as_directed_mut(source, &target, capacity_msat, liquidity_offset_half_life) - .successful(amount_msat); + .successful(amount_msat, format_args!("SCID {}, towards {:?}", hop.short_channel_id, target), &self.logger); + } else { + log_debug!(self.logger, "Not able to learn for channel with SCID {} as we do not have graph info for it (likely a route-hint last-hop).", + hop.short_channel_id); } } } @@ -1206,7 +1238,7 @@ mod approx { } } -impl, T: Time> Writeable for ProbabilisticScorerUsingTime { +impl, L: Deref, T: Time> Writeable for ProbabilisticScorerUsingTime where L::Target: Logger { #[inline] fn write(&self, w: &mut W) -> Result<(), io::Error> { write_tlv_fields!(w, { @@ -1216,13 +1248,13 @@ impl, T: Time> Writeable for ProbabilisticScorer } } -impl, T: Time> -ReadableArgs<(ProbabilisticScoringParameters, G)> for ProbabilisticScorerUsingTime { +impl, L: Deref, T: Time> +ReadableArgs<(ProbabilisticScoringParameters, G, L)> for ProbabilisticScorerUsingTime where L::Target: Logger { #[inline] fn read( - r: &mut R, args: (ProbabilisticScoringParameters, G) + r: &mut R, args: (ProbabilisticScoringParameters, G, L) ) -> Result { - let (params, network_graph) = args; + let (params, network_graph, logger) = args; let mut channel_liquidities = HashMap::new(); read_tlv_fields!(r, { (0, channel_liquidities, required) @@ -1230,6 +1262,7 @@ ReadableArgs<(ProbabilisticScoringParameters, G)> for ProbabilisticScorerUsingTi Ok(Self { params, network_graph, + logger, channel_liquidities, }) } @@ -1351,6 +1384,7 @@ mod tests { use routing::network_graph::{NetworkGraph, NodeId}; use routing::router::RouteHop; use util::ser::{Readable, ReadableArgs, Writeable}; + use util::test_utils::TestLogger; use bitcoin::blockdata::constants::genesis_block; use bitcoin::hashes::Hash; @@ -1695,7 +1729,7 @@ mod tests { // `ProbabilisticScorer` tests /// A probabilistic scorer for testing with time that can be manually advanced. - type ProbabilisticScorer<'a> = ProbabilisticScorerUsingTime::<&'a NetworkGraph, SinceEpoch>; + type ProbabilisticScorer<'a> = ProbabilisticScorerUsingTime::<&'a NetworkGraph, &'a TestLogger, SinceEpoch>; fn sender_privkey() -> SecretKey { SecretKey::from_slice(&[41; 32]).unwrap() @@ -1821,10 +1855,11 @@ mod tests { #[test] fn liquidity_bounds_directed_from_lowest_node_id() { + let logger = TestLogger::new(); let last_updated = SinceEpoch::now(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters::default(); - let mut scorer = ProbabilisticScorer::new(params, &network_graph) + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger) .with_channel(42, ChannelLiquidity { min_liquidity_offset_msat: 700, max_liquidity_offset_msat: 100, last_updated @@ -1895,10 +1930,11 @@ mod tests { #[test] fn resets_liquidity_upper_bound_when_crossed_by_lower_bound() { + let logger = TestLogger::new(); let last_updated = SinceEpoch::now(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters::default(); - let mut scorer = ProbabilisticScorer::new(params, &network_graph) + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger) .with_channel(42, ChannelLiquidity { min_liquidity_offset_msat: 200, max_liquidity_offset_msat: 400, last_updated @@ -1952,10 +1988,11 @@ mod tests { #[test] fn resets_liquidity_lower_bound_when_crossed_by_upper_bound() { + let logger = TestLogger::new(); let last_updated = SinceEpoch::now(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters::default(); - let mut scorer = ProbabilisticScorer::new(params, &network_graph) + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger) .with_channel(42, ChannelLiquidity { min_liquidity_offset_msat: 200, max_liquidity_offset_msat: 400, last_updated @@ -2009,12 +2046,13 @@ mod tests { #[test] fn increased_penalty_nearing_liquidity_upper_bound() { + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, ..ProbabilisticScoringParameters::zero_penalty() }; - let scorer = ProbabilisticScorer::new(params, &network_graph); + let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let source = source_node_id(); let target = target_node_id(); @@ -2034,13 +2072,14 @@ mod tests { #[test] fn constant_penalty_outside_liquidity_bounds() { + let logger = TestLogger::new(); let last_updated = SinceEpoch::now(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, ..ProbabilisticScoringParameters::zero_penalty() }; - let scorer = ProbabilisticScorer::new(params, &network_graph) + let scorer = ProbabilisticScorer::new(params, &network_graph, &logger) .with_channel(42, ChannelLiquidity { min_liquidity_offset_msat: 40, max_liquidity_offset_msat: 40, last_updated @@ -2056,12 +2095,13 @@ mod tests { #[test] fn does_not_further_penalize_own_channel() { + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, ..ProbabilisticScoringParameters::zero_penalty() }; - let mut scorer = ProbabilisticScorer::new(params, &network_graph); + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let sender = sender_node_id(); let source = source_node_id(); let failed_path = payment_path_for_amount(500); @@ -2078,12 +2118,13 @@ mod tests { #[test] fn sets_liquidity_lower_bound_on_downstream_failure() { + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, ..ProbabilisticScoringParameters::zero_penalty() }; - let mut scorer = ProbabilisticScorer::new(params, &network_graph); + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let source = source_node_id(); let target = target_node_id(); let path = payment_path_for_amount(500); @@ -2101,12 +2142,13 @@ mod tests { #[test] fn sets_liquidity_upper_bound_on_failure() { + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, ..ProbabilisticScoringParameters::zero_penalty() }; - let mut scorer = ProbabilisticScorer::new(params, &network_graph); + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let source = source_node_id(); let target = target_node_id(); let path = payment_path_for_amount(500); @@ -2124,12 +2166,13 @@ mod tests { #[test] fn reduces_liquidity_upper_bound_along_path_on_success() { + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, ..ProbabilisticScoringParameters::zero_penalty() }; - let mut scorer = ProbabilisticScorer::new(params, &network_graph); + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let sender = sender_node_id(); let source = source_node_id(); let target = target_node_id(); @@ -2149,13 +2192,14 @@ mod tests { #[test] fn decays_liquidity_bounds_over_time() { + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, liquidity_offset_half_life: Duration::from_secs(10), ..ProbabilisticScoringParameters::zero_penalty() }; - let mut scorer = ProbabilisticScorer::new(params, &network_graph); + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let source = source_node_id(); let target = target_node_id(); @@ -2201,13 +2245,14 @@ mod tests { #[test] fn decays_liquidity_bounds_without_shift_overflow() { + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, liquidity_offset_half_life: Duration::from_secs(10), ..ProbabilisticScoringParameters::zero_penalty() }; - let mut scorer = ProbabilisticScorer::new(params, &network_graph); + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let source = source_node_id(); let target = target_node_id(); assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 125); @@ -2226,13 +2271,14 @@ mod tests { #[test] fn restricts_liquidity_bounds_after_decay() { + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, liquidity_offset_half_life: Duration::from_secs(10), ..ProbabilisticScoringParameters::zero_penalty() }; - let mut scorer = ProbabilisticScorer::new(params, &network_graph); + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let source = source_node_id(); let target = target_node_id(); @@ -2264,13 +2310,14 @@ mod tests { #[test] fn restores_persisted_liquidity_bounds() { + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, liquidity_offset_half_life: Duration::from_secs(10), ..ProbabilisticScoringParameters::zero_penalty() }; - let mut scorer = ProbabilisticScorer::new(params, &network_graph); + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let source = source_node_id(); let target = target_node_id(); @@ -2288,19 +2335,20 @@ mod tests { let mut serialized_scorer = io::Cursor::new(&serialized_scorer); let deserialized_scorer = - ::read(&mut serialized_scorer, (params, &network_graph)).unwrap(); + ::read(&mut serialized_scorer, (params, &network_graph, &logger)).unwrap(); assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 300); } #[test] fn decays_persisted_liquidity_bounds() { + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, liquidity_offset_half_life: Duration::from_secs(10), ..ProbabilisticScoringParameters::zero_penalty() }; - let mut scorer = ProbabilisticScorer::new(params, &network_graph); + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let source = source_node_id(); let target = target_node_id(); @@ -2314,7 +2362,7 @@ mod tests { let mut serialized_scorer = io::Cursor::new(&serialized_scorer); let deserialized_scorer = - ::read(&mut serialized_scorer, (params, &network_graph)).unwrap(); + ::read(&mut serialized_scorer, (params, &network_graph, &logger)).unwrap(); assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 473); scorer.payment_path_failed(&payment_path_for_amount(250).iter().collect::>(), 43); @@ -2328,9 +2376,10 @@ mod tests { fn scores_realistic_payments() { // Shows the scores of "realistic" sends of 100k sats over channels of 1-10m sats (with a // 50k sat reserve). + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters::default(); - let scorer = ProbabilisticScorer::new(params, &network_graph); + let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let source = source_node_id(); let target = target_node_id(); @@ -2349,6 +2398,7 @@ mod tests { #[test] fn adds_base_penalty_to_liquidity_penalty() { + let logger = TestLogger::new(); let network_graph = network_graph(); let source = source_node_id(); let target = target_node_id(); @@ -2357,18 +2407,19 @@ mod tests { liquidity_penalty_multiplier_msat: 1_000, ..ProbabilisticScoringParameters::zero_penalty() }; - let scorer = ProbabilisticScorer::new(params, &network_graph); + let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 58); let params = ProbabilisticScoringParameters { base_penalty_msat: 500, liquidity_penalty_multiplier_msat: 1_000, ..Default::default() }; - let scorer = ProbabilisticScorer::new(params, &network_graph); + let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 558); } #[test] fn adds_amount_penalty_to_liquidity_penalty() { + let logger = TestLogger::new(); let network_graph = network_graph(); let source = source_node_id(); let target = target_node_id(); @@ -2378,7 +2429,7 @@ mod tests { amount_penalty_multiplier_msat: 0, ..ProbabilisticScoringParameters::zero_penalty() }; - let scorer = ProbabilisticScorer::new(params, &network_graph); + let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); assert_eq!(scorer.channel_penalty_msat(42, 512_000, 1_024_000, &source, &target), 300); let params = ProbabilisticScoringParameters { @@ -2386,12 +2437,13 @@ mod tests { amount_penalty_multiplier_msat: 256, ..ProbabilisticScoringParameters::zero_penalty() }; - let scorer = ProbabilisticScorer::new(params, &network_graph); + let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); assert_eq!(scorer.channel_penalty_msat(42, 512_000, 1_024_000, &source, &target), 337); } #[test] fn calculates_log10_without_overflowing_u64_max_value() { + let logger = TestLogger::new(); let network_graph = network_graph(); let source = source_node_id(); let target = target_node_id(); @@ -2400,7 +2452,7 @@ mod tests { liquidity_penalty_multiplier_msat: 40_000, ..ProbabilisticScoringParameters::zero_penalty() }; - let scorer = ProbabilisticScorer::new(params, &network_graph); + let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); assert_eq!( scorer.channel_penalty_msat(42, u64::max_value(), u64::max_value(), &source, &target), 80_000,