X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Frouting%2Fscoring.rs;h=96f90ad4e466e8dbb2f2a46ee3c1d728aa20b78b;hb=1f6b334da9a2ec4af0adaafe19ed5281031c7681;hp=54400624dce63a30ec0e506bfc1c87800a6bac4a;hpb=8e5cf757717398bb8a3dd83dd751c8065ac5aebe;p=rust-lightning diff --git a/lightning/src/routing/scoring.rs b/lightning/src/routing/scoring.rs index 54400624..96f90ad4 100644 --- a/lightning/src/routing/scoring.rs +++ b/lightning/src/routing/scoring.rs @@ -28,7 +28,7 @@ //! # impl Logger for FakeLogger { //! # fn log(&self, record: &Record) { unimplemented!() } //! # } -//! # fn find_scored_route(payer: PublicKey, route_params: RouteParameters, network_graph: NetworkGraph) { +//! # fn find_scored_route(payer: PublicKey, route_params: RouteParameters, network_graph: NetworkGraph<&FakeLogger>) { //! # let logger = FakeLogger {}; //! # //! // Use the default channel penalties. @@ -43,7 +43,7 @@ //! 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); +//! let route = find_route(&payer, &route_params, &network_graph.read_only(), None, &logger, &scorer, &random_seed_bytes); //! # } //! ``` //! @@ -63,7 +63,9 @@ use util::time::Time; use prelude::*; use core::fmt; -use core::cell::{RefCell, RefMut}; +#[cfg(not(c_bindings))] +use core::cell::RefCell; +use core::cell::RefMut; use core::ops::{Deref, DerefMut}; use core::time::Duration; use io::{self, Read}; @@ -146,11 +148,13 @@ pub trait LockableScore<'a> { /// /// 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. +#[cfg(not(c_bindings))] // This doesn't make sense in bindings as all `Score`s are `Writeable`. pub trait WriteableScore<'a>: LockableScore<'a> + Writeable {} +#[cfg(not(c_bindings))] // This doesn't make sense in bindings as all `Score`s are `Writeable`. impl<'a, T> WriteableScore<'a> for T where T: LockableScore<'a> + Writeable {} -/// (C-not exported) +#[cfg(not(c_bindings))] impl<'a, T: 'a + Score> LockableScore<'a> for Mutex { type Locked = MutexGuard<'a, T>; @@ -159,6 +163,7 @@ impl<'a, T: 'a + Score> LockableScore<'a> for Mutex { } } +#[cfg(not(c_bindings))] impl<'a, T: 'a + Score> LockableScore<'a> for RefCell { type Locked = RefMut<'a, T>; @@ -174,14 +179,21 @@ pub struct MultiThreadedLockableScore { } #[cfg(c_bindings)] /// (C-not exported) -impl<'a, T: Score + 'a> LockableScore<'a> for MultiThreadedLockableScore { - type Locked = MutexGuard<'a, T>; +impl<'a, S: Score + 'a> LockableScore<'a> for MultiThreadedLockableScore { + type Locked = MutexGuard<'a, S>; - fn lock(&'a self) -> MutexGuard<'a, T> { + fn lock(&'a self) -> MutexGuard<'a, S> { Mutex::lock(&self.score).unwrap() } } +#[cfg(c_bindings)] +impl Writeable for MultiThreadedLockableScore { + fn write(&self, writer: &mut W) -> Result<(), io::Error> { + S::write(&*self.lock(), writer) + } +} + #[cfg(c_bindings)] impl MultiThreadedLockableScore { /// Creates a new [`MultiThreadedLockableScore`] given an underlying [`Score`]. @@ -260,12 +272,30 @@ impl ReadableArgs for FixedPenaltyScorer { } #[cfg(not(feature = "no-std"))] -type ConfiguredTime = std::time::Instant; -#[cfg(feature = "no-std")] -use util::time::Eternity; +/// [`Score`] implementation using channel success probability distributions. +/// +/// Based on *Optimally Reliable & Cheap Payment Flows on the Lightning Network* by Rene Pickhardt +/// and Stefan Richter [[1]]. Given the uncertainty of channel liquidity balances, probability +/// distributions are defined based on knowledge learned from successful and unsuccessful attempts. +/// Then the negative `log10` of the success probability is used to determine the cost of routing a +/// specific HTLC amount through a channel. +/// +/// Knowledge about channel liquidity balances takes the form of upper and lower bounds on the +/// possible liquidity. Certainty of the bounds is decreased over time using a decay function. See +/// [`ProbabilisticScoringParameters`] for details. +/// +/// Since the scorer aims to learn the current channel liquidity balances, it works best for nodes +/// with high payment volume or that actively probe the [`NetworkGraph`]. Nodes with low payment +/// volume are more likely to experience failed payment paths, which would need to be retried. +/// +/// # Note +/// +/// Mixing the `no-std` feature between serialization and deserialization results in undefined +/// behavior. +/// +/// [1]: https://arxiv.org/abs/2107.05322 +pub type ProbabilisticScorer = ProbabilisticScorerUsingTime::; #[cfg(feature = "no-std")] -type ConfiguredTime = Eternity; - /// [`Score`] implementation using channel success probability distributions. /// /// Based on *Optimally Reliable & Cheap Payment Flows on the Lightning Network* by Rene Pickhardt @@ -288,12 +318,13 @@ type ConfiguredTime = Eternity; /// 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, L: Deref, T: Time> where L::Target: Logger { +pub struct ProbabilisticScorerUsingTime>, L: Deref, T: Time> +where L::Target: Logger { params: ProbabilisticScoringParameters, network_graph: G, logger: L, @@ -389,7 +420,7 @@ struct DirectedChannelLiquidity, T: Time, U: Deref, L: Deref, T: Time> ProbabilisticScorerUsingTime where L::Target: Logger { +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, logger: L) -> Self { @@ -650,7 +681,7 @@ impl, T: Time, U: DerefMut> DirectedChanne } } -impl, L: Deref, T: Time> Score for ProbabilisticScorerUsingTime where L::Target: Logger { +impl>, L: Deref, T: Time> Score for ProbabilisticScorerUsingTime where L::Target: Logger { fn channel_penalty_msat( &self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage ) -> u64 { @@ -1050,7 +1081,7 @@ mod approx { } } -impl, L: Deref, T: Time> Writeable for ProbabilisticScorerUsingTime where L::Target: Logger { +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, { @@ -1060,7 +1091,7 @@ impl, L: Deref, T: Time> Writeable for Probabili } } -impl, L: Deref, T: Time> +impl>, L: Deref, T: Time> ReadableArgs<(ProbabilisticScoringParameters, G, L)> for ProbabilisticScorerUsingTime where L::Target: Logger { #[inline] fn read( @@ -1163,7 +1194,7 @@ mod tests { // `ProbabilisticScorer` tests /// A probabilistic scorer for testing with time that can be manually advanced. - type ProbabilisticScorer<'a> = ProbabilisticScorerUsingTime::<&'a NetworkGraph, &'a TestLogger, SinceEpoch>; + type ProbabilisticScorer<'a> = ProbabilisticScorerUsingTime::<&'a NetworkGraph<&'a TestLogger>, &'a TestLogger, SinceEpoch>; fn sender_privkey() -> SecretKey { SecretKey::from_slice(&[41; 32]).unwrap() @@ -1191,9 +1222,9 @@ mod tests { NodeId::from_pubkey(&recipient_pubkey()) } - fn network_graph() -> NetworkGraph { + fn network_graph(logger: &TestLogger) -> NetworkGraph<&TestLogger> { let genesis_hash = genesis_block(Network::Testnet).header.block_hash(); - let mut network_graph = NetworkGraph::new(genesis_hash); + let mut network_graph = NetworkGraph::new(genesis_hash, logger); add_channel(&mut network_graph, 42, source_privkey(), target_privkey()); add_channel(&mut network_graph, 43, target_privkey(), recipient_privkey()); @@ -1201,7 +1232,7 @@ mod tests { } fn add_channel( - network_graph: &mut NetworkGraph, short_channel_id: u64, node_1_key: SecretKey, + network_graph: &mut NetworkGraph<&TestLogger>, short_channel_id: u64, node_1_key: SecretKey, node_2_key: SecretKey ) { let genesis_hash = genesis_block(Network::Testnet).header.block_hash(); @@ -1228,13 +1259,14 @@ mod tests { }; let chain_source: Option<&::util::test_utils::TestChainSource> = None; network_graph.update_channel_from_announcement( - &signed_announcement, &chain_source, &secp_ctx).unwrap(); + &signed_announcement, &chain_source).unwrap(); update_channel(network_graph, short_channel_id, node_1_key, 0); update_channel(network_graph, short_channel_id, node_2_key, 1); } fn update_channel( - network_graph: &mut NetworkGraph, short_channel_id: u64, node_key: SecretKey, flags: u8 + network_graph: &mut NetworkGraph<&TestLogger>, short_channel_id: u64, node_key: SecretKey, + flags: u8 ) { let genesis_hash = genesis_block(Network::Testnet).header.block_hash(); let secp_ctx = Secp256k1::new(); @@ -1255,7 +1287,7 @@ mod tests { signature: secp_ctx.sign_ecdsa(&msghash, &node_key), contents: unsigned_update, }; - network_graph.update_channel(&signed_update, &secp_ctx).unwrap(); + network_graph.update_channel(&signed_update).unwrap(); } fn payment_path_for_amount(amount_msat: u64) -> Vec { @@ -1291,7 +1323,7 @@ mod tests { fn liquidity_bounds_directed_from_lowest_node_id() { let logger = TestLogger::new(); let last_updated = SinceEpoch::now(); - let network_graph = network_graph(); + let network_graph = network_graph(&logger); let params = ProbabilisticScoringParameters::default(); let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger) .with_channel(42, @@ -1366,7 +1398,7 @@ mod tests { 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 network_graph = network_graph(&logger); let params = ProbabilisticScoringParameters::default(); let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger) .with_channel(42, @@ -1424,7 +1456,7 @@ mod tests { 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 network_graph = network_graph(&logger); let params = ProbabilisticScoringParameters::default(); let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger) .with_channel(42, @@ -1481,7 +1513,7 @@ mod tests { #[test] fn increased_penalty_nearing_liquidity_upper_bound() { let logger = TestLogger::new(); - let network_graph = network_graph(); + let network_graph = network_graph(&logger); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, ..ProbabilisticScoringParameters::zero_penalty() @@ -1527,7 +1559,7 @@ mod tests { fn constant_penalty_outside_liquidity_bounds() { let logger = TestLogger::new(); let last_updated = SinceEpoch::now(); - let network_graph = network_graph(); + let network_graph = network_graph(&logger); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, ..ProbabilisticScoringParameters::zero_penalty() @@ -1556,7 +1588,7 @@ mod tests { #[test] fn does_not_further_penalize_own_channel() { let logger = TestLogger::new(); - let network_graph = network_graph(); + let network_graph = network_graph(&logger); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, ..ProbabilisticScoringParameters::zero_penalty() @@ -1584,7 +1616,7 @@ mod tests { #[test] fn sets_liquidity_lower_bound_on_downstream_failure() { let logger = TestLogger::new(); - let network_graph = network_graph(); + let network_graph = network_graph(&logger); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, ..ProbabilisticScoringParameters::zero_penalty() @@ -1618,7 +1650,7 @@ mod tests { #[test] fn sets_liquidity_upper_bound_on_failure() { let logger = TestLogger::new(); - let network_graph = network_graph(); + let network_graph = network_graph(&logger); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, ..ProbabilisticScoringParameters::zero_penalty() @@ -1652,7 +1684,7 @@ mod tests { #[test] fn reduces_liquidity_upper_bound_along_path_on_success() { let logger = TestLogger::new(); - let network_graph = network_graph(); + let network_graph = network_graph(&logger); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, ..ProbabilisticScoringParameters::zero_penalty() @@ -1683,7 +1715,7 @@ mod tests { #[test] fn decays_liquidity_bounds_over_time() { let logger = TestLogger::new(); - let network_graph = network_graph(); + let network_graph = network_graph(&logger); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, liquidity_offset_half_life: Duration::from_secs(10), @@ -1762,7 +1794,7 @@ mod tests { #[test] fn decays_liquidity_bounds_without_shift_overflow() { let logger = TestLogger::new(); - let network_graph = network_graph(); + let network_graph = network_graph(&logger); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, liquidity_offset_half_life: Duration::from_secs(10), @@ -1793,7 +1825,7 @@ mod tests { #[test] fn restricts_liquidity_bounds_after_decay() { let logger = TestLogger::new(); - let network_graph = network_graph(); + let network_graph = network_graph(&logger); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, liquidity_offset_half_life: Duration::from_secs(10), @@ -1837,7 +1869,7 @@ mod tests { #[test] fn restores_persisted_liquidity_bounds() { let logger = TestLogger::new(); - let network_graph = network_graph(); + let network_graph = network_graph(&logger); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, liquidity_offset_half_life: Duration::from_secs(10), @@ -1873,7 +1905,7 @@ mod tests { #[test] fn decays_persisted_liquidity_bounds() { let logger = TestLogger::new(); - let network_graph = network_graph(); + let network_graph = network_graph(&logger); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, liquidity_offset_half_life: Duration::from_secs(10), @@ -1913,7 +1945,7 @@ mod tests { // 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 network_graph = network_graph(&logger); let params = ProbabilisticScoringParameters::default(); let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let source = source_node_id(); @@ -1970,7 +2002,7 @@ mod tests { #[test] fn adds_base_penalty_to_liquidity_penalty() { let logger = TestLogger::new(); - let network_graph = network_graph(); + let network_graph = network_graph(&logger); let source = source_node_id(); let target = target_node_id(); let usage = ChannelUsage { @@ -1996,7 +2028,7 @@ mod tests { #[test] fn adds_amount_penalty_to_liquidity_penalty() { let logger = TestLogger::new(); - let network_graph = network_graph(); + let network_graph = network_graph(&logger); let source = source_node_id(); let target = target_node_id(); let usage = ChannelUsage { @@ -2025,7 +2057,7 @@ mod tests { #[test] fn calculates_log10_without_overflowing_u64_max_value() { let logger = TestLogger::new(); - let network_graph = network_graph(); + let network_graph = network_graph(&logger); let source = source_node_id(); let target = target_node_id(); let usage = ChannelUsage { @@ -2044,8 +2076,8 @@ mod tests { #[test] fn accounts_for_inflight_htlc_usage() { - let network_graph = network_graph(); let logger = TestLogger::new(); + let network_graph = network_graph(&logger); let params = ProbabilisticScoringParameters::default(); let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let source = source_node_id(); @@ -2064,8 +2096,8 @@ mod tests { #[test] fn removes_uncertainity_when_exact_liquidity_known() { - let network_graph = network_graph(); let logger = TestLogger::new(); + let network_graph = network_graph(&logger); let params = ProbabilisticScoringParameters::default(); let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let source = source_node_id();