Support OnionMessenger in functional_test_utils
authorJeffrey Czyz <jkczyz@gmail.com>
Fri, 27 Oct 2023 18:27:55 +0000 (13:27 -0500)
committerJeffrey Czyz <jkczyz@gmail.com>
Tue, 16 Jan 2024 17:12:20 +0000 (11:12 -0600)
OnionMessenger is needed to write functional tests for ChannelManager's
OffersMessageHandler implementation. Also adds a TestMessageRouter,
which simply wraps DefaultMessageRouter for now.

lightning/src/ln/functional_test_utils.rs
lightning/src/ln/functional_tests.rs
lightning/src/onion_message/messenger.rs
lightning/src/routing/scoring.rs
lightning/src/sign/mod.rs
lightning/src/util/test_utils.rs

index dd7b7c2cd15aa2f5031c438e335083fa7d187b52..5971fc9eaf57fd027f45b0cf570bd4b157d4d554 100644 (file)
@@ -19,10 +19,12 @@ use crate::ln::{ChannelId, PaymentPreimage, PaymentHash, PaymentSecret};
 use crate::ln::channelmanager::{AChannelManager, ChainParameters, ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentSendFailure, RecipientOnionFields, PaymentId, MIN_CLTV_EXPIRY_DELTA};
 use crate::ln::features::InitFeatures;
 use crate::ln::msgs;
-use crate::ln::msgs::{ChannelMessageHandler,RoutingMessageHandler};
+use crate::ln::msgs::{ChannelMessageHandler, OnionMessageHandler, RoutingMessageHandler};
+use crate::ln::peer_handler::IgnoringMessageHandler;
+use crate::onion_message::messenger::OnionMessenger;
 use crate::routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate};
 use crate::routing::router::{self, PaymentParameters, Route, RouteParameters};
-use crate::sign::EntropySource;
+use crate::sign::{EntropySource, RandomBytes};
 use crate::util::config::{UserConfig, MaxDustHTLCExposure};
 use crate::util::errors::APIError;
 #[cfg(test)]
@@ -47,6 +49,7 @@ use alloc::rc::Rc;
 use core::cell::RefCell;
 use core::iter::repeat;
 use core::mem;
+use core::ops::Deref;
 use crate::io;
 use crate::prelude::*;
 use crate::sync::{Arc, Mutex, LockTestExt, RwLock};
@@ -388,6 +391,7 @@ pub struct NodeCfg<'a> {
        pub tx_broadcaster: &'a test_utils::TestBroadcaster,
        pub fee_estimator: &'a test_utils::TestFeeEstimator,
        pub router: test_utils::TestRouter<'a>,
+       pub message_router: test_utils::TestMessageRouter<'a>,
        pub chain_monitor: test_utils::TestChainMonitor<'a>,
        pub keys_manager: &'a test_utils::TestKeysInterface,
        pub logger: &'a test_utils::TestLogger,
@@ -407,6 +411,26 @@ type TestChannelManager<'node_cfg, 'chan_mon_cfg> = ChannelManager<
        &'chan_mon_cfg test_utils::TestLogger,
 >;
 
+type TestOnionMessenger<'chan_man, 'node_cfg, 'chan_mon_cfg> = OnionMessenger<
+       DedicatedEntropy,
+       &'node_cfg test_utils::TestKeysInterface,
+       &'chan_mon_cfg test_utils::TestLogger,
+       &'node_cfg test_utils::TestMessageRouter<'chan_mon_cfg>,
+       &'chan_man TestChannelManager<'node_cfg, 'chan_mon_cfg>,
+       IgnoringMessageHandler,
+>;
+
+/// For use with [`OnionMessenger`] otherwise `test_restored_packages_retry` will fail. This is
+/// because that test uses older serialized data produced by calling [`EntropySource`] in a specific
+/// manner. Using the same [`EntropySource`] with [`OnionMessenger`] would introduce another call,
+/// causing the produced data to no longer match.
+pub struct DedicatedEntropy(RandomBytes);
+
+impl Deref for DedicatedEntropy {
+       type Target = RandomBytes;
+       fn deref(&self) -> &Self::Target { &self.0 }
+}
+
 pub struct Node<'chan_man, 'node_cfg: 'chan_man, 'chan_mon_cfg: 'node_cfg> {
        pub chain_source: &'chan_mon_cfg test_utils::TestChainSource,
        pub tx_broadcaster: &'chan_mon_cfg test_utils::TestBroadcaster,
