Merge pull request #2697 from jkczyz/2023-10-offer-functional-tests

Functional tests for BOLT 12 Offers payment flow
This commit is contained in:
Matt Corallo 2024-01-24 23:14:01 +00:00 committed by GitHub
commit 76fff953bd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 1155 additions and 124 deletions

View file

@ -7787,15 +7787,15 @@ where
let payment_paths = self.create_blinded_payment_paths(amount_msats, payment_secret) let payment_paths = self.create_blinded_payment_paths(amount_msats, payment_secret)
.map_err(|_| Bolt12SemanticError::MissingPaths)?; .map_err(|_| Bolt12SemanticError::MissingPaths)?;
#[cfg(not(feature = "no-std"))] #[cfg(feature = "std")]
let builder = refund.respond_using_derived_keys( let builder = refund.respond_using_derived_keys(
payment_paths, payment_hash, expanded_key, entropy payment_paths, payment_hash, expanded_key, entropy
)?; )?;
#[cfg(feature = "no-std")] #[cfg(not(feature = "std"))]
let created_at = Duration::from_secs( let created_at = Duration::from_secs(
self.highest_seen_timestamp.load(Ordering::Acquire) as u64 self.highest_seen_timestamp.load(Ordering::Acquire) as u64
); );
#[cfg(feature = "no-std")] #[cfg(not(feature = "std"))]
let builder = refund.respond_using_derived_keys_no_std( let builder = refund.respond_using_derived_keys_no_std(
payment_paths, payment_hash, created_at, expanded_key, entropy payment_paths, payment_hash, created_at, expanded_key, entropy
)?; )?;
@ -9232,17 +9232,17 @@ where
}, },
}; };
#[cfg(feature = "no-std")] #[cfg(not(feature = "std"))]
let created_at = Duration::from_secs( let created_at = Duration::from_secs(
self.highest_seen_timestamp.load(Ordering::Acquire) as u64 self.highest_seen_timestamp.load(Ordering::Acquire) as u64
); );
if invoice_request.keys.is_some() { if invoice_request.keys.is_some() {
#[cfg(not(feature = "no-std"))] #[cfg(feature = "std")]
let builder = invoice_request.respond_using_derived_keys( let builder = invoice_request.respond_using_derived_keys(
payment_paths, payment_hash payment_paths, payment_hash
); );
#[cfg(feature = "no-std")] #[cfg(not(feature = "std"))]
let builder = invoice_request.respond_using_derived_keys_no_std( let builder = invoice_request.respond_using_derived_keys_no_std(
payment_paths, payment_hash, created_at payment_paths, payment_hash, created_at
); );
@ -9251,9 +9251,9 @@ where
Err(error) => Some(OffersMessage::InvoiceError(error.into())), Err(error) => Some(OffersMessage::InvoiceError(error.into())),
} }
} else { } else {
#[cfg(not(feature = "no-std"))] #[cfg(feature = "std")]
let builder = invoice_request.respond_with(payment_paths, payment_hash); let builder = invoice_request.respond_with(payment_paths, payment_hash);
#[cfg(feature = "no-std")] #[cfg(not(feature = "std"))]
let builder = invoice_request.respond_with_no_std( let builder = invoice_request.respond_with_no_std(
payment_paths, payment_hash, created_at payment_paths, payment_hash, created_at
); );
@ -12497,7 +12497,7 @@ pub mod bench {
use bitcoin::blockdata::locktime::absolute::LockTime; use bitcoin::blockdata::locktime::absolute::LockTime;
use bitcoin::hashes::Hash; use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::{Block, Transaction, TxOut}; use bitcoin::{Transaction, TxOut};
use crate::sync::{Arc, Mutex, RwLock}; use crate::sync::{Arc, Mutex, RwLock};
@ -12537,7 +12537,7 @@ pub mod bench {
let fee_estimator = test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) }; let fee_estimator = test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) };
let logger_a = test_utils::TestLogger::with_id("node a".to_owned()); let logger_a = test_utils::TestLogger::with_id("node a".to_owned());
let scorer = RwLock::new(test_utils::TestScorer::new()); let scorer = RwLock::new(test_utils::TestScorer::new());
let router = test_utils::TestRouter::new(Arc::new(NetworkGraph::new(network, &logger_a)), &scorer); let router = test_utils::TestRouter::new(Arc::new(NetworkGraph::new(network, &logger_a)), &logger_a, &scorer);
let mut config: UserConfig = Default::default(); let mut config: UserConfig = Default::default();
config.channel_config.max_dust_htlc_exposure = MaxDustHTLCExposure::FeeRateMultiplier(5_000_000 / 253); config.channel_config.max_dust_htlc_exposure = MaxDustHTLCExposure::FeeRateMultiplier(5_000_000 / 253);

View file

