Skip to content

Commit ee6d232

Browse files
committed
Configurable test store
1 parent 7d3c1b8 commit ee6d232

File tree

2 files changed

+175
-7
lines changed

2 files changed

+175
-7
lines changed

tests/common/mod.rs

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,11 @@ use electrsd::corepc_node::{Client as BitcoindClient, Node as BitcoinD};
2929
use electrsd::{corepc_node, ElectrsD};
3030
use electrum_client::ElectrumApi;
3131
use ldk_node::config::{AsyncPaymentsRole, Config, ElectrumSyncConfig, EsploraSyncConfig};
32-
use ldk_node::io::sqlite_store::SqliteStore;
32+
use ldk_node::io::sqlite_store::{SqliteStore, KV_TABLE_NAME, SQLITE_DB_FILE_NAME};
3333
use ldk_node::payment::{PaymentDirection, PaymentKind, PaymentStatus};
3434
use ldk_node::{
35-
Builder, CustomTlvRecord, Event, LightningBalance, Node, NodeError, PendingSweepBalance,
35+
Builder, CustomTlvRecord, DynStore, Event, LightningBalance, Node, NodeError,
36+
PendingSweepBalance,
3637
};
3738
use lightning::io;
3839
use lightning::ln::msgs::SocketAddress;
@@ -262,10 +263,23 @@ pub(crate) enum TestChainSource<'a> {
262263
BitcoindRestSync(&'a BitcoinD),
263264
}
264265

266+
#[derive(Clone, Copy)]
267+
pub(crate) enum TestStoreType {
268+
InMemory,
269+
Sqlite,
270+
}
271+
272+
impl Default for TestStoreType {
273+
fn default() -> Self {
274+
TestStoreType::InMemory
275+
}
276+
}
277+
265278
#[derive(Clone, Default)]
266279
pub(crate) struct TestConfig {
267280
pub node_config: Config,
268281
pub log_writer: TestLogWriter,
282+
pub store_type: TestStoreType,
269283
}
270284

