Merge pull request #1349 from TheBlueMatt/2022-03-listen-send-sync
authorJeffrey Czyz <jkczyz@gmail.com>
Wed, 9 Mar 2022 22:41:40 +0000 (16:41 -0600)
committerGitHub <noreply@github.com>
Wed, 9 Mar 2022 22:41:40 +0000 (16:41 -0600)
Require `chain::Listen` impls in block sync be `Send + Sync`

22 files changed:
.github/workflows/build.yml
.gitignore
CONTRIBUTING.md
Cargo.toml
fuzz/src/full_stack.rs
fuzz/src/router.rs
lightning-background-processor/src/lib.rs
lightning-invoice/src/utils.rs
lightning/Cargo.toml
lightning/src/ln/channelmanager.rs
lightning/src/ln/functional_test_utils.rs
lightning/src/ln/functional_tests.rs
lightning/src/ln/onion_route_tests.rs
lightning/src/ln/payment_tests.rs
lightning/src/ln/peer_handler.rs
lightning/src/ln/shutdown_tests.rs
lightning/src/routing/network_graph.rs
lightning/src/routing/router.rs
lightning/src/routing/scoring.rs
lightning/src/util/test_utils.rs
no-std-check/Cargo.toml [new file with mode: 0644]
no-std-check/src/lib.rs [new file with mode: 0644]

index 964a752056d301ccc9c2275ebc5b5091d2d40cf5..d25e9a43c19ab4682e4ab75105c372d3ab35b9df 100644 (file)
@@ -10,10 +10,8 @@ jobs:
         platform: [ ubuntu-latest ]
         toolchain: [ stable,
                      beta,
-                     # 1.36.0 is MSRV for Rust-Lightning, lightning-invoice, and lightning-persister
-                     1.36.0,
-                     # 1.41.0 is Debian stable
-                     1.41.0,
+                     # 1.41.1 is MSRV for Rust-Lightning, lightning-invoice, and lightning-persister
+                     1.41.1,
                      # 1.45.2 is MSRV for lightning-net-tokio, lightning-block-sync, and coverage generation
                      1.45.2,
                      # 1.47.0 will be the MSRV for no-std builds using hashbrown once core2 is updated
@@ -41,11 +39,9 @@ jobs:
           - toolchain: beta
             build-net-tokio: true
             build-no-std: true
-          - toolchain: 1.36.0
+          - toolchain: 1.41.1
             build-no-std: false
             test-log-variants: true
-          - toolchain: 1.41.0
-            build-no-std: false
           - toolchain: 1.45.2
             build-net-old-tokio: true
             build-net-tokio: true
@@ -56,7 +52,7 @@ jobs:
     runs-on: ${{ matrix.platform }}
     steps:
       - name: Checkout source code
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Install Rust ${{ matrix.toolchain }} toolchain
         uses: actions-rs/toolchain@v1
         with:
@@ -126,6 +122,10 @@ jobs:
           cargo test --verbose --color always --no-default-features --features no-std
           # check if there is a conflict between no-std and the default std feature
           cargo test --verbose --color always --features no-std
+          # check no-std compatibility across dependencies
+          cd ..
+          cd no-std-check
+          cargo check --verbose --color always
           cd ..
       - name: Test on no-std builds Rust ${{ matrix.toolchain }} and full code-linking for coverage generation
         if: "matrix.build-no-std && matrix.coverage"
@@ -202,7 +202,7 @@ jobs:
       TOOLCHAIN: nightly
     steps:
       - name: Checkout source code
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Install Rust ${{ env.TOOLCHAIN }} toolchain
         uses: actions-rs/toolchain@v1
         with:
@@ -241,7 +241,7 @@ jobs:
       TOOLCHAIN: 1.52.1
     steps:
       - name: Checkout source code
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
         with:
           fetch-depth: 0
       - name: Install Rust ${{ env.TOOLCHAIN }} toolchain
@@ -266,7 +266,7 @@ jobs:
       TOOLCHAIN: stable
     steps:
       - name: Checkout source code
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Install Rust 1.58 toolchain
         uses: actions-rs/toolchain@v1
         with:
@@ -290,7 +290,7 @@ jobs:
       TOOLCHAIN: 1.47.0
     steps:
       - name: Checkout source code
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Install Rust ${{ env.TOOLCHAIN }} toolchain
         uses: actions-rs/toolchain@v1
         with:
index a108267c2fd1d725aa0e6d8bb1b8f0371b7eea51..10e905af5145fbae9e0c61afa82f3beeeeb9d0c9 100644 (file)
@@ -8,3 +8,4 @@ lightning-c-bindings/a.out
 Cargo.lock
 .idea
 lightning/target
+no-std-check/target
index 2c1fb0d8a7376db6a0b7624fa8d0c0583423c64c..ab6ba2a410fb245c13fc1c7a6157a36448e9d07f 100644 (file)
@@ -75,7 +75,7 @@ be covered by functional tests.
 When refactoring, structure your PR to make it easy to review and don't
 hestitate to split it into multiple small, focused PRs.
 
-The Minimal Supported Rust Version is 1.36.0 (enforced by our GitHub Actions).
+The Minimum Supported Rust Version is 1.41.1 (enforced by our GitHub Actions).
 
 Commits should cover both the issue fixed and the solution's rationale.
 These [guidelines](https://chris.beams.io/posts/git-commit/) should be kept in mind.
index df32ac5d9cf047ea597a75a0736de6f0a3537ce5..6e03fc1ac4cfc4bab1feed65368651bc5135cb9e 100644 (file)
@@ -9,6 +9,10 @@ members = [
     "lightning-background-processor",
 ]
 
+exclude = [
+    "no-std-check",
+]
+
 # Our tests do actual crypo and lots of work, the tradeoff for -O1 is well worth it.
 # Ideally we would only do this in profile.test, but profile.test only applies to
 # the test binary, not dependencies, which means most of the critical code still
index 6800a3fe1798a8a328e139cfbde733fbf3c52e5a..a4e1a08648f3aac50413659f0a2bcea7985ca006 100644 (file)
@@ -459,7 +459,8 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
                                        final_value_msat,
                                        final_cltv_expiry_delta: 42,
                                };
-                               let route = match find_route(&our_id, &params, &network_graph, None, Arc::clone(&logger), &scorer) {
+                               let random_seed_bytes: [u8; 32] = keys_manager.get_secure_random_bytes();
+                               let route = match find_route(&our_id, &params, &network_graph, None, Arc::clone(&logger), &scorer, &random_seed_bytes) {
                                        Ok(route) => route,
                                        Err(_) => return,
                                };
@@ -482,7 +483,8 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
                                        final_value_msat,
                                        final_cltv_expiry_delta: 42,
                                };
-                               let mut route = match find_route(&our_id, &params, &network_graph, None, Arc::clone(&logger), &scorer) {
+                               let random_seed_bytes: [u8; 32] = keys_manager.get_secure_random_bytes();
+                               let mut route = match find_route(&our_id, &params, &network_graph, None, Arc::clone(&logger), &scorer, &random_seed_bytes) {
                                        Ok(route) => route,
                                        Err(_) => return,
                                };
index 095059c5183676232c48b36093a244a9fe69ba3b..bb33918b44bb1f1e91da9f3b2c6c44dcb6f95344 100644 (file)
@@ -250,6 +250,7 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
                                        }
                                }
                                let scorer = FixedPenaltyScorer::with_penalty(0);
+                               let random_seed_bytes: [u8; 32] = [get_slice!(1)[0]; 32];
                                for target in node_pks.iter() {
                                        let route_params = RouteParameters {
                                                payment_params: PaymentParameters::from_node_id(*target).with_route_hints(last_hops.clone()),
@@ -258,7 +259,7 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
                                        };
                                        let _ = find_route(&our_pubkey, &route_params, &net_graph,
                                                first_hops.map(|c| c.iter().collect::<Vec<_>>()).as_ref().map(|a| a.as_slice()),
-                                               Arc::clone(&logger), &scorer);
+                                               Arc::clone(&logger), &scorer, &random_seed_bytes);
                                }
                        },
                }