@ -932,6 +932,13 @@ impl<T: sealed::AnchorsZeroFeeHtlcTx> Features<T> {
} }
} }
impl<T: sealed::RouteBlinding> Features<T> {
#[cfg(test)]
pub(crate) fn clear_route_blinding(&mut self) {
<T as sealed::RouteBlinding>::clear_bits(&mut self.flags);
}
}
#[cfg(test)] #[cfg(test)]
impl<T: sealed::UnknownFeature> Features<T> { impl<T: sealed::UnknownFeature> Features<T> {
pub(crate) fn unknown() -> Self { pub(crate) fn unknown() -> Self {

View file

@ -11,27 +11,29 @@
//! nodes for functional tests. //! nodes for functional tests.
use crate::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen, Watch, chainmonitor::Persist}; use crate::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen, Watch, chainmonitor::Persist};
use crate::sign::EntropySource;
use crate::chain::channelmonitor::ChannelMonitor; use crate::chain::channelmonitor::ChannelMonitor;
use crate::chain::transaction::OutPoint; use crate::chain::transaction::OutPoint;
use crate::events::{ClaimedHTLC, ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose, PaymentFailureReason}; use crate::events::{ClaimedHTLC, ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose, PaymentFailureReason};
use crate::events::bump_transaction::{BumpTransactionEvent, BumpTransactionEventHandler, Wallet, WalletSource}; use crate::events::bump_transaction::{BumpTransactionEvent, BumpTransactionEventHandler, Wallet, WalletSource};
use crate::ln::{ChannelId, PaymentPreimage, PaymentHash, PaymentSecret}; 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::channelmanager::{AChannelManager, ChainParameters, ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentSendFailure, RecipientOnionFields, PaymentId, MIN_CLTV_EXPIRY_DELTA};
use crate::routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate};
use crate::routing::router::{self, PaymentParameters, Route, RouteParameters};
use crate::ln::features::InitFeatures; use crate::ln::features::InitFeatures;
use crate::ln::msgs; use crate::ln::msgs;
use crate::ln::msgs::{ChannelMessageHandler,RoutingMessageHandler}; use crate::ln::msgs::{ChannelMessageHandler, OnionMessageHandler, RoutingMessageHandler};
use crate::util::test_channel_signer::TestChannelSigner; use crate::ln::peer_handler::IgnoringMessageHandler;
use crate::util::scid_utils; use crate::onion_message::messenger::OnionMessenger;
use crate::util::test_utils; use crate::routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate};
use crate::util::test_utils::{panicking, TestChainMonitor, TestScorer, TestKeysInterface}; use crate::routing::router::{self, PaymentParameters, Route, RouteParameters};
use crate::util::errors::APIError; use crate::sign::{EntropySource, RandomBytes};
use crate::util::config::{UserConfig, MaxDustHTLCExposure}; use crate::util::config::{UserConfig, MaxDustHTLCExposure};
use crate::util::ser::{ReadableArgs, Writeable}; use crate::util::errors::APIError;
#[cfg(test)] #[cfg(test)]
use crate::util::logger::Logger; use crate::util::logger::Logger;
use crate::util::scid_utils;
use crate::util::test_channel_signer::TestChannelSigner;
use crate::util::test_utils;
use crate::util::test_utils::{panicking, TestChainMonitor, TestScorer, TestKeysInterface};
use crate::util::ser::{ReadableArgs, Writeable};
use bitcoin::blockdata::block::{Block, Header, Version}; use bitcoin::blockdata::block::{Block, Header, Version};
use bitcoin::blockdata::locktime::absolute::LockTime; use bitcoin::blockdata::locktime::absolute::LockTime;
@ -43,13 +45,14 @@ use bitcoin::network::constants::Network;
use bitcoin::pow::CompactTarget; use bitcoin::pow::CompactTarget;
use bitcoin::secp256k1::{PublicKey, SecretKey}; use bitcoin::secp256k1::{PublicKey, SecretKey};
use alloc::rc::Rc;
use core::cell::RefCell;
use core::iter::repeat;
use core::mem;
use core::ops::Deref;
use crate::io; use crate::io;
use crate::prelude::*; use crate::prelude::*;
use core::cell::RefCell;
use alloc::rc::Rc;
use crate::sync::{Arc, Mutex, LockTestExt, RwLock}; use crate::sync::{Arc, Mutex, LockTestExt, RwLock};
use core::mem;
use core::iter::repeat;
pub const CHAN_CONFIRM_DEPTH: u32 = 10; pub const CHAN_CONFIRM_DEPTH: u32 = 10;
@ -388,6 +391,7 @@ pub struct NodeCfg<'a> {
pub tx_broadcaster: &'a test_utils::TestBroadcaster, pub tx_broadcaster: &'a test_utils::TestBroadcaster,
pub fee_estimator: &'a test_utils::TestFeeEstimator, pub fee_estimator: &'a test_utils::TestFeeEstimator,
pub router: test_utils::TestRouter<'a>, pub router: test_utils::TestRouter<'a>,
pub message_router: test_utils::TestMessageRouter<'a>,
pub chain_monitor: test_utils::TestChainMonitor<'a>, pub chain_monitor: test_utils::TestChainMonitor<'a>,
pub keys_manager: &'a test_utils::TestKeysInterface, pub keys_manager: &'a test_utils::TestKeysInterface,
pub logger: &'a test_utils::TestLogger, pub logger: &'a test_utils::TestLogger,
@ -407,6 +411,26 @@ type TestChannelManager<'node_cfg, 'chan_mon_cfg> = ChannelManager<
&'chan_mon_cfg test_utils::TestLogger, &'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 struct Node<'chan_man, 'node_cfg: 'chan_man, 'chan_mon_cfg: 'node_cfg> {
pub chain_source: &'chan_mon_cfg test_utils::TestChainSource, pub chain_source: &'chan_mon_cfg test_utils::TestChainSource,
pub tx_broadcaster: &'chan_mon_cfg test_utils::TestBroadcaster, 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 chain_monitor: &'node_cfg test_utils::TestChainMonitor<'chan_mon_cfg>,
pub keys_manager: &'chan_mon_cfg test_utils::TestKeysInterface, pub keys_manager: &'chan_mon_cfg test_utils::TestKeysInterface,
pub node: &'chan_man TestChannelManager<'node_cfg, 'chan_mon_cfg>, 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 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 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], 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, &'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")] #[cfg(feature = "std")]
impl<'a, 'b, 'c> std::panic::UnwindSafe for Node<'a, 'b, 'c> {} impl<'a, 'b, 'c> std::panic::UnwindSafe for Node<'a, 'b, 'c> {}
#[cfg(feature = "std")] #[cfg(feature = "std")]
@ -599,7 +632,7 @@ impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> {
node_signer: self.keys_manager, node_signer: self.keys_manager,
signer_provider: self.keys_manager, signer_provider: self.keys_manager,
fee_estimator: &test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) }, fee_estimator: &test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) },
router: &test_utils::TestRouter::new(Arc::new(network_graph), &scorer), router: &test_utils::TestRouter::new(Arc::new(network_graph), &self.logger, &scorer),
chain_monitor: self.chain_monitor, chain_monitor: self.chain_monitor,
tx_broadcaster: &broadcaster, tx_broadcaster: &broadcaster,
logger: &self.logger, logger: &self.logger,
@ -1054,6 +1087,7 @@ macro_rules! reload_node {
$new_channelmanager = _reload_node(&$node, $new_config, &chanman_encoded, $monitors_encoded); $new_channelmanager = _reload_node(&$node, $new_config, &chanman_encoded, $monitors_encoded);
$node.node = &$new_channelmanager; $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) => { ($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); reload_node!($node, $crate::util::config::UserConfig::default(), $chanman_encoded, $monitors_encoded, $persister, $new_chain_monitor, $new_channelmanager);
@ -2897,7 +2931,8 @@ pub fn create_node_cfgs_with_persisters<'a>(node_count: usize, chanmon_cfgs: &'a
logger: &chanmon_cfgs[i].logger, logger: &chanmon_cfgs[i].logger,
tx_broadcaster: &chanmon_cfgs[i].tx_broadcaster, tx_broadcaster: &chanmon_cfgs[i].tx_broadcaster,
fee_estimator: &chanmon_cfgs[i].fee_estimator, fee_estimator: &chanmon_cfgs[i].fee_estimator,
router: test_utils::TestRouter::new(network_graph.clone(), &chanmon_cfgs[i].scorer), router: test_utils::TestRouter::new(network_graph.clone(), &chanmon_cfgs[i].logger, &chanmon_cfgs[i].scorer),
message_router: test_utils::TestMessageRouter::new(network_graph.clone()),
chain_monitor, chain_monitor,
keys_manager: &chanmon_cfgs[i].keys_manager, keys_manager: &chanmon_cfgs[i].keys_manager,
node_seed: seed, 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())); let connect_style = Rc::new(RefCell::new(ConnectStyle::random_style()));
for i in 0..node_count { 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 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())); let wallet_source = Arc::new(test_utils::TestWalletSource::new(SecretKey::from_slice(&[i as u8 + 1; 32]).unwrap()));
nodes.push(Node{ 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, fee_estimator: cfgs[i].fee_estimator, router: &cfgs[i].router,
chain_monitor: &cfgs[i].chain_monitor, keys_manager: &cfgs[i].keys_manager, 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: &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, network_payment_count: payment_count.clone(), logger: cfgs[i].logger,
blocks: Arc::clone(&cfgs[i].tx_broadcaster.blocks), blocks: Arc::clone(&cfgs[i].tx_broadcaster.blocks),
connect_style: Rc::clone(&connect_style), 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 i in 0..node_count {
for j in (i+1)..node_count { for j in (i+1)..node_count {
nodes[i].node.peer_connected(&nodes[j].node.get_our_node_id(), &msgs::Init { let node_id_i = nodes[i].node.get_our_node_id();
features: nodes[j].override_init_features.borrow().clone().unwrap_or_else(|| nodes[j].node.init_features()), 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, networks: None,
remote_network_address: None, remote_network_address: None,
}, true).unwrap(); };
nodes[j].node.peer_connected(&nodes[i].node.get_our_node_id(), &msgs::Init { let init_j = msgs::Init {
features: nodes[i].override_init_features.borrow().clone().unwrap_or_else(|| nodes[i].node.init_features()), features: nodes[j].init_features(&node_id_i),
networks: None, networks: None,
remote_network_address: 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();
} }
} }

View file

@ -5533,8 +5533,9 @@ fn test_key_derivation_params() {
let chain_monitor = test_utils::TestChainMonitor::new(Some(&chanmon_cfgs[0].chain_source), &chanmon_cfgs[0].tx_broadcaster, &chanmon_cfgs[0].logger, &chanmon_cfgs[0].fee_estimator, &chanmon_cfgs[0].persister, &keys_manager); let chain_monitor = test_utils::TestChainMonitor::new(Some(&chanmon_cfgs[0].chain_source), &chanmon_cfgs[0].tx_broadcaster, &chanmon_cfgs[0].logger, &chanmon_cfgs[0].fee_estimator, &chanmon_cfgs[0].persister, &keys_manager);
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &chanmon_cfgs[0].logger)); let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &chanmon_cfgs[0].logger));
let scorer = RwLock::new(test_utils::TestScorer::new()); let scorer = RwLock::new(test_utils::TestScorer::new());
let router = test_utils::TestRouter::new(network_graph.clone(), &scorer); let router = test_utils::TestRouter::new(network_graph.clone(), &chanmon_cfgs[0].logger, &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); let mut node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
node_cfgs.remove(0); node_cfgs.remove(0);
node_cfgs.insert(0, node); node_cfgs.insert(0, node);

View file

@ -79,6 +79,9 @@ mod shutdown_tests;
#[cfg(all(test, async_signing))] #[cfg(all(test, async_signing))]
#[allow(unused_mut)] #[allow(unused_mut)]
mod async_signer_tests; mod async_signer_tests;
#[cfg(test)]
#[allow(unused_mut)]
mod offers_tests;
pub use self::peer_channel_encryptor::LN_MAX_MSG_LEN; pub use self::peer_channel_encryptor::LN_MAX_MSG_LEN;

View file