271285
macro_rules! setup_builder {
@@ -282,13 +296,28 @@ pub(crate) use setup_builder;
282296
pub(crate) fn setup_two_nodes(
283297
chain_source: &TestChainSource, allow_0conf: bool, anchor_channels: bool,
284298
anchors_trusted_no_reserve: bool,
299+
) -> (TestNode, TestNode) {
300+
setup_two_nodes_with_store(
301+
chain_source,
302+
allow_0conf,
303+
anchor_channels,
304+
anchors_trusted_no_reserve,
305+
TestStoreType::InMemory,
306+
)
307+
}
308+
309+
pub(crate) fn setup_two_nodes_with_store(
310+
chain_source: &TestChainSource, allow_0conf: bool, anchor_channels: bool,
311+
anchors_trusted_no_reserve: bool, store_type: TestStoreType,
285312
) -> (TestNode, TestNode) {
286313
println!("== Node A ==");
287-
let config_a = random_config(anchor_channels);
314+
let mut config_a = random_config(anchor_channels);
315+
config_a.store_type = store_type;
288316
let node_a = setup_node(chain_source, config_a, None);
289317

290318
println!("\n== Node B ==");
291319
let mut config_b = random_config(anchor_channels);
320+
config_b.store_type = store_type;
292321
if allow_0conf {
293322
config_b.node_config.trusted_peers_0conf.push(node_a.node_id());
294323
}
@@ -381,8 +410,21 @@ pub(crate) fn setup_node_for_async_payments(
381410

382411
builder.set_async_payments_role(async_payments_role).unwrap();
383412

384-
let test_sync_store = Arc::new(TestSyncStore::new(config.node_config.storage_dir_path.into()));
385-
let node = builder.build_with_store(test_sync_store).unwrap();
413+
let kv_store: Arc<DynStore> = match config.store_type {
414+
TestStoreType::InMemory => {
415+
Arc::new(TestSyncStore::new(config.node_config.storage_dir_path.into()))
416+
},
417+
TestStoreType::Sqlite => Arc::new(
418+
SqliteStore::new(
419+
config.node_config.storage_dir_path.into(),
420+
Some(SQLITE_DB_FILE_NAME.to_string()),
421+
Some(KV_TABLE_NAME.to_string()),
422+
)
423+
.unwrap(),
424+
),
425+
};
426+
427+
let node = builder.build_with_store(kv_store).unwrap();
386428
node.start().unwrap();
387429
assert!(node.status().is_running);
388430
assert!(node.status().latest_fee_rate_cache_update_timestamp.is_some());

tests/integration_tests_rust.rs

Lines changed: 128 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ mod common;
1010
use std::collections::HashSet;
1111
use std::str::FromStr;
1212
use std::sync::Arc;
13+
use std::time::Instant;
1314

1415
use bitcoin::address::NetworkUnchecked;
1516
use bitcoin::hashes::sha256::Hash as Sha256Hash;
@@ -23,21 +24,24 @@ use common::{
2324
generate_blocks_and_wait, open_channel, open_channel_push_amt, premine_and_distribute_funds,
2425
premine_blocks, prepare_rbf, random_config, random_listening_addresses,
2526
setup_bitcoind_and_electrsd, setup_builder, setup_node, setup_node_for_async_payments,
26-
setup_two_nodes, wait_for_tx, TestChainSource, TestSyncStore,
27+
setup_two_nodes, setup_two_nodes_with_store, wait_for_tx, TestChainSource, TestSyncStore,
2728
};
2829
use ldk_node::config::{AsyncPaymentsRole, EsploraSyncConfig};
2930
use ldk_node::liquidity::LSPS2ServiceConfig;
3031
use ldk_node::payment::{
3132
ConfirmationStatus, PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus,
3233
QrPaymentResult,
3334
};
34-
use ldk_node::{Builder, DynStore, Event, NodeError};
35+
use ldk_node::{Builder, DynStore, Event, Node, NodeError};
3536
use lightning::ln::channelmanager::PaymentId;
3637
use lightning::routing::gossip::{NodeAlias, NodeId};
3738
use lightning::routing::router::RouteParametersConfig;
39+
use lightning::util::hash_tables::new_hash_map;
3840
use lightning_invoice::{Bolt11InvoiceDescription, Description};
3941
use lightning_types::payment::{PaymentHash, PaymentPreimage};
4042
use log::LevelFilter;
43+
use tokio::sync::Mutex;
44+
use tokio::task::{self, JoinSet};
4145

4246
#[test]
4347
fn channel_full_cycle() {
@@ -1761,6 +1765,128 @@ fn facade_logging() {
17611765
}
17621766
}
17631767

1768+
fn spawn_payment(node_a: Arc<Node>, node_b: Arc<Node>, cur_id: u32) {
1769+
let mut preimage_bytes = [0u8; 32];
1770+
1771+
preimage_bytes[0..4].copy_from_slice(&cur_id.to_le_bytes());
1772+
1773+
// Spawn each payment as a separate async task
1774+
task::spawn(async move {
1775+
println!("Starting payment {}", cur_id);
1776+
let custom_preimage = PaymentPreimage(preimage_bytes);
1777+
let amount_msat = 10_000_000;
1778+
1779+
loop {
1780+
// Pre-check the HTLC slots to try to avoid the performance impact of a failed payment.
1781+
while node_a.list_channels()[0].next_outbound_htlc_limit_msat == 0 {
1782+
println!("Waiting for HTLC slots to free up... ({})", cur_id);
1783+
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
1784+
}
1785+
1786+
let payment_id = node_a.spontaneous_payment().send_with_preimage(
1787+
amount_msat,
1788+
node_b.node_id(),
1789+
custom_preimage,
1790+
None,
1791+
);
1792+
1793+
match payment_id {
1794+
Ok(payment_id) => {
1795+
println!("Awaiting payment {} ({})", payment_id, cur_id);
1796+
break;
1797+
},
1798+
Err(e) => {
1799+
println!("Payment attempt failed: {:?}, retrying... ({})", e, cur_id);
1800+
1801+
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
1802+
},
1803+
}
1804+
}
1805+
});
1806+
}
1807+
1808+
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
1809+
async fn payment_benchmark() {
1810+
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
1811+
let chain_source = TestChainSource::Esplora(&electrsd);
1812+
let (node_a, node_b) = setup_two_nodes_with_store(
1813+
&chain_source,
1814+
false,
1815+
true,
1816+
false,
1817+
common::TestStoreType::Sqlite,
1818+
);
1819+
1820+
let address_a = node_a.onchain_payment().new_address().unwrap();
1821+
let premine_sat = 25_000_000;
1822+
premine_and_distribute_funds(
1823+
&bitcoind.client,
1824+
&electrsd.client,
1825+
vec![address_a],
1826+
Amount::from_sat(premine_sat),
1827+
);
1828+
node_a.sync_wallets().unwrap();
1829+
node_b.sync_wallets().unwrap();
1830+
open_channel(&node_a, &node_b, 16_000_000, true, &electrsd);
1831+
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6);
1832+
node_a.sync_wallets().unwrap();
1833+
node_b.sync_wallets().unwrap();
1834+
expect_channel_ready_event!(node_a, node_b.node_id());
1835+
expect_channel_ready_event!(node_b, node_a.node_id());
1836+
1837+
let start = Instant::now();
1838+
1839+
let node_a = Arc::new(node_a);
1840+
let node_b = Arc::new(node_b);
1841+
1842+
let total_payments = 1000;
1843+
let max_in_flight = 20;
1844+
1845+
let mut cur_id = 0u32;
1846+
let mut in_flight = 0;
1847+
let mut success_count = 0;
1848+
1849+
while success_count < total_payments {
1850+
// Spawn new payments if we aren't at max in-flight and haven't sent all payments yet.
1851+
let to_spawn =
1852+
std::cmp::min(max_in_flight - in_flight, total_payments - (in_flight + success_count));
1853+
1854+
println!(
1855+
"Spawning {} new payments ({} in flight, {} successes)",
1856+
to_spawn, in_flight, success_count
1857+
);
1858+
for _ in 0..to_spawn {
1859+
spawn_payment(node_a.clone(), node_b.clone(), cur_id);
1860+
cur_id += 1;
1861+
in_flight += 1;
1862+
}
1863+
1864+
match node_a.next_event_async().await {
1865+
Event::PaymentSuccessful { payment_id, .. } => {
1866+
if let Some(id) = payment_id {
1867+
success_count += 1;
1868+
in_flight -= 1;
1869+
println!("Payment {:?} completed", id);
1870+
} else {
1871+
println!("Payment completed (no payment_id)");
1872+
}
1873+
},
1874+
Event::PaymentFailed { payment_id, .. } => {
1875+
in_flight -= 1;
1876+
println!("Payment {:?} failed", payment_id);
1877+
},
1878+
ref e => {
1879+
println!("Received non-payment event: {:?}", e);
1880+
},
1881+
}
1882+
1883+
node_a.event_handled().unwrap();
1884+
}
1885+
1886+
let duration = start.elapsed();
1887+
println!("Time elapsed: {:?}", duration);
1888+
}
1889+
17641890
#[test]
17651891
fn spontaneous_send_with_custom_preimage() {
17661892
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();

0 commit comments

Comments
 (0)