@@ -415,6 +439,7 @@ pub struct Node<'chan_man, 'node_cfg: 'chan_man, 'chan_mon_cfg: 'node_cfg> {
        pub chain_monitor: &'node_cfg test_utils::TestChainMonitor<'chan_mon_cfg>,
        pub keys_manager: &'chan_mon_cfg test_utils::TestKeysInterface,
        pub node: &'chan_man TestChannelManager<'node_cfg, 'chan_mon_cfg>,
+       pub onion_messenger: TestOnionMessenger<'chan_man, 'node_cfg, 'chan_mon_cfg>,
        pub network_graph: &'node_cfg NetworkGraph<&'chan_mon_cfg test_utils::TestLogger>,
        pub gossip_sync: P2PGossipSync<&'node_cfg NetworkGraph<&'chan_mon_cfg test_utils::TestLogger>, &'chan_mon_cfg test_utils::TestChainSource, &'chan_mon_cfg test_utils::TestLogger>,
        pub node_seed: [u8; 32],
@@ -432,6 +457,14 @@ pub struct Node<'chan_man, 'node_cfg: 'chan_man, 'chan_mon_cfg: 'node_cfg> {
                &'chan_mon_cfg test_utils::TestLogger,
        >,
 }
+
+impl<'a, 'b, 'c> Node<'a, 'b, 'c> {
+       pub fn init_features(&self, peer_node_id: &PublicKey) -> InitFeatures {
+               self.override_init_features.borrow().clone()
+                       .unwrap_or_else(|| self.node.init_features() | self.onion_messenger.provided_init_features(peer_node_id))
+       }
+}
+
 #[cfg(feature = "std")]
 impl<'a, 'b, 'c> std::panic::UnwindSafe for Node<'a, 'b, 'c> {}
 #[cfg(feature = "std")]