@ -0,0 +1,894 @@
// This file is Copyright its original authors, visible in version control
// history.
//
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// You may not use this file except in accordance with one or both of these
// licenses.
//! Functional tests for the BOLT 12 Offers payment flow.
//!
//! [`ChannelManager`] provides utilities to create [`Offer`]s and [`Refund`]s along with utilities
//! to initiate and request payment for them, respectively. It also manages the payment flow via
//! implementing [`OffersMessageHandler`]. This module tests that functionality, including the
//! resulting [`Event`] generation.
//!
//! Two-node success tests use an announced channel:
//!
//! Alice --- Bob
//!
//! While two-node failure tests use an unannounced channel:
//!
//! Alice ... Bob
//!
//! Six-node tests use unannounced channels for the sender and recipient and announced channels for
//! the rest of the network.
//!
//! nodes[4]
//! / \
//! / \
//! / \
//! Alice ... Bob -------- Charlie ... David
//! \ /
//! \ /
//! \ /
//! nodes[5]
//!
//! Unnamed nodes are needed to ensure unannounced nodes can create two-hop blinded paths.
//!
//! Nodes without channels are disconnected and connected as needed to ensure that deterministic
//! blinded paths are used.
use core::time::Duration;
use crate::blinded_path::BlindedPath;
use crate::events::{Event, MessageSendEventsProvider, PaymentPurpose};
use crate::ln::channelmanager::{PaymentId, RecentPaymentDetails, Retry, self};
use crate::ln::functional_test_utils::*;
use crate::ln::msgs::{ChannelMessageHandler, Init, OnionMessage, OnionMessageHandler};
use crate::offers::invoice::Bolt12Invoice;
use crate::offers::invoice_error::InvoiceError;
use crate::offers::invoice_request::InvoiceRequest;
use crate::offers::parse::Bolt12SemanticError;
use crate::onion_message::messenger::PeeledOnion;
use crate::onion_message::offers::OffersMessage;
use crate::onion_message::packet::ParsedOnionMessageContents;
use crate::prelude::*;
macro_rules! expect_recent_payment {
($node: expr, $payment_state: path, $payment_id: expr) => {
match $node.node.list_recent_payments().first() {
Some(&$payment_state { payment_id: actual_payment_id, .. }) => {
assert_eq!($payment_id, actual_payment_id);
},
Some(_) => panic!("Unexpected recent payment state"),
None => panic!("No recent payments"),
}
}
}
fn connect_peers<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, 'b, 'c>) {
let node_id_a = node_a.node.get_our_node_id();
let node_id_b = node_b.node.get_our_node_id();
let init_a = Init {
features: node_a.init_features(&node_id_b),
networks: None,
remote_network_address: None,
};
let init_b = Init {
features: node_b.init_features(&node_id_a),
networks: None,
remote_network_address: None,
};
node_a.node.peer_connected(&node_id_b, &init_b, true).unwrap();
node_b.node.peer_connected(&node_id_a, &init_a, false).unwrap();
node_a.onion_messenger.peer_connected(&node_id_b, &init_b, true).unwrap();
node_b.onion_messenger.peer_connected(&node_id_a, &init_a, false).unwrap();
}
fn disconnect_peers<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, peers: &[&Node<'a, 'b, 'c>]) {
for node_b in peers {
node_a.node.peer_disconnected(&node_b.node.get_our_node_id());
node_b.node.peer_disconnected(&node_a.node.get_our_node_id());
node_a.onion_messenger.peer_disconnected(&node_b.node.get_our_node_id());
node_b.onion_messenger.peer_disconnected(&node_a.node.get_our_node_id());
}
}
fn route_bolt12_payment<'a, 'b, 'c>(
node: &Node<'a, 'b, 'c>, path: &[&Node<'a, 'b, 'c>], invoice: &Bolt12Invoice
) {
// Monitor added when handling the invoice onion message.
check_added_monitors(node, 1);
let mut events = node.node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
let ev = remove_first_msg_event_to_node(&path[0].node.get_our_node_id(), &mut events);
// Use a fake payment_hash and bypass checking for the PaymentClaimable event since the
// invoice contains the payment_hash but it was encrypted inside an onion message.
let amount_msats = invoice.amount_msats();
let payment_hash = invoice.payment_hash();
do_pass_along_path(
node, path, amount_msats, payment_hash, None, ev, false, false, None, false
);
}
fn claim_bolt12_payment<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, path: &[&Node<'a, 'b, 'c>]) {
let recipient = &path[path.len() - 1];
match get_event!(recipient, Event::PaymentClaimable) {
Event::PaymentClaimable {
purpose: PaymentPurpose::InvoicePayment {
payment_preimage: Some(payment_preimage), ..
}, ..
} => claim_payment(node, path, payment_preimage),
_ => panic!(),
};
}
fn extract_invoice_request<'a, 'b, 'c>(
node: &Node<'a, 'b, 'c>, message: &OnionMessage
) -> (InvoiceRequest, Option<BlindedPath>) {
match node.onion_messenger.peel_onion_message(message) {
Ok(PeeledOnion::Receive(message, _, reply_path)) => match message {
ParsedOnionMessageContents::Offers(offers_message) => match offers_message {
OffersMessage::InvoiceRequest(invoice_request) => (invoice_request, reply_path),
OffersMessage::Invoice(invoice) => panic!("Unexpected invoice: {:?}", invoice),
OffersMessage::InvoiceError(error) => panic!("Unexpected invoice_error: {:?}", error),
},
ParsedOnionMessageContents::Custom(message) => panic!("Unexpected custom message: {:?}", message),
},
Ok(PeeledOnion::Forward(_, _)) => panic!("Unexpected onion message forward"),
Err(e) => panic!("Failed to process onion message {:?}", e),
}
}
fn extract_invoice<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, message: &OnionMessage) -> Bolt12Invoice {
match node.onion_messenger.peel_onion_message(message) {
Ok(PeeledOnion::Receive(message, _, _)) => match message {
ParsedOnionMessageContents::Offers(offers_message) => match offers_message {
OffersMessage::InvoiceRequest(invoice_request) => panic!("Unexpected invoice_request: {:?}", invoice_request),
OffersMessage::Invoice(invoice) => invoice,
OffersMessage::InvoiceError(error) => panic!("Unexpected invoice_error: {:?}", error),
},
ParsedOnionMessageContents::Custom(message) => panic!("Unexpected custom message: {:?}", message),
},
Ok(PeeledOnion::Forward(_, _)) => panic!("Unexpected onion message forward"),
Err(e) => panic!("Failed to process onion message {:?}", e),
}
}
fn extract_invoice_error<'a, 'b, 'c>(
node: &Node<'a, 'b, 'c>, message: &OnionMessage
) -> InvoiceError {
match node.onion_messenger.peel_onion_message(message) {
Ok(PeeledOnion::Receive(message, _, _)) => match message {
ParsedOnionMessageContents::Offers(offers_message) => match offers_message {
OffersMessage::InvoiceRequest(invoice_request) => panic!("Unexpected invoice_request: {:?}", invoice_request),
OffersMessage::Invoice(invoice) => panic!("Unexpected invoice: {:?}", invoice),
OffersMessage::InvoiceError(error) => error,
},
ParsedOnionMessageContents::Custom(message) => panic!("Unexpected custom message: {:?}", message),
},
Ok(PeeledOnion::Forward(_, _)) => panic!("Unexpected onion message forward"),
Err(e) => panic!("Failed to process onion message {:?}", e),
}
}
/// Checks that an offer can be paid through blinded paths and that ephemeral pubkeys are used
/// rather than exposing a node's pubkey.
#[test]
fn creates_and_pays_for_offer_using_two_hop_blinded_path() {
let mut accept_forward_cfg = test_default_channel_config();
accept_forward_cfg.accept_forwards_to_priv_channels = true;
let mut features = channelmanager::provided_init_features(&accept_forward_cfg);
features.set_onion_messages_optional();
features.set_route_blinding_optional();
let chanmon_cfgs = create_chanmon_cfgs(6);
let node_cfgs = create_node_cfgs(6, &chanmon_cfgs);
*node_cfgs[1].override_init_features.borrow_mut() = Some(features);
let node_chanmgrs = create_node_chanmgrs(
6, &node_cfgs, &[None, Some(accept_forward_cfg), None, None, None, None]
);
let nodes = create_network(6, &node_cfgs, &node_chanmgrs);
create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
create_unannounced_chan_between_nodes_with_value(&nodes, 2, 3, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 1, 4, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 1, 5, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 2, 4, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 2, 5, 10_000_000, 1_000_000_000);
let (alice, bob, charlie, david) = (&nodes[0], &nodes[1], &nodes[2], &nodes[3]);
let alice_id = alice.node.get_our_node_id();
let bob_id = bob.node.get_our_node_id();
let charlie_id = charlie.node.get_our_node_id();
let david_id = david.node.get_our_node_id();
disconnect_peers(alice, &[charlie, david, &nodes[4], &nodes[5]]);
disconnect_peers(david, &[bob, &nodes[4], &nodes[5]]);
let offer = alice.node
.create_offer_builder("coffee".to_string()).unwrap()
.amount_msats(10_000_000)
.build().unwrap();
assert_ne!(offer.signing_pubkey(), alice_id);
assert!(!offer.paths().is_empty());
for path in offer.paths() {
assert_eq!(path.introduction_node_id, bob_id);
}
let payment_id = PaymentId([1; 32]);
david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), None)
.unwrap();
expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id);
connect_peers(david, bob);
let onion_message = david.onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
bob.onion_messenger.handle_onion_message(&david_id, &onion_message);
connect_peers(alice, charlie);
let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
alice.onion_messenger.handle_onion_message(&bob_id, &onion_message);
let (invoice_request, reply_path) = extract_invoice_request(alice, &onion_message);
assert_eq!(invoice_request.amount_msats(), None);
assert_ne!(invoice_request.payer_id(), david_id);
assert_eq!(reply_path.unwrap().introduction_node_id, charlie_id);
let onion_message = alice.onion_messenger.next_onion_message_for_peer(charlie_id).unwrap();
charlie.onion_messenger.handle_onion_message(&alice_id, &onion_message);
let onion_message = charlie.onion_messenger.next_onion_message_for_peer(david_id).unwrap();
david.onion_messenger.handle_onion_message(&charlie_id, &onion_message);
let invoice = extract_invoice(david, &onion_message);
assert_eq!(invoice.amount_msats(), 10_000_000);
assert_ne!(invoice.signing_pubkey(), alice_id);
assert!(!invoice.payment_paths().is_empty());
for (_, path) in invoice.payment_paths() {
assert_eq!(path.introduction_node_id, bob_id);
}
route_bolt12_payment(david, &[charlie, bob, alice], &invoice);
expect_recent_payment!(david, RecentPaymentDetails::Pending, payment_id);
claim_bolt12_payment(david, &[charlie, bob, alice]);
expect_recent_payment!(david, RecentPaymentDetails::Fulfilled, payment_id);
}
/// Checks that a refund can be paid through blinded paths and that ephemeral pubkeys are used
/// rather than exposing a node's pubkey.
#[test]
fn creates_and_pays_for_refund_using_two_hop_blinded_path() {
let mut accept_forward_cfg = test_default_channel_config();
accept_forward_cfg.accept_forwards_to_priv_channels = true;
let mut features = channelmanager::provided_init_features(&accept_forward_cfg);
features.set_onion_messages_optional();
features.set_route_blinding_optional();
let chanmon_cfgs = create_chanmon_cfgs(6);
let node_cfgs = create_node_cfgs(6, &chanmon_cfgs);
*node_cfgs[1].override_init_features.borrow_mut() = Some(features);
let node_chanmgrs = create_node_chanmgrs(
6, &node_cfgs, &[None, Some(accept_forward_cfg), None, None, None, None]
);
let nodes = create_network(6, &node_cfgs, &node_chanmgrs);
create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
create_unannounced_chan_between_nodes_with_value(&nodes, 2, 3, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 1, 4, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 1, 5, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 2, 4, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 2, 5, 10_000_000, 1_000_000_000);
let (alice, bob, charlie, david) = (&nodes[0], &nodes[1], &nodes[2], &nodes[3]);
let alice_id = alice.node.get_our_node_id();
let bob_id = bob.node.get_our_node_id();
let charlie_id = charlie.node.get_our_node_id();
let david_id = david.node.get_our_node_id();
disconnect_peers(alice, &[charlie, david, &nodes[4], &nodes[5]]);
disconnect_peers(david, &[bob, &nodes[4], &nodes[5]]);
let absolute_expiry = Duration::from_secs(u64::MAX);
let payment_id = PaymentId([1; 32]);
let refund = david.node
.create_refund_builder(
"refund".to_string(), 10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None
)
.unwrap()
.build().unwrap();
assert_eq!(refund.amount_msats(), 10_000_000);
assert_eq!(refund.absolute_expiry(), Some(absolute_expiry));
assert_ne!(refund.payer_id(), david_id);
assert!(!refund.paths().is_empty());
for path in refund.paths() {
assert_eq!(path.introduction_node_id, charlie_id);
}
expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id);
alice.node.request_refund_payment(&refund).unwrap();
connect_peers(alice, charlie);
let onion_message = alice.onion_messenger.next_onion_message_for_peer(charlie_id).unwrap();
charlie.onion_messenger.handle_onion_message(&alice_id, &onion_message);
let onion_message = charlie.onion_messenger.next_onion_message_for_peer(david_id).unwrap();
david.onion_messenger.handle_onion_message(&charlie_id, &onion_message);
let invoice = extract_invoice(david, &onion_message);
assert_eq!(invoice.amount_msats(), 10_000_000);
assert_ne!(invoice.signing_pubkey(), alice_id);
assert!(!invoice.payment_paths().is_empty());
for (_, path) in invoice.payment_paths() {
assert_eq!(path.introduction_node_id, bob_id);
}
route_bolt12_payment(david, &[charlie, bob, alice], &invoice);
expect_recent_payment!(david, RecentPaymentDetails::Pending, payment_id);
claim_bolt12_payment(david, &[charlie, bob, alice]);
expect_recent_payment!(david, RecentPaymentDetails::Fulfilled, payment_id);
}
/// Checks that an offer can be paid through a one-hop blinded path and that ephemeral pubkeys are
/// used rather than exposing a node's pubkey. However, the node's pubkey is still used as the
/// introduction node of the blinded path.
#[test]
fn creates_and_pays_for_offer_using_one_hop_blinded_path() {
let chanmon_cfgs = create_chanmon_cfgs(2);
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
let alice = &nodes[0];
let alice_id = alice.node.get_our_node_id();
let bob = &nodes[1];
let bob_id = bob.node.get_our_node_id();
let offer = alice.node
.create_offer_builder("coffee".to_string()).unwrap()
.amount_msats(10_000_000)
.build().unwrap();
assert_ne!(offer.signing_pubkey(), alice_id);
assert!(!offer.paths().is_empty());
for path in offer.paths() {
assert_eq!(path.introduction_node_id, alice_id);
}
let payment_id = PaymentId([1; 32]);
bob.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), None).unwrap();
expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id);
let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
alice.onion_messenger.handle_onion_message(&bob_id, &onion_message);
let (invoice_request, reply_path) = extract_invoice_request(alice, &onion_message);
assert_eq!(invoice_request.amount_msats(), None);
assert_ne!(invoice_request.payer_id(), bob_id);
assert_eq!(reply_path.unwrap().introduction_node_id, bob_id);
let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
bob.onion_messenger.handle_onion_message(&alice_id, &onion_message);
let invoice = extract_invoice(bob, &onion_message);
assert_eq!(invoice.amount_msats(), 10_000_000);
assert_ne!(invoice.signing_pubkey(), alice_id);
assert!(!invoice.payment_paths().is_empty());
for (_, path) in invoice.payment_paths() {
assert_eq!(path.introduction_node_id, alice_id);
}
route_bolt12_payment(bob, &[alice], &invoice);
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
claim_bolt12_payment(bob, &[alice]);
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
}
/// Checks that a refund can be paid through a one-hop blinded path and that ephemeral pubkeys are
/// used rather than exposing a node's pubkey. However, the node's pubkey is still used as the
/// introduction node of the blinded path.
#[test]
fn creates_and_pays_for_refund_using_one_hop_blinded_path() {
let chanmon_cfgs = create_chanmon_cfgs(2);
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
let alice = &nodes[0];
let alice_id = alice.node.get_our_node_id();
let bob = &nodes[1];
let bob_id = bob.node.get_our_node_id();
let absolute_expiry = Duration::from_secs(u64::MAX);
let payment_id = PaymentId([1; 32]);
let refund = bob.node
.create_refund_builder(
"refund".to_string(), 10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None
)
.unwrap()
.build().unwrap();
assert_eq!(refund.amount_msats(), 10_000_000);
assert_eq!(refund.absolute_expiry(), Some(absolute_expiry));
assert_ne!(refund.payer_id(), bob_id);
assert!(!refund.paths().is_empty());
for path in refund.paths() {
assert_eq!(path.introduction_node_id, bob_id);
}
expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id);
alice.node.request_refund_payment(&refund).unwrap();
let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
bob.onion_messenger.handle_onion_message(&alice_id, &onion_message);
let invoice = extract_invoice(bob, &onion_message);
assert_eq!(invoice.amount_msats(), 10_000_000);
assert_ne!(invoice.signing_pubkey(), alice_id);
assert!(!invoice.payment_paths().is_empty());
for (_, path) in invoice.payment_paths() {
assert_eq!(path.introduction_node_id, alice_id);
}
route_bolt12_payment(bob, &[alice], &invoice);
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
claim_bolt12_payment(bob, &[alice]);
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
}
/// Checks that an invoice for an offer without any blinded paths can be requested. Note that while
/// the requested is sent directly using the node's pubkey, the response and the payment still use
/// blinded paths as required by the spec.
#[test]
fn pays_for_offer_without_blinded_paths() {
let chanmon_cfgs = create_chanmon_cfgs(2);
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
let alice = &nodes[0];
let alice_id = alice.node.get_our_node_id();
let bob = &nodes[1];
let bob_id = bob.node.get_our_node_id();
let offer = alice.node
.create_offer_builder("coffee".to_string()).unwrap()
.clear_paths()
.amount_msats(10_000_000)
.build().unwrap();
assert_eq!(offer.signing_pubkey(), alice_id);
assert!(offer.paths().is_empty());
let payment_id = PaymentId([1; 32]);
bob.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), None).unwrap();
expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id);
let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
alice.onion_messenger.handle_onion_message(&bob_id, &onion_message);
let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
bob.onion_messenger.handle_onion_message(&alice_id, &onion_message);
let invoice = extract_invoice(bob, &onion_message);
route_bolt12_payment(bob, &[alice], &invoice);
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
claim_bolt12_payment(bob, &[alice]);
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
}
/// Checks that a refund without any blinded paths can be paid. Note that while the invoice is sent
/// directly using the node's pubkey, the payment still use blinded paths as required by the spec.
#[test]
fn pays_for_refund_without_blinded_paths() {
let chanmon_cfgs = create_chanmon_cfgs(2);
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
let alice = &nodes[0];
let alice_id = alice.node.get_our_node_id();
let bob = &nodes[1];
let bob_id = bob.node.get_our_node_id();
let absolute_expiry = Duration::from_secs(u64::MAX);
let payment_id = PaymentId([1; 32]);
let refund = bob.node
.create_refund_builder(
"refund".to_string(), 10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None
)
.unwrap()
.clear_paths()
.build().unwrap();
assert_eq!(refund.payer_id(), bob_id);
assert!(refund.paths().is_empty());
expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id);
alice.node.request_refund_payment(&refund).unwrap();
let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
bob.onion_messenger.handle_onion_message(&alice_id, &onion_message);
let invoice = extract_invoice(bob, &onion_message);
route_bolt12_payment(bob, &[alice], &invoice);
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
claim_bolt12_payment(bob, &[alice]);
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
}
/// Fails creating an offer when a blinded path cannot be created without exposing the node's id.
#[test]
fn fails_creating_offer_without_blinded_paths() {
let chanmon_cfgs = create_chanmon_cfgs(2);
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
match nodes[0].node.create_offer_builder("coffee".to_string()) {
Ok(_) => panic!("Expected error"),
Err(e) => assert_eq!(e, Bolt12SemanticError::MissingPaths),
}
}
/// Fails creating a refund when a blinded path cannot be created without exposing the node's id.
#[test]
fn fails_creating_refund_without_blinded_paths() {
let chanmon_cfgs = create_chanmon_cfgs(2);
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
let absolute_expiry = Duration::from_secs(u64::MAX);
let payment_id = PaymentId([1; 32]);
match nodes[0].node.create_refund_builder(
"refund".to_string(), 10_000, absolute_expiry, payment_id, Retry::Attempts(0), None
) {
Ok(_) => panic!("Expected error"),
Err(e) => assert_eq!(e, Bolt12SemanticError::MissingPaths),
}
assert!(nodes[0].node.list_recent_payments().is_empty());
}
/// Fails creating an invoice request when a blinded reply path cannot be created without exposing
/// the node's id.
#[test]
fn fails_creating_invoice_request_without_blinded_reply_path() {
let chanmon_cfgs = create_chanmon_cfgs(6);
let node_cfgs = create_node_cfgs(6, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(6, &node_cfgs, &[None, None, None, None, None, None]);
let nodes = create_network(6, &node_cfgs, &node_chanmgrs);
create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
create_unannounced_chan_between_nodes_with_value(&nodes, 2, 3, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 1, 4, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 1, 5, 10_000_000, 1_000_000_000);
let (alice, bob, charlie, david) = (&nodes[0], &nodes[1], &nodes[2], &nodes[3]);
disconnect_peers(alice, &[charlie, david, &nodes[4], &nodes[5]]);
disconnect_peers(david, &[bob, &nodes[4], &nodes[5]]);
let offer = alice.node
.create_offer_builder("coffee".to_string()).unwrap()
.amount_msats(10_000_000)
.build().unwrap();
let payment_id = PaymentId([1; 32]);
match david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), None) {
Ok(_) => panic!("Expected error"),
Err(e) => assert_eq!(e, Bolt12SemanticError::MissingPaths),
}
assert!(nodes[0].node.list_recent_payments().is_empty());
}
#[test]
fn fails_creating_invoice_request_with_duplicate_payment_id() {
let chanmon_cfgs = create_chanmon_cfgs(6);
let node_cfgs = create_node_cfgs(6, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(6, &node_cfgs, &[None, None, None, None, None, None]);
let nodes = create_network(6, &node_cfgs, &node_chanmgrs);
create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
create_unannounced_chan_between_nodes_with_value(&nodes, 2, 3, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 1, 4, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 1, 5, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 2, 4, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 2, 5, 10_000_000, 1_000_000_000);
let (alice, _bob, charlie, david) = (&nodes[0], &nodes[1], &nodes[2], &nodes[3]);
disconnect_peers(alice, &[charlie, david, &nodes[4], &nodes[5]]);
let offer = alice.node
.create_offer_builder("coffee".to_string()).unwrap()
.amount_msats(10_000_000)
.build().unwrap();
let payment_id = PaymentId([1; 32]);
assert!(
david.node.pay_for_offer(
&offer, None, None, None, payment_id, Retry::Attempts(0), None
).is_ok()
);
expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id);
match david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), None) {
Ok(_) => panic!("Expected error"),
Err(e) => assert_eq!(e, Bolt12SemanticError::DuplicatePaymentId),
}
expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id);
}
#[test]
fn fails_creating_refund_with_duplicate_payment_id() {
let chanmon_cfgs = create_chanmon_cfgs(2);
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
let absolute_expiry = Duration::from_secs(u64::MAX);
let payment_id = PaymentId([1; 32]);
assert!(
nodes[0].node.create_refund_builder(
"refund".to_string(), 10_000, absolute_expiry, payment_id, Retry::Attempts(0), None
).is_ok()
);
expect_recent_payment!(nodes[0], RecentPaymentDetails::AwaitingInvoice, payment_id);
match nodes[0].node.create_refund_builder(
"refund".to_string(), 10_000, absolute_expiry, payment_id, Retry::Attempts(0), None
) {
Ok(_) => panic!("Expected error"),
Err(e) => assert_eq!(e, Bolt12SemanticError::DuplicatePaymentId),
}
expect_recent_payment!(nodes[0], RecentPaymentDetails::AwaitingInvoice, payment_id);
}
#[test]
fn fails_sending_invoice_without_blinded_payment_paths_for_offer() {
let mut accept_forward_cfg = test_default_channel_config();
accept_forward_cfg.accept_forwards_to_priv_channels = true;
// Clearing route_blinding prevents forming any payment paths since the node is unannounced.
let mut features = channelmanager::provided_init_features(&accept_forward_cfg);
features.set_onion_messages_optional();
features.clear_route_blinding();
let chanmon_cfgs = create_chanmon_cfgs(6);
let node_cfgs = create_node_cfgs(6, &chanmon_cfgs);
*node_cfgs[1].override_init_features.borrow_mut() = Some(features);
let node_chanmgrs = create_node_chanmgrs(
6, &node_cfgs, &[None, Some(accept_forward_cfg), None, None, None, None]
);
let nodes = create_network(6, &node_cfgs, &node_chanmgrs);
create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
create_unannounced_chan_between_nodes_with_value(&nodes, 2, 3, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 1, 4, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 1, 5, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 2, 4, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 2, 5, 10_000_000, 1_000_000_000);
let (alice, bob, charlie, david) = (&nodes[0], &nodes[1], &nodes[2], &nodes[3]);
let alice_id = alice.node.get_our_node_id();
let bob_id = bob.node.get_our_node_id();
let charlie_id = charlie.node.get_our_node_id();
let david_id = david.node.get_our_node_id();
disconnect_peers(alice, &[charlie, david, &nodes[4], &nodes[5]]);
disconnect_peers(david, &[bob, &nodes[4], &nodes[5]]);
let offer = alice.node
.create_offer_builder("coffee".to_string()).unwrap()
.amount_msats(10_000_000)
.build().unwrap();
let payment_id = PaymentId([1; 32]);
david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), None)
.unwrap();
connect_peers(david, bob);
let onion_message = david.onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
bob.onion_messenger.handle_onion_message(&david_id, &onion_message);
connect_peers(alice, charlie);
let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
alice.onion_messenger.handle_onion_message(&bob_id, &onion_message);
let onion_message = alice.onion_messenger.next_onion_message_for_peer(charlie_id).unwrap();
charlie.onion_messenger.handle_onion_message(&alice_id, &onion_message);
let onion_message = charlie.onion_messenger.next_onion_message_for_peer(david_id).unwrap();
david.onion_messenger.handle_onion_message(&charlie_id, &onion_message);
let invoice_error = extract_invoice_error(david, &onion_message);
assert_eq!(invoice_error, InvoiceError::from(Bolt12SemanticError::MissingPaths));
}
#[test]
fn fails_sending_invoice_without_blinded_payment_paths_for_refund() {
let mut accept_forward_cfg = test_default_channel_config();
accept_forward_cfg.accept_forwards_to_priv_channels = true;
// Clearing route_blinding prevents forming any payment paths since the node is unannounced.
let mut features = channelmanager::provided_init_features(&accept_forward_cfg);
features.set_onion_messages_optional();
features.clear_route_blinding();
let chanmon_cfgs = create_chanmon_cfgs(6);
let node_cfgs = create_node_cfgs(6, &chanmon_cfgs);
*node_cfgs[1].override_init_features.borrow_mut() = Some(features);
let node_chanmgrs = create_node_chanmgrs(
6, &node_cfgs, &[None, Some(accept_forward_cfg), None, None, None, None]
);
let nodes = create_network(6, &node_cfgs, &node_chanmgrs);
create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
create_unannounced_chan_between_nodes_with_value(&nodes, 2, 3, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 1, 4, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 1, 5, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 2, 4, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 2, 5, 10_000_000, 1_000_000_000);
let (alice, bob, charlie, david) = (&nodes[0], &nodes[1], &nodes[2], &nodes[3]);
disconnect_peers(alice, &[charlie, david, &nodes[4], &nodes[5]]);
disconnect_peers(david, &[bob, &nodes[4], &nodes[5]]);
let absolute_expiry = Duration::from_secs(u64::MAX);
let payment_id = PaymentId([1; 32]);
let refund = david.node
.create_refund_builder(
"refund".to_string(), 10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None
)
.unwrap()
.build().unwrap();
match alice.node.request_refund_payment(&refund) {
Ok(_) => panic!("Expected error"),
Err(e) => assert_eq!(e, Bolt12SemanticError::MissingPaths),
}
}
#[test]
fn fails_paying_invoice_more_than_once() {
let mut accept_forward_cfg = test_default_channel_config();
accept_forward_cfg.accept_forwards_to_priv_channels = true;
let mut features = channelmanager::provided_init_features(&accept_forward_cfg);
features.set_onion_messages_optional();
features.set_route_blinding_optional();
let chanmon_cfgs = create_chanmon_cfgs(6);
let node_cfgs = create_node_cfgs(6, &chanmon_cfgs);
*node_cfgs[1].override_init_features.borrow_mut() = Some(features);
let node_chanmgrs = create_node_chanmgrs(
6, &node_cfgs, &[None, Some(accept_forward_cfg), None, None, None, None]
);
let nodes = create_network(6, &node_cfgs, &node_chanmgrs);
create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
create_unannounced_chan_between_nodes_with_value(&nodes, 2, 3, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 1, 4, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 1, 5, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 2, 4, 10_000_000, 1_000_000_000);
create_announced_chan_between_nodes_with_value(&nodes, 2, 5, 10_000_000, 1_000_000_000);
let (alice, bob, charlie, david) = (&nodes[0], &nodes[1], &nodes[2], &nodes[3]);
let alice_id = alice.node.get_our_node_id();
let bob_id = bob.node.get_our_node_id();
let charlie_id = charlie.node.get_our_node_id();
let david_id = david.node.get_our_node_id();
disconnect_peers(alice, &[charlie, david, &nodes[4], &nodes[5]]);
disconnect_peers(david, &[bob, &nodes[4], &nodes[5]]);
let absolute_expiry = Duration::from_secs(u64::MAX);
let payment_id = PaymentId([1; 32]);
let refund = david.node
.create_refund_builder(
"refund".to_string(), 10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None
)
.unwrap()
.build().unwrap();
expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id);
// Alice sends the first invoice
alice.node.request_refund_payment(&refund).unwrap();
connect_peers(alice, charlie);
let onion_message = alice.onion_messenger.next_onion_message_for_peer(charlie_id).unwrap();
charlie.onion_messenger.handle_onion_message(&alice_id, &onion_message);
let onion_message = charlie.onion_messenger.next_onion_message_for_peer(david_id).unwrap();
david.onion_messenger.handle_onion_message(&charlie_id, &onion_message);
// David pays the first invoice
let invoice1 = extract_invoice(david, &onion_message);
route_bolt12_payment(david, &[charlie, bob, alice], &invoice1);
expect_recent_payment!(david, RecentPaymentDetails::Pending, payment_id);
claim_bolt12_payment(david, &[charlie, bob, alice]);
expect_recent_payment!(david, RecentPaymentDetails::Fulfilled, payment_id);
disconnect_peers(alice, &[charlie]);
// Alice sends the second invoice
alice.node.request_refund_payment(&refund).unwrap();
connect_peers(alice, charlie);
connect_peers(david, bob);
let onion_message = alice.onion_messenger.next_onion_message_for_peer(charlie_id).unwrap();
charlie.onion_messenger.handle_onion_message(&alice_id, &onion_message);
let onion_message = charlie.onion_messenger.next_onion_message_for_peer(david_id).unwrap();
david.onion_messenger.handle_onion_message(&charlie_id, &onion_message);
let invoice2 = extract_invoice(david, &onion_message);
assert_eq!(invoice1.payer_metadata(), invoice2.payer_metadata());
// David sends an error instead of paying the second invoice
let onion_message = david.onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
bob.onion_messenger.handle_onion_message(&david_id, &onion_message);
let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
alice.onion_messenger.handle_onion_message(&bob_id, &onion_message);
let invoice_error = extract_invoice_error(alice, &onion_message);
assert_eq!(invoice_error, InvoiceError::from_string("DuplicateInvoice".to_string()));
}