index f2058b108ce4c32c677787173ba107c76d2f3728..084ea62efb6faf51f0ff67b0b5fb01fc61f965ef 100644 (file)
@@ -669,13 +669,15 @@ mod tests {
 
        #[test]
        fn test_invoice_payer() {
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
                let nodes = create_nodes(2, "test_invoice_payer".to_string());
 
                // Initiate the background processors to watch each node.
                let data_dir = nodes[0].persister.get_data_dir();
                let persister = move |node: &ChannelManager<InMemorySigner, Arc<ChainMonitor>, Arc<test_utils::TestBroadcaster>, Arc<KeysManager>, Arc<test_utils::TestFeeEstimator>, Arc<test_utils::TestLogger>>| FilesystemPersister::persist_manager(data_dir.clone(), node);
-               let router = DefaultRouter::new(Arc::clone(&nodes[0].network_graph), Arc::clone(&nodes[0].logger));
                let scorer = Arc::new(Mutex::new(test_utils::TestScorer::with_penalty(0)));
+               let router = DefaultRouter::new(Arc::clone(&nodes[0].network_graph), Arc::clone(&nodes[0].logger), random_seed_bytes);
                let invoice_payer = Arc::new(InvoicePayer::new(Arc::clone(&nodes[0].node), router, scorer, Arc::clone(&nodes[0].logger), |_: &_| {}, RetryAttempts(2)));
                let event_handler = Arc::clone(&invoice_payer);
                let bg_processor = BackgroundProcessor::start(persister, event_handler, nodes[0].chain_monitor.clone(), nodes[0].node.clone(), nodes[0].net_graph_msg_handler.clone(), nodes[0].peer_manager.clone(), nodes[0].logger.clone());
index fc82541a564e8cbd82c64f75fd37d4fa87ecd8af..c2bd4b49c6b2091e3c381a8a63017c3741fc9774 100644 (file)
@@ -4,13 +4,15 @@ use {CreationError, Currency, DEFAULT_EXPIRY_TIME, Invoice, InvoiceBuilder, Sign
 use payment::{Payer, Router};
 
 use bech32::ToBase32;
-use bitcoin_hashes::Hash;
+use bitcoin_hashes::{Hash, sha256};
 use crate::prelude::*;
 use lightning::chain;
 use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
 use lightning::chain::keysinterface::{Recipient, KeysInterface, Sign};
 use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
-use lightning::ln::channelmanager::{ChannelDetails, ChannelManager, PaymentId, PaymentSendFailure, PhantomRouteHints, MIN_FINAL_CLTV_EXPIRY, MIN_CLTV_EXPIRY_DELTA};
+use lightning::ln::channelmanager::{ChannelDetails, ChannelManager, PaymentId, PaymentSendFailure, MIN_FINAL_CLTV_EXPIRY};
+#[cfg(feature = "std")]
+use lightning::ln::channelmanager::{PhantomRouteHints, MIN_CLTV_EXPIRY_DELTA};
 use lightning::ln::msgs::LightningError;
 use lightning::routing::scoring::Score;
 use lightning::routing::network_graph::{NetworkGraph, RoutingFees};
@@ -20,6 +22,7 @@ use secp256k1::key::PublicKey;
 use core::convert::TryInto;
 use core::ops::Deref;
 use core::time::Duration;
+use sync::Mutex;
 
 #[cfg(feature = "std")]
 /// Utility to create an invoice that can be paid to one of multiple nodes, or a "phantom invoice."
@@ -222,12 +225,15 @@ where
 pub struct DefaultRouter<G: Deref<Target = NetworkGraph>, L: Deref> where L::Target: Logger {
        network_graph: G,
        logger: L,
+       random_seed_bytes: Mutex<[u8; 32]>,
 }
 
 impl<G: Deref<Target = NetworkGraph>, L: Deref> DefaultRouter<G, L> where L::Target: Logger {
-       /// Creates a new router using the given [`NetworkGraph`] and  [`Logger`].
-       pub fn new(network_graph: G, logger: L) -> Self {
-               Self { network_graph, logger }
+       /// Creates a new router using the given [`NetworkGraph`], a [`Logger`], and a randomness source
+       /// `random_seed_bytes`.
+       pub fn new(network_graph: G, logger: L, random_seed_bytes: [u8; 32]) -> Self {
+               let random_seed_bytes = Mutex::new(random_seed_bytes);
+               Self { network_graph, logger, random_seed_bytes }
        }
 }
 
@@ -237,7 +243,12 @@ where L::Target: Logger {
                &self, payer: &PublicKey, params: &RouteParameters, _payment_hash: &PaymentHash,
                first_hops: Option<&[&ChannelDetails]>, scorer: &S
        ) -> Result<Route, LightningError> {
-               find_route(payer, params, &*self.network_graph, first_hops, &*self.logger, scorer)
+               let random_seed_bytes = {
+                       let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
+                       *locked_random_seed_bytes = sha256::Hash::hash(&*locked_random_seed_bytes).into_inner();
+                       *locked_random_seed_bytes
+               };
+               find_route(payer, params, &*self.network_graph, first_hops, &*self.logger, scorer, &random_seed_bytes)
        }
 }
 
@@ -297,6 +308,7 @@ mod test {
        use lightning::util::enforcing_trait_impls::EnforcingSigner;
        use lightning::util::events::{MessageSendEvent, MessageSendEventsProvider, Event};
        use lightning::util::test_utils;
+       use lightning::chain::keysinterface::KeysInterface;
        use utils::create_invoice_from_channelmanager_and_duration_since_epoch;
 
        #[test]
@@ -325,9 +337,10 @@ mod test {
                let network_graph = node_cfgs[0].network_graph;
                let logger = test_utils::TestLogger::new();
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
                let route = find_route(
                        &nodes[0].node.get_our_node_id(), &route_params, network_graph,
-                       Some(&first_hops.iter().collect::<Vec<_>>()), &logger, &scorer,
+                       Some(&first_hops.iter().collect::<Vec<_>>()), &logger, &scorer, &random_seed_bytes
                ).unwrap();
 
                let payment_event = {
@@ -413,9 +426,10 @@ mod test {
                let network_graph = node_cfgs[0].network_graph;
                let logger = test_utils::TestLogger::new();
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
                let route = find_route(
                        &nodes[0].node.get_our_node_id(), &params, network_graph,
-                       Some(&first_hops.iter().collect::<Vec<_>>()), &logger, &scorer,
+                       Some(&first_hops.iter().collect::<Vec<_>>()), &logger, &scorer, &random_seed_bytes
                ).unwrap();
                let (payment_event, fwd_idx) = {
                        let mut payment_hash = PaymentHash([0; 32]);
index ae4f48fb4eeb65d90f9dccab9cbc9a66cb7d1358..f38bb80511189ccaf92cce9bbb4a734949ff0e3b 100644 (file)
@@ -41,14 +41,14 @@ secp256k1 = { version = "0.20.2", default-features = false, features = ["alloc"]
 
 hashbrown = { version = "0.11", optional = true }
 hex = { version = "0.4", optional = true }
-regex = { version = "0.1.80", optional = true }
+regex = { version = "0.2.11", optional = true }
 backtrace = { version = "0.3", optional = true }
 
 core2 = { version = "0.3.0", optional = true, default-features = false }
 
 [dev-dependencies]
 hex = "0.4"
-regex = "0.1.80"
+regex = "0.2.11"
 # TODO remove this once rust-bitcoin PR #637 is released
 secp256k1 = { version = "0.20.2", default-features = false, features = ["alloc"] }
 
index 674977faaebc2ecd519682ea8f5edb17589fc81b..e9ee8f6ab3dd6e4a355275e080216fb954db359e 100644 (file)
@@ -1322,6 +1322,7 @@ pub enum PaymentSendFailure {
 /// Route hints used in constructing invoices for [phantom node payents].
 ///
 /// [phantom node payments]: crate::chain::keysinterface::PhantomKeysManager
+#[derive(Clone)]
 pub struct PhantomRouteHints {
        /// The list of channels to be included in the invoice route hints.
        pub channels: Vec<ChannelDetails>,
@@ -6843,6 +6844,7 @@ mod tests {
        use util::errors::APIError;
        use util::events::{Event, MessageSendEvent, MessageSendEventsProvider};
        use util::test_utils;
+       use chain::keysinterface::KeysInterface;
 
        #[cfg(feature = "std")]
        #[test]
@@ -7096,6 +7098,7 @@ mod tests {
                let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
                create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
 
                // To start (1), send a regular payment but don't claim it.
                let expected_route = [&nodes[1]];
@@ -7109,7 +7112,7 @@ mod tests {
                };
                let route = find_route(
                        &nodes[0].node.get_our_node_id(), &route_params, nodes[0].network_graph, None,
-                       nodes[0].logger, &scorer
+                       nodes[0].logger, &scorer, &random_seed_bytes
                ).unwrap();
                nodes[0].node.send_spontaneous_payment(&route, Some(payment_preimage)).unwrap();
                check_added_monitors!(nodes[0], 1);
@@ -7140,7 +7143,7 @@ mod tests {
                let payment_preimage = PaymentPreimage([42; 32]);
                let route = find_route(
                        &nodes[0].node.get_our_node_id(), &route_params, nodes[0].network_graph, None,
-                       nodes[0].logger, &scorer
+                       nodes[0].logger, &scorer, &random_seed_bytes
                ).unwrap();
                let (payment_hash, _) = nodes[0].node.send_spontaneous_payment(&route, Some(payment_preimage)).unwrap();
                check_added_monitors!(nodes[0], 1);
@@ -7201,9 +7204,10 @@ mod tests {
                let network_graph = nodes[0].network_graph;
                let first_hops = nodes[0].node.list_usable_channels();
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
                let route = find_route(
                        &payer_pubkey, &route_params, network_graph, Some(&first_hops.iter().collect::<Vec<_>>()),
-                       nodes[0].logger, &scorer
+                       nodes[0].logger, &scorer, &random_seed_bytes
                ).unwrap();
 
                let test_preimage = PaymentPreimage([42; 32]);
@@ -7244,9 +7248,10 @@ mod tests {
                let network_graph = nodes[0].network_graph;
                let first_hops = nodes[0].node.list_usable_channels();
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
                let route = find_route(
                        &payer_pubkey, &route_params, network_graph, Some(&first_hops.iter().collect::<Vec<_>>()),
-                       nodes[0].logger, &scorer
+                       nodes[0].logger, &scorer, &random_seed_bytes
                ).unwrap();
 
                let test_preimage = PaymentPreimage([42; 32]);
@@ -7330,7 +7335,7 @@ mod tests {
 pub mod bench {
        use chain::Listen;
        use chain::chainmonitor::{ChainMonitor, Persist};
-       use chain::keysinterface::{KeysManager, InMemorySigner};
+       use chain::keysinterface::{KeysManager, KeysInterface, InMemorySigner};
        use ln::channelmanager::{BestBlock, ChainParameters, ChannelManager, PaymentHash, PaymentPreimage};
        use ln::features::{InitFeatures, InvoiceFeatures};
        use ln::functional_test_utils::*;
@@ -7339,7 +7344,7 @@ pub mod bench {
        use routing::router::{PaymentParameters, get_route};
        use util::test_utils;
        use util::config::UserConfig;
-       use util::events::{Event, MessageSendEvent, MessageSendEventsProvider, PaymentPurpose};
+       use util::events::{Event, MessageSendEvent, MessageSendEventsProvider};
 
        use bitcoin::hashes::Hash;
        use bitcoin::hashes::sha256::Hash as Sha256;
@@ -7447,8 +7452,11 @@ pub mod bench {
                                let payment_params = PaymentParameters::from_node_id($node_b.get_our_node_id())
                                        .with_features(InvoiceFeatures::known());
                                let scorer = test_utils::TestScorer::with_penalty(0);
-                               let route = get_route(&$node_a.get_our_node_id(), &payment_params, &dummy_graph,
-                                       Some(&usable_channels.iter().map(|r| r).collect::<Vec<_>>()), 10_000, TEST_FINAL_CLTV, &logger_a, &scorer).unwrap();
+                               let seed = [3u8; 32];
+                               let keys_manager = KeysManager::new(&seed, 42, 42);
+                               let random_seed_bytes = keys_manager.get_secure_random_bytes();
+                               let route = get_route(&$node_a.get_our_node_id(), &payment_params, &dummy_graph.read_only(),
+                                       Some(&usable_channels.iter().map(|r| r).collect::<Vec<_>>()), 10_000, TEST_FINAL_CLTV, &logger_a, &scorer, &random_seed_bytes).unwrap();
 
                                let mut payment_preimage = PaymentPreimage([0; 32]);
                                payment_preimage.0[0..8].copy_from_slice(&payment_count.to_le_bytes());
index a908cd416587d8b872e821d315dcd00093a5a4e8..6efd175c3a99a4defc7e6b0cc8050216eeffc312 100644 (file)
@@ -10,7 +10,7 @@
 //! A bunch of useful utilities for building networks of nodes and exchanging messages between
 //! nodes for functional tests.
 
-use chain::{BestBlock, Confirm, Listen, Watch};
+use chain::{BestBlock, Confirm, Listen, Watch, keysinterface::KeysInterface};
 use chain::channelmonitor::ChannelMonitor;
 use chain::transaction::OutPoint;
 use ln::{PaymentPreimage, PaymentHash, PaymentSecret};
@@ -1094,15 +1094,18 @@ macro_rules! get_route_and_payment_hash {
                $crate::get_route_and_payment_hash!($send_node, $recv_node, vec![], $recv_value, TEST_FINAL_CLTV)
        }};
        ($send_node: expr, $recv_node: expr, $last_hops: expr, $recv_value: expr, $cltv: expr) => {{
+               use $crate::chain::keysinterface::KeysInterface;
                let (payment_preimage, payment_hash, payment_secret) = $crate::get_payment_preimage_hash!($recv_node, Some($recv_value));
                let payment_params = $crate::routing::router::PaymentParameters::from_node_id($recv_node.node.get_our_node_id())
                        .with_features($crate::ln::features::InvoiceFeatures::known())
                        .with_route_hints($last_hops);
                let scorer = $crate::util::test_utils::TestScorer::with_penalty(0);
+               let keys_manager = $crate::util::test_utils::TestKeysInterface::new(&[0u8; 32], bitcoin::network::constants::Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
                let route = $crate::routing::router::get_route(
-                       &$send_node.node.get_our_node_id(), &payment_params, $send_node.network_graph,
+                       &$send_node.node.get_our_node_id(), &payment_params, &$send_node.network_graph.read_only(),
                        Some(&$send_node.node.list_usable_channels().iter().collect::<Vec<_>>()),
-                       $recv_value, $cltv, $send_node.logger, &scorer
+                       $recv_value, $cltv, $send_node.logger, &scorer, &random_seed_bytes
                ).unwrap();
                (route, payment_hash, payment_preimage, payment_secret)
        }}
@@ -1557,11 +1560,15 @@ pub const TEST_FINAL_CLTV: u32 = 70;
 pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash, PaymentSecret) {
        let payment_params = PaymentParameters::from_node_id(expected_route.last().unwrap().node.get_our_node_id())
                .with_features(InvoiceFeatures::known());
+       let network_graph = origin_node.network_graph.read_only();
        let scorer = test_utils::TestScorer::with_penalty(0);
+       let seed = [0u8; 32];
+       let keys_manager = test_utils::TestKeysInterface::new(&seed, Network::Testnet);
+       let random_seed_bytes = keys_manager.get_secure_random_bytes();
        let route = get_route(
-               &origin_node.node.get_our_node_id(), &payment_params, &origin_node.network_graph,
+               &origin_node.node.get_our_node_id(), &payment_params, &network_graph,
                Some(&origin_node.node.list_usable_channels().iter().collect::<Vec<_>>()),
-               recv_value, TEST_FINAL_CLTV, origin_node.logger, &scorer).unwrap();
+               recv_value, TEST_FINAL_CLTV, origin_node.logger, &scorer, &random_seed_bytes).unwrap();
        assert_eq!(route.paths.len(), 1);
        assert_eq!(route.paths[0].len(), expected_route.len());
        for (node, hop) in expected_route.iter().zip(route.paths[0].iter()) {
@@ -1575,10 +1582,14 @@ pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route:
 pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64)  {
        let payment_params = PaymentParameters::from_node_id(expected_route.last().unwrap().node.get_our_node_id())
                .with_features(InvoiceFeatures::known());
+       let network_graph = origin_node.network_graph.read_only();
        let scorer = test_utils::TestScorer::with_penalty(0);
+       let seed = [0u8; 32];
+       let keys_manager = test_utils::TestKeysInterface::new(&seed, Network::Testnet);
+       let random_seed_bytes = keys_manager.get_secure_random_bytes();
        let route = get_route(
-               &origin_node.node.get_our_node_id(), &payment_params, origin_node.network_graph,
-               None, recv_value, TEST_FINAL_CLTV, origin_node.logger, &scorer).unwrap();
+               &origin_node.node.get_our_node_id(), &payment_params, &network_graph,
+               None, recv_value, TEST_FINAL_CLTV, origin_node.logger, &scorer, &random_seed_bytes).unwrap();
        assert_eq!(route.paths.len(), 1);
        assert_eq!(route.paths[0].len(), expected_route.len());
        for (node, hop) in expected_route.iter().zip(route.paths[0].iter()) {
index 3c7174a9d71a63a8f74116229ee46f6da5f42f4a..ead2a8e2f4eccba54d09e9d849b256d40193a20f 100644 (file)
@@ -16,7 +16,7 @@ use chain::{Confirm, Listen, Watch};
 use chain::channelmonitor;
 use chain::channelmonitor::{ChannelMonitor, CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY};
 use chain::transaction::OutPoint;
-use chain::keysinterface::BaseSign;
+use chain::keysinterface::{BaseSign, KeysInterface};
 use ln::{PaymentPreimage, PaymentSecret, PaymentHash};
 use ln::channel::{commitment_tx_base_weight, COMMITMENT_TX_WEIGHT_PER_HTLC, CONCURRENT_INBOUND_HTLC_FEE_BUFFER, FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE, MIN_AFFORDABLE_HTLC_COUNT};
 use ln::channelmanager::{ChannelManager, ChannelManagerReadArgs, PaymentId, RAACommitmentOrder, PaymentSendFailure, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA, PAYMENT_EXPIRY_BLOCKS };
@@ -6001,7 +6001,7 @@ fn bolt2_open_channel_sending_node_checks_part1() { //This test needs to be on i
        let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
        //Force duplicate channel ids
        for node in nodes.iter() {
-               *node.keys_manager.override_channel_id_priv.lock().unwrap() = Some([0; 32]);
+               *node.keys_manager.override_random_bytes.lock().unwrap() = Some([0; 32]);
        }
 
        // BOLT #2 spec: Sending node must ensure temporary_channel_id is unique from any other channel ID with the same peer.
@@ -6010,9 +6010,10 @@ fn bolt2_open_channel_sending_node_checks_part1() { //This test needs to be on i
        nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), channel_value_satoshis, push_msat, 42, None).unwrap();
        let node0_to_1_send_open_channel = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
        nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), InitFeatures::known(), &node0_to_1_send_open_channel);
+       get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id());
 
        //Create a second channel with a channel_id collision
-       assert!(nodes[0].node.create_channel(nodes[0].node.get_our_node_id(), channel_value_satoshis, push_msat, 42, None).is_err());
+       assert!(nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), channel_value_satoshis, push_msat, 42, None).is_err());
 }
 
 #[test]
@@ -7440,8 +7441,9 @@ fn test_check_htlc_underpaying() {
        create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
 
        let scorer = test_utils::TestScorer::with_penalty(0);
+       let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
        let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id()).with_features(InvoiceFeatures::known());
-       let route = get_route(&nodes[0].node.get_our_node_id(), &payment_params, nodes[0].network_graph, None, 10_000, TEST_FINAL_CLTV, nodes[0].logger, &scorer).unwrap();
+       let route = get_route(&nodes[0].node.get_our_node_id(), &payment_params, &nodes[0].network_graph.read_only(), None, 10_000, TEST_FINAL_CLTV, nodes[0].logger, &scorer, &random_seed_bytes).unwrap();
        let (_, our_payment_hash, _) = get_payment_preimage_hash!(nodes[0]);
        let our_payment_secret = nodes[1].node.create_inbound_payment_for_hash(our_payment_hash, Some(100_000), 7200).unwrap();
        nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
@@ -7843,12 +7845,13 @@ fn test_bump_penalty_txn_on_revoked_htlcs() {
        // Lock HTLC in both directions (using a slightly lower CLTV delay to provide timely RBF bumps)
        let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id()).with_features(InvoiceFeatures::known());
        let scorer = test_utils::TestScorer::with_penalty(0);
-       let route = get_route(&nodes[0].node.get_our_node_id(), &payment_params, &nodes[0].network_graph, None,
-               3_000_000, 50, nodes[0].logger, &scorer).unwrap();
+       let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
+       let route = get_route(&nodes[0].node.get_our_node_id(), &payment_params, &nodes[0].network_graph.read_only(), None,
+               3_000_000, 50, nodes[0].logger, &scorer, &random_seed_bytes).unwrap();
        let payment_preimage = send_along_route(&nodes[0], route, &[&nodes[1]], 3_000_000).0;
        let payment_params = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id()).with_features(InvoiceFeatures::known());
-       let route = get_route(&nodes[1].node.get_our_node_id(), &payment_params, nodes[1].network_graph, None,
-               3_000_000, 50, nodes[0].logger, &scorer).unwrap();
+       let route = get_route(&nodes[1].node.get_our_node_id(), &payment_params, &nodes[1].network_graph.read_only(), None,
+               3_000_000, 50, nodes[0].logger, &scorer, &random_seed_bytes).unwrap();
        send_along_route(&nodes[1], route, &[&nodes[0]], 3_000_000);
 
        let revoked_local_txn = get_local_commitment_txn!(nodes[1], chan.2);
@@ -9617,10 +9620,11 @@ fn test_dup_htlc_second_fail_panic() {
        let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id())
                .with_features(InvoiceFeatures::known());
        let scorer = test_utils::TestScorer::with_penalty(0);
+       let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
        let route = get_route(
-               &nodes[0].node.get_our_node_id(), &payment_params, &nodes[0].network_graph,
+               &nodes[0].node.get_our_node_id(), &payment_params, &nodes[0].network_graph.read_only(),
                Some(&nodes[0].node.list_usable_channels().iter().collect::<Vec<_>>()),
-               10_000, TEST_FINAL_CLTV, nodes[0].logger, &scorer).unwrap();
+               10_000, TEST_FINAL_CLTV, nodes[0].logger, &scorer, &random_seed_bytes).unwrap();
 
        let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(&nodes[1]);
 
@@ -9689,7 +9693,8 @@ fn test_keysend_payments_to_public_node() {
                final_cltv_expiry_delta: 40,
        };
        let scorer = test_utils::TestScorer::with_penalty(0);
-       let route = find_route(&payer_pubkey, &route_params, network_graph, None, nodes[0].logger, &scorer).unwrap();
+       let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
+       let route = find_route(&payer_pubkey, &route_params, network_graph, None, nodes[0].logger, &scorer, &random_seed_bytes).unwrap();
 
        let test_preimage = PaymentPreimage([42; 32]);
        let (payment_hash, _) = nodes[0].node.send_spontaneous_payment(&route, Some(test_preimage)).unwrap();
@@ -9723,9 +9728,10 @@ fn test_keysend_payments_to_private_node() {
        let network_graph = nodes[0].network_graph;
        let first_hops = nodes[0].node.list_usable_channels();
        let scorer = test_utils::TestScorer::with_penalty(0);
+       let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
        let route = find_route(
                &payer_pubkey, &route_params, network_graph, Some(&first_hops.iter().collect::<Vec<_>>()),
-               nodes[0].logger, &scorer
+               nodes[0].logger, &scorer, &random_seed_bytes
        ).unwrap();
 
        let test_preimage = PaymentPreimage([42; 32]);
index 414c98d4e85da80ee3f0ada988d5835f36ea8e13..dbdb760924eb8d16c2ce142f649230f7711251ce 100644 (file)
@@ -320,7 +320,7 @@ fn test_onion_failure() {
        let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[Some(config), Some(config), Some(node_2_cfg)]);
        let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs);
        for node in nodes.iter() {
-               *node.keys_manager.override_session_priv.lock().unwrap() = Some([3; 32]);
+               *node.keys_manager.override_random_bytes.lock().unwrap() = Some([3; 32]);
        }
        let channels = [create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known()), create_announced_chan_between_nodes(&nodes, 1, 2, InitFeatures::known(), InitFeatures::known())];
        let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], 40000);
@@ -610,10 +610,11 @@ macro_rules! get_phantom_route {
                                        }
                ])]);
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let network_graph = $nodes[0].network_graph.read_only();
                (get_route(
-                       &$nodes[0].node.get_our_node_id(), &payment_params, $nodes[0].network_graph,
+                       &$nodes[0].node.get_our_node_id(), &payment_params, &network_graph,
                        Some(&$nodes[0].node.list_usable_channels().iter().collect::<Vec<_>>()),
-                       $amt, TEST_FINAL_CLTV, $nodes[0].logger, &scorer
+                       $amt, TEST_FINAL_CLTV, $nodes[0].logger, &scorer, &[0u8; 32]
                ).unwrap(), phantom_route_hint.phantom_scid)
        }
 }}
@@ -693,7 +694,7 @@ fn test_phantom_invalid_onion_payload() {
 
        // We'll use the session priv later when constructing an invalid onion packet.
        let session_priv = [3; 32];
-       *nodes[0].keys_manager.override_session_priv.lock().unwrap() = Some(session_priv);
+       *nodes[0].keys_manager.override_random_bytes.lock().unwrap() = Some(session_priv);
        nodes[0].node.send_payment(&route, payment_hash.clone(), &Some(payment_secret)).unwrap();
        check_added_monitors!(nodes[0], 1);
        let update_0 = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
index 6557295b553d6c4c56b320fc5eafda5d051cf32b..bae9a2914fa8014a87b8a97015bc010d5580a366 100644 (file)
@@ -14,6 +14,7 @@
 use chain::{ChannelMonitorUpdateErr, Confirm, Listen, Watch};
 use chain::channelmonitor::{ANTI_REORG_DELAY, ChannelMonitor, LATENCY_GRACE_PERIOD_BLOCKS};
 use chain::transaction::OutPoint;
+use chain::keysinterface::KeysInterface;
 use ln::channelmanager::{BREAKDOWN_TIMEOUT, ChannelManager, ChannelManagerReadArgs, PaymentId, PaymentSendFailure};
 use ln::features::{InitFeatures, InvoiceFeatures};
 use ln::msgs;
@@ -27,6 +28,7 @@ use util::ser::{ReadableArgs, Writeable};
 use io;
 
 use bitcoin::{Block, BlockHeader, BlockHash};
+use bitcoin::network::constants::Network;
 
 use prelude::*;
 
@@ -730,10 +732,12 @@ fn get_ldk_payment_preimage() {
        let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id())
                .with_features(InvoiceFeatures::known());
        let scorer = test_utils::TestScorer::with_penalty(0);
+       let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+       let random_seed_bytes = keys_manager.get_secure_random_bytes();
        let route = get_route(
-               &nodes[0].node.get_our_node_id(), &payment_params, &nodes[0].network_graph,
+               &nodes[0].node.get_our_node_id(), &payment_params, &nodes[0].network_graph.read_only(),
                Some(&nodes[0].node.list_usable_channels().iter().collect::<Vec<_>>()),
-               amt_msat, TEST_FINAL_CLTV, nodes[0].logger, &scorer).unwrap();
+               amt_msat, TEST_FINAL_CLTV, nodes[0].logger, &scorer, &random_seed_bytes).unwrap();
        let _payment_id = nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
        check_added_monitors!(nodes[0], 1);
 
index b910d4bf7798135ec08d8e84fc550fb907e318fb..d3ac2ebe9a39bf2e918f58dbf4d012ac0434ae99 100644 (file)
@@ -307,12 +307,12 @@ const OUTBOUND_BUFFER_LIMIT_DROP_GOSSIP: usize = OUTBOUND_BUFFER_LIMIT_READ_PAUS
 /// Thus, to avoid needlessly disconnecting a peer, we allow a peer to take this many timer ticks
 /// per connected peer to respond to a ping, as long as they send us at least one message during
 /// each tick, ensuring we aren't actually just disconnected.
-/// With a timer tick interval of five seconds, this translates to about 30 seconds per connected
+/// With a timer tick interval of ten seconds, this translates to about 40 seconds per connected
 /// peer.
 ///
 /// When we improve parallelism somewhat we should reduce this to e.g. this many timer ticks per
 /// two connected peers, assuming most LDK-running systems have at least two cores.
-const MAX_BUFFER_DRAIN_TICK_INTERVALS_PER_PEER: i8 = 6;
+const MAX_BUFFER_DRAIN_TICK_INTERVALS_PER_PEER: i8 = 4;
 
 /// This is the minimum number of messages we expect a peer to be able to handle within one timer
 /// tick. Once we have sent this many messages since the last ping, we send a ping right away to
@@ -1572,9 +1572,9 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, L: Deref, CMH: Deref> P
        /// Send pings to each peer and disconnect those which did not respond to the last round of
        /// pings.
        ///
-       /// This may be called on any timescale you want, however, roughly once every five to ten
-       /// seconds is preferred. The call rate determines both how often we send a ping to our peers
-       /// and how much time they have to respond before we disconnect them.
+       /// This may be called on any timescale you want, however, roughly once every ten seconds is
+       /// preferred. The call rate determines both how often we send a ping to our peers and how much
+       /// time they have to respond before we disconnect them.
        ///
        /// May call [`send_data`] on all [`SocketDescriptor`]s. Thus, be very careful with reentrancy
        /// issues!
index a687df8886ca3f7193339e8120618152cd0722b6..cd0f77448a5d5c2c587286dcc7ec9612d5c9ad8c 100644 (file)
@@ -25,6 +25,7 @@ use util::config::UserConfig;
 
 use bitcoin::blockdata::script::Builder;
 use bitcoin::blockdata::opcodes;
+use bitcoin::network::constants::Network;
 
 use regex;
 
@@ -77,6 +78,8 @@ fn updates_shutdown_wait() {
        let chan_2 = create_announced_chan_between_nodes(&nodes, 1, 2, InitFeatures::known(), InitFeatures::known());
        let logger = test_utils::TestLogger::new();
        let scorer = test_utils::TestScorer::with_penalty(0);
+       let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+       let random_seed_bytes = keys_manager.get_secure_random_bytes();
 
        let (payment_preimage, _, _) = route_payment(&nodes[0], &[&nodes[1], &nodes[2]], 100000);
 
@@ -92,9 +95,9 @@ fn updates_shutdown_wait() {
        let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[0]);
 
        let payment_params_1 = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id()).with_features(InvoiceFeatures::known());
-       let route_1 = get_route(&nodes[0].node.get_our_node_id(), &payment_params_1, nodes[0].network_graph, None, 100000, TEST_FINAL_CLTV, &logger, &scorer).unwrap();
+       let route_1 = get_route(&nodes[0].node.get_our_node_id(), &payment_params_1, &nodes[0].network_graph.read_only(), None, 100000, TEST_FINAL_CLTV, &logger, &scorer, &random_seed_bytes).unwrap();
        let payment_params_2 = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id()).with_features(InvoiceFeatures::known());
-       let route_2 = get_route(&nodes[1].node.get_our_node_id(), &payment_params_2, nodes[1].network_graph, None, 100000, TEST_FINAL_CLTV, &logger, &scorer).unwrap();
+       let route_2 = get_route(&nodes[1].node.get_our_node_id(), &payment_params_2, &nodes[1].network_graph.read_only(), None, 100000, TEST_FINAL_CLTV, &logger, &scorer, &random_seed_bytes).unwrap();
        unwrap_send_err!(nodes[0].node.send_payment(&route_1, payment_hash, &Some(payment_secret)), true, APIError::ChannelUnavailable {..}, {});
        unwrap_send_err!(nodes[1].node.send_payment(&route_2, payment_hash, &Some(payment_secret)), true, APIError::ChannelUnavailable {..}, {});
 
index 5a84f35c28dd4f1990ef0eda4611c53a139cb615..a5e497bb1cf0f279137fd3f5e923e8972317181d 100644 (file)
@@ -674,6 +674,21 @@ impl ChannelInfo {
                };
                Some((DirectedChannelInfo { channel: self, direction }, source))
        }