@@ -1054,6 +1087,7 @@ macro_rules! reload_node {
 
                $new_channelmanager = _reload_node(&$node, $new_config, &chanman_encoded, $monitors_encoded);
                $node.node = &$new_channelmanager;
+               $node.onion_messenger.set_offers_handler(&$new_channelmanager);
        };
        ($node: expr, $chanman_encoded: expr, $monitors_encoded: expr, $persister: ident, $new_chain_monitor: ident, $new_channelmanager: ident) => {
                reload_node!($node, $crate::util::config::UserConfig::default(), $chanman_encoded, $monitors_encoded, $persister, $new_chain_monitor, $new_channelmanager);
@@ -2898,6 +2932,7 @@ pub fn create_node_cfgs_with_persisters<'a>(node_count: usize, chanmon_cfgs: &'a
                        tx_broadcaster: &chanmon_cfgs[i].tx_broadcaster,
                        fee_estimator: &chanmon_cfgs[i].fee_estimator,
                        router: test_utils::TestRouter::new(network_graph.clone(), &chanmon_cfgs[i].scorer),
+                       message_router: test_utils::TestMessageRouter::new(network_graph.clone()),
                        chain_monitor,
                        keys_manager: &chanmon_cfgs[i].keys_manager,
                        node_seed: seed,
@@ -2951,6 +2986,11 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(node_count: usize, cfgs: &'b Vec<NodeC
        let connect_style = Rc::new(RefCell::new(ConnectStyle::random_style()));
 
        for i in 0..node_count {
+               let dedicated_entropy = DedicatedEntropy(RandomBytes::new([i as u8; 32]));
+               let onion_messenger = OnionMessenger::new(
+                       dedicated_entropy, cfgs[i].keys_manager, cfgs[i].logger, &cfgs[i].message_router,
+                       &chan_mgrs[i], IgnoringMessageHandler {},
+               );
                let gossip_sync = P2PGossipSync::new(cfgs[i].network_graph.as_ref(), None, cfgs[i].logger);
                let wallet_source = Arc::new(test_utils::TestWalletSource::new(SecretKey::from_slice(&[i as u8 + 1; 32]).unwrap()));
                nodes.push(Node{
@@ -2958,7 +2998,7 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(node_count: usize, cfgs: &'b Vec<NodeC
                        fee_estimator: cfgs[i].fee_estimator, router: &cfgs[i].router,
                        chain_monitor: &cfgs[i].chain_monitor, keys_manager: &cfgs[i].keys_manager,
                        node: &chan_mgrs[i], network_graph: cfgs[i].network_graph.as_ref(), gossip_sync,
-                       node_seed: cfgs[i].node_seed, network_chan_count: chan_count.clone(),
+                       node_seed: cfgs[i].node_seed, onion_messenger, network_chan_count: chan_count.clone(),
                        network_payment_count: payment_count.clone(), logger: cfgs[i].logger,
                        blocks: Arc::clone(&cfgs[i].tx_broadcaster.blocks),
                        connect_style: Rc::clone(&connect_style),
@@ -2973,16 +3013,24 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(node_count: usize, cfgs: &'b Vec<NodeC
 
        for i in 0..node_count {
                for j in (i+1)..node_count {
-                       nodes[i].node.peer_connected(&nodes[j].node.get_our_node_id(), &msgs::Init {
-                               features: nodes[j].override_init_features.borrow().clone().unwrap_or_else(|| nodes[j].node.init_features()),
+                       let node_id_i = nodes[i].node.get_our_node_id();
+                       let node_id_j = nodes[j].node.get_our_node_id();
+
+                       let init_i = msgs::Init {
+                               features: nodes[i].init_features(&node_id_j),
                                networks: None,
                                remote_network_address: None,
-                       }, true).unwrap();
-                       nodes[j].node.peer_connected(&nodes[i].node.get_our_node_id(), &msgs::Init {
-                               features: nodes[i].override_init_features.borrow().clone().unwrap_or_else(|| nodes[i].node.init_features()),
+                       };
+                       let init_j = msgs::Init {
+                               features: nodes[j].init_features(&node_id_i),
                                networks: None,
                                remote_network_address: None,
-                       }, false).unwrap();
+                       };
+
+                       nodes[i].node.peer_connected(&node_id_j, &init_j, true).unwrap();
+                       nodes[j].node.peer_connected(&node_id_i, &init_i, false).unwrap();
+                       nodes[i].onion_messenger.peer_connected(&node_id_j, &init_j, true).unwrap();
+                       nodes[j].onion_messenger.peer_connected(&node_id_i, &init_i, false).unwrap();
                }
        }
 
index 5711f238aa0c519a8ccf58aae91bb80694dcf70f..db3861c2efb46e79466865749515a7efe47eb64f 100644 (file)
@@ -5534,7 +5534,8 @@ fn test_key_derivation_params() {
        let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &chanmon_cfgs[0].logger));
        let scorer = RwLock::new(test_utils::TestScorer::new());
        let router = test_utils::TestRouter::new(network_graph.clone(), &scorer);
-       let node = NodeCfg { chain_source: &chanmon_cfgs[0].chain_source, logger: &chanmon_cfgs[0].logger, tx_broadcaster: &chanmon_cfgs[0].tx_broadcaster, fee_estimator: &chanmon_cfgs[0].fee_estimator, router, chain_monitor, keys_manager: &keys_manager, network_graph, node_seed: seed, override_init_features: alloc::rc::Rc::new(core::cell::RefCell::new(None)) };
+       let message_router = test_utils::TestMessageRouter::new(network_graph.clone());
+       let node = NodeCfg { chain_source: &chanmon_cfgs[0].chain_source, logger: &chanmon_cfgs[0].logger, tx_broadcaster: &chanmon_cfgs[0].tx_broadcaster, fee_estimator: &chanmon_cfgs[0].fee_estimator, router, message_router, chain_monitor, keys_manager: &keys_manager, network_graph, node_seed: seed, override_init_features: alloc::rc::Rc::new(core::cell::RefCell::new(None)) };
        let mut node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
        node_cfgs.remove(0);
        node_cfgs.insert(0, node);
index 812429f907d133b8ac67b7602e0cc9a8ba6e37f2..350fed49dbe19ee51c234915e823063a3ae8f387 100644 (file)
@@ -714,6 +714,11 @@ where
                }
        }
 
+       #[cfg(test)]
+       pub(crate) fn set_offers_handler(&mut self, offers_handler: OMH) {
+               self.offers_handler = offers_handler;
+       }
+
        /// Sends an [`OnionMessage`] with the given `contents` to `destination`.
        ///
        /// See [`OnionMessenger`] for example usage.
index 646405c6287ac150d88199890137020e0350079f..8c36bd100e5b0a2cd51d08d828d63a66216f18a8 100644 (file)
@@ -251,7 +251,7 @@ impl<'a, T: Score + 'a> LockableScore<'a> for RefCell<T> {
        }
 }
 
-#[cfg(not(c_bindings))]
+#[cfg(any(not(c_bindings), feature = "_test_utils", test))]
 impl<'a, T: Score + 'a> LockableScore<'a> for RwLock<T> {
        type ScoreUpdate = T;
        type ScoreLookUp = T;
index 31220879416f2837fd6af0c73e118fccb2bf6e4c..717c05517093ec917c82957195652f1174093d84 100644 (file)
@@ -1874,9 +1874,9 @@ impl PhantomKeysManager {
        }
 }
 
-/// An implementation of [`EntropySource`] using [`ChaCha20`].
+/// An implementation of [`EntropySource`] using ChaCha20.
 #[derive(Debug)]
-struct RandomBytes {
+pub struct RandomBytes {
        /// Seed from which all randomness produced is derived from.
        seed: [u8; 32],
        /// Tracks the number of times we've produced randomness to ensure we don't return the same
index 1c45c4a4c5d3f3d36120c6ce5fffb515c5032cb3..c1692f9e3eda199713c8de1c3201a446170d26ef 100644 (file)
@@ -32,7 +32,7 @@ use crate::ln::msgs::LightningError;
 use crate::ln::script::ShutdownScript;
 use crate::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
 use crate::offers::invoice_request::UnsignedInvoiceRequest;
-use crate::onion_message::messenger::{Destination, MessageRouter, OnionMessagePath};
+use crate::onion_message::messenger::{DefaultMessageRouter, Destination, MessageRouter, OnionMessagePath};
 use crate::routing::gossip::{EffectiveCapacity, NetworkGraph, NodeId, RoutingFees};
 use crate::routing::utxo::{UtxoLookup, UtxoLookupError, UtxoResult};
 use crate::routing::router::{find_route, InFlightHtlcs, Path, Route, RouteParameters, RouteHintHop, Router, ScorerAccountingForInFlightHtlcs};
@@ -231,6 +231,33 @@ impl<'a> Drop for TestRouter<'a> {
        }
 }
 
+pub struct TestMessageRouter<'a> {
+       inner: DefaultMessageRouter<Arc<NetworkGraph<&'a TestLogger>>, &'a TestLogger>,
+}
+
+impl<'a> TestMessageRouter<'a> {
+       pub fn new(network_graph: Arc<NetworkGraph<&'a TestLogger>>) -> Self {
+               Self { inner: DefaultMessageRouter::new(network_graph) }
+       }
+}
+
+impl<'a> MessageRouter for TestMessageRouter<'a> {
+       fn find_path(
+               &self, sender: PublicKey, peers: Vec<PublicKey>, destination: Destination
+       ) -> Result<OnionMessagePath, ()> {
+               self.inner.find_path(sender, peers, destination)
+       }
+
+       fn create_blinded_paths<
+               ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
+       >(
+               &self, recipient: PublicKey, peers: Vec<PublicKey>, entropy_source: &ES,
+               secp_ctx: &Secp256k1<T>
+       ) -> Result<Vec<BlindedPath>, ()> {
+               self.inner.create_blinded_paths(recipient, peers, entropy_source, secp_ctx)
+       }
+}
+
 pub struct OnlyReadsKeysInterface {}
 
 impl EntropySource for OnlyReadsKeysInterface {
@@ -1390,6 +1417,9 @@ impl ScoreUpdate for TestScorer {
        fn time_passed(&mut self, _duration_since_epoch: Duration) {}
 }
 
+#[cfg(c_bindings)]
+impl crate::routing::scoring::Score for TestScorer {}
+
 impl Drop for TestScorer {
        fn drop(&mut self) {
                #[cfg(feature = "std")] {