View file

@ -23,7 +23,7 @@ use crate::routing::router::{BlindedTail, InFlightHtlcs, Path, PaymentParameters
use crate::util::errors::APIError; use crate::util::errors::APIError;
use crate::util::logger::Logger; use crate::util::logger::Logger;
use crate::util::time::Time; use crate::util::time::Time;
#[cfg(all(not(feature = "no-std"), test))] #[cfg(all(feature = "std", test))]
use crate::util::time::tests::SinceEpoch; use crate::util::time::tests::SinceEpoch;
use crate::util::ser::ReadableArgs; use crate::util::ser::ReadableArgs;
@ -287,7 +287,7 @@ pub enum Retry {
/// retry, and may retry multiple failed HTLCs at once if they failed around the same time and /// retry, and may retry multiple failed HTLCs at once if they failed around the same time and
/// were retried along a route from a single call to [`Router::find_route_with_id`]. /// were retried along a route from a single call to [`Router::find_route_with_id`].
Attempts(u32), Attempts(u32),
#[cfg(not(feature = "no-std"))] #[cfg(feature = "std")]
/// Time elapsed before abandoning retries for a payment. At least one attempt at payment is made; /// Time elapsed before abandoning retries for a payment. At least one attempt at payment is made;
/// see [`PaymentParameters::expiry_time`] to avoid any attempt at payment after a specific time. /// see [`PaymentParameters::expiry_time`] to avoid any attempt at payment after a specific time.
/// ///
@ -295,13 +295,13 @@ pub enum Retry {
Timeout(core::time::Duration), Timeout(core::time::Duration),
} }
#[cfg(feature = "no-std")] #[cfg(not(feature = "std"))]
impl_writeable_tlv_based_enum!(Retry, impl_writeable_tlv_based_enum!(Retry,
; ;
(0, Attempts) (0, Attempts)
); );
#[cfg(not(feature = "no-std"))] #[cfg(feature = "std")]
impl_writeable_tlv_based_enum!(Retry, impl_writeable_tlv_based_enum!(Retry,
; ;
(0, Attempts), (0, Attempts),
@ -314,10 +314,10 @@ impl Retry {
(Retry::Attempts(max_retry_count), PaymentAttempts { count, .. }) => { (Retry::Attempts(max_retry_count), PaymentAttempts { count, .. }) => {
max_retry_count > count max_retry_count > count
}, },
#[cfg(all(not(feature = "no-std"), not(test)))] #[cfg(all(feature = "std", not(test)))]
(Retry::Timeout(max_duration), PaymentAttempts { first_attempted_at, .. }) => (Retry::Timeout(max_duration), PaymentAttempts { first_attempted_at, .. }) =>
*max_duration >= crate::util::time::MonotonicTime::now().duration_since(*first_attempted_at), *max_duration >= crate::util::time::MonotonicTime::now().duration_since(*first_attempted_at),
#[cfg(all(not(feature = "no-std"), test))] #[cfg(all(feature = "std", test))]
(Retry::Timeout(max_duration), PaymentAttempts { first_attempted_at, .. }) => (Retry::Timeout(max_duration), PaymentAttempts { first_attempted_at, .. }) =>
*max_duration >= SinceEpoch::now().duration_since(*first_attempted_at), *max_duration >= SinceEpoch::now().duration_since(*first_attempted_at),
} }
@ -343,27 +343,27 @@ pub(crate) struct PaymentAttemptsUsingTime<T: Time> {
/// it means the result of the first attempt is not known yet. /// it means the result of the first attempt is not known yet.
pub(crate) count: u32, pub(crate) count: u32,
/// This field is only used when retry is `Retry::Timeout` which is only build with feature std /// This field is only used when retry is `Retry::Timeout` which is only build with feature std
#[cfg(not(feature = "no-std"))] #[cfg(feature = "std")]
first_attempted_at: T, first_attempted_at: T,
#[cfg(feature = "no-std")] #[cfg(not(feature = "std"))]
phantom: core::marker::PhantomData<T>, phantom: core::marker::PhantomData<T>,
} }
#[cfg(not(any(feature = "no-std", test)))] #[cfg(not(feature = "std"))]
type ConfiguredTime = crate::util::time::MonotonicTime;
#[cfg(feature = "no-std")]
type ConfiguredTime = crate::util::time::Eternity; type ConfiguredTime = crate::util::time::Eternity;
#[cfg(all(not(feature = "no-std"), test))] #[cfg(all(feature = "std", not(test)))]
type ConfiguredTime = crate::util::time::MonotonicTime;
#[cfg(all(feature = "std", test))]
type ConfiguredTime = SinceEpoch; type ConfiguredTime = SinceEpoch;
impl<T: Time> PaymentAttemptsUsingTime<T> { impl<T: Time> PaymentAttemptsUsingTime<T> {
pub(crate) fn new() -> Self { pub(crate) fn new() -> Self {
PaymentAttemptsUsingTime { PaymentAttemptsUsingTime {
count: 0, count: 0,
#[cfg(not(feature = "no-std"))] #[cfg(feature = "std")]
first_attempted_at: T::now(), first_attempted_at: T::now(),
#[cfg(feature = "no-std")] #[cfg(not(feature = "std"))]
phantom: core::marker::PhantomData, phantom: core::marker::PhantomData,
} }
} }
@ -371,9 +371,9 @@ impl<T: Time> PaymentAttemptsUsingTime<T> {
impl<T: Time> Display for PaymentAttemptsUsingTime<T> { impl<T: Time> Display for PaymentAttemptsUsingTime<T> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
#[cfg(feature = "no-std")] #[cfg(not(feature = "std"))]
return write!(f, "attempts: {}", self.count); return write!(f, "attempts: {}", self.count);
#[cfg(not(feature = "no-std"))] #[cfg(feature = "std")]
return write!( return write!(
f, f,
"attempts: {}, duration: {}s", "attempts: {}, duration: {}s",
@ -1888,7 +1888,7 @@ mod tests {
let logger = test_utils::TestLogger::new(); let logger = test_utils::TestLogger::new();
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger)); let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
let scorer = RwLock::new(test_utils::TestScorer::new()); let scorer = RwLock::new(test_utils::TestScorer::new());
let router = test_utils::TestRouter::new(network_graph, &scorer); let router = test_utils::TestRouter::new(network_graph, &logger, &scorer);
let secp_ctx = Secp256k1::new(); let secp_ctx = Secp256k1::new();
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet); let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
@ -1932,7 +1932,7 @@ mod tests {
let logger = test_utils::TestLogger::new(); let logger = test_utils::TestLogger::new();
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger)); let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
let scorer = RwLock::new(test_utils::TestScorer::new()); let scorer = RwLock::new(test_utils::TestScorer::new());
let router = test_utils::TestRouter::new(network_graph, &scorer); let router = test_utils::TestRouter::new(network_graph, &logger, &scorer);
let secp_ctx = Secp256k1::new(); let secp_ctx = Secp256k1::new();
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet); let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
@ -1971,7 +1971,7 @@ mod tests {
let logger = test_utils::TestLogger::new(); let logger = test_utils::TestLogger::new();
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger)); let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
let scorer = RwLock::new(test_utils::TestScorer::new()); let scorer = RwLock::new(test_utils::TestScorer::new());
let router = test_utils::TestRouter::new(network_graph, &scorer); let router = test_utils::TestRouter::new(network_graph, &logger, &scorer);
let secp_ctx = Secp256k1::new(); let secp_ctx = Secp256k1::new();
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet); let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
@ -2178,7 +2178,7 @@ mod tests {
let logger = test_utils::TestLogger::new(); let logger = test_utils::TestLogger::new();
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger)); let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
let scorer = RwLock::new(test_utils::TestScorer::new()); let scorer = RwLock::new(test_utils::TestScorer::new());
let router = test_utils::TestRouter::new(network_graph, &scorer); let router = test_utils::TestRouter::new(network_graph, &logger, &scorer);
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet); let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
let pending_events = Mutex::new(VecDeque::new()); let pending_events = Mutex::new(VecDeque::new());
@ -2229,7 +2229,7 @@ mod tests {
let logger = test_utils::TestLogger::new(); let logger = test_utils::TestLogger::new();
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger)); let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
let scorer = RwLock::new(test_utils::TestScorer::new()); let scorer = RwLock::new(test_utils::TestScorer::new());
let router = test_utils::TestRouter::new(network_graph, &scorer); let router = test_utils::TestRouter::new(network_graph, &logger, &scorer);
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet); let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
let pending_events = Mutex::new(VecDeque::new()); let pending_events = Mutex::new(VecDeque::new());
@ -2288,7 +2288,7 @@ mod tests {
let logger = test_utils::TestLogger::new(); let logger = test_utils::TestLogger::new();
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger)); let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
let scorer = RwLock::new(test_utils::TestScorer::new()); let scorer = RwLock::new(test_utils::TestScorer::new());
let router = test_utils::TestRouter::new(network_graph, &scorer); let router = test_utils::TestRouter::new(network_graph, &logger, &scorer);
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet); let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
let pending_events = Mutex::new(VecDeque::new()); let pending_events = Mutex::new(VecDeque::new());
@ -2347,7 +2347,7 @@ mod tests {
let logger = test_utils::TestLogger::new(); let logger = test_utils::TestLogger::new();
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger)); let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
let scorer = RwLock::new(test_utils::TestScorer::new()); let scorer = RwLock::new(test_utils::TestScorer::new());
let router = test_utils::TestRouter::new(network_graph, &scorer); let router = test_utils::TestRouter::new(network_graph, &logger, &scorer);
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet); let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
let pending_events = Mutex::new(VecDeque::new()); let pending_events = Mutex::new(VecDeque::new());

View file

@ -42,10 +42,12 @@ use crate::prelude::*;
use crate::ln::functional_test_utils; use crate::ln::functional_test_utils;
use crate::ln::functional_test_utils::*; use crate::ln::functional_test_utils::*;
use crate::routing::gossip::NodeId; use crate::routing::gossip::NodeId;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::time::{SystemTime, Instant, Duration}; use {
#[cfg(not(feature = "no-std"))] crate::util::time::tests::SinceEpoch,
use crate::util::time::tests::SinceEpoch; std::time::{SystemTime, Instant, Duration},
};
#[test] #[test]
fn mpp_failure() { fn mpp_failure() {
@ -2313,7 +2315,7 @@ fn do_automatic_retries(test: AutoRetry) {
let mut msg_events = nodes[0].node.get_and_clear_pending_msg_events(); let mut msg_events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(msg_events.len(), 0); assert_eq!(msg_events.len(), 0);
} else if test == AutoRetry::FailTimeout { } else if test == AutoRetry::FailTimeout {
#[cfg(not(feature = "no-std"))] { #[cfg(feature = "std")] {
// Ensure ChannelManager will not retry a payment if it times out due to Retry::Timeout. // Ensure ChannelManager will not retry a payment if it times out due to Retry::Timeout.
nodes[0].node.send_payment(payment_hash, RecipientOnionFields::secret_only(payment_secret), nodes[0].node.send_payment(payment_hash, RecipientOnionFields::secret_only(payment_secret),
PaymentId(payment_hash.0), route_params, Retry::Timeout(Duration::from_secs(60))).unwrap(); PaymentId(payment_hash.0), route_params, Retry::Timeout(Duration::from_secs(60))).unwrap();

View file

@ -24,14 +24,10 @@ use crate::ln::ChannelId;
use crate::ln::features::{InitFeatures, NodeFeatures}; use crate::ln::features::{InitFeatures, NodeFeatures};
use crate::ln::msgs; use crate::ln::msgs;
use crate::ln::msgs::{ChannelMessageHandler, LightningError, SocketAddress, OnionMessageHandler, RoutingMessageHandler}; use crate::ln::msgs::{ChannelMessageHandler, LightningError, SocketAddress, OnionMessageHandler, RoutingMessageHandler};
#[cfg(not(c_bindings))]
use crate::ln::channelmanager::{SimpleArcChannelManager, SimpleRefChannelManager};
use crate::util::ser::{VecWriter, Writeable, Writer}; use crate::util::ser::{VecWriter, Writeable, Writer};
use crate::ln::peer_channel_encryptor::{PeerChannelEncryptor, NextNoiseStep, MessageBuf, MSG_BUF_ALLOC_SIZE}; use crate::ln::peer_channel_encryptor::{PeerChannelEncryptor, NextNoiseStep, MessageBuf, MSG_BUF_ALLOC_SIZE};
use crate::ln::wire; use crate::ln::wire;
use crate::ln::wire::{Encode, Type}; use crate::ln::wire::{Encode, Type};
#[cfg(not(c_bindings))]
use crate::onion_message::messenger::{SimpleArcOnionMessenger, SimpleRefOnionMessenger};
use crate::onion_message::messenger::{CustomOnionMessageHandler, PendingOnionMessage}; use crate::onion_message::messenger::{CustomOnionMessageHandler, PendingOnionMessage};
use crate::onion_message::offers::{OffersMessage, OffersMessageHandler}; use crate::onion_message::offers::{OffersMessage, OffersMessageHandler};
use crate::onion_message::packet::OnionMessageContents; use crate::onion_message::packet::OnionMessageContents;
@ -52,6 +48,8 @@ use core::convert::Infallible;
use std::error; use std::error;
#[cfg(not(c_bindings))] #[cfg(not(c_bindings))]
use { use {
crate::ln::channelmanager::{SimpleArcChannelManager, SimpleRefChannelManager},
crate::onion_message::messenger::{SimpleArcOnionMessenger, SimpleRefOnionMessenger},
crate::routing::gossip::{NetworkGraph, P2PGossipSync}, crate::routing::gossip::{NetworkGraph, P2PGossipSync},
crate::sign::KeysManager, crate::sign::KeysManager,
crate::sync::Arc, crate::sync::Arc,

View file

@ -339,6 +339,11 @@ impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> {
self self
} }
pub(crate) fn clear_paths(mut self) -> Self {
self.offer.paths = None;
self
}
pub(super) fn build_unchecked(self) -> Offer { pub(super) fn build_unchecked(self) -> Offer {
self.build_without_checks() self.build_without_checks()
} }

View file

@ -297,6 +297,11 @@ impl<'a, T: secp256k1::Signing> RefundBuilder<'a, T> {
#[cfg(test)] #[cfg(test)]
impl<'a, T: secp256k1::Signing> RefundBuilder<'a, T> { impl<'a, T: secp256k1::Signing> RefundBuilder<'a, T> {
pub(crate) fn clear_paths(mut self) -> Self {
self.refund.paths = None;
self
}
fn features_unchecked(mut self, features: InvoiceRequestFeatures) -> Self { fn features_unchecked(mut self, features: InvoiceRequestFeatures) -> Self {
self.refund.features = features; self.refund.features = features;
self self

View file

@ -20,8 +20,6 @@ use crate::blinded_path::message::{advance_path_by_one, ForwardTlvs, ReceiveTlvs
use crate::blinded_path::utils; use crate::blinded_path::utils;
use crate::events::{Event, EventHandler, EventsProvider}; use crate::events::{Event, EventHandler, EventsProvider};
use crate::sign::{EntropySource, NodeSigner, Recipient}; use crate::sign::{EntropySource, NodeSigner, Recipient};
#[cfg(not(c_bindings))]
use crate::ln::channelmanager::{SimpleArcChannelManager, SimpleRefChannelManager};
use crate::ln::features::{InitFeatures, NodeFeatures}; use crate::ln::features::{InitFeatures, NodeFeatures};
use crate::ln::msgs::{self, OnionMessage, OnionMessageHandler, SocketAddress}; use crate::ln::msgs::{self, OnionMessage, OnionMessageHandler, SocketAddress};
use crate::ln::onion_utils; use crate::ln::onion_utils;
@ -42,6 +40,7 @@ use crate::prelude::*;
#[cfg(not(c_bindings))] #[cfg(not(c_bindings))]
use { use {
crate::sign::KeysManager, crate::sign::KeysManager,
crate::ln::channelmanager::{SimpleArcChannelManager, SimpleRefChannelManager},
crate::ln::peer_handler::IgnoringMessageHandler, crate::ln::peer_handler::IgnoringMessageHandler,
crate::sync::Arc, crate::sync::Arc,
}; };
@ -714,6 +713,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`. /// Sends an [`OnionMessage`] with the given `contents` to `destination`.
/// ///
/// See [`OnionMessenger`] for example usage. /// See [`OnionMessenger`] for example usage.
@ -814,6 +818,14 @@ where
self.enqueue_onion_message(path, contents, reply_path, format_args!("")) self.enqueue_onion_message(path, contents, reply_path, format_args!(""))
} }
pub(crate) fn peel_onion_message(
&self, msg: &OnionMessage
) -> Result<PeeledOnion<<<CMH>::Target as CustomOnionMessageHandler>::CustomMessage>, ()> {
peel_onion_message(
msg, &self.secp_ctx, &*self.node_signer, &*self.logger, &*self.custom_handler
)
}
fn handle_onion_message_response<T: OnionMessageContents>( fn handle_onion_message_response<T: OnionMessageContents>(
&self, response: Option<T>, reply_path: Option<BlindedPath>, log_suffix: fmt::Arguments &self, response: Option<T>, reply_path: Option<BlindedPath>, log_suffix: fmt::Arguments
) { ) {
@ -899,9 +911,7 @@ where
CMH::Target: CustomOnionMessageHandler, CMH::Target: CustomOnionMessageHandler,
{ {
fn handle_onion_message(&self, _peer_node_id: &PublicKey, msg: &OnionMessage) { fn handle_onion_message(&self, _peer_node_id: &PublicKey, msg: &OnionMessage) {
match peel_onion_message( match self.peel_onion_message(msg) {
msg, &self.secp_ctx, &*self.node_signer, &*self.logger, &*self.custom_handler
) {
Ok(PeeledOnion::Receive(message, path_id, reply_path)) => { Ok(PeeledOnion::Receive(message, path_id, reply_path)) => {
log_trace!( log_trace!(
self.logger, self.logger,

View file

@ -1845,7 +1845,7 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
// NOTE: In the case of no-std, we won't have access to the current UNIX time at the time of removal, // NOTE: In the case of no-std, we won't have access to the current UNIX time at the time of removal,
// so we'll just set the removal time here to the current UNIX time on the very next invocation // so we'll just set the removal time here to the current UNIX time on the very next invocation
// of this function. // of this function.
#[cfg(feature = "no-std")] #[cfg(not(feature = "std"))]
{ {
let mut tracked_time = Some(current_time_unix); let mut tracked_time = Some(current_time_unix);
core::mem::swap(time, &mut tracked_time); core::mem::swap(time, &mut tracked_time);

View file

@ -6936,7 +6936,7 @@ mod tests {
(route.paths[1].hops[1].short_channel_id == 4 && route.paths[0].hops[1].short_channel_id == 13)); (route.paths[1].hops[1].short_channel_id == 4 && route.paths[0].hops[1].short_channel_id == 13));
} }
#[cfg(not(feature = "no-std"))] #[cfg(feature = "std")]
pub(super) fn random_init_seed() -> u64 { pub(super) fn random_init_seed() -> u64 {
// Because the default HashMap in std pulls OS randomness, we can use it as a (bad) RNG. // Because the default HashMap in std pulls OS randomness, we can use it as a (bad) RNG.
use core::hash::{BuildHasher, Hasher}; use core::hash::{BuildHasher, Hasher};
@ -6946,7 +6946,7 @@ mod tests {
} }
#[test] #[test]
#[cfg(not(feature = "no-std"))] #[cfg(feature = "std")]
fn generate_routes() { fn generate_routes() {
use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters}; use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters};
@ -6967,7 +6967,7 @@ mod tests {
} }
#[test] #[test]
#[cfg(not(feature = "no-std"))] #[cfg(feature = "std")]
fn generate_routes_mpp() { fn generate_routes_mpp() {
use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters}; use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters};
@ -6988,7 +6988,7 @@ mod tests {
} }
#[test] #[test]
#[cfg(not(feature = "no-std"))] #[cfg(feature = "std")]
fn generate_large_mpp_routes() { fn generate_large_mpp_routes() {
use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters}; use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters};
@ -8324,7 +8324,7 @@ mod tests {
} }
} }
#[cfg(all(any(test, ldk_bench), not(feature = "no-std")))] #[cfg(all(any(test, ldk_bench), feature = "std"))]
pub(crate) mod bench_utils { pub(crate) mod bench_utils {
use super::*; use super::*;
use std::fs::File; use std::fs::File;

View 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> { impl<'a, T: Score + 'a> LockableScore<'a> for RwLock<T> {
type ScoreUpdate = T; type ScoreUpdate = T;
type ScoreLookUp = T; type ScoreLookUp = T;

View file

@ -822,11 +822,8 @@ pub struct InMemorySigner {
channel_value_satoshis: u64, channel_value_satoshis: u64,
/// Key derivation parameters. /// Key derivation parameters.
channel_keys_id: [u8; 32], channel_keys_id: [u8; 32],
/// Seed from which all randomness produced is derived from. /// A source of random bytes.
rand_bytes_unique_start: [u8; 32], entropy_source: RandomBytes,
/// Tracks the number of times we've produced randomness to ensure we don't return the same
/// bytes twice.
rand_bytes_index: AtomicCounter,
} }
impl PartialEq for InMemorySigner { impl PartialEq for InMemorySigner {
@ -857,8 +854,7 @@ impl Clone for InMemorySigner {
channel_parameters: self.channel_parameters.clone(), channel_parameters: self.channel_parameters.clone(),
channel_value_satoshis: self.channel_value_satoshis, channel_value_satoshis: self.channel_value_satoshis,
channel_keys_id: self.channel_keys_id, channel_keys_id: self.channel_keys_id,
rand_bytes_unique_start: self.get_secure_random_bytes(), entropy_source: RandomBytes::new(self.get_secure_random_bytes()),
rand_bytes_index: AtomicCounter::new(),
} }
} }
} }
@ -892,8 +888,7 @@ impl InMemorySigner {
holder_channel_pubkeys, holder_channel_pubkeys,
channel_parameters: None, channel_parameters: None,
channel_keys_id, channel_keys_id,
rand_bytes_unique_start, entropy_source: RandomBytes::new(rand_bytes_unique_start),
rand_bytes_index: AtomicCounter::new(),
} }
} }
@ -1069,10 +1064,7 @@ impl InMemorySigner {
impl EntropySource for InMemorySigner { impl EntropySource for InMemorySigner {
fn get_secure_random_bytes(&self) -> [u8; 32] { fn get_secure_random_bytes(&self) -> [u8; 32] {
let index = self.rand_bytes_index.get_increment(); self.entropy_source.get_secure_random_bytes()
let mut nonce = [0u8; 16];
nonce[..8].copy_from_slice(&index.to_be_bytes());
ChaCha20::get_single_block(&self.rand_bytes_unique_start, &nonce)
} }
} }
@ -1350,8 +1342,7 @@ impl<ES: Deref> ReadableArgs<ES> for InMemorySigner where ES::Target: EntropySou
holder_channel_pubkeys, holder_channel_pubkeys,
channel_parameters: counterparty_channel_data, channel_parameters: counterparty_channel_data,
channel_keys_id: keys_id, channel_keys_id: keys_id,
rand_bytes_unique_start: entropy_source.get_secure_random_bytes(), entropy_source: RandomBytes::new(entropy_source.get_secure_random_bytes()),
rand_bytes_index: AtomicCounter::new(),
}) })
} }
} }
@ -1379,8 +1370,7 @@ pub struct KeysManager {
channel_master_key: ExtendedPrivKey, channel_master_key: ExtendedPrivKey,
channel_child_index: AtomicUsize, channel_child_index: AtomicUsize,
rand_bytes_unique_start: [u8; 32], entropy_source: RandomBytes,
rand_bytes_index: AtomicCounter,
seed: [u8; 32], seed: [u8; 32],
starting_time_secs: u64, starting_time_secs: u64,
@ -1449,8 +1439,7 @@ impl KeysManager {
channel_master_key, channel_master_key,
channel_child_index: AtomicUsize::new(0), channel_child_index: AtomicUsize::new(0),
rand_bytes_unique_start, entropy_source: RandomBytes::new(rand_bytes_unique_start),
rand_bytes_index: AtomicCounter::new(),
seed: *seed, seed: *seed,
starting_time_secs, starting_time_secs,
@ -1631,10 +1620,7 @@ impl KeysManager {
impl EntropySource for KeysManager { impl EntropySource for KeysManager {
fn get_secure_random_bytes(&self) -> [u8; 32] { fn get_secure_random_bytes(&self) -> [u8; 32] {
let index = self.rand_bytes_index.get_increment(); self.entropy_source.get_secure_random_bytes()
let mut nonce = [0u8; 16];
nonce[..8].copy_from_slice(&index.to_be_bytes());
ChaCha20::get_single_block(&self.rand_bytes_unique_start, &nonce)
} }
} }
@ -1888,6 +1874,35 @@ impl PhantomKeysManager {
} }
} }
/// An implementation of [`EntropySource`] using ChaCha20.
#[derive(Debug)]
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
/// bytes twice.
index: AtomicCounter,
}
impl RandomBytes {
/// Creates a new instance using the given seed.
pub fn new(seed: [u8; 32]) -> Self {
Self {
seed,
index: AtomicCounter::new(),
}
}
}
impl EntropySource for RandomBytes {
fn get_secure_random_bytes(&self) -> [u8; 32] {
let index = self.index.get_increment();
let mut nonce = [0u8; 16];
nonce[..8].copy_from_slice(&index.to_be_bytes());
ChaCha20::get_single_block(&self.seed, &nonce)
}
}
// Ensure that EcdsaChannelSigner can have a vtable // Ensure that EcdsaChannelSigner can have a vtable
#[test] #[test]
pub fn dyn_sign() { pub fn dyn_sign() {

View file

@ -32,10 +32,10 @@ use crate::ln::msgs::LightningError;
use crate::ln::script::ShutdownScript; use crate::ln::script::ShutdownScript;
use crate::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice}; use crate::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
use crate::offers::invoice_request::UnsignedInvoiceRequest; 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::gossip::{EffectiveCapacity, NetworkGraph, NodeId, RoutingFees};
use crate::routing::utxo::{UtxoLookup, UtxoLookupError, UtxoResult}; use crate::routing::utxo::{UtxoLookup, UtxoLookupError, UtxoResult};
use crate::routing::router::{find_route, InFlightHtlcs, Path, Route, RouteParameters, RouteHintHop, Router, ScorerAccountingForInFlightHtlcs}; use crate::routing::router::{DefaultRouter, InFlightHtlcs, Path, Route, RouteParameters, RouteHintHop, Router, ScorerAccountingForInFlightHtlcs};
use crate::routing::scoring::{ChannelUsage, ScoreUpdate, ScoreLookUp}; use crate::routing::scoring::{ChannelUsage, ScoreUpdate, ScoreLookUp};
use crate::sync::RwLock; use crate::sync::RwLock;
use crate::util::config::UserConfig; use crate::util::config::UserConfig;
@ -104,14 +104,29 @@ impl chaininterface::FeeEstimator for TestFeeEstimator {
} }
pub struct TestRouter<'a> { pub struct TestRouter<'a> {
pub router: DefaultRouter<
Arc<NetworkGraph<&'a TestLogger>>,
&'a TestLogger,
&'a RwLock<TestScorer>,
(),
TestScorer,
>,
pub network_graph: Arc<NetworkGraph<&'a TestLogger>>, pub network_graph: Arc<NetworkGraph<&'a TestLogger>>,
pub next_routes: Mutex<VecDeque<(RouteParameters, Result<Route, LightningError>)>>, pub next_routes: Mutex<VecDeque<(RouteParameters, Result<Route, LightningError>)>>,
pub scorer: &'a RwLock<TestScorer>, pub scorer: &'a RwLock<TestScorer>,
} }
impl<'a> TestRouter<'a> { impl<'a> TestRouter<'a> {
pub fn new(network_graph: Arc<NetworkGraph<&'a TestLogger>>, scorer: &'a RwLock<TestScorer>) -> Self { pub fn new(
Self { network_graph, next_routes: Mutex::new(VecDeque::new()), scorer } network_graph: Arc<NetworkGraph<&'a TestLogger>>, logger: &'a TestLogger,
scorer: &'a RwLock<TestScorer>
) -> Self {
Self {
router: DefaultRouter::new(network_graph.clone(), logger, [42u8; 32], scorer, ()),
network_graph,
next_routes: Mutex::new(VecDeque::new()),
scorer,
}
} }
pub fn expect_find_route(&self, query: RouteParameters, result: Result<Route, LightningError>) { pub fn expect_find_route(&self, query: RouteParameters, result: Result<Route, LightningError>) {
@ -185,38 +200,36 @@ impl<'a> Router for TestRouter<'a> {
} }
return find_route_res; return find_route_res;
} }
let logger = TestLogger::new();
find_route( self.router.find_route(payer, params, first_hops, inflight_htlcs)
payer, params, &self.network_graph, first_hops, &logger,
&ScorerAccountingForInFlightHtlcs::new(self.scorer.read().unwrap(), &inflight_htlcs), &Default::default(),
&[42; 32]
)
} }
fn create_blinded_payment_paths< fn create_blinded_payment_paths<
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
>( >(
&self, _recipient: PublicKey, _first_hops: Vec<ChannelDetails>, _tlvs: ReceiveTlvs, &self, recipient: PublicKey, first_hops: Vec<ChannelDetails>, tlvs: ReceiveTlvs,
_amount_msats: u64, _entropy_source: &ES, _secp_ctx: &Secp256k1<T> amount_msats: u64, entropy_source: &ES, secp_ctx: &Secp256k1<T>
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()> { ) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()> {
unreachable!() self.router.create_blinded_payment_paths(
recipient, first_hops, tlvs, amount_msats, entropy_source, secp_ctx
)
} }
} }
impl<'a> MessageRouter for TestRouter<'a> { impl<'a> MessageRouter for TestRouter<'a> {
fn find_path( fn find_path(
&self, _sender: PublicKey, _peers: Vec<PublicKey>, _destination: Destination &self, sender: PublicKey, peers: Vec<PublicKey>, destination: Destination
) -> Result<OnionMessagePath, ()> { ) -> Result<OnionMessagePath, ()> {
unreachable!() self.router.find_path(sender, peers, destination)
} }
fn create_blinded_paths< fn create_blinded_paths<
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
>( >(
&self, _recipient: PublicKey, _peers: Vec<PublicKey>, _entropy_source: &ES, &self, recipient: PublicKey, peers: Vec<PublicKey>, entropy_source: &ES,
_secp_ctx: &Secp256k1<T> secp_ctx: &Secp256k1<T>
) -> Result<Vec<BlindedPath>, ()> { ) -> Result<Vec<BlindedPath>, ()> {
unreachable!() self.router.create_blinded_paths(recipient, peers, entropy_source, secp_ctx)
} }
} }
@ -231,6 +244,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 {} pub struct OnlyReadsKeysInterface {}
impl EntropySource for OnlyReadsKeysInterface { impl EntropySource for OnlyReadsKeysInterface {
@ -1390,6 +1430,9 @@ impl ScoreUpdate for TestScorer {
fn time_passed(&mut self, _duration_since_epoch: Duration) {} fn time_passed(&mut self, _duration_since_epoch: Duration) {}
} }
#[cfg(c_bindings)]
impl crate::routing::scoring::Score for TestScorer {}
impl Drop for TestScorer { impl Drop for TestScorer {
fn drop(&mut self) { fn drop(&mut self) {
#[cfg(feature = "std")] { #[cfg(feature = "std")] {

View file

@ -59,15 +59,15 @@ impl Sub<Duration> for Eternity {
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg(not(feature = "no-std"))] #[cfg(feature = "std")]
pub struct MonotonicTime(std::time::Instant); pub struct MonotonicTime(std::time::Instant);
/// The amount of time to shift `Instant` forward to prevent overflow when subtracting a `Duration` /// The amount of time to shift `Instant` forward to prevent overflow when subtracting a `Duration`
/// from `Instant::now` on some operating systems (e.g., iOS representing `Instance` as `u64`). /// from `Instant::now` on some operating systems (e.g., iOS representing `Instance` as `u64`).
#[cfg(not(feature = "no-std"))] #[cfg(feature = "std")]
const SHIFT: Duration = Duration::from_secs(10 * 365 * 24 * 60 * 60); // 10 years. const SHIFT: Duration = Duration::from_secs(10 * 365 * 24 * 60 * 60); // 10 years.
#[cfg(not(feature = "no-std"))] #[cfg(feature = "std")]
impl Time for MonotonicTime { impl Time for MonotonicTime {
fn now() -> Self { fn now() -> Self {
let instant = std::time::Instant::now().checked_add(SHIFT).expect("Overflow on MonotonicTime instantiation"); let instant = std::time::Instant::now().checked_add(SHIFT).expect("Overflow on MonotonicTime instantiation");
@ -93,7 +93,7 @@ impl Time for MonotonicTime {
} }
} }
#[cfg(not(feature = "no-std"))] #[cfg(feature = "std")]
impl Sub<Duration> for MonotonicTime { impl Sub<Duration> for MonotonicTime {
type Output = Self; type Output = Self;
@ -177,7 +177,7 @@ pub mod tests {
} }
#[test] #[test]
#[cfg(not(feature = "no-std"))] #[cfg(feature = "std")]
fn monotonic_time_subtracts() { fn monotonic_time_subtracts() {
let now = super::MonotonicTime::now(); let now = super::MonotonicTime::now();
assert!(now.elapsed() < Duration::from_secs(10)); assert!(now.elapsed() < Duration::from_secs(10));