//! A bunch of useful utilities for building networks of nodes and exchanging messages between
//! nodes for functional tests.
-use crate::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen, Watch, keysinterface::EntropySource};
+use crate::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen, Watch};
+use crate::sign::EntropySource;
use crate::chain::channelmonitor::ChannelMonitor;
use crate::chain::transaction::OutPoint;
use crate::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose, PaymentFailureReason};
use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
-use crate::ln::channelmanager::{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};
use crate::ln::features::InitFeatures;
use crate::util::ser::{ReadableArgs, Writeable};
use bitcoin::blockdata::block::{Block, BlockHeader};
-use bitcoin::blockdata::constants::genesis_block;
use bitcoin::blockdata::transaction::{Transaction, TxOut};
use bitcoin::network::constants::Network;
if conf_height > first_connect_height {
connect_blocks(node, conf_height - first_connect_height);
}
- let mut block = Block {
- header: BlockHeader { version: 0x20000000, prev_blockhash: node.best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: conf_height, bits: 42, nonce: 42 },
- txdata: Vec::new(),
- };
+ let mut txdata = Vec::new();
for _ in 0..*node.network_chan_count.borrow() { // Make sure we don't end up with channels at the same short id by offsetting by chan_count
- block.txdata.push(Transaction { version: 0, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: Vec::new() });
+ txdata.push(Transaction { version: 0, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: Vec::new() });
}
for tx in txn {
- block.txdata.push((*tx).clone());
+ txdata.push((*tx).clone());
}
+ let block = create_dummy_block(node.best_block_hash(), conf_height, txdata);
connect_block(node, &block);
scid_utils::scid_from_parts(conf_height as u64, block.txdata.len() as u64 - 1, 0).unwrap()
}
}
impl ConnectStyle {
+ pub fn skips_blocks(&self) -> bool {
+ match self {
+ ConnectStyle::BestBlockFirst => false,
+ ConnectStyle::BestBlockFirstSkippingBlocks => true,
+ ConnectStyle::BestBlockFirstReorgsOnlyTip => true,
+ ConnectStyle::TransactionsFirst => false,
+ ConnectStyle::TransactionsFirstSkippingBlocks => true,
+ ConnectStyle::TransactionsDuplicativelyFirstSkippingBlocks => true,
+ ConnectStyle::HighlyRedundantTransactionsFirstSkippingBlocks => true,
+ ConnectStyle::TransactionsFirstReorgsOnlyTip => true,
+ ConnectStyle::FullBlockViaListen => false,
+ }
+ }
+
+ pub fn updates_best_block_first(&self) -> bool {
+ match self {
+ ConnectStyle::BestBlockFirst => true,
+ ConnectStyle::BestBlockFirstSkippingBlocks => true,
+ ConnectStyle::BestBlockFirstReorgsOnlyTip => true,
+ ConnectStyle::TransactionsFirst => false,
+ ConnectStyle::TransactionsFirstSkippingBlocks => false,
+ ConnectStyle::TransactionsDuplicativelyFirstSkippingBlocks => false,
+ ConnectStyle::HighlyRedundantTransactionsFirstSkippingBlocks => false,
+ ConnectStyle::TransactionsFirstReorgsOnlyTip => false,
+ ConnectStyle::FullBlockViaListen => false,
+ }
+ }
+
fn random_style() -> ConnectStyle {
#[cfg(feature = "std")] {
use core::hash::{BuildHasher, Hasher};
}
}
+pub fn create_dummy_header(prev_blockhash: BlockHash, time: u32) -> BlockHeader {
+ BlockHeader {
+ version: 0x2000_0000,
+ prev_blockhash,
+ merkle_root: TxMerkleNode::all_zeros(),
+ time,
+ bits: 42,
+ nonce: 42,
+ }
+}
+
+pub fn create_dummy_block(prev_blockhash: BlockHash, time: u32, txdata: Vec<Transaction>) -> Block {
+ Block { header: create_dummy_header(prev_blockhash, time), txdata }
+}
+
pub fn connect_blocks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, depth: u32) -> BlockHash {
- let skip_intermediaries = match *node.connect_style.borrow() {
- ConnectStyle::BestBlockFirstSkippingBlocks|ConnectStyle::TransactionsFirstSkippingBlocks|
- ConnectStyle::TransactionsDuplicativelyFirstSkippingBlocks|ConnectStyle::HighlyRedundantTransactionsFirstSkippingBlocks|
- ConnectStyle::BestBlockFirstReorgsOnlyTip|ConnectStyle::TransactionsFirstReorgsOnlyTip => true,
- _ => false,
- };
+ let skip_intermediaries = node.connect_style.borrow().skips_blocks();
let height = node.best_block_info().1 + 1;
- let mut block = Block {
- header: BlockHeader { version: 0x2000000, prev_blockhash: node.best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: height, bits: 42, nonce: 42 },
- txdata: vec![],
- };
+ let mut block = create_dummy_block(node.best_block_hash(), height, Vec::new());
assert!(depth >= 1);
for i in 1..depth {
let prev_blockhash = block.header.block_hash();
do_connect_block(node, block, skip_intermediaries);
- block = Block {
- header: BlockHeader { version: 0x20000000, prev_blockhash, merkle_root: TxMerkleNode::all_zeros(), time: height + i, bits: 42, nonce: 42 },
- txdata: vec![],
- };
+ block = create_dummy_block(prev_blockhash, height + i, Vec::new());
}
let hash = block.header.block_hash();
do_connect_block(node, block, false);
#[cfg(feature = "std")] {
eprintln!("Connecting block using Block Connection Style: {:?}", *node.connect_style.borrow());
}
+ // Update the block internally before handing it over to LDK, to ensure our assertions regarding
+ // transaction broadcast are correct.
+ node.blocks.lock().unwrap().push((block.clone(), height));
if !skip_intermediaries {
let txdata: Vec<_> = block.txdata.iter().enumerate().collect();
match *node.connect_style.borrow() {
}
call_claimable_balances(node);
node.node.test_process_background_events();
- node.blocks.lock().unwrap().push((block, height));
}
pub fn disconnect_blocks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, count: u32) {
pub override_init_features: Rc<RefCell<Option<InitFeatures>>>,
}
+type TestChannelManager<'a, 'b, 'c> = ChannelManager<&'b TestChainMonitor<'c>, &'c test_utils::TestBroadcaster, &'b test_utils::TestKeysInterface, &'b test_utils::TestKeysInterface, &'b test_utils::TestKeysInterface, &'c test_utils::TestFeeEstimator, &'b test_utils::TestRouter<'c>, &'c test_utils::TestLogger>;
+
pub struct Node<'a, 'b: 'a, 'c: 'b> {
pub chain_source: &'c test_utils::TestChainSource,
pub tx_broadcaster: &'c test_utils::TestBroadcaster,
pub router: &'b test_utils::TestRouter<'c>,
pub chain_monitor: &'b test_utils::TestChainMonitor<'c>,
pub keys_manager: &'b test_utils::TestKeysInterface,
- pub node: &'a ChannelManager<&'b TestChainMonitor<'c>, &'c test_utils::TestBroadcaster, &'b test_utils::TestKeysInterface, &'b test_utils::TestKeysInterface, &'b test_utils::TestKeysInterface, &'c test_utils::TestFeeEstimator, &'b test_utils::TestRouter<'c>, &'c test_utils::TestLogger>,
+ pub node: &'a TestChannelManager<'a, 'b, 'c>,
pub network_graph: &'a NetworkGraph<&'c test_utils::TestLogger>,
pub gossip_sync: P2PGossipSync<&'b NetworkGraph<&'c test_utils::TestLogger>, &'c test_utils::TestChainSource, &'c test_utils::TestLogger>,
pub node_seed: [u8; 32],
unsafe impl Send for NodePtr {}
unsafe impl Sync for NodePtr {}
+
+pub trait NodeHolder {
+ type CM: AChannelManager;
+ fn node(&self) -> &ChannelManager<
+ <Self::CM as AChannelManager>::M,
+ <Self::CM as AChannelManager>::T,
+ <Self::CM as AChannelManager>::ES,
+ <Self::CM as AChannelManager>::NS,
+ <Self::CM as AChannelManager>::SP,
+ <Self::CM as AChannelManager>::F,
+ <Self::CM as AChannelManager>::R,
+ <Self::CM as AChannelManager>::L>;
+ fn chain_monitor(&self) -> Option<&test_utils::TestChainMonitor>;
+}
+impl<H: NodeHolder> NodeHolder for &H {
+ type CM = H::CM;
+ fn node(&self) -> &ChannelManager<
+ <Self::CM as AChannelManager>::M,
+ <Self::CM as AChannelManager>::T,
+ <Self::CM as AChannelManager>::ES,
+ <Self::CM as AChannelManager>::NS,
+ <Self::CM as AChannelManager>::SP,
+ <Self::CM as AChannelManager>::F,
+ <Self::CM as AChannelManager>::R,
+ <Self::CM as AChannelManager>::L> { (*self).node() }
+ fn chain_monitor(&self) -> Option<&test_utils::TestChainMonitor> { (*self).chain_monitor() }
+}
+impl<'a, 'b: 'a, 'c: 'b> NodeHolder for Node<'a, 'b, 'c> {
+ type CM = TestChannelManager<'a, 'b, 'c>;
+ fn node(&self) -> &TestChannelManager<'a, 'b, 'c> { &self.node }
+ fn chain_monitor(&self) -> Option<&test_utils::TestChainMonitor> { Some(self.chain_monitor) }
+}
+
impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> {
fn drop(&mut self) {
if !panicking() {
}
/// Gets an RAA and CS which were sent in response to a commitment update
-///
-/// Should only be used directly when the `$node` is not actually a [`Node`].
-macro_rules! do_get_revoke_commit_msgs {
- ($node: expr, $recipient: expr) => { {
- let events = $node.node.get_and_clear_pending_msg_events();
- assert_eq!(events.len(), 2);
- (match events[0] {
- MessageSendEvent::SendRevokeAndACK { ref node_id, ref msg } => {
- assert_eq!(node_id, $recipient);
- (*msg).clone()
- },
- _ => panic!("Unexpected event"),
- }, match events[1] {
- MessageSendEvent::UpdateHTLCs { ref node_id, ref updates } => {
- assert_eq!(node_id, $recipient);
- assert!(updates.update_add_htlcs.is_empty());
- assert!(updates.update_fulfill_htlcs.is_empty());
- assert!(updates.update_fail_htlcs.is_empty());
- assert!(updates.update_fail_malformed_htlcs.is_empty());
- assert!(updates.update_fee.is_none());
- updates.commitment_signed.clone()
- },
- _ => panic!("Unexpected event"),
- })
- } }
-}
-
-/// Gets an RAA and CS which were sent in response to a commitment update
-pub fn get_revoke_commit_msgs(node: &Node, recipient: &PublicKey) -> (msgs::RevokeAndACK, msgs::CommitmentSigned) {
- do_get_revoke_commit_msgs!(node, recipient)
+pub fn get_revoke_commit_msgs<CM: AChannelManager, H: NodeHolder<CM=CM>>(node: &H, recipient: &PublicKey) -> (msgs::RevokeAndACK, msgs::CommitmentSigned) {
+ let events = node.node().get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 2);
+ (match events[0] {
+ MessageSendEvent::SendRevokeAndACK { ref node_id, ref msg } => {
+ assert_eq!(node_id, recipient);
+ (*msg).clone()
+ },
+ _ => panic!("Unexpected event"),
+ }, match events[1] {
+ MessageSendEvent::UpdateHTLCs { ref node_id, ref updates } => {
+ assert_eq!(node_id, recipient);
+ assert!(updates.update_add_htlcs.is_empty());
+ assert!(updates.update_fulfill_htlcs.is_empty());
+ assert!(updates.update_fail_htlcs.is_empty());
+ assert!(updates.update_fail_malformed_htlcs.is_empty());
+ assert!(updates.update_fee.is_none());
+ updates.commitment_signed.clone()
+ },
+ _ => panic!("Unexpected event"),
+ })
}
#[macro_export]
MessageSendEvent::SendGossipTimestampFilter { node_id, .. } => {
node_id == msg_node_id
},
+ MessageSendEvent::SendAcceptChannelV2 { node_id, .. } => {
+ node_id == msg_node_id
+ },
+ MessageSendEvent::SendOpenChannelV2 { node_id, .. } => {
+ node_id == msg_node_id
+ },
+ MessageSendEvent::SendTxAddInput { node_id, .. } => {
+ node_id == msg_node_id
+ },
+ MessageSendEvent::SendTxAddOutput { node_id, .. } => {
+ node_id == msg_node_id
+ },
+ MessageSendEvent::SendTxRemoveInput { node_id, .. } => {
+ node_id == msg_node_id
+ },
+ MessageSendEvent::SendTxRemoveOutput { node_id, .. } => {
+ node_id == msg_node_id
+ },
+ MessageSendEvent::SendTxComplete { node_id, .. } => {
+ node_id == msg_node_id
+ },
+ MessageSendEvent::SendTxSignatures { node_id, .. } => {
+ node_id == msg_node_id
+ },
+ MessageSendEvent::SendTxInitRbf { node_id, .. } => {
+ node_id == msg_node_id
+ },
+ MessageSendEvent::SendTxAckRbf { node_id, .. } => {
+ node_id == msg_node_id
+ },
+ MessageSendEvent::SendTxAbort { node_id, .. } => {
+ node_id == msg_node_id
+ },
}});
if ev_index.is_some() {
msg_events.remove(ev_index.unwrap())
}
/// Check whether N channel monitor(s) have been added.
-pub fn check_added_monitors(node: &Node, count: usize) {
- let mut added_monitors = node.chain_monitor.added_monitors.lock().unwrap();
- assert_eq!(added_monitors.len(), count);
- added_monitors.clear();
+pub fn check_added_monitors<CM: AChannelManager, H: NodeHolder<CM=CM>>(node: &H, count: usize) {
+ if let Some(chain_monitor) = node.chain_monitor() {
+ let mut added_monitors = chain_monitor.added_monitors.lock().unwrap();
+ assert_eq!(added_monitors.len(), count);
+ added_monitors.clear();
+ }
}
/// Check whether N channel monitor(s) have been added.
bs_revoke_and_ack
}
};
- ($node_a: expr, $node_b: expr, (), $fail_backwards: expr, true /* skip last step */, true /* return extra message */) => {
- {
- let (extra_msg_option, bs_revoke_and_ack) = $crate::ln::functional_test_utils::do_main_commitment_signed_dance(&$node_a, &$node_b, $fail_backwards);
- $node_a.node.handle_revoke_and_ack(&$node_b.node.get_our_node_id(), &bs_revoke_and_ack);
- $crate::ln::functional_test_utils::check_added_monitors(&$node_a, 1);
- extra_msg_option
- }
- };
($node_a: expr, $node_b: expr, (), $fail_backwards: expr, true /* skip last step */, false /* no extra message */) => {
- assert!(commitment_signed_dance!($node_a, $node_b, (), $fail_backwards, true, true).is_none());
+ assert!($crate::ln::functional_test_utils::commitment_signed_dance_through_cp_raa(&$node_a, &$node_b, $fail_backwards).is_none());
};
($node_a: expr, $node_b: expr, $commitment_signed: expr, $fail_backwards: expr) => {
$crate::ln::functional_test_utils::do_commitment_signed_dance(&$node_a, &$node_b, &$commitment_signed, $fail_backwards, false);
}
}
-
+/// Runs the commitment_signed dance after the initial commitment_signed is delivered through to
+/// the initiator's `revoke_and_ack` response. i.e. [`do_main_commitment_signed_dance`] plus the
+/// `revoke_and_ack` response to it.
+///
+/// Returns any additional message `node_b` generated in addition to the `revoke_and_ack` response.
+pub fn commitment_signed_dance_through_cp_raa(node_a: &Node<'_, '_, '_>, node_b: &Node<'_, '_, '_>, fail_backwards: bool) -> Option<MessageSendEvent> {
+ let (extra_msg_option, bs_revoke_and_ack) = do_main_commitment_signed_dance(node_a, node_b, fail_backwards);
+ node_a.node.handle_revoke_and_ack(&node_b.node.get_our_node_id(), &bs_revoke_and_ack);
+ check_added_monitors(node_a, 1);
+ extra_msg_option
+}
+
+/// Does the main logic in the commitment_signed dance. After the first `commitment_signed` has
+/// been delivered, this method picks up and delivers the response `revoke_and_ack` and
+/// `commitment_signed`, returning the recipient's `revoke_and_ack` and any extra message it may
+/// have included.
pub fn do_main_commitment_signed_dance(node_a: &Node<'_, '_, '_>, node_b: &Node<'_, '_, '_>, fail_backwards: bool) -> (Option<MessageSendEvent>, msgs::RevokeAndACK) {
let (as_revoke_and_ack, as_commitment_signed) = get_revoke_commit_msgs!(node_a, node_b.node.get_our_node_id());
check_added_monitors!(node_b, 0);
(extra_msg_option, bs_revoke_and_ack)
}
+/// Runs a full commitment_signed dance, delivering a commitment_signed, the responding
+/// `revoke_and_ack` and `commitment_signed`, and then the final `revoke_and_ack` response.
+///
+/// If `skip_last_step` is unset, also checks for the payment failure update for the previous hop
+/// on failure or that no new messages are left over on success.
pub fn do_commitment_signed_dance(node_a: &Node<'_, '_, '_>, node_b: &Node<'_, '_, '_>, commitment_signed: &msgs::CommitmentSigned, fail_backwards: bool, skip_last_step: bool) {
check_added_monitors!(node_a, 0);
assert!(node_a.node.get_and_clear_pending_msg_events().is_empty());
}
/// Gets a route from the given sender to the node described in `payment_params`.
-pub fn get_route(send_node: &Node, payment_params: &PaymentParameters, recv_value: u64, final_cltv_expiry_delta: u32) -> Result<Route, msgs::LightningError> {
+pub fn get_route(send_node: &Node, payment_params: &PaymentParameters, recv_value: u64) -> Result<Route, msgs::LightningError> {
let scorer = TestScorer::new();
let keys_manager = TestKeysInterface::new(&[0u8; 32], bitcoin::network::constants::Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
router::get_route(
&send_node.node.get_our_node_id(), payment_params, &send_node.network_graph.read_only(),
Some(&send_node.node.list_usable_channels().iter().collect::<Vec<_>>()),
- recv_value, final_cltv_expiry_delta, send_node.logger, &scorer, &random_seed_bytes
+ recv_value, send_node.logger, &scorer, &(), &random_seed_bytes
)
}
/// Don't use this, use the identically-named function instead.
#[macro_export]
macro_rules! get_route {
- ($send_node: expr, $payment_params: expr, $recv_value: expr, $cltv: expr) => {
- $crate::ln::functional_test_utils::get_route(&$send_node, &$payment_params, $recv_value, $cltv)
+ ($send_node: expr, $payment_params: expr, $recv_value: expr) => {
+ $crate::ln::functional_test_utils::get_route(&$send_node, &$payment_params, $recv_value)
}
}
macro_rules! get_route_and_payment_hash {
($send_node: expr, $recv_node: expr, $recv_value: expr) => {{
let payment_params = $crate::routing::router::PaymentParameters::from_node_id($recv_node.node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_features($recv_node.node.invoice_features());
- $crate::get_route_and_payment_hash!($send_node, $recv_node, payment_params, $recv_value, TEST_FINAL_CLTV)
+ .with_bolt11_features($recv_node.node.invoice_features()).unwrap();
+ $crate::get_route_and_payment_hash!($send_node, $recv_node, payment_params, $recv_value)
}};
- ($send_node: expr, $recv_node: expr, $payment_params: expr, $recv_value: expr, $cltv: expr) => {{
+ ($send_node: expr, $recv_node: expr, $payment_params: expr, $recv_value: expr) => {{
let (payment_preimage, payment_hash, payment_secret) =
$crate::ln::functional_test_utils::get_payment_preimage_hash(&$recv_node, Some($recv_value), None);
- let route = $crate::ln::functional_test_utils::get_route(&$send_node, &$payment_params, $recv_value, $cltv);
+ let route = $crate::ln::functional_test_utils::get_route(&$send_node, &$payment_params, $recv_value);
(route.unwrap(), payment_hash, payment_preimage, payment_secret)
}}
}
#[macro_export]
-#[cfg(any(test, feature = "_bench_unstable", feature = "_test_utils"))]
+#[cfg(any(test, ldk_bench, feature = "_test_utils"))]
macro_rules! expect_payment_claimable {
($node: expr, $expected_payment_hash: expr, $expected_payment_secret: expr, $expected_recv_value: expr) => {
expect_payment_claimable!($node, $expected_payment_hash, $expected_payment_secret, $expected_recv_value, None, $node.node.get_our_node_id())
}
#[macro_export]
-#[cfg(any(test, feature = "_bench_unstable", feature = "_test_utils"))]
+#[cfg(any(test, ldk_bench, feature = "_test_utils"))]
macro_rules! expect_payment_claimed {
($node: expr, $expected_payment_hash: expr, $expected_recv_value: expr) => {
let events = $node.node.get_and_clear_pending_events();
}
}
+pub fn expect_payment_sent<CM: AChannelManager, H: NodeHolder<CM=CM>>(node: &H,
+ expected_payment_preimage: PaymentPreimage, expected_fee_msat_opt: Option<Option<u64>>,
+ expect_per_path_claims: bool,
+) {
+ let events = node.node().get_and_clear_pending_events();
+ let expected_payment_hash = PaymentHash(
+ bitcoin::hashes::sha256::Hash::hash(&expected_payment_preimage.0).into_inner());
+ if expect_per_path_claims {
+ assert!(events.len() > 1);
+ } else {
+ assert_eq!(events.len(), 1);
+ }
+ let expected_payment_id = match events[0] {
+ Event::PaymentSent { ref payment_id, ref payment_preimage, ref payment_hash, ref fee_paid_msat } => {
+ assert_eq!(expected_payment_preimage, *payment_preimage);
+ assert_eq!(expected_payment_hash, *payment_hash);
+ if let Some(expected_fee_msat) = expected_fee_msat_opt {
+ assert_eq!(*fee_paid_msat, expected_fee_msat);
+ } else {
+ assert!(fee_paid_msat.is_some());
+ }
+ payment_id.unwrap()
+ },
+ _ => panic!("Unexpected event"),
+ };
+ if expect_per_path_claims {
+ for i in 1..events.len() {
+ match events[i] {
+ Event::PaymentPathSuccessful { payment_id, payment_hash, .. } => {
+ assert_eq!(payment_id, expected_payment_id);
+ assert_eq!(payment_hash, Some(expected_payment_hash));
+ },
+ _ => panic!("Unexpected event"),
+ }
+ }
+ }
+}
+
#[cfg(test)]
#[macro_export]
macro_rules! expect_payment_sent_without_paths {
($node: expr, $expected_payment_preimage: expr, $expected_fee_msat_opt: expr) => {
$crate::expect_payment_sent!($node, $expected_payment_preimage, $expected_fee_msat_opt, true);
};
- ($node: expr, $expected_payment_preimage: expr, $expected_fee_msat_opt: expr, $expect_paths: expr) => { {
- use bitcoin::hashes::Hash as _;
- let events = $node.node.get_and_clear_pending_events();
- let expected_payment_hash = $crate::ln::PaymentHash(
- bitcoin::hashes::sha256::Hash::hash(&$expected_payment_preimage.0).into_inner());
- if $expect_paths {
- assert!(events.len() > 1);
- } else {
- assert_eq!(events.len(), 1);
- }
- let expected_payment_id = match events[0] {
- $crate::events::Event::PaymentSent { ref payment_id, ref payment_preimage, ref payment_hash, ref fee_paid_msat } => {
- assert_eq!($expected_payment_preimage, *payment_preimage);
- assert_eq!(expected_payment_hash, *payment_hash);
- assert!(fee_paid_msat.is_some());
- if $expected_fee_msat_opt.is_some() {
- assert_eq!(*fee_paid_msat, $expected_fee_msat_opt);
- }
- payment_id.unwrap()
- },
- _ => panic!("Unexpected event"),
- };
- if $expect_paths {
- for i in 1..events.len() {
- match events[i] {
- $crate::events::Event::PaymentPathSuccessful { payment_id, payment_hash, .. } => {
- assert_eq!(payment_id, expected_payment_id);
- assert_eq!(payment_hash, Some(expected_payment_hash));
- },
- _ => panic!("Unexpected event"),
- }
- }
- }
- } }
+ ($node: expr, $expected_payment_preimage: expr, $expected_fee_msat_opt: expr, $expect_paths: expr) => {
+ $crate::ln::functional_test_utils::expect_payment_sent(&$node, $expected_payment_preimage,
+ $expected_fee_msat_opt.map(|o| Some(o)), $expect_paths);
+ }
}
#[cfg(test)]
}
}
-#[cfg(any(test, feature = "_bench_unstable", feature = "_test_utils"))]
+#[cfg(any(test, ldk_bench, feature = "_test_utils"))]
pub fn expect_channel_pending_event<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, expected_counterparty_node_id: &PublicKey) {
let events = node.node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
}
}
-#[cfg(any(test, feature = "_bench_unstable", feature = "_test_utils"))]
+#[cfg(any(test, ldk_bench, feature = "_test_utils"))]
pub fn expect_channel_ready_event<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, expected_counterparty_node_id: &PublicKey) {
let events = node.node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
let events_2 = node.node.get_and_clear_pending_events();
if payment_claimable_expected {
assert_eq!(events_2.len(), 1);
- match events_2[0] {
- Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, ref via_channel_id, ref via_user_channel_id, claim_deadline } => {
+ match &events_2[0] {
+ Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat,
+ receiver_node_id, ref via_channel_id, ref via_user_channel_id,
+ claim_deadline, onion_fields,
+ } => {
assert_eq!(our_payment_hash, *payment_hash);
assert_eq!(node.node.get_our_node_id(), receiver_node_id.unwrap());
+ assert!(onion_fields.is_some());
match &purpose {
PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
assert_eq!(expected_preimage, *payment_preimage);
assert_eq!(our_payment_secret.unwrap(), *payment_secret);
+ assert_eq!(Some(*payment_secret), onion_fields.as_ref().unwrap().payment_secret);
},
PaymentPurpose::SpontaneousPayment(payment_preimage) => {
assert_eq!(expected_preimage.unwrap(), *payment_preimage);
assert!(our_payment_secret.is_none());
},
}
- assert_eq!(amount_msat, recv_value);
+ assert_eq!(*amount_msat, recv_value);
assert!(node.node.list_channels().iter().any(|details| details.channel_id == via_channel_id.unwrap()));
assert!(node.node.list_channels().iter().any(|details| details.user_channel_id == via_user_channel_id.unwrap()));
assert!(claim_deadline.unwrap() > node.best_block_info().1);
pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash, PaymentSecret) {
let payment_params = PaymentParameters::from_node_id(expected_route.last().unwrap().node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_features(expected_route.last().unwrap().node.invoice_features());
- let route = get_route(origin_node, &payment_params, recv_value, TEST_FINAL_CLTV).unwrap();
+ .with_bolt11_features(expected_route.last().unwrap().node.invoice_features()).unwrap();
+ let route = get_route(origin_node, &payment_params, recv_value).unwrap();
assert_eq!(route.paths.len(), 1);
- assert_eq!(route.paths[0].len(), expected_route.len());
- for (node, hop) in expected_route.iter().zip(route.paths[0].iter()) {
+ assert_eq!(route.paths[0].hops.len(), expected_route.len());
+ for (node, hop) in expected_route.iter().zip(route.paths[0].hops.iter()) {
assert_eq!(hop.pubkey, node.node.get_our_node_id());
}
pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) {
let payment_params = PaymentParameters::from_node_id(expected_route.last().unwrap().node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_features(expected_route.last().unwrap().node.invoice_features());
+ .with_bolt11_features(expected_route.last().unwrap().node.invoice_features()).unwrap();
let network_graph = origin_node.network_graph.read_only();
let scorer = test_utils::TestScorer::new();
let seed = [0u8; 32];
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let route = router::get_route(
&origin_node.node.get_our_node_id(), &payment_params, &network_graph,
- None, recv_value, TEST_FINAL_CLTV, origin_node.logger, &scorer, &random_seed_bytes).unwrap();
+ None, recv_value, origin_node.logger, &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 1);
- assert_eq!(route.paths[0].len(), expected_route.len());
- for (node, hop) in expected_route.iter().zip(route.paths[0].iter()) {
+ assert_eq!(route.paths[0].hops.len(), expected_route.len());
+ for (node, hop) in expected_route.iter().zip(route.paths[0].hops.iter()) {
assert_eq!(hop.pubkey, node.node.get_our_node_id());
}
assert_eq!(payment_hash, our_payment_hash);
assert!(payment_failed_permanently);
for (idx, hop) in expected_route.iter().enumerate() {
- assert_eq!(hop.node.get_our_node_id(), path[idx].pubkey);
+ assert_eq!(hop.node.get_our_node_id(), path.hops[idx].pubkey);
}
payment_id.unwrap()
},
pub fn create_chanmon_cfgs(node_count: usize) -> Vec<TestChanMonCfg> {
let mut chan_mon_cfgs = Vec::new();
for i in 0..node_count {
- let tx_broadcaster = test_utils::TestBroadcaster {
- txn_broadcasted: Mutex::new(Vec::new()),
- blocks: Arc::new(Mutex::new(vec![(genesis_block(Network::Testnet), 0)])),
- };
+ let tx_broadcaster = test_utils::TestBroadcaster::new(Network::Testnet);
let fee_estimator = test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) };
let chain_source = test_utils::TestChainSource::new(Network::Testnet);
let logger = test_utils::TestLogger::with_id(format!("node {}", i));
/// also fail.
pub fn test_txn_broadcast<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, chan: &(msgs::ChannelUpdate, msgs::ChannelUpdate, [u8; 32], Transaction), commitment_tx: Option<Transaction>, has_htlc_tx: HTLCType) -> Vec<Transaction> {
let mut node_txn = node.tx_broadcaster.txn_broadcasted.lock().unwrap();
+ let mut txn_seen = HashSet::new();
+ node_txn.retain(|tx| txn_seen.insert(tx.txid()));
assert!(node_txn.len() >= if commitment_tx.is_some() { 0 } else { 1 } + if has_htlc_tx == HTLCType::NONE { 0 } else { 1 });
let mut res = Vec::with_capacity(2);
pub fn check_preimage_claim<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, prev_txn: &Vec<Transaction>) -> Vec<Transaction> {
let mut node_txn = node.tx_broadcaster.txn_broadcasted.lock().unwrap();
+ let mut txn_seen = HashSet::new();
+ node_txn.retain(|tx| txn_seen.insert(tx.txid()));
- assert!(node_txn.len() >= 1);
- assert_eq!(node_txn[0].input.len(), 1);
let mut found_prev = false;
-
- for tx in prev_txn {
- if node_txn[0].input[0].previous_output.txid == tx.txid() {
- check_spends!(node_txn[0], tx);
- let mut iter = node_txn[0].input[0].witness.iter();
- iter.next().expect("expected 3 witness items");
- iter.next().expect("expected 3 witness items");
- assert!(iter.next().expect("expected 3 witness items").len() > 106); // must spend an htlc output
- assert_eq!(tx.input.len(), 1); // must spend a commitment tx
-
- found_prev = true;
- break;
+ for prev_tx in prev_txn {
+ for tx in &*node_txn {
+ if tx.input[0].previous_output.txid == prev_tx.txid() {
+ check_spends!(tx, prev_tx);
+ let mut iter = tx.input[0].witness.iter();
+ iter.next().expect("expected 3 witness items");
+ iter.next().expect("expected 3 witness items");
+ assert!(iter.next().expect("expected 3 witness items").len() > 106); // must spend an htlc output
+ assert_eq!(tx.input.len(), 1); // must spend a commitment tx
+
+ found_prev = true;
+ break;
+ }
}
}
assert!(found_prev);
}
let mut had_channel_update = false; // ChannelUpdate may be now or later, but not both
- if let Some(&MessageSendEvent::SendChannelUpdate { ref node_id, ref msg }) = msg_events.get(idx) {
+ if let Some(&MessageSendEvent::SendChannelUpdate { ref node_id, .. }) = msg_events.get(idx) {
assert_eq!(*node_id, $dst_node.node.get_our_node_id());
idx += 1;
- assert_eq!(msg.contents.flags & 2, 0); // "disabled" flag must not be set as we just reconnected.
had_channel_update = true;
}
}
}
- if let Some(&MessageSendEvent::SendChannelUpdate { ref node_id, ref msg }) = msg_events.get(idx) {
+ if let Some(&MessageSendEvent::SendChannelUpdate { ref node_id, .. }) = msg_events.get(idx) {
assert_eq!(*node_id, $dst_node.node.get_our_node_id());
idx += 1;
- assert_eq!(msg.contents.flags & 2, 0); // "disabled" flag must not be set as we just reconnected.
assert!(!had_channel_update);
}