//! 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::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;
}
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 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 {
#[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) {
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())
}
/// 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)
}}
}
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);