+
+       /// Returns a [`DirectedChannelInfo`] for the channel directed from the given `source` to a
+       /// returned `target`, or `None` if `source` is not one of the channel's counterparties.
+       pub fn as_directed_from(&self, source: &NodeId) -> Option<(DirectedChannelInfo, &NodeId)> {
+               let (direction, target) = {
+                       if source == &self.node_one {
+                               (self.one_to_two.as_ref(), &self.node_two)
+                       } else if source == &self.node_two {
+                               (self.two_to_one.as_ref(), &self.node_one)
+                       } else {
+                               return None;
+                       }
+               };
+               Some((DirectedChannelInfo { channel: self, direction }, target))
+       }
 }
 
 impl fmt::Display for ChannelInfo {
index c45ce59ac13581a3312c535f5e143747fd52220e..d6aef902f0c1f28bddd8ad23279324410cd988d6 100644 (file)
@@ -18,9 +18,10 @@ use ln::channelmanager::ChannelDetails;
 use ln::features::{ChannelFeatures, InvoiceFeatures, NodeFeatures};
 use ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT};
 use routing::scoring::Score;
-use routing::network_graph::{DirectedChannelInfoWithUpdate, EffectiveCapacity, NetworkGraph, NodeId, RoutingFees};
+use routing::network_graph::{DirectedChannelInfoWithUpdate, EffectiveCapacity, NetworkGraph, ReadOnlyNetworkGraph, NodeId, RoutingFees};
 use util::ser::{Writeable, Readable};
 use util::logger::{Level, Logger};
+use util::chacha20::ChaCha20;
 
 use io;
 use prelude::*;
@@ -176,6 +177,9 @@ impl_writeable_tlv_based!(RouteParameters, {
 /// Maximum total CTLV difference we allow for a full payment path.
 pub const DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA: u32 = 1008;
 
+/// The median hop CLTV expiry delta currently seen in the network.
+const MEDIAN_HOP_CLTV_EXPIRY_DELTA: u32 = 40;
+
 /// The recipient of a payment.
 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
 pub struct PaymentParameters {
@@ -258,7 +262,6 @@ impl PaymentParameters {
 #[derive(Clone, Debug, Hash, Eq, PartialEq)]
 pub struct RouteHint(pub Vec<RouteHintHop>);
 
-
 impl Writeable for RouteHint {
        fn write<W: ::util::ser::Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
                (self.0.len() as u64).write(writer)?;
@@ -623,19 +626,26 @@ fn compute_fees(amount_msat: u64, channel_fees: RoutingFees) -> Option<u64> {
 /// [`Event::PaymentPathFailed`]: crate::util::events::Event::PaymentPathFailed
 pub fn find_route<L: Deref, S: Score>(
        our_node_pubkey: &PublicKey, route_params: &RouteParameters, network: &NetworkGraph,
-       first_hops: Option<&[&ChannelDetails]>, logger: L, scorer: &S
+       first_hops: Option<&[&ChannelDetails]>, logger: L, scorer: &S, random_seed_bytes: &[u8; 32]
 ) -> Result<Route, LightningError>
 where L::Target: Logger {
-       get_route(
-               our_node_pubkey, &route_params.payment_params, network, first_hops, route_params.final_value_msat,
-               route_params.final_cltv_expiry_delta, logger, scorer
-       )
+       let network_graph = network.read_only();
+       match get_route(
+               our_node_pubkey, &route_params.payment_params, &network_graph, first_hops, route_params.final_value_msat,
+               route_params.final_cltv_expiry_delta, logger, scorer, random_seed_bytes
+       ) {
+               Ok(mut route) => {
+                       add_random_cltv_offset(&mut route, &route_params.payment_params, &network_graph, random_seed_bytes);
+                       Ok(route)
+               },
+               Err(err) => Err(err),
+       }
 }
 
 pub(crate) fn get_route<L: Deref, S: Score>(
-       our_node_pubkey: &PublicKey, payment_params: &PaymentParameters, network: &NetworkGraph,
-       first_hops: Option<&[&ChannelDetails]>, final_value_msat: u64, final_cltv_expiry_delta: u32, 
-       logger: L, scorer: &S
+       our_node_pubkey: &PublicKey, payment_params: &PaymentParameters, network_graph: &ReadOnlyNetworkGraph,
+       first_hops: Option<&[&ChannelDetails]>, final_value_msat: u64, final_cltv_expiry_delta: u32,
+       logger: L, scorer: &S, _random_seed_bytes: &[u8; 32]
 ) -> Result<Route, LightningError>
 where L::Target: Logger {
        let payee_node_id = NodeId::from_pubkey(&payment_params.payee_pubkey);
@@ -672,12 +682,13 @@ where L::Target: Logger {
        //    - OR if we could not construct a new path. Any next attempt will fail too.
        //    Otherwise, repeat step 2.
        // 4. See if we managed to collect paths which aggregately are able to transfer target value
-       //    (not recommended value). If yes, proceed. If not, fail routing.
-       // 5. Randomly combine paths into routes having enough to fulfill the payment. (TODO: knapsack)
-       // 6. Of all the found paths, select only those with the lowest total fee.
-       // 7. The last path in every selected route is likely to be more than we need.
+       //    (not recommended value).
+       // 5. If yes, proceed. If not, fail routing.
+       // 6. Randomly combine paths into routes having enough to fulfill the payment. (TODO: knapsack)
+       // 7. Of all the found paths, select only those with the lowest total fee.
+       // 8. The last path in every selected route is likely to be more than we need.
        //    Reduce its value-to-transfer and recompute fees.
-       // 8. Choose the best route by the lowest total fee.
+       // 9. Choose the best route by the lowest total fee.
 
        // As for the actual search algorithm,
        // we do a payee-to-payer pseudo-Dijkstra's sorting by each node's distance from the payee
@@ -721,7 +732,6 @@ where L::Target: Logger {
        // to use as the A* heuristic beyond just the cost to get one node further than the current
        // one.
 
-       let network_graph = network.read_only();
        let network_channels = network_graph.channels();
        let network_nodes = network_graph.nodes();
 
@@ -853,7 +863,12 @@ where L::Target: Logger {
                                        let contributes_sufficient_value = available_value_contribution_msat >= minimal_value_contribution_msat;
 
                                        // Do not consider candidates that exceed the maximum total cltv expiry limit.
-                                       let max_total_cltv_expiry_delta = payment_params.max_total_cltv_expiry_delta;
+                                       // In order to already account for some of the privacy enhancing random CLTV
+                                       // expiry delta offset we add on top later, we subtract a rough estimate
+                                       // (2*MEDIAN_HOP_CLTV_EXPIRY_DELTA) here.
+                                       let max_total_cltv_expiry_delta = payment_params.max_total_cltv_expiry_delta
+                                               .checked_sub(2*MEDIAN_HOP_CLTV_EXPIRY_DELTA)
+                                               .unwrap_or(payment_params.max_total_cltv_expiry_delta);
                                        let hop_total_cltv_delta = ($next_hops_cltv_delta as u32)
                                                .checked_add($candidate.cltv_expiry_delta())
                                                .unwrap_or(u32::max_value());
@@ -1535,17 +1550,92 @@ where L::Target: Logger {
        Ok(route)
 }
 
+// When an adversarial intermediary node observes a payment, it may be able to infer its
+// destination, if the remaining CLTV expiry delta exactly matches a feasible path in the network
+// graph. In order to improve privacy, this method obfuscates the CLTV expiry deltas along the
+// payment path by adding a randomized 'shadow route' offset to the final hop.
+fn add_random_cltv_offset(route: &mut Route, payment_params: &PaymentParameters, network_graph: &ReadOnlyNetworkGraph, random_seed_bytes: &[u8; 32]) {
+       let network_channels = network_graph.channels();
+       let network_nodes = network_graph.nodes();
+
+       for path in route.paths.iter_mut() {
+               let mut shadow_ctlv_expiry_delta_offset: u32 = 0;
+
+               // Choose the last publicly known node as the starting point for the random walk
+               if let Some(starting_hop) = path.iter().rev().find(|h| network_nodes.contains_key(&NodeId::from_pubkey(&h.pubkey))) {
+                       let mut cur_node_id = NodeId::from_pubkey(&starting_hop.pubkey);
+
+                       // Init PRNG with path nonce
+                       let mut path_nonce = [0u8; 12];
+                       path_nonce.copy_from_slice(&cur_node_id.as_slice()[..12]);
+                       let mut prng = ChaCha20::new(random_seed_bytes, &path_nonce);
+                       let mut random_path_bytes = [0u8; ::core::mem::size_of::<usize>()];
+
+                       // Pick a random path length in [1 .. 3]
+                       prng.process_in_place(&mut random_path_bytes);
+                       let random_walk_length = usize::from_be_bytes(random_path_bytes).wrapping_rem(3).wrapping_add(1);
+
+                       for _random_hop in 0..random_walk_length {
+                               if let Some(cur_node) = network_nodes.get(&cur_node_id) {
+                                       // Randomly choose the next hop
+                                       prng.process_in_place(&mut random_path_bytes);
+                                       if let Some(random_channel) = usize::from_be_bytes(random_path_bytes).checked_rem(cur_node.channels.len())
+                                               .and_then(|index| cur_node.channels.get(index))
+                                               .and_then(|id| network_channels.get(id)) {
+                                                       random_channel.as_directed_from(&cur_node_id).map(|(dir_info, next_id)| {
+                                                               dir_info.direction().map(|channel_update_info|
+                                                                       shadow_ctlv_expiry_delta_offset = shadow_ctlv_expiry_delta_offset
+                                                                               .checked_add(channel_update_info.cltv_expiry_delta.into())
+                                                                               .unwrap_or(shadow_ctlv_expiry_delta_offset));
+                                                               cur_node_id = *next_id;
+                                                       });
+                                               }
+                               }
+                       }
+               } else {
+                       // If the entire path is private, choose a random offset from multiples of
+                       // MEDIAN_HOP_CLTV_EXPIRY_DELTA
+                       let mut prng = ChaCha20::new(random_seed_bytes, &[0u8; 8]);
+                       let mut random_bytes = [0u8; 4];
+                       prng.process_in_place(&mut random_bytes);
+                       let random_walk_length = u32::from_be_bytes(random_bytes).wrapping_rem(3).wrapping_add(1);
+                       shadow_ctlv_expiry_delta_offset = random_walk_length * MEDIAN_HOP_CLTV_EXPIRY_DELTA;
+               }
+
+               // Limit the total offset to reduce the worst-case locked liquidity timevalue
+               const MAX_SHADOW_CLTV_EXPIRY_DELTA_OFFSET: u32 = 3*144;
+               shadow_ctlv_expiry_delta_offset = cmp::min(shadow_ctlv_expiry_delta_offset, MAX_SHADOW_CLTV_EXPIRY_DELTA_OFFSET);
+
+               // Limit the offset so we never exceed the max_total_cltv_expiry_delta. To improve plausibility,
+               // we choose the limit to be the largest possible multiple of MEDIAN_HOP_CLTV_EXPIRY_DELTA.
+               let path_total_cltv_expiry_delta: u32 = path.iter().map(|h| h.cltv_expiry_delta).sum();
+               let mut max_path_offset = payment_params.max_total_cltv_expiry_delta - path_total_cltv_expiry_delta;
+               max_path_offset = cmp::max(
+                       max_path_offset - (max_path_offset % MEDIAN_HOP_CLTV_EXPIRY_DELTA),
+                       max_path_offset % MEDIAN_HOP_CLTV_EXPIRY_DELTA);
+               shadow_ctlv_expiry_delta_offset = cmp::min(shadow_ctlv_expiry_delta_offset, max_path_offset);
+
+               // Add 'shadow' CLTV offset to the final hop
+               if let Some(last_hop) = path.last_mut() {
+                       last_hop.cltv_expiry_delta = last_hop.cltv_expiry_delta
+                               .checked_add(shadow_ctlv_expiry_delta_offset).unwrap_or(last_hop.cltv_expiry_delta);
+               }
+       }
+}
+
 #[cfg(test)]
 mod tests {
-       use routing::scoring::{ProbabilisticScorer, ProbabilisticScoringParameters, Score};
        use routing::network_graph::{NetworkGraph, NetGraphMsgHandler, NodeId};
-       use routing::router::{get_route, PaymentParameters, Route, RouteHint, RouteHintHop, RouteHop, RoutingFees};
+       use routing::router::{get_route, add_random_cltv_offset, PaymentParameters, Route, RouteHint, RouteHintHop, RouteHop, RoutingFees, DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA};
+       use routing::scoring::Score;
        use chain::transaction::OutPoint;
+       use chain::keysinterface::KeysInterface;
        use ln::features::{ChannelFeatures, InitFeatures, InvoiceFeatures, NodeFeatures};
        use ln::msgs::{ErrorAction, LightningError, OptionalField, UnsignedChannelAnnouncement, ChannelAnnouncement, RoutingMessageHandler,
           NodeAnnouncement, UnsignedNodeAnnouncement, ChannelUpdate, UnsignedChannelUpdate};
        use ln::channelmanager;
        use util::test_utils;
+       use util::chacha20::ChaCha20;
        use util::ser::Writeable;
        #[cfg(c_bindings)]
        use util::ser::Writer;
@@ -2013,14 +2103,16 @@ mod tests {
                let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
                let payment_params = PaymentParameters::from_node_id(nodes[2]);
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
 
                // Simple route to 2 via 1
 
-               if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph, None, 0, 42, Arc::clone(&logger), &scorer) {
+               if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 0, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
                        assert_eq!(err, "Cannot send a payment of 0 msat");
                } else { panic!(); }
 
-               let route = get_route(&our_id, &payment_params, &network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap();
+               let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                assert_eq!(route.paths[0].len(), 2);
 
                assert_eq!(route.paths[0][0].pubkey, nodes[1]);
@@ -2044,16 +2136,19 @@ mod tests {
                let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
                let payment_params = PaymentParameters::from_node_id(nodes[2]);
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
 
                // Simple route to 2 via 1
 
                let our_chans = vec![get_channel_details(Some(2), our_id, InitFeatures::from_le_bytes(vec![0b11]), 100000)];
 
-               if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph, Some(&our_chans.iter().collect::<Vec<_>>()), 100, 42, Arc::clone(&logger), &scorer) {
+               if let Err(LightningError{err, action: ErrorAction::IgnoreError}) =
+                       get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
                        assert_eq!(err, "First hop cannot have our_node_pubkey as a destination.");
                } else { panic!(); }
 
-               let route = get_route(&our_id, &payment_params, &network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap();
+               let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                assert_eq!(route.paths[0].len(), 2);
        }
 
@@ -2063,6 +2158,8 @@ mod tests {
                let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
                let payment_params = PaymentParameters::from_node_id(nodes[2]);
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
 
                // Simple route to 2 via 1
 
@@ -2159,7 +2256,7 @@ mod tests {
                });
 
                // Not possible to send 199_999_999, because the minimum on channel=2 is 200_000_000.
-               if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph, None, 199_999_999, 42, Arc::clone(&logger), &scorer) {
+               if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 199_999_999, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
                        assert_eq!(err, "Failed to find a path to the given destination");
                } else { panic!(); }
 
@@ -2178,7 +2275,7 @@ mod tests {
                });
 
                // A payment above the minimum should pass
-               let route = get_route(&our_id, &payment_params, &network_graph, None, 199_999_999, 42, Arc::clone(&logger), &scorer).unwrap();
+               let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 199_999_999, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                assert_eq!(route.paths[0].len(), 2);
        }
 
@@ -2188,6 +2285,8 @@ mod tests {
                let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
                let payment_params = PaymentParameters::from_node_id(nodes[2]).with_features(InvoiceFeatures::known());
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
 
                // A route to node#2 via two paths.
                // One path allows transferring 35-40 sats, another one also allows 35-40 sats.
@@ -2257,7 +2356,7 @@ mod tests {
                        excess_data: Vec::new()
                });
 
-               let route = get_route(&our_id, &payment_params, &network_graph, None, 60_000, 42, Arc::clone(&logger), &scorer).unwrap();
+               let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 60_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                // Overpay fees to hit htlc_minimum_msat.
                let overpaid_fees = route.paths[0][0].fee_msat + route.paths[1][0].fee_msat;
                // TODO: this could be better balanced to overpay 10k and not 15k.
@@ -2302,14 +2401,14 @@ mod tests {
                        excess_data: Vec::new()
                });
 
-               let route = get_route(&our_id, &payment_params, &network_graph, None, 60_000, 42, Arc::clone(&logger), &scorer).unwrap();
+               let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 60_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                // Fine to overpay for htlc_minimum_msat if it allows us to save fee.
                assert_eq!(route.paths.len(), 1);
                assert_eq!(route.paths[0][0].short_channel_id, 12);
                let fees = route.paths[0][0].fee_msat;
                assert_eq!(fees, 5_000);
 
-               let route = get_route(&our_id, &payment_params, &network_graph, None, 50_000, 42, Arc::clone(&logger), &scorer).unwrap();
+               let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 50_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                // Not fine to overpay for htlc_minimum_msat if it requires paying more than fee on
                // the other channel.
                assert_eq!(route.paths.len(), 1);
@@ -2324,6 +2423,8 @@ mod tests {
                let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
                let payment_params = PaymentParameters::from_node_id(nodes[2]);
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
 
                // // Disable channels 4 and 12 by flags=2
                update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[1], UnsignedChannelUpdate {
@@ -2352,13 +2453,13 @@ mod tests {
                });
 
                // If all the channels require some features we don't understand, route should fail
-               if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph, None, 100, 42, Arc::clone(&logger), &scorer) {
+               if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
                        assert_eq!(err, "Failed to find a path to the given destination");
                } else { panic!(); }
 
                // If we specify a channel to node7, that overrides our local channel view and that gets used
                let our_chans = vec![get_channel_details(Some(42), nodes[7].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)];
-               let route = get_route(&our_id, &payment_params, &network_graph, Some(&our_chans.iter().collect::<Vec<_>>()), 100, 42, Arc::clone(&logger), &scorer).unwrap();
+               let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                assert_eq!(route.paths[0].len(), 2);
 
                assert_eq!(route.paths[0][0].pubkey, nodes[7]);
@@ -2382,6 +2483,8 @@ mod tests {
                let (_, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
                let payment_params = PaymentParameters::from_node_id(nodes[2]);
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
 
                // Disable nodes 1, 2, and 8 by requiring unknown feature bits
                let unknown_features = NodeFeatures::known().set_unknown_feature_required();
@@ -2390,13 +2493,13 @@ mod tests {
                add_or_update_node(&net_graph_msg_handler, &secp_ctx, &privkeys[7], unknown_features.clone(), 1);
 
                // If all nodes require some features we don't understand, route should fail
-               if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph, None, 100, 42, Arc::clone(&logger), &scorer) {
+               if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
                        assert_eq!(err, "Failed to find a path to the given destination");
                } else { panic!(); }
 
                // If we specify a channel to node7, that overrides our local channel view and that gets used
                let our_chans = vec![get_channel_details(Some(42), nodes[7].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)];
-               let route = get_route(&our_id, &payment_params, &network_graph, Some(&our_chans.iter().collect::<Vec<_>>()), 100, 42, Arc::clone(&logger), &scorer).unwrap();
+               let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                assert_eq!(route.paths[0].len(), 2);
 
                assert_eq!(route.paths[0][0].pubkey, nodes[7]);
@@ -2423,10 +2526,12 @@ mod tests {
                let (secp_ctx, network_graph, _, _, logger) = build_graph();
                let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
 
                // Route to 1 via 2 and 3 because our channel to 1 is disabled
                let payment_params = PaymentParameters::from_node_id(nodes[0]);
-               let route = get_route(&our_id, &payment_params, &network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap();
+               let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                assert_eq!(route.paths[0].len(), 3);
 
                assert_eq!(route.paths[0][0].pubkey, nodes[1]);
@@ -2453,7 +2558,7 @@ mod tests {
                // If we specify a channel to node7, that overrides our local channel view and that gets used
                let payment_params = PaymentParameters::from_node_id(nodes[2]);
                let our_chans = vec![get_channel_details(Some(42), nodes[7].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)];
-               let route = get_route(&our_id, &payment_params, &network_graph, Some(&our_chans.iter().collect::<Vec<_>>()), 100, 42, Arc::clone(&logger), &scorer).unwrap();
+               let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                assert_eq!(route.paths[0].len(), 2);
 
                assert_eq!(route.paths[0][0].pubkey, nodes[7]);
@@ -2552,6 +2657,8 @@ mod tests {
                let (secp_ctx, network_graph, _, _, logger) = build_graph();
                let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
 
                // Simple test across 2, 3, 5, and 4 via a last_hop channel
                // Tests the behaviour when the RouteHint contains a suboptimal hop.
@@ -2574,13 +2681,13 @@ mod tests {
                invalid_last_hops.push(invalid_last_hop);
                {
                        let payment_params = PaymentParameters::from_node_id(nodes[6]).with_route_hints(invalid_last_hops);
-                       if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph, None, 100, 42, Arc::clone(&logger), &scorer) {
+                       if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
                                assert_eq!(err, "Route hint cannot have the payee as the source.");
                        } else { panic!(); }
                }
 
                let payment_params = PaymentParameters::from_node_id(nodes[6]).with_route_hints(last_hops_multi_private_channels(&nodes));
-               let route = get_route(&our_id, &payment_params, &network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap();
+               let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                assert_eq!(route.paths[0].len(), 5);
 
                assert_eq!(route.paths[0][0].pubkey, nodes[1]);
@@ -2651,10 +2758,12 @@ mod tests {
                let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
                let payment_params = PaymentParameters::from_node_id(nodes[6]).with_route_hints(empty_last_hop(&nodes));
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
 
                // Test handling of an empty RouteHint passed in Invoice.
 
-               let route = get_route(&our_id, &payment_params, &network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap();
+               let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                assert_eq!(route.paths[0].len(), 5);
 
                assert_eq!(route.paths[0][0].pubkey, nodes[1]);
@@ -2729,6 +2838,8 @@ mod tests {
                let last_hops = multi_hop_last_hops_hint([nodes[2], nodes[3]]);
                let payment_params = PaymentParameters::from_node_id(nodes[6]).with_route_hints(last_hops.clone());
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
                // Test through channels 2, 3, 0xff00, 0xff01.
                // Test shows that multiple hop hints are considered.
 
@@ -2758,7 +2869,7 @@ mod tests {
                        excess_data: Vec::new()
                });
 
-               let route = get_route(&our_id, &payment_params, &network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap();
+               let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                assert_eq!(route.paths[0].len(), 4);
 
                assert_eq!(route.paths[0][0].pubkey, nodes[1]);
@@ -2830,7 +2941,7 @@ mod tests {
                        excess_data: Vec::new()
                });
 
-               let route = get_route(&our_id, &payment_params, &network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap();
+               let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &[42u8; 32]).unwrap();
                assert_eq!(route.paths[0].len(), 4);
 
                assert_eq!(route.paths[0][0].pubkey, nodes[1]);
@@ -2907,10 +3018,12 @@ mod tests {
                let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
                let payment_params = PaymentParameters::from_node_id(nodes[6]).with_route_hints(last_hops_with_public_channel(&nodes));
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
                // This test shows that public routes can be present in the invoice
                // which would be handled in the same manner.
 
-               let route = get_route(&our_id, &payment_params, &network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap();
+               let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                assert_eq!(route.paths[0].len(), 5);
 
                assert_eq!(route.paths[0][0].pubkey, nodes[1]);
@@ -2956,12 +3069,14 @@ mod tests {
                let (secp_ctx, network_graph, _, _, logger) = build_graph();
                let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
 
                // Simple test with outbound channel to 4 to test that last_hops and first_hops connect
                let our_chans = vec![get_channel_details(Some(42), nodes[3].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)];
                let mut last_hops = last_hops(&nodes);
                let payment_params = PaymentParameters::from_node_id(nodes[6]).with_route_hints(last_hops.clone());
-               let route = get_route(&our_id, &payment_params, &network_graph, Some(&our_chans.iter().collect::<Vec<_>>()), 100, 42, Arc::clone(&logger), &scorer).unwrap();
+               let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                assert_eq!(route.paths[0].len(), 2);
 
                assert_eq!(route.paths[0][0].pubkey, nodes[3]);
@@ -2982,7 +3097,7 @@ mod tests {
 
                // Revert to via 6 as the fee on 8 goes up
                let payment_params = PaymentParameters::from_node_id(nodes[6]).with_route_hints(last_hops);
-               let route = get_route(&our_id, &payment_params, &network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap();
+               let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                assert_eq!(route.paths[0].len(), 4);
 
                assert_eq!(route.paths[0][0].pubkey, nodes[1]);
@@ -3016,7 +3131,7 @@ mod tests {
                assert_eq!(route.paths[0][3].channel_features.le_flags(), &Vec::<u8>::new()); // We can't learn any flags from invoices, sadly
 
                // ...but still use 8 for larger payments as 6 has a variable feerate
-               let route = get_route(&our_id, &payment_params, &network_graph, None, 2000, 42, Arc::clone(&logger), &scorer).unwrap();
+               let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 2000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                assert_eq!(route.paths[0].len(), 5);
 
                assert_eq!(route.paths[0][0].pubkey, nodes[1]);
@@ -3077,7 +3192,10 @@ mod tests {
                let payment_params = PaymentParameters::from_node_id(target_node_id).with_route_hints(vec![last_hops]);
                let our_chans = vec![get_channel_details(Some(42), middle_node_id, InitFeatures::from_le_bytes(vec![0b11]), outbound_capacity_msat)];
                let scorer = test_utils::TestScorer::with_penalty(0);
-               get_route(&source_node_id, &payment_params, &NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash()), Some(&our_chans.iter().collect::<Vec<_>>()), route_val, 42, &test_utils::TestLogger::new(), &scorer)
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
+               get_route(&source_node_id, &payment_params, &NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash()).read_only(),
+                               Some(&our_chans.iter().collect::<Vec<_>>()), route_val, 42, &test_utils::TestLogger::new(), &scorer, &random_seed_bytes)
        }
 
        #[test]
@@ -3131,6 +3249,8 @@ mod tests {
                let (secp_ctx, network_graph, mut net_graph_msg_handler, chain_monitor, logger) = build_graph();
                let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
                let payment_params = PaymentParameters::from_node_id(nodes[2]).with_features(InvoiceFeatures::known());
 
                // We will use a simple single-path route from
@@ -3195,14 +3315,14 @@ mod tests {
                {
                        // Attempt to route more than available results in a failure.
                        if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
-                                       &our_id, &payment_params, &network_graph, None, 250_000_001, 42, Arc::clone(&logger), &scorer) {
+                                       &our_id, &payment_params, &network_graph.read_only(), None, 250_000_001, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
                                assert_eq!(err, "Failed to find a sufficient route to the given destination");
                        } else { panic!(); }
                }
 
                {
                        // Now, attempt to route an exact amount we have should be fine.
-                       let route = get_route(&our_id, &payment_params, &network_graph, None, 250_000_000, 42, Arc::clone(&logger), &scorer).unwrap();
+                       let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 250_000_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                        assert_eq!(route.paths.len(), 1);
                        let path = route.paths.last().unwrap();
                        assert_eq!(path.len(), 2);
@@ -3231,14 +3351,14 @@ mod tests {
                {
                        // Attempt to route more than available results in a failure.
                        if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
-                                       &our_id, &payment_params, &network_graph, Some(&our_chans.iter().collect::<Vec<_>>()), 200_000_001, 42, Arc::clone(&logger), &scorer) {
+                                       &our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 200_000_001, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
                                assert_eq!(err, "Failed to find a sufficient route to the given destination");
                        } else { panic!(); }
                }
 
                {
                        // Now, attempt to route an exact amount we have should be fine.
-                       let route = get_route(&our_id, &payment_params, &network_graph, Some(&our_chans.iter().collect::<Vec<_>>()), 200_000_000, 42, Arc::clone(&logger), &scorer).unwrap();
+                       let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 200_000_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                        assert_eq!(route.paths.len(), 1);
                        let path = route.paths.last().unwrap();
                        assert_eq!(path.len(), 2);
@@ -3278,14 +3398,14 @@ mod tests {
                {
                        // Attempt to route more than available results in a failure.
                        if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
-                                       &our_id, &payment_params, &network_graph, None, 15_001, 42, Arc::clone(&logger), &scorer) {
+                                       &our_id, &payment_params, &network_graph.read_only(), None, 15_001, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
                                assert_eq!(err, "Failed to find a sufficient route to the given destination");
                        } else { panic!(); }
                }
 
                {
                        // Now, attempt to route an exact amount we have should be fine.
-                       let route = get_route(&our_id, &payment_params, &network_graph, None, 15_000, 42, Arc::clone(&logger), &scorer).unwrap();
+                       let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 15_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                        assert_eq!(route.paths.len(), 1);
                        let path = route.paths.last().unwrap();
                        assert_eq!(path.len(), 2);
@@ -3348,14 +3468,14 @@ mod tests {
                {
                        // Attempt to route more than available results in a failure.
                        if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
-                                       &our_id, &payment_params, &network_graph, None, 15_001, 42, Arc::clone(&logger), &scorer) {
+                                       &our_id, &payment_params, &network_graph.read_only(), None, 15_001, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
                                assert_eq!(err, "Failed to find a sufficient route to the given destination");
                        } else { panic!(); }
                }
 
                {
                        // Now, attempt to route an exact amount we have should be fine.
-                       let route = get_route(&our_id, &payment_params, &network_graph, None, 15_000, 42, Arc::clone(&logger), &scorer).unwrap();
+                       let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 15_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                        assert_eq!(route.paths.len(), 1);
                        let path = route.paths.last().unwrap();
                        assert_eq!(path.len(), 2);
@@ -3380,14 +3500,14 @@ mod tests {
                {
                        // Attempt to route more than available results in a failure.
                        if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
-                                       &our_id, &payment_params, &network_graph, None, 10_001, 42, Arc::clone(&logger), &scorer) {
+                                       &our_id, &payment_params, &network_graph.read_only(), None, 10_001, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
                                assert_eq!(err, "Failed to find a sufficient route to the given destination");
                        } else { panic!(); }
                }
 
                {
                        // Now, attempt to route an exact amount we have should be fine.
-                       let route = get_route(&our_id, &payment_params, &network_graph, None, 10_000, 42, Arc::clone(&logger), &scorer).unwrap();
+                       let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 10_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                        assert_eq!(route.paths.len(), 1);
                        let path = route.paths.last().unwrap();
                        assert_eq!(path.len(), 2);
@@ -3403,6 +3523,8 @@ mod tests {
                let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
                let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
                let payment_params = PaymentParameters::from_node_id(nodes[3]).with_features(InvoiceFeatures::known());
 
                // Path via {node7, node2, node4} is channels {12, 13, 6, 11}.
@@ -3489,14 +3611,14 @@ mod tests {
                {
                        // Attempt to route more than available results in a failure.
                        if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
-                                       &our_id, &payment_params, &network_graph, None, 60_000, 42, Arc::clone(&logger), &scorer) {
+                                       &our_id, &payment_params, &network_graph.read_only(), None, 60_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
                                assert_eq!(err, "Failed to find a sufficient route to the given destination");
                        } else { panic!(); }
                }
 
                {
                        // Now, attempt to route 49 sats (just a bit below the capacity).
-                       let route = get_route(&our_id, &payment_params, &network_graph, None, 49_000, 42, Arc::clone(&logger), &scorer).unwrap();
+                       let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 49_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                        assert_eq!(route.paths.len(), 1);
                        let mut total_amount_paid_msat = 0;
                        for path in &route.paths {
@@ -3509,7 +3631,7 @@ mod tests {
 
                {
                        // Attempt to route an exact amount is also fine
-                       let route = get_route(&our_id, &payment_params, &network_graph, None, 50_000, 42, Arc::clone(&logger), &scorer).unwrap();
+                       let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 50_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                        assert_eq!(route.paths.len(), 1);
                        let mut total_amount_paid_msat = 0;
                        for path in &route.paths {
@@ -3526,6 +3648,8 @@ mod tests {
                let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
                let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
                let payment_params = PaymentParameters::from_node_id(nodes[2]);
 
                // Path via node0 is channels {1, 3}. Limit them to 100 and 50 sats (total limit 50).
@@ -3555,7 +3679,7 @@ mod tests {
                });
 
                {
-                       let route = get_route(&our_id, &payment_params, &network_graph, None, 50_000, 42, Arc::clone(&logger), &scorer).unwrap();
+                       let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 50_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                        assert_eq!(route.paths.len(), 1);
                        let mut total_amount_paid_msat = 0;
                        for path in &route.paths {
@@ -3572,6 +3696,8 @@ mod tests {
                let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
                let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
                let payment_params = PaymentParameters::from_node_id(nodes[2]).with_features(InvoiceFeatures::known());
 
                // We need a route consisting of 3 paths:
@@ -3665,7 +3791,7 @@ mod tests {
                {
                        // Attempt to route more than available results in a failure.
                        if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
-                                       &our_id, &payment_params, &network_graph, None, 300_000, 42, Arc::clone(&logger), &scorer) {
+                                       &our_id, &payment_params, &network_graph.read_only(), None, 300_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
                                assert_eq!(err, "Failed to find a sufficient route to the given destination");
                        } else { panic!(); }
                }
@@ -3673,7 +3799,7 @@ mod tests {
                {
                        // Now, attempt to route 250 sats (just a bit below the capacity).
                        // Our algorithm should provide us with these 3 paths.
-                       let route = get_route(&our_id, &payment_params, &network_graph, None, 250_000, 42, Arc::clone(&logger), &scorer).unwrap();
+                       let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 250_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                        assert_eq!(route.paths.len(), 3);
                        let mut total_amount_paid_msat = 0;
                        for path in &route.paths {
@@ -3686,7 +3812,7 @@ mod tests {
 
                {
                        // Attempt to route an exact amount is also fine
-                       let route = get_route(&our_id, &payment_params, &network_graph, None, 290_000, 42, Arc::clone(&logger), &scorer).unwrap();
+                       let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 290_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                        assert_eq!(route.paths.len(), 3);
                        let mut total_amount_paid_msat = 0;
                        for path in &route.paths {
@@ -3703,6 +3829,8 @@ mod tests {
                let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
                let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
                let payment_params = PaymentParameters::from_node_id(nodes[3]).with_features(InvoiceFeatures::known());
 
                // We need a route consisting of 3 paths:
@@ -3839,7 +3967,7 @@ mod tests {
                {
                        // Attempt to route more than available results in a failure.
                        if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
-                                       &our_id, &payment_params, &network_graph, None, 350_000, 42, Arc::clone(&logger), &scorer) {
+                                       &our_id, &payment_params, &network_graph.read_only(), None, 350_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
                                assert_eq!(err, "Failed to find a sufficient route to the given destination");
                        } else { panic!(); }
                }
@@ -3847,7 +3975,7 @@ mod tests {
                {
                        // Now, attempt to route 300 sats (exact amount we can route).
                        // Our algorithm should provide us with these 3 paths, 100 sats each.
-                       let route = get_route(&our_id, &payment_params, &network_graph, None, 300_000, 42, Arc::clone(&logger), &scorer).unwrap();
+                       let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 300_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                        assert_eq!(route.paths.len(), 3);
 
                        let mut total_amount_paid_msat = 0;
@@ -3865,6 +3993,8 @@ mod tests {
                let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
                let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
                let payment_params = PaymentParameters::from_node_id(nodes[3]).with_features(InvoiceFeatures::known());
 
                // This test checks that if we have two cheaper paths and one more expensive path,
@@ -4005,7 +4135,7 @@ mod tests {
                {
                        // Now, attempt to route 180 sats.
                        // Our algorithm should provide us with these 2 paths.
-                       let route = get_route(&our_id, &payment_params, &network_graph, None, 180_000, 42, Arc::clone(&logger), &scorer).unwrap();
+                       let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 180_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                        assert_eq!(route.paths.len(), 2);
 
                        let mut total_value_transferred_msat = 0;
@@ -4032,6 +4162,8 @@ mod tests {
                let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
                let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
                let payment_params = PaymentParameters::from_node_id(nodes[3]).with_features(InvoiceFeatures::known());
 
                // We need a route consisting of 2 paths:
@@ -4173,14 +4305,14 @@ mod tests {
                {
                        // Attempt to route more than available results in a failure.
                        if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
-                                       &our_id, &payment_params, &network_graph, None, 210_000, 42, Arc::clone(&logger), &scorer) {
+                                       &our_id, &payment_params, &network_graph.read_only(), None, 210_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
                                assert_eq!(err, "Failed to find a sufficient route to the given destination");
                        } else { panic!(); }
                }
 
                {
                        // Now, attempt to route 200 sats (exact amount we can route).
-                       let route = get_route(&our_id, &payment_params, &network_graph, None, 200_000, 42, Arc::clone(&logger), &scorer).unwrap();
+                       let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 200_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                        assert_eq!(route.paths.len(), 2);
 
                        let mut total_amount_paid_msat = 0;
@@ -4211,6 +4343,8 @@ mod tests {
                let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
                let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
                let payment_params = PaymentParameters::from_node_id(PublicKey::from_slice(&[02; 33]).unwrap()).with_features(InvoiceFeatures::known())
                        .with_route_hints(vec![RouteHint(vec![RouteHintHop {
                                src_node_id: nodes[2],
@@ -4277,7 +4411,7 @@ mod tests {
 
                // Get a route for 100 sats and check that we found the MPP route no problem and didn't
                // overpay at all.
-               let route = get_route(&our_id, &payment_params, &network_graph, None, 100_000, 42, Arc::clone(&logger), &scorer).unwrap();
+               let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                assert_eq!(route.paths.len(), 2);
                // Paths are somewhat randomly ordered, but:
                // * the first is channel 2 (1 msat fee) -> channel 4 -> channel 42
@@ -4299,6 +4433,8 @@ mod tests {
                let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
                let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
                let payment_params = PaymentParameters::from_node_id(nodes[2]).with_features(InvoiceFeatures::known());
 
                // We need a route consisting of 3 paths:
@@ -4391,7 +4527,7 @@ mod tests {
                {
                        // Attempt to route more than available results in a failure.
                        if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
-                                       &our_id, &payment_params, &network_graph, None, 150_000, 42, Arc::clone(&logger), &scorer) {
+                                       &our_id, &payment_params, &network_graph.read_only(), None, 150_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
                                assert_eq!(err, "Failed to find a sufficient route to the given destination");
                        } else { panic!(); }
                }
@@ -4399,7 +4535,7 @@ mod tests {
                {
                        // Now, attempt to route 125 sats (just a bit below the capacity of 3 channels).
                        // Our algorithm should provide us with these 3 paths.
-                       let route = get_route(&our_id, &payment_params, &network_graph, None, 125_000, 42, Arc::clone(&logger), &scorer).unwrap();
+                       let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 125_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                        assert_eq!(route.paths.len(), 3);
                        let mut total_amount_paid_msat = 0;
                        for path in &route.paths {
@@ -4412,7 +4548,7 @@ mod tests {
 
                {
                        // Attempt to route without the last small cheap channel
-                       let route = get_route(&our_id, &payment_params, &network_graph, None, 90_000, 42, Arc::clone(&logger), &scorer).unwrap();
+                       let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 90_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                        assert_eq!(route.paths.len(), 2);
                        let mut total_amount_paid_msat = 0;
                        for path in &route.paths {
@@ -4452,10 +4588,12 @@ mod tests {
                // "previous hop" being set to node 3, creating a loop in the path.
                let secp_ctx = Secp256k1::new();
                let logger = Arc::new(test_utils::TestLogger::new());
-               let network_graph = Arc::new(NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash()));
-               let net_graph_msg_handler = NetGraphMsgHandler::new(Arc::clone(&network_graph), None, Arc::clone(&logger));
+               let network = Arc::new(NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash()));
+               let net_graph_msg_handler = NetGraphMsgHandler::new(Arc::clone(&network), None, Arc::clone(&logger));
                let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
                let payment_params = PaymentParameters::from_node_id(nodes[6]);
 
                add_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, &privkeys[1], ChannelFeatures::from_le_bytes(id_to_feature_flags(6)), 6);
@@ -4549,7 +4687,7 @@ mod tests {
 
                {
                        // Now ensure the route flows simply over nodes 1 and 4 to 6.
-                       let route = get_route(&our_id, &payment_params, &network_graph, None, 10_000, 42, Arc::clone(&logger), &scorer).unwrap();
+                       let route = get_route(&our_id, &payment_params, &network.read_only(), None, 10_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                        assert_eq!(route.paths.len(), 1);
                        assert_eq!(route.paths[0].len(), 3);
 
@@ -4585,6 +4723,8 @@ mod tests {
                let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
                let (our_privkey, our_id, _, nodes) = get_nodes(&secp_ctx);
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
                let payment_params = PaymentParameters::from_node_id(nodes[2]);
 
                // We modify the graph to set the htlc_maximum of channel 2 to below the value we wish to
@@ -4618,7 +4758,7 @@ mod tests {
                {
                        // Now, attempt to route 90 sats, which is exactly 90 sats at the last hop, plus the
                        // 200% fee charged channel 13 in the 1-to-2 direction.
-                       let route = get_route(&our_id, &payment_params, &network_graph, None, 90_000, 42, Arc::clone(&logger), &scorer).unwrap();
+                       let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 90_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                        assert_eq!(route.paths.len(), 1);
                        assert_eq!(route.paths[0].len(), 2);
 
@@ -4647,6 +4787,8 @@ mod tests {
                let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
                let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
                let payment_params = PaymentParameters::from_node_id(nodes[2]).with_features(InvoiceFeatures::known());
 
                // We modify the graph to set the htlc_minimum of channel 2 and 4 as needed - channel 2
@@ -4681,7 +4823,7 @@ mod tests {
                        // Now, attempt to route 90 sats, hitting the htlc_minimum on channel 4, but
                        // overshooting the htlc_maximum on channel 2. Thus, we should pick the (absurdly
                        // expensive) channels 12-13 path.
-                       let route = get_route(&our_id, &payment_params, &network_graph, None, 90_000, 42, Arc::clone(&logger), &scorer).unwrap();
+                       let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 90_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                        assert_eq!(route.paths.len(), 1);
                        assert_eq!(route.paths[0].len(), 2);
 
@@ -4715,12 +4857,14 @@ mod tests {
                let network_graph = NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash());
                let scorer = test_utils::TestScorer::with_penalty(0);
                let payment_params = PaymentParameters::from_node_id(nodes[0]).with_features(InvoiceFeatures::known());
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
 
                {
-                       let route = get_route(&our_id, &payment_params, &network_graph, Some(&[
+                       let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&[
                                &get_channel_details(Some(3), nodes[0], InitFeatures::known(), 200_000),
                                &get_channel_details(Some(2), nodes[0], InitFeatures::known(), 10_000),
-                       ]), 100_000, 42, Arc::clone(&logger), &scorer).unwrap();
+                       ]), 100_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                        assert_eq!(route.paths.len(), 1);
                        assert_eq!(route.paths[0].len(), 1);
 
@@ -4729,10 +4873,10 @@ mod tests {
                        assert_eq!(route.paths[0][0].fee_msat, 100_000);
                }
                {
-                       let route = get_route(&our_id, &payment_params, &network_graph, Some(&[
+                       let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&[
                                &get_channel_details(Some(3), nodes[0], InitFeatures::known(), 50_000),
                                &get_channel_details(Some(2), nodes[0], InitFeatures::known(), 50_000),
-                       ]), 100_000, 42, Arc::clone(&logger), &scorer).unwrap();
+                       ]), 100_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                        assert_eq!(route.paths.len(), 2);
                        assert_eq!(route.paths[0].len(), 1);
                        assert_eq!(route.paths[1].len(), 1);
@@ -4755,9 +4899,11 @@ mod tests {
 
                // Without penalizing each hop 100 msats, a longer path with lower fees is chosen.
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
                let route = get_route(
-                       &our_id, &payment_params, &network_graph, None, 100, 42,
-                       Arc::clone(&logger), &scorer
+                       &our_id, &payment_params, &network_graph.read_only(), None, 100, 42,
+                       Arc::clone(&logger), &scorer, &random_seed_bytes
                ).unwrap();
                let path = route.paths[0].iter().map(|hop| hop.short_channel_id).collect::<Vec<_>>();
 
@@ -4769,8 +4915,8 @@ mod tests {
                // from nodes[2] rather than channel 6, 11, and 8, even though the longer path is cheaper.
                let scorer = test_utils::TestScorer::with_penalty(100);
                let route = get_route(
-                       &our_id, &payment_params, &network_graph, None, 100, 42,
-                       Arc::clone(&logger), &scorer
+                       &our_id, &payment_params, &network_graph.read_only(), None, 100, 42,
+                       Arc::clone(&logger), &scorer, &random_seed_bytes
                ).unwrap();
                let path = route.paths[0].iter().map(|hop| hop.short_channel_id).collect::<Vec<_>>();
 
@@ -4816,15 +4962,18 @@ mod tests {
 
        #[test]
        fn avoids_routing_through_bad_channels_and_nodes() {
-               let (secp_ctx, network_graph, _, _, logger) = build_graph();
+               let (secp_ctx, network, _, _, logger) = build_graph();
                let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
                let payment_params = PaymentParameters::from_node_id(nodes[6]).with_route_hints(last_hops(&nodes));
+               let network_graph = network.read_only();
 
                // A path to nodes[6] exists when no penalties are applied to any channel.
                let scorer = test_utils::TestScorer::with_penalty(0);
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
                let route = get_route(
                        &our_id, &payment_params, &network_graph, None, 100, 42,
-                       Arc::clone(&logger), &scorer
+                       Arc::clone(&logger), &scorer, &random_seed_bytes
                ).unwrap();
                let path = route.paths[0].iter().map(|hop| hop.short_channel_id).collect::<Vec<_>>();
 
@@ -4836,7 +4985,7 @@ mod tests {
                let scorer = BadChannelScorer { short_channel_id: 6 };
                let route = get_route(
                        &our_id, &payment_params, &network_graph, None, 100, 42,
-                       Arc::clone(&logger), &scorer
+                       Arc::clone(&logger), &scorer, &random_seed_bytes
                ).unwrap();
                let path = route.paths[0].iter().map(|hop| hop.short_channel_id).collect::<Vec<_>>();
 
@@ -4848,7 +4997,7 @@ mod tests {
                let scorer = BadNodeScorer { node_id: NodeId::from_pubkey(&nodes[2]) };
                match get_route(
                        &our_id, &payment_params, &network_graph, None, 100, 42,
-                       Arc::clone(&logger), &scorer
+                       Arc::clone(&logger), &scorer, &random_seed_bytes
                ) {
                        Err(LightningError { err, .. } ) => {
                                assert_eq!(err, "Failed to find a path to the given destination");
@@ -4930,8 +5079,9 @@ mod tests {
 
        #[test]
        fn limits_total_cltv_delta() {
-               let (secp_ctx, network_graph, _, _, logger) = build_graph();
+               let (secp_ctx, network, _, _, logger) = build_graph();
                let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
+               let network_graph = network.read_only();
 
                let scorer = test_utils::TestScorer::with_penalty(0);
 
@@ -4939,7 +5089,9 @@ mod tests {
                let feasible_max_total_cltv_delta = 1008;
                let feasible_payment_params = PaymentParameters::from_node_id(nodes[6]).with_route_hints(last_hops(&nodes))
                        .with_max_total_cltv_expiry_delta(feasible_max_total_cltv_delta);
-               let route = get_route(&our_id, &feasible_payment_params, &network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap();
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
+               let route = get_route(&our_id, &feasible_payment_params, &network_graph, None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
                let path = route.paths[0].iter().map(|hop| hop.short_channel_id).collect::<Vec<_>>();
                assert_ne!(path.len(), 0);
 
@@ -4947,7 +5099,7 @@ mod tests {
                let fail_max_total_cltv_delta = 23;
                let fail_payment_params = PaymentParameters::from_node_id(nodes[6]).with_route_hints(last_hops(&nodes))
                        .with_max_total_cltv_expiry_delta(fail_max_total_cltv_delta);
-               match get_route(&our_id, &fail_payment_params, &network_graph, None, 100, 42, Arc::clone(&logger), &scorer)
+               match get_route(&our_id, &fail_payment_params, &network_graph, None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes)
                {
                        Err(LightningError { err, .. } ) => {
                                assert_eq!(err, "Failed to find a path to the given destination");
@@ -4956,6 +5108,106 @@ mod tests {
                }
        }
 
+       #[test]
+       fn adds_and_limits_cltv_offset() {
+               let (secp_ctx, network_graph, _, _, logger) = build_graph();
+               let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
+
+               let scorer = test_utils::TestScorer::with_penalty(0);
+
+               let payment_params = PaymentParameters::from_node_id(nodes[6]).with_route_hints(last_hops(&nodes));
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
+               let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+               assert_eq!(route.paths.len(), 1);
+
+               let cltv_expiry_deltas_before = route.paths[0].iter().map(|h| h.cltv_expiry_delta).collect::<Vec<u32>>();
+
+               // Check whether the offset added to the last hop by default is in [1 .. DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA]
+               let mut route_default = route.clone();
+               add_random_cltv_offset(&mut route_default, &payment_params, &network_graph.read_only(), &random_seed_bytes);
+               let cltv_expiry_deltas_default = route_default.paths[0].iter().map(|h| h.cltv_expiry_delta).collect::<Vec<u32>>();
+               assert_eq!(cltv_expiry_deltas_before.split_last().unwrap().1, cltv_expiry_deltas_default.split_last().unwrap().1);
+               assert!(cltv_expiry_deltas_default.last() > cltv_expiry_deltas_before.last());
+               assert!(cltv_expiry_deltas_default.last().unwrap() <= &DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA);
+
+               // Check that no offset is added when we restrict the max_total_cltv_expiry_delta
+               let mut route_limited = route.clone();
+               let limited_max_total_cltv_expiry_delta = cltv_expiry_deltas_before.iter().sum();
+               let limited_payment_params = payment_params.with_max_total_cltv_expiry_delta(limited_max_total_cltv_expiry_delta);
+               add_random_cltv_offset(&mut route_limited, &limited_payment_params, &network_graph.read_only(), &random_seed_bytes);
+               let cltv_expiry_deltas_limited = route_limited.paths[0].iter().map(|h| h.cltv_expiry_delta).collect::<Vec<u32>>();
+               assert_eq!(cltv_expiry_deltas_before, cltv_expiry_deltas_limited);
+       }
+
+       #[test]
+       fn adds_plausible_cltv_offset() {
+               let (secp_ctx, network, _, _, logger) = build_graph();
+               let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
+               let network_graph = network.read_only();
+               let network_nodes = network_graph.nodes();
+               let network_channels = network_graph.channels();
+               let scorer = test_utils::TestScorer::with_penalty(0);
+               let payment_params = PaymentParameters::from_node_id(nodes[3]);
+               let keys_manager = test_utils::TestKeysInterface::new(&[4u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
+
+               let mut route = get_route(&our_id, &payment_params, &network_graph, None, 100, 0,
+                                                                 Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+               add_random_cltv_offset(&mut route, &payment_params, &network_graph, &random_seed_bytes);
+
+               let mut path_plausibility = vec![];
+
+               for p in route.paths {
+                       // 1. Select random observation point
+                       let mut prng = ChaCha20::new(&random_seed_bytes, &[0u8; 12]);
+                       let mut random_bytes = [0u8; ::core::mem::size_of::<usize>()];
+
+                       prng.process_in_place(&mut random_bytes);
+                       let random_path_index = usize::from_be_bytes(random_bytes).wrapping_rem(p.len());
+                       let observation_point = NodeId::from_pubkey(&p.get(random_path_index).unwrap().pubkey);
+
+                       // 2. Calculate what CLTV expiry delta we would observe there
+                       let observed_cltv_expiry_delta: u32 = p[random_path_index..].iter().map(|h| h.cltv_expiry_delta).sum();
+
+                       // 3. Starting from the observation point, find candidate paths
+                       let mut candidates: VecDeque<(NodeId, Vec<u32>)> = VecDeque::new();
+                       candidates.push_back((observation_point, vec![]));
+
+                       let mut found_plausible_candidate = false;
+
+                       'candidate_loop: while let Some((cur_node_id, cur_path_cltv_deltas)) = candidates.pop_front() {
+                               if let Some(remaining) = observed_cltv_expiry_delta.checked_sub(cur_path_cltv_deltas.iter().sum::<u32>()) {
+                                       if remaining == 0 || remaining.wrapping_rem(40) == 0 || remaining.wrapping_rem(144) == 0 {
+                                               found_plausible_candidate = true;
+                                               break 'candidate_loop;
+                                       }
+                               }
+
+                               if let Some(cur_node) = network_nodes.get(&cur_node_id) {
+                                       for channel_id in &cur_node.channels {
+                                               if let Some(channel_info) = network_channels.get(&channel_id) {
+                                                       if let Some((dir_info, next_id)) = channel_info.as_directed_from(&cur_node_id) {
+                                                               if let Some(channel_update_info) = dir_info.direction() {
+                                                                       let next_cltv_expiry_delta = channel_update_info.cltv_expiry_delta as u32;
+                                                                       if cur_path_cltv_deltas.iter().sum::<u32>()
+                                                                               .saturating_add(next_cltv_expiry_delta) <= observed_cltv_expiry_delta {
+                                                                               let mut new_path_cltv_deltas = cur_path_cltv_deltas.clone();
+                                                                               new_path_cltv_deltas.push(next_cltv_expiry_delta);
+                                                                               candidates.push_back((*next_id, new_path_cltv_deltas));
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+
+                       path_plausibility.push(found_plausible_candidate);
+               }
+               assert!(path_plausibility.iter().all(|x| *x));
+       }
+
        #[cfg(not(feature = "no-std"))]
        pub(super) fn random_init_seed() -> u64 {
                // Because the default HashMap in std pulls OS randomness, we can use it as a (bad) RNG.
@@ -4970,6 +5222,8 @@ mod tests {
        #[test]
        #[cfg(not(feature = "no-std"))]
        fn generate_routes() {
+               use routing::scoring::{ProbabilisticScorer, ProbabilisticScoringParameters};
+
                let mut d = match super::test_utils::get_route_file() {
                        Ok(f) => f,
                        Err(e) => {
@@ -4978,6 +5232,8 @@ mod tests {
                        },
                };
                let graph = NetworkGraph::read(&mut d).unwrap();
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
 
                // First, get 100 (source, destination) pairs for which route-getting actually succeeds...
                let mut seed = random_init_seed() as usize;
@@ -4992,7 +5248,7 @@ mod tests {
                                let amt = seed as u64 % 200_000_000;
                                let params = ProbabilisticScoringParameters::default();
                                let scorer = ProbabilisticScorer::new(params, &graph);
-                               if get_route(src, &payment_params, &graph, None, amt, 42, &test_utils::TestLogger::new(), &scorer).is_ok() {
+                               if get_route(src, &payment_params, &graph.read_only(), None, amt, 42, &test_utils::TestLogger::new(), &scorer, &random_seed_bytes).is_ok() {
                                        continue 'load_endpoints;
                                }
                        }
@@ -5002,6 +5258,8 @@ mod tests {
        #[test]
        #[cfg(not(feature = "no-std"))]
        fn generate_routes_mpp() {
+               use routing::scoring::{ProbabilisticScorer, ProbabilisticScoringParameters};
+
                let mut d = match super::test_utils::get_route_file() {
                        Ok(f) => f,
                        Err(e) => {
@@ -5010,6 +5268,8 @@ mod tests {
                        },
                };
                let graph = NetworkGraph::read(&mut d).unwrap();
+               let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
 
                // First, get 100 (source, destination) pairs for which route-getting actually succeeds...
                let mut seed = random_init_seed() as usize;
@@ -5024,7 +5284,7 @@ mod tests {
                                let amt = seed as u64 % 200_000_000;
                                let params = ProbabilisticScoringParameters::default();
                                let scorer = ProbabilisticScorer::new(params, &graph);
-                               if get_route(src, &payment_params, &graph, None, amt, 42, &test_utils::TestLogger::new(), &scorer).is_ok() {
+                               if get_route(src, &payment_params, &graph.read_only(), None, amt, 42, &test_utils::TestLogger::new(), &scorer, &random_seed_bytes).is_ok() {
                                        continue 'load_endpoints;
                                }
                        }
@@ -5065,6 +5325,7 @@ mod benches {
        use bitcoin::hashes::Hash;
        use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
        use chain::transaction::OutPoint;
+       use chain::keysinterface::{KeysManager,KeysInterface};
        use ln::channelmanager::{ChannelCounterparty, ChannelDetails};
        use ln::features::{InitFeatures, InvoiceFeatures};
        use routing::scoring::{FixedPenaltyScorer, ProbabilisticScorer, ProbabilisticScoringParameters, Scorer};
@@ -5165,6 +5426,8 @@ mod benches {
        ) {
                let nodes = graph.read_only().nodes().clone();
                let payer = payer_pubkey();
+               let keys_manager = KeysManager::new(&[0u8; 32], 42, 42);
+               let random_seed_bytes = keys_manager.get_secure_random_bytes();
 
                // First, get 100 (source, destination) pairs for which route-getting actually succeeds...
                let mut routes = Vec::new();
@@ -5179,7 +5442,7 @@ mod benches {
                                let params = PaymentParameters::from_node_id(dst).with_features(features.clone());
                                let first_hop = first_hop(src);
                                let amt = seed as u64 % 1_000_000;
-                               if let Ok(route) = get_route(&payer, &params, &graph, Some(&[&first_hop]), amt, 42, &DummyLogger{}, &scorer) {
+                               if let Ok(route) = get_route(&payer, &params, &graph.read_only(), Some(&[&first_hop]), amt, 42, &DummyLogger{}, &scorer, &random_seed_bytes) {
                                        routes.push(route);
                                        route_endpoints.push((first_hop, params, amt));
                                        continue 'load_endpoints;
@@ -5206,7 +5469,7 @@ mod benches {
                let mut idx = 0;
                bench.iter(|| {
                        let (first_hop, params, amt) = &route_endpoints[idx % route_endpoints.len()];
-                       assert!(get_route(&payer, params, &graph, Some(&[first_hop]), *amt, 42, &DummyLogger{}, &scorer).is_ok());
+                       assert!(get_route(&payer, params, &graph.read_only(), Some(&[first_hop]), *amt, 42, &DummyLogger{}, &scorer, &random_seed_bytes).is_ok());
                        idx += 1;
                });
        }
index dfda3d2f867d189c7f36de9b45072c01bed98816..c39abd1d0ff71d2f613935959007b333204371b2 100644 (file)
@@ -20,6 +20,7 @@
 //! # use lightning::routing::network_graph::NetworkGraph;
 //! # use lightning::routing::router::{RouteParameters, find_route};
 //! # use lightning::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringParameters, Scorer, ScoringParameters};
+//! # use lightning::chain::keysinterface::{KeysManager, KeysInterface};
 //! # use lightning::util::logger::{Logger, Record};
 //! # use secp256k1::key::PublicKey;
 //! #
@@ -40,8 +41,9 @@
 //!     ..ProbabilisticScoringParameters::default()
 //! };
 //! let scorer = ProbabilisticScorer::new(params, &network_graph);
+//! # let random_seed_bytes = [42u8; 32];
 //!
-//! let route = find_route(&payer, &route_params, &network_graph, None, &logger, &scorer);
+//! let route = find_route(&payer, &route_params, &network_graph, None, &logger, &scorer, &random_seed_bytes);
 //! # }
 //! ```
 //!
@@ -189,6 +191,7 @@ impl<'a, S: Writeable> Writeable for MutexGuard<'a, S> {
        }
 }
 
+#[derive(Clone)]
 /// [`Score`] implementation that uses a fixed penalty.
 pub struct FixedPenaltyScorer {
        penalty_msat: u64,
@@ -252,6 +255,7 @@ pub struct ScorerUsingTime<T: Time> {
        channel_failures: HashMap<u64, ChannelFailure<T>>,
 }
 
+#[derive(Clone)]
 /// Parameters for configuring [`Scorer`].
 pub struct ScoringParameters {
        /// A fixed penalty in msats to apply to each channel.
@@ -646,20 +650,23 @@ impl<T: Time> ChannelLiquidity<T> {
 }
 
 impl<L: Deref<Target = u64>, T: Time, U: Deref<Target = T>> DirectedChannelLiquidity<L, T, U> {
-       /// Returns the success probability of routing the given HTLC `amount_msat` through the channel
-       /// in this direction.
-       fn success_probability(&self, amount_msat: u64) -> f64 {
+       /// Returns a penalty for routing the given HTLC `amount_msat` through the channel in this
+       /// direction.
+       fn penalty_msat(&self, amount_msat: u64, liquidity_penalty_multiplier_msat: u64) -> u64 {
                let max_liquidity_msat = self.max_liquidity_msat();
                let min_liquidity_msat = core::cmp::min(self.min_liquidity_msat(), max_liquidity_msat);
                if amount_msat > max_liquidity_msat {
-                       0.0
+                       u64::max_value()
                } else if amount_msat <= min_liquidity_msat {
-                       1.0
+                       0
                } else {
                        let numerator = max_liquidity_msat + 1 - amount_msat;
                        let denominator = max_liquidity_msat + 1 - min_liquidity_msat;
-                       numerator as f64 / denominator as f64
-               }.max(0.01) // Lower bound the success probability to ensure some channel is selected.
+                       approx::negative_log10_times_1024(numerator, denominator)
+                               .saturating_mul(liquidity_penalty_multiplier_msat) / 1024
+               }
+               // Upper bound the penalty to ensure some channel is selected.
+               .min(2 * liquidity_penalty_multiplier_msat)
        }
 
        /// Returns the lower bound of the channel liquidity balance in this direction.
@@ -733,15 +740,11 @@ impl<G: Deref<Target = NetworkGraph>, T: Time> Score for ProbabilisticScorerUsin
        ) -> u64 {
                let liquidity_penalty_multiplier_msat = self.params.liquidity_penalty_multiplier_msat;
                let liquidity_offset_half_life = self.params.liquidity_offset_half_life;
-               let success_probability = self.channel_liquidities
+               self.channel_liquidities
                        .get(&short_channel_id)
                        .unwrap_or(&ChannelLiquidity::new())
                        .as_directed(source, target, capacity_msat, liquidity_offset_half_life)
-                       .success_probability(amount_msat);
-               // NOTE: If success_probability is ever changed to return 0.0, log10 is undefined so return
-               // u64::max_value instead.
-               debug_assert!(success_probability > core::f64::EPSILON);
-               (-(success_probability.log10()) * liquidity_penalty_multiplier_msat as f64) as u64
+                       .penalty_msat(amount_msat, liquidity_penalty_multiplier_msat)
        }
 
        fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) {
@@ -798,6 +801,122 @@ impl<G: Deref<Target = NetworkGraph>, T: Time> Score for ProbabilisticScorerUsin
        }
 }
 
+mod approx {
+       const BITS: u32 = 64;
+       const HIGHEST_BIT: u32 = BITS - 1;
+       const LOWER_BITS: u32 = 4;
+       const LOWER_BITS_BOUND: u64 = 1 << LOWER_BITS;
+       const LOWER_BITMASK: u64 = (1 << LOWER_BITS) - 1;
+
+       /// Look-up table for `log10(x) * 1024` where row `i` is used for each `x` having `i` as the
+       /// most significant bit. The next 4 bits of `x`, if applicable, are used for the second index.
+       const LOG10_TIMES_1024: [[u16; LOWER_BITS_BOUND as usize]; BITS as usize] = [
+               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+               [308, 308, 308, 308, 308, 308, 308, 308, 489, 489, 489, 489, 489, 489, 489, 489],
+               [617, 617, 617, 617, 716, 716, 716, 716, 797, 797, 797, 797, 865, 865, 865, 865],
+               [925, 925, 977, 977, 1024, 1024, 1066, 1066, 1105, 1105, 1141, 1141, 1174, 1174, 1204, 1204],
+               [1233, 1260, 1285, 1309, 1332, 1354, 1375, 1394, 1413, 1431, 1449, 1466, 1482, 1497, 1513, 1527],
+               [1541, 1568, 1594, 1618, 1641, 1662, 1683, 1703, 1722, 1740, 1757, 1774, 1790, 1806, 1821, 1835],
+               [1850, 1876, 1902, 1926, 1949, 1970, 1991, 2011, 2030, 2048, 2065, 2082, 2098, 2114, 2129, 2144],
+               [2158, 2185, 2210, 2234, 2257, 2279, 2299, 2319, 2338, 2356, 2374, 2390, 2407, 2422, 2437, 2452],
+               [2466, 2493, 2518, 2542, 2565, 2587, 2608, 2627, 2646, 2665, 2682, 2699, 2715, 2731, 2746, 2760],
+               [2774, 2801, 2827, 2851, 2874, 2895, 2916, 2936, 2955, 2973, 2990, 3007, 3023, 3039, 3054, 3068],
+               [3083, 3110, 3135, 3159, 3182, 3203, 3224, 3244, 3263, 3281, 3298, 3315, 3331, 3347, 3362, 3377],
+               [3391, 3418, 3443, 3467, 3490, 3512, 3532, 3552, 3571, 3589, 3607, 3623, 3640, 3655, 3670, 3685],
+               [3699, 3726, 3751, 3775, 3798, 3820, 3841, 3860, 3879, 3898, 3915, 3932, 3948, 3964, 3979, 3993],
+               [4007, 4034, 4060, 4084, 4107, 4128, 4149, 4169, 4188, 4206, 4223, 4240, 4256, 4272, 4287, 4301],
+               [4316, 4343, 4368, 4392, 4415, 4436, 4457, 4477, 4496, 4514, 4531, 4548, 4564, 4580, 4595, 4610],
+               [4624, 4651, 4676, 4700, 4723, 4745, 4765, 4785, 4804, 4822, 4840, 4857, 4873, 4888, 4903, 4918],
+               [4932, 4959, 4984, 5009, 5031, 5053, 5074, 5093, 5112, 5131, 5148, 5165, 5181, 5197, 5212, 5226],
+               [5240, 5267, 5293, 5317, 5340, 5361, 5382, 5402, 5421, 5439, 5456, 5473, 5489, 5505, 5520, 5534],
+               [5549, 5576, 5601, 5625, 5648, 5670, 5690, 5710, 5729, 5747, 5764, 5781, 5797, 5813, 5828, 5843],
+               [5857, 5884, 5909, 5933, 5956, 5978, 5998, 6018, 6037, 6055, 6073, 6090, 6106, 6121, 6136, 6151],
+               [6165, 6192, 6217, 6242, 6264, 6286, 6307, 6326, 6345, 6364, 6381, 6398, 6414, 6430, 6445, 6459],
+               [6473, 6500, 6526, 6550, 6573, 6594, 6615, 6635, 6654, 6672, 6689, 6706, 6722, 6738, 6753, 6767],
+               [6782, 6809, 6834, 6858, 6881, 6903, 6923, 6943, 6962, 6980, 6998, 7014, 7030, 7046, 7061, 7076],
+               [7090, 7117, 7142, 7166, 7189, 7211, 7231, 7251, 7270, 7288, 7306, 7323, 7339, 7354, 7369, 7384],
+               [7398, 7425, 7450, 7475, 7497, 7519, 7540, 7560, 7578, 7597, 7614, 7631, 7647, 7663, 7678, 7692],
+               [7706, 7733, 7759, 7783, 7806, 7827, 7848, 7868, 7887, 7905, 7922, 7939, 7955, 7971, 7986, 8001],
+               [8015, 8042, 8067, 8091, 8114, 8136, 8156, 8176, 8195, 8213, 8231, 8247, 8263, 8279, 8294, 8309],
+               [8323, 8350, 8375, 8399, 8422, 8444, 8464, 8484, 8503, 8521, 8539, 8556, 8572, 8587, 8602, 8617],
+               [8631, 8658, 8684, 8708, 8730, 8752, 8773, 8793, 8811, 8830, 8847, 8864, 8880, 8896, 8911, 8925],
+               [8939, 8966, 8992, 9016, 9039, 9060, 9081, 9101, 9120, 9138, 9155, 9172, 9188, 9204, 9219, 9234],
+               [9248, 9275, 9300, 9324, 9347, 9369, 9389, 9409, 9428, 9446, 9464, 9480, 9497, 9512, 9527, 9542],
+               [9556, 9583, 9608, 9632, 9655, 9677, 9698, 9717, 9736, 9754, 9772, 9789, 9805, 9820, 9835, 9850],
+               [9864, 9891, 9917, 9941, 9963, 9985, 10006, 10026, 10044, 10063, 10080, 10097, 10113, 10129, 10144, 10158],
+               [10172, 10199, 10225, 10249, 10272, 10293, 10314, 10334, 10353, 10371, 10388, 10405, 10421, 10437, 10452, 10467],
+               [10481, 10508, 10533, 10557, 10580, 10602, 10622, 10642, 10661, 10679, 10697, 10713, 10730, 10745, 10760, 10775],
+               [10789, 10816, 10841, 10865, 10888, 10910, 10931, 10950, 10969, 10987, 11005, 11022, 11038, 11053, 11068, 11083],
+               [11097, 11124, 11150, 11174, 11196, 11218, 11239, 11259, 11277, 11296, 11313, 11330, 11346, 11362, 11377, 11391],
+               [11405, 11432, 11458, 11482, 11505, 11526, 11547, 11567, 11586, 11604, 11621, 11638, 11654, 11670, 11685, 11700],
+               [11714, 11741, 11766, 11790, 11813, 11835, 11855, 11875, 11894, 11912, 11930, 11946, 11963, 11978, 11993, 12008],
+               [12022, 12049, 12074, 12098, 12121, 12143, 12164, 12183, 12202, 12220, 12238, 12255, 12271, 12286, 12301, 12316],
+               [12330, 12357, 12383, 12407, 12429, 12451, 12472, 12492, 12511, 12529, 12546, 12563, 12579, 12595, 12610, 12624],
+               [12638, 12665, 12691, 12715, 12738, 12759, 12780, 12800, 12819, 12837, 12854, 12871, 12887, 12903, 12918, 12933],
+               [12947, 12974, 12999, 13023, 13046, 13068, 13088, 13108, 13127, 13145, 13163, 13179, 13196, 13211, 13226, 13241],
+               [13255, 13282, 13307, 13331, 13354, 13376, 13397, 13416, 13435, 13453, 13471, 13488, 13504, 13519, 13535, 13549],
+               [13563, 13590, 13616, 13640, 13662, 13684, 13705, 13725, 13744, 13762, 13779, 13796, 13812, 13828, 13843, 13857],
+               [13871, 13898, 13924, 13948, 13971, 13992, 14013, 14033, 14052, 14070, 14087, 14104, 14120, 14136, 14151, 14166],
+               [14180, 14207, 14232, 14256, 14279, 14301, 14321, 14341, 14360, 14378, 14396, 14412, 14429, 14444, 14459, 14474],
+               [14488, 14515, 14540, 14564, 14587, 14609, 14630, 14649, 14668, 14686, 14704, 14721, 14737, 14752, 14768, 14782],
+               [14796, 14823, 14849, 14873, 14895, 14917, 14938, 14958, 14977, 14995, 15012, 15029, 15045, 15061, 15076, 15090],
+               [15104, 15131, 15157, 15181, 15204, 15225, 15246, 15266, 15285, 15303, 15320, 15337, 15353, 15369, 15384, 15399],
+               [15413, 15440, 15465, 15489, 15512, 15534, 15554, 15574, 15593, 15611, 15629, 15645, 15662, 15677, 15692, 15707],
+               [15721, 15748, 15773, 15797, 15820, 15842, 15863, 15882, 15901, 15919, 15937, 15954, 15970, 15985, 16001, 16015],
+               [16029, 16056, 16082, 16106, 16128, 16150, 16171, 16191, 16210, 16228, 16245, 16262, 16278, 16294, 16309, 16323],
+               [16337, 16364, 16390, 16414, 16437, 16458, 16479, 16499, 16518, 16536, 16553, 16570, 16586, 16602, 16617, 16632],
+               [16646, 16673, 16698, 16722, 16745, 16767, 16787, 16807, 16826, 16844, 16862, 16878, 16895, 16910, 16925, 16940],
+               [16954, 16981, 17006, 17030, 17053, 17075, 17096, 17115, 17134, 17152, 17170, 17187, 17203, 17218, 17234, 17248],
+               [17262, 17289, 17315, 17339, 17361, 17383, 17404, 17424, 17443, 17461, 17478, 17495, 17511, 17527, 17542, 17556],
+               [17571, 17597, 17623, 17647, 17670, 17691, 17712, 17732, 17751, 17769, 17786, 17803, 17819, 17835, 17850, 17865],
+               [17879, 17906, 17931, 17955, 17978, 18000, 18020, 18040, 18059, 18077, 18095, 18111, 18128, 18143, 18158, 18173],
+               [18187, 18214, 18239, 18263, 18286, 18308, 18329, 18348, 18367, 18385, 18403, 18420, 18436, 18452, 18467, 18481],
+               [18495, 18522, 18548, 18572, 18595, 18616, 18637, 18657, 18676, 18694, 18711, 18728, 18744, 18760, 18775, 18789],
+               [18804, 18830, 18856, 18880, 18903, 18924, 18945, 18965, 18984, 19002, 19019, 19036, 19052, 19068, 19083, 19098],
+               [19112, 19139, 19164, 19188, 19211, 19233, 19253, 19273, 19292, 19310, 19328, 19344, 19361, 19376, 19391, 19406],
+               [19420, 19447, 19472, 19496, 19519, 19541, 19562, 19581, 19600, 19619, 19636, 19653, 19669, 19685, 19700, 19714],
+       ];
+
+       /// Approximate `log10(numerator / denominator) * 1024` using a look-up table.
+       #[inline]
+       pub fn negative_log10_times_1024(numerator: u64, denominator: u64) -> u64 {
+               // Multiply the -1 through to avoid needing to use signed numbers.
+               (log10_times_1024(denominator) - log10_times_1024(numerator)) as u64
+       }
+
+       #[inline]
+       fn log10_times_1024(x: u64) -> u16 {
+               debug_assert_ne!(x, 0);
+               let most_significant_bit = HIGHEST_BIT - x.leading_zeros();
+               let lower_bits = (x >> most_significant_bit.saturating_sub(LOWER_BITS)) & LOWER_BITMASK;
+               LOG10_TIMES_1024[most_significant_bit as usize][lower_bits as usize]
+       }
+
+       #[cfg(test)]
+       mod tests {
+               use super::*;
+
+               #[test]
+               fn prints_negative_log10_times_1024_lookup_table() {
+                       for msb in 0..BITS {
+                               for i in 0..LOWER_BITS_BOUND {
+                                       let x = ((LOWER_BITS_BOUND + i) << (HIGHEST_BIT - LOWER_BITS)) >> (HIGHEST_BIT - msb);
+                                       let log10_times_1024 = ((x as f64).log10() * 1024.0).round() as u16;
+                                       assert_eq!(log10_times_1024, LOG10_TIMES_1024[msb as usize][i as usize]);
+
+                                       if i % LOWER_BITS_BOUND == 0 {
+                                               print!("\t\t[{}, ", log10_times_1024);
+                                       } else if i % LOWER_BITS_BOUND == LOWER_BITS_BOUND - 1 {
+                                               println!("{}],", log10_times_1024);
+                                       } else {
+                                               print!("{}, ", log10_times_1024);
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
 impl<G: Deref<Target = NetworkGraph>, T: Time> Writeable for ProbabilisticScorerUsingTime<G, T> {
        #[inline]
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
@@ -1612,18 +1731,18 @@ mod tests {
                let source = source_node_id();
                let target = target_node_id();
 
-               assert_eq!(scorer.channel_penalty_msat(42, 100, 100_000, &source, &target), 0);
-               assert_eq!(scorer.channel_penalty_msat(42, 1_000, 100_000, &source, &target), 4);
-               assert_eq!(scorer.channel_penalty_msat(42, 10_000, 100_000, &source, &target), 45);
-               assert_eq!(scorer.channel_penalty_msat(42, 100_000, 100_000, &source, &target), 2_000);
+               assert_eq!(scorer.channel_penalty_msat(42, 1_024, 1_024_000, &source, &target), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, 10_240, 1_024_000, &source, &target), 14);
+               assert_eq!(scorer.channel_penalty_msat(42, 102_400, 1_024_000, &source, &target), 43);
+               assert_eq!(scorer.channel_penalty_msat(42, 1_024_000, 1_024_000, &source, &target), 2_000);
 
-               assert_eq!(scorer.channel_penalty_msat(42, 125, 1_000, &source, &target), 57);
-               assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 124);
-               assert_eq!(scorer.channel_penalty_msat(42, 375, 1_000, &source, &target), 203);
-               assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 300);
-               assert_eq!(scorer.channel_penalty_msat(42, 625, 1_000, &source, &target), 425);
-               assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 600);
-               assert_eq!(scorer.channel_penalty_msat(42, 875, 1_000, &source, &target), 900);
+               assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 58);
+               assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 125);
+               assert_eq!(scorer.channel_penalty_msat(42, 374, 1_024, &source, &target), 204);
+               assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 301);
+               assert_eq!(scorer.channel_penalty_msat(42, 640, 1_024, &source, &target), 426);
+               assert_eq!(scorer.channel_penalty_msat(42, 768, 1_024, &source, &target), 602);
+               assert_eq!(scorer.channel_penalty_msat(42, 896, 1_024, &source, &target), 903);
        }
 
        #[test]
@@ -1679,9 +1798,9 @@ mod tests {
                let target = target_node_id();
                let path = payment_path_for_amount(500);
 
-               assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 124);
+               assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 128);
                assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 300);
-               assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 600);
+               assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 601);
 
                scorer.payment_path_failed(&path.iter().collect::<Vec<_>>(), 43);
 
@@ -1701,9 +1820,9 @@ mod tests {
                let target = target_node_id();
                let path = payment_path_for_amount(500);
 
-               assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 124);
+               assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 128);
                assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 300);
-               assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 600);
+               assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 601);
 
                scorer.payment_path_failed(&path.iter().collect::<Vec<_>>(), 42);
 
@@ -1725,13 +1844,13 @@ mod tests {
                let recipient = recipient_node_id();
                let path = payment_path_for_amount(500);
 
-               assert_eq!(scorer.channel_penalty_msat(41, 250, 1_000, &sender, &source), 124);
-               assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 124);
-               assert_eq!(scorer.channel_penalty_msat(43, 250, 1_000, &target, &recipient), 124);
+               assert_eq!(scorer.channel_penalty_msat(41, 250, 1_000, &sender, &source), 128);
+               assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 128);
+               assert_eq!(scorer.channel_penalty_msat(43, 250, 1_000, &target, &recipient), 128);
 
                scorer.payment_path_successful(&path.iter().collect::<Vec<_>>());
 
-               assert_eq!(scorer.channel_penalty_msat(41, 250, 1_000, &sender, &source), 124);
+               assert_eq!(scorer.channel_penalty_msat(41, 250, 1_000, &sender, &source), 128);
                assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 300);
                assert_eq!(scorer.channel_penalty_msat(43, 250, 1_000, &target, &recipient), 300);
        }
@@ -1754,20 +1873,20 @@ mod tests {
                scorer.payment_path_failed(&payment_path_for_amount(128).iter().collect::<Vec<_>>(), 43);
 
                assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 0);
-               assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 92);
-               assert_eq!(scorer.channel_penalty_msat(42, 768, 1_024, &source, &target), 1_424);
+               assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 97);
+               assert_eq!(scorer.channel_penalty_msat(42, 768, 1_024, &source, &target), 1_409);
                assert_eq!(scorer.channel_penalty_msat(42, 896, 1_024, &source, &target), 2_000);
 
                SinceEpoch::advance(Duration::from_secs(9));
                assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 0);
-               assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 92);
-               assert_eq!(scorer.channel_penalty_msat(42, 768, 1_024, &source, &target), 1_424);
+               assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 97);
+               assert_eq!(scorer.channel_penalty_msat(42, 768, 1_024, &source, &target), 1_409);
                assert_eq!(scorer.channel_penalty_msat(42, 896, 1_024, &source, &target), 2_000);
 
                SinceEpoch::advance(Duration::from_secs(1));
                assert_eq!(scorer.channel_penalty_msat(42, 64, 1_024, &source, &target), 0);
                assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 34);
-               assert_eq!(scorer.channel_penalty_msat(42, 896, 1_024, &source, &target), 1_812);
+               assert_eq!(scorer.channel_penalty_msat(42, 896, 1_024, &source, &target), 1_773);
                assert_eq!(scorer.channel_penalty_msat(42, 960, 1_024, &source, &target), 2_000);
 
                // Fully decay liquidity lower bound.
@@ -1797,18 +1916,18 @@ mod tests {
                let mut scorer = ProbabilisticScorer::new(params, &network_graph);
                let source = source_node_id();
                let target = target_node_id();
-               assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 124);
+               assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 125);
 
                scorer.payment_path_failed(&payment_path_for_amount(512).iter().collect::<Vec<_>>(), 42);
-               assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 281);
+               assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 274);
 
                // An unchecked right shift 64 bits or more in DirectedChannelLiquidity::decayed_offset_msat
                // would cause an overflow.
                SinceEpoch::advance(Duration::from_secs(10 * 64));
-               assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 124);
+               assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 125);
 
                SinceEpoch::advance(Duration::from_secs(10));
-               assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 124);
+               assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 125);
        }
 
        #[test]
@@ -1822,30 +1941,30 @@ mod tests {
                let source = source_node_id();
                let target = target_node_id();
 
-               assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 300);
+               assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 301);
 
                // More knowledge gives higher confidence (256, 768), meaning a lower penalty.
                scorer.payment_path_failed(&payment_path_for_amount(768).iter().collect::<Vec<_>>(), 42);
                scorer.payment_path_failed(&payment_path_for_amount(256).iter().collect::<Vec<_>>(), 43);
-               assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 281);
+               assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 274);
 
                // Decaying knowledge gives less confidence (128, 896), meaning a higher penalty.
                SinceEpoch::advance(Duration::from_secs(10));
-               assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 293);
+               assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 301);
 
                // Reducing the upper bound gives more confidence (128, 832) that the payment amount (512)
                // is closer to the upper bound, meaning a higher penalty.
                scorer.payment_path_successful(&payment_path_for_amount(64).iter().collect::<Vec<_>>());
-               assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 333);
+               assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 342);
 
                // Increasing the lower bound gives more confidence (256, 832) that the payment amount (512)
                // is closer to the lower bound, meaning a lower penalty.
                scorer.payment_path_failed(&payment_path_for_amount(256).iter().collect::<Vec<_>>(), 43);
-               assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 247);
+               assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 255);
 
                // Further decaying affects the lower bound more than the upper bound (128, 928).
                SinceEpoch::advance(Duration::from_secs(10));
-               assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 280);
+               assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 284);
        }
 
        #[test]
@@ -1863,7 +1982,7 @@ mod tests {
                assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 2_000);
 
                SinceEpoch::advance(Duration::from_secs(10));
-               assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 475);
+               assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 472);
 
                scorer.payment_path_failed(&payment_path_for_amount(250).iter().collect::<Vec<_>>(), 43);
                assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 300);
@@ -1899,12 +2018,12 @@ mod tests {
                let mut serialized_scorer = io::Cursor::new(&serialized_scorer);
                let deserialized_scorer =
                        <ProbabilisticScorer>::read(&mut serialized_scorer, (params, &network_graph)).unwrap();
-               assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 475);
+               assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 472);
 
                scorer.payment_path_failed(&payment_path_for_amount(250).iter().collect::<Vec<_>>(), 43);
                assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 300);
 
                SinceEpoch::advance(Duration::from_secs(10));
-               assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 367);
+               assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 371);
        }
 }
index 9cd2f9ec13e8bd49de15be2fc0f283fe34660eee..27c2d9de874ed45abca03dabe171ca238d7288ff 100644 (file)
@@ -411,7 +411,10 @@ impl events::MessageSendEventsProvider for TestRoutingMessageHandler {
 
 pub struct TestLogger {
        level: Level,
+       #[cfg(feature = "std")]
        id: String,
+       #[cfg(not(feature = "std"))]
+       _id: String,
        pub lines: Mutex<HashMap<(String, String), usize>>,
 }
 
@@ -422,7 +425,10 @@ impl TestLogger {
        pub fn with_id(id: String) -> TestLogger {
                TestLogger {
                        level: Level::Trace,
+                       #[cfg(feature = "std")]
                        id,
+                       #[cfg(not(feature = "std"))]
+                       _id: id,
                        lines: Mutex::new(HashMap::new())
                }
        }
@@ -471,8 +477,7 @@ impl Logger for TestLogger {
 
 pub struct TestKeysInterface {
        pub backing: keysinterface::PhantomKeysManager,
-       pub override_session_priv: Mutex<Option<[u8; 32]>>,
-       pub override_channel_id_priv: Mutex<Option<[u8; 32]>>,
+       pub override_random_bytes: Mutex<Option<[u8; 32]>>,
        pub disable_revocation_policy_check: bool,
        enforcement_states: Mutex<HashMap<[u8;32], Arc<Mutex<EnforcementState>>>>,
        expectations: Mutex<Option<VecDeque<OnGetShutdownScriptpubkey>>>,
@@ -506,16 +511,9 @@ impl keysinterface::KeysInterface for TestKeysInterface {
        }
 
        fn get_secure_random_bytes(&self) -> [u8; 32] {
-               let override_channel_id = self.override_channel_id_priv.lock().unwrap();
-               let override_session_key = self.override_session_priv.lock().unwrap();
-               if override_channel_id.is_some() && override_session_key.is_some() {
-                       panic!("We don't know which override key to use!");
-               }
-               if let Some(key) = &*override_channel_id {
-                       return *key;
-               }
-               if let Some(key) = &*override_session_key {
-                       return *key;
+               let override_random_bytes = self.override_random_bytes.lock().unwrap();
+               if let Some(bytes) = &*override_random_bytes {
+                       return *bytes;
                }
                self.backing.get_secure_random_bytes()
        }
@@ -543,8 +541,7 @@ impl TestKeysInterface {
                let now = Duration::from_secs(genesis_block(network).header.time as u64);
                Self {
                        backing: keysinterface::PhantomKeysManager::new(seed, now.as_secs(), now.subsec_nanos(), seed),
-                       override_session_priv: Mutex::new(None),
-                       override_channel_id_priv: Mutex::new(None),
+                       override_random_bytes: Mutex::new(None),
                        disable_revocation_policy_check: false,
                        enforcement_states: Mutex::new(HashMap::new()),
                        expectations: Mutex::new(None),
diff --git a/no-std-check/Cargo.toml b/no-std-check/Cargo.toml
new file mode 100644 (file)
index 0000000..15d2c19
--- /dev/null
@@ -0,0 +1,11 @@
+[package]
+name = "no-std-check"
+version = "0.1.0"
+edition = "2018"
+
+[features]
+default = ["lightning/no-std", "lightning-invoice/no-std"]
+
+[dependencies]
+lightning = { path = "../lightning", default-features = false }
+lightning-invoice = { path = "../lightning-invoice", default-features = false }
diff --git a/no-std-check/src/lib.rs b/no-std-check/src/lib.rs
new file mode 100644 (file)
index 0000000..e69de29