diff --git a/benches/payments.rs b/benches/payments.rs index 52769d7949..ee886a4c62 100644 --- a/benches/payments.rs +++ b/benches/payments.rs @@ -35,14 +35,15 @@ fn spawn_payment(node_a: Arc, node_b: Arc, amount_msat: u64) { tokio::time::sleep(std::time::Duration::from_millis(100)).await; } - let payment_id = node_a.spontaneous_payment().send_with_preimage( + let spontaneous_payment = node_a.spontaneous_payment(); + let payment_id = spontaneous_payment.send_with_preimage( amount_msat, node_b.node_id(), preimage, None, ); - match payment_id { + match payment_id.await { Ok(payment_id) => { println!( "{}: Awaiting payment with id {}", @@ -93,7 +94,7 @@ async fn send_payments(node_a: Arc, node_b: Arc) -> std::time::Durat }, } - node_a.event_handled().unwrap(); + node_a.event_handled().await.unwrap(); } let duration = start.elapsed(); @@ -110,6 +111,7 @@ async fn send_payments(node_a: Arc, node_b: Arc) -> std::time::Durat PaymentPreimage(preimage_bytes), None, ) + .await .ok() .unwrap(); @@ -117,30 +119,28 @@ async fn send_payments(node_a: Arc, node_b: Arc) -> std::time::Durat } fn payment_benchmark(c: &mut Criterion) { - // Set up two nodes. Because this is slow, we reuse the same nodes for each sample. - let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); - let chain_source = random_chain_source(&bitcoind, &electrsd); - - let (node_a, node_b) = setup_two_nodes_with_store( - &chain_source, - false, - true, - false, - common::TestStoreType::Sqlite, - ); - let runtime = tokio::runtime::Builder::new_multi_thread().worker_threads(4).enable_all().build().unwrap(); - let node_a = Arc::new(node_a); - let node_b = Arc::new(node_b); + // Set up two nodes. Because this is slow, we reuse the same nodes for each sample. + let (setup_done, setup_result) = std::sync::mpsc::channel(); + runtime.spawn(async move { + let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); + let chain_source = random_chain_source(&bitcoind, &electrsd); + let (node_a, node_b) = setup_two_nodes_with_store( + &chain_source, + false, + true, + false, + common::TestStoreType::Sqlite, + ) + .await; + + let node_a = Arc::new(node_a); + let node_b = Arc::new(node_b); - // Fund the nodes and setup a channel between them. The criterion function cannot be async, so we need to execute - // the setup using a runtime. - let node_a_cloned = Arc::clone(&node_a); - let node_b_cloned = Arc::clone(&node_b); - runtime.block_on(async move { - let address_a = node_a_cloned.onchain_payment().new_address().unwrap(); + // Fund the nodes and setup a channel between them. + let address_a = node_a.onchain_payment().new_address().await.unwrap(); let premine_sat = 25_000_000; premine_and_distribute_funds( &bitcoind.client, @@ -149,23 +149,18 @@ fn payment_benchmark(c: &mut Criterion) { Amount::from_sat(premine_sat), ) .await; - node_a_cloned.sync_wallets().unwrap(); - node_b_cloned.sync_wallets().unwrap(); - open_channel_push_amt( - &node_a_cloned, - &node_b_cloned, - 16_000_000, - Some(1_000_000_000), - false, - &electrsd, - ) - .await; + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); + open_channel_push_amt(&node_a, &node_b, 16_000_000, Some(1_000_000_000), false, &electrsd) + .await; generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a_cloned.sync_wallets().unwrap(); - node_b_cloned.sync_wallets().unwrap(); - expect_channel_ready_event!(node_a_cloned, node_b_cloned.node_id()); - expect_channel_ready_event!(node_b_cloned, node_a_cloned.node_id()); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); + expect_channel_ready_event!(node_a, node_b.node_id()); + expect_channel_ready_event!(node_b, node_a.node_id()); + setup_done.send((node_a, node_b)).unwrap(); }); + let (node_a, node_b) = setup_result.recv().unwrap(); let mut group = c.benchmark_group("payments"); group.sample_size(10); diff --git a/bindings/ldk_node.udl b/bindings/ldk_node.udl index 7e9e61f5d5..af189c3ec7 100644 --- a/bindings/ldk_node.udl +++ b/bindings/ldk_node.udl @@ -61,34 +61,36 @@ interface Builder { [Throws=BuildError] void set_async_payments_role(AsyncPaymentsRole? role); void set_wallet_recovery_mode(); - [Throws=BuildError] + [Async, Throws=BuildError] Node build(NodeEntropy node_entropy); - [Throws=BuildError] + [Async, Throws=BuildError] Node build_with_postgres_store(NodeEntropy node_entropy, string connection_string, string? db_name, string? kv_table_name, string? certificate_pem); - [Throws=BuildError] + [Async, Throws=BuildError] Node build_with_fs_store(NodeEntropy node_entropy); - [Throws=BuildError] + [Async, Throws=BuildError] Node build_with_vss_store(NodeEntropy node_entropy, string vss_url, string store_id, record fixed_headers); - [Throws=BuildError] + [Async, Throws=BuildError] Node build_with_vss_store_and_lnurl_auth(NodeEntropy node_entropy, string vss_url, string store_id, string lnurl_auth_server_url, record fixed_headers); - [Throws=BuildError] + [Async, Throws=BuildError] Node build_with_vss_store_and_fixed_headers(NodeEntropy node_entropy, string vss_url, string store_id, record fixed_headers); - [Throws=BuildError] + [Async, Throws=BuildError] Node build_with_vss_store_and_header_provider(NodeEntropy node_entropy, string vss_url, string store_id, VssHeaderProvider header_provider); }; interface Node { - [Throws=NodeError] + [Async, Throws=NodeError] void start(); - [Throws=NodeError] + [Async, Throws=NodeError] void stop(); + [Async] NodeStatus status(); Config config(); Event? next_event(); + [Async] Event wait_next_event(); [Async] Event next_event_async(); - [Throws=NodeError] + [Async, Throws=NodeError] void event_handled(); PublicKey node_id(); sequence? listening_addresses(); @@ -100,49 +102,52 @@ interface Node { OnchainPayment onchain_payment(); UnifiedPayment unified_payment(); LSPS1Liquidity lsps1_liquidity(); - [Throws=NodeError] + [Async, Throws=NodeError] void lnurl_auth(string lnurl); - [Throws=NodeError] + [Async, Throws=NodeError] void connect(PublicKey node_id, SocketAddress address, boolean persist); - [Throws=NodeError] + [Async, Throws=NodeError] void disconnect(PublicKey node_id); - [Throws=NodeError] + [Async, Throws=NodeError] UserChannelId open_channel(PublicKey node_id, SocketAddress address, u64 channel_amount_sats, u64? push_to_counterparty_msat, ChannelConfig? channel_config); - [Throws=NodeError] + [Async, Throws=NodeError] UserChannelId open_announced_channel(PublicKey node_id, SocketAddress address, u64 channel_amount_sats, u64? push_to_counterparty_msat, ChannelConfig? channel_config); - [Throws=NodeError] + [Async, Throws=NodeError] UserChannelId open_channel_with_all(PublicKey node_id, SocketAddress address, u64? push_to_counterparty_msat, ChannelConfig? channel_config); - [Throws=NodeError] + [Async, Throws=NodeError] UserChannelId open_announced_channel_with_all(PublicKey node_id, SocketAddress address, u64? push_to_counterparty_msat, ChannelConfig? channel_config); - [Throws=NodeError] + [Async, Throws=NodeError] UserChannelId open_0reserve_channel(PublicKey node_id, SocketAddress address, u64 channel_amount_sats, u64? push_to_counterparty_msat, ChannelConfig? channel_config); - [Throws=NodeError] + [Async, Throws=NodeError] UserChannelId open_0reserve_channel_with_all(PublicKey node_id, SocketAddress address, u64? push_to_counterparty_msat, ChannelConfig? channel_config); - [Throws=NodeError] + [Async, Throws=NodeError] void splice_in([ByRef]UserChannelId user_channel_id, PublicKey counterparty_node_id, u64 splice_amount_sats); - [Throws=NodeError] + [Async, Throws=NodeError] void splice_in_with_all([ByRef]UserChannelId user_channel_id, PublicKey counterparty_node_id); [Throws=NodeError] void splice_out([ByRef]UserChannelId user_channel_id, PublicKey counterparty_node_id, [ByRef]Address address, u64 splice_amount_sats); - [Throws=NodeError] + [Async, Throws=NodeError] void close_channel([ByRef]UserChannelId user_channel_id, PublicKey counterparty_node_id); - [Throws=NodeError] + [Async, Throws=NodeError] void force_close_channel([ByRef]UserChannelId user_channel_id, PublicKey counterparty_node_id, string? reason); [Throws=NodeError] void update_channel_config([ByRef]UserChannelId user_channel_id, PublicKey counterparty_node_id, ChannelConfig channel_config); - [Throws=NodeError] + [Async, Throws=NodeError] void sync_wallets(); + [Async] PaymentDetails? payment([ByRef]PaymentId payment_id); - [Throws=NodeError] + [Async, Throws=NodeError] void remove_payment([ByRef]PaymentId payment_id); BalanceDetails list_balances(); + [Async] sequence list_payments(); + [Async] sequence list_peers(); sequence list_channels(); NetworkGraph network_graph(); string sign_message([ByRef]sequence msg); boolean verify_signature([ByRef]sequence msg, [ByRef]string sig, [ByRef]PublicKey pkey); - [Throws=NodeError] + [Async, Throws=NodeError] bytes export_pathfinding_scores(); }; diff --git a/src/builder.rs b/src/builder.rs index c88c867cc1..c190fcb108 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -43,6 +43,7 @@ use lightning::util::persist::{ use lightning::util::ser::ReadableArgs; use lightning::util::sweep::OutputSweeper; use lightning_dns_resolver::OMDomainResolver; +use tokio::sync::RwLock as AsyncRwLock; use vss_client::headers::VssHeaderProvider; use crate::chain::ChainSource; @@ -85,7 +86,7 @@ use crate::types::{ }; use crate::wallet::persist::KVStoreWalletPersister; use crate::wallet::Wallet; -use crate::{Node, NodeMetrics, PersistedNodeMetrics}; +use crate::{Node, NodeMetrics}; const LSPS_HARDENED_CHILD_INDEX: u32 = 577; const PERSISTER_MAX_PENDING_UPDATES: u64 = 100; @@ -281,7 +282,7 @@ impl std::error::Error for BuildError {} /// [`set_custom_logger`]: Self::set_custom_logger /// [`log`]: https://crates.io/crates/log /// [Logging]: #logging -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct NodeBuilder { config: Config, chain_data_source_config: Option, @@ -627,7 +628,7 @@ impl NodeBuilder { /// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options /// previously configured. - pub fn build(&self, node_entropy: NodeEntropy) -> Result { + pub async fn build(&self, node_entropy: NodeEntropy) -> Result { let logger = setup_logger(&self.log_writer_config, &self.config)?; let storage_dir_path = self.config.storage_dir_path.clone(); fs::create_dir_all(storage_dir_path.clone()) @@ -641,7 +642,7 @@ impl NodeBuilder { log_error!(logger, "Failed to setup Sqlite store: {}", e); BuildError::KVStoreSetupFailed })?; - self.build_with_store_and_logger(node_entropy, kv_store, logger) + self.build_with_store_and_logger(node_entropy, kv_store, logger).await } /// Builds a [`Node`] instance with a [PostgreSQL] backend and according to the options @@ -667,25 +668,25 @@ impl NodeBuilder { /// /// [PostgreSQL]: https://www.postgresql.org #[cfg(feature = "postgres")] - pub fn build_with_postgres_store( + pub async fn build_with_postgres_store( &self, node_entropy: NodeEntropy, connection_string: String, db_name: Option, kv_table_name: Option, certificate_pem: Option, ) -> Result { let logger = setup_logger(&self.log_writer_config, &self.config)?; let runtime = self.setup_runtime(&logger)?; - let kv_store = runtime - .block_on(io::postgres_store::PostgresStore::new_with_logger( - connection_string, - db_name, - kv_table_name, - certificate_pem, - Some(Arc::clone(&logger)), - )) - .map_err(|e| { - log_error!(logger, "Failed to set up Postgres store: {e}"); - BuildError::KVStoreSetupFailed - })?; - self.build_with_store_runtime_and_logger(node_entropy, kv_store, runtime, logger) + let kv_store = io::postgres_store::PostgresStore::new_with_logger( + connection_string, + db_name, + kv_table_name, + certificate_pem, + Some(Arc::clone(&logger)), + ) + .await + .map_err(|e| { + log_error!(logger, "Failed to set up Postgres store: {e}"); + BuildError::KVStoreSetupFailed + })?; + self.build_with_store_runtime_and_logger(node_entropy, kv_store, runtime, logger).await } /// Builds a [`Node`] instance with a [`FilesystemStoreV2`] backend and according to the options @@ -695,14 +696,14 @@ impl NodeBuilder { /// automatically migrated to the v2 format. /// /// [`FilesystemStoreV2`]: lightning_persister::fs_store::v2::FilesystemStoreV2 - pub fn build_with_fs_store(&self, node_entropy: NodeEntropy) -> Result { + pub async fn build_with_fs_store(&self, node_entropy: NodeEntropy) -> Result { let logger = setup_logger(&self.log_writer_config, &self.config)?; let runtime = self.setup_runtime(&logger)?; let mut storage_dir_path: PathBuf = self.config.storage_dir_path.clone().into(); storage_dir_path.push("fs_store"); - let kv_store = runtime.block_on(open_or_migrate_fs_store(storage_dir_path))?; - self.build_with_store_runtime_and_logger(node_entropy, kv_store, runtime, logger) + let kv_store = open_or_migrate_fs_store(storage_dir_path).await?; + self.build_with_store_runtime_and_logger(node_entropy, kv_store, runtime, logger).await } /// Builds a [`Node`] instance with a [VSS] backend and according to the options @@ -722,18 +723,23 @@ impl NodeBuilder { /// unrecoverable, i.e., if they remain unresolved after internal retries are exhausted. /// /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md - pub fn build_with_vss_store( + pub async fn build_with_vss_store( &self, node_entropy: NodeEntropy, vss_url: String, store_id: String, fixed_headers: HashMap, ) -> Result { let logger = setup_logger(&self.log_writer_config, &self.config)?; + let runtime = self.setup_runtime(&logger)?; let builder = VssStoreBuilder::new(node_entropy, vss_url, store_id, self.config.network); let vss_store = builder.build_with_sigs_auth(fixed_headers).map_err(|e| { log_error!(logger, "Failed to setup VSS store: {}", e); BuildError::KVStoreSetupFailed })?; + vss_store.setup_schema_version().await.map_err(|e| { + log_error!(logger, "Failed to setup VSS store: {}", e); + BuildError::KVStoreSetupFailed + })?; - self.build_with_store_and_logger(node_entropy, vss_store, logger) + self.build_with_store_runtime_and_logger(node_entropy, vss_store, runtime, logger).await } /// Builds a [`Node`] instance with a [VSS] backend and according to the options @@ -758,19 +764,24 @@ impl NodeBuilder { /// /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md /// [LNURL-auth]: https://github.com/lnurl/luds/blob/luds/04.md - pub fn build_with_vss_store_and_lnurl_auth( + pub async fn build_with_vss_store_and_lnurl_auth( &self, node_entropy: NodeEntropy, vss_url: String, store_id: String, lnurl_auth_server_url: String, fixed_headers: HashMap, ) -> Result { let logger = setup_logger(&self.log_writer_config, &self.config)?; + let runtime = self.setup_runtime(&logger)?; let builder = VssStoreBuilder::new(node_entropy, vss_url, store_id, self.config.network); let vss_store = builder.build_with_lnurl(lnurl_auth_server_url, fixed_headers).map_err(|e| { log_error!(logger, "Failed to setup VSS store: {}", e); BuildError::KVStoreSetupFailed })?; + vss_store.setup_schema_version().await.map_err(|e| { + log_error!(logger, "Failed to setup VSS store: {}", e); + BuildError::KVStoreSetupFailed + })?; - self.build_with_store_and_logger(node_entropy, vss_store, logger) + self.build_with_store_runtime_and_logger(node_entropy, vss_store, runtime, logger).await } /// Builds a [`Node`] instance with a [VSS] backend and according to the options @@ -786,18 +797,23 @@ impl NodeBuilder { /// /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md /// [`FixedHeaders`]: vss_client::headers::FixedHeaders - pub fn build_with_vss_store_and_fixed_headers( + pub async fn build_with_vss_store_and_fixed_headers( &self, node_entropy: NodeEntropy, vss_url: String, store_id: String, fixed_headers: HashMap, ) -> Result { let logger = setup_logger(&self.log_writer_config, &self.config)?; + let runtime = self.setup_runtime(&logger)?; let builder = VssStoreBuilder::new(node_entropy, vss_url, store_id, self.config.network); let vss_store = builder.build_with_fixed_headers(fixed_headers).map_err(|e| { log_error!(logger, "Failed to setup VSS store: {}", e); BuildError::KVStoreSetupFailed })?; + vss_store.setup_schema_version().await.map_err(|e| { + log_error!(logger, "Failed to setup VSS store: {}", e); + BuildError::KVStoreSetupFailed + })?; - self.build_with_store_and_logger(node_entropy, vss_store, logger) + self.build_with_store_runtime_and_logger(node_entropy, vss_store, runtime, logger).await } /// Builds a [`Node`] instance with a [VSS] backend and according to the options @@ -811,27 +827,32 @@ impl NodeBuilder { /// unrecoverable, i.e., if they remain unresolved after internal retries are exhausted. /// /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md - pub fn build_with_vss_store_and_header_provider( + pub async fn build_with_vss_store_and_header_provider( &self, node_entropy: NodeEntropy, vss_url: String, store_id: String, header_provider: Arc, ) -> Result { let logger = setup_logger(&self.log_writer_config, &self.config)?; + let runtime = self.setup_runtime(&logger)?; let builder = VssStoreBuilder::new(node_entropy, vss_url, store_id, self.config.network); let vss_store = builder.build_with_header_provider(header_provider).map_err(|e| { log_error!(logger, "Failed to setup VSS store: {}", e); BuildError::KVStoreSetupFailed })?; + vss_store.setup_schema_version().await.map_err(|e| { + log_error!(logger, "Failed to setup VSS store: {}", e); + BuildError::KVStoreSetupFailed + })?; - self.build_with_store_and_logger(node_entropy, vss_store, logger) + self.build_with_store_runtime_and_logger(node_entropy, vss_store, runtime, logger).await } /// Builds a [`Node`] instance according to the options previously configured. - pub fn build_with_store( + pub async fn build_with_store( &self, node_entropy: NodeEntropy, kv_store: S, ) -> Result { let logger = setup_logger(&self.log_writer_config, &self.config)?; - self.build_with_store_and_logger(node_entropy, kv_store, logger) + self.build_with_store_and_logger(node_entropy, kv_store, logger).await } fn setup_runtime(&self, logger: &Arc) -> Result, BuildError> { @@ -845,14 +866,14 @@ impl NodeBuilder { } } - fn build_with_store_and_logger( + async fn build_with_store_and_logger( &self, node_entropy: NodeEntropy, kv_store: S, logger: Arc, ) -> Result { let runtime = self.setup_runtime(&logger)?; - self.build_with_store_runtime_and_logger(node_entropy, kv_store, runtime, logger) + self.build_with_store_runtime_and_logger(node_entropy, kv_store, runtime, logger).await } - fn build_with_store_runtime_and_logger( + async fn build_with_store_runtime_and_logger( &self, node_entropy: NodeEntropy, kv_store: S, runtime: Arc, logger: Arc, ) -> Result { let seed_bytes = node_entropy.to_seed_bytes(); @@ -871,6 +892,7 @@ impl NodeBuilder { logger, Arc::new(DynStoreWrapper(kv_store)), ) + .await } } @@ -1166,8 +1188,9 @@ impl ArcedNodeBuilder { /// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options /// previously configured. - pub fn build(&self, node_entropy: Arc) -> Result, BuildError> { - self.inner.read().expect("lock").build(*node_entropy).map(Arc::new) + pub async fn build(&self, node_entropy: Arc) -> Result, BuildError> { + let builder = self.inner.read().expect("lock").clone(); + builder.build(*node_entropy).await.map(Arc::new) } /// Builds a [`Node`] instance with a [PostgreSQL] backend and according to the options @@ -1193,13 +1216,12 @@ impl ArcedNodeBuilder { /// /// [PostgreSQL]: https://www.postgresql.org #[cfg(feature = "postgres")] - pub fn build_with_postgres_store( + pub async fn build_with_postgres_store( &self, node_entropy: Arc, connection_string: String, db_name: Option, kv_table_name: Option, certificate_pem: Option, ) -> Result, BuildError> { - self.inner - .read() - .unwrap() + let builder = self.inner.read().expect("lock").clone(); + builder .build_with_postgres_store( *node_entropy, connection_string, @@ -1207,6 +1229,7 @@ impl ArcedNodeBuilder { kv_table_name, certificate_pem, ) + .await .map(Arc::new) } @@ -1215,7 +1238,7 @@ impl ArcedNodeBuilder { /// /// This requires the `postgres` crate feature. #[cfg(not(feature = "postgres"))] - pub fn build_with_postgres_store( + pub async fn build_with_postgres_store( &self, _node_entropy: Arc, _connection_string: String, _db_name: Option, _kv_table_name: Option, _certificate_pem: Option, ) -> Result, BuildError> { @@ -1224,10 +1247,11 @@ impl ArcedNodeBuilder { /// Builds a [`Node`] instance with a [`FilesystemStoreV2`] backend and according to the options /// previously configured. - pub fn build_with_fs_store( + pub async fn build_with_fs_store( &self, node_entropy: Arc, ) -> Result, BuildError> { - self.inner.read().expect("lock").build_with_fs_store(*node_entropy).map(Arc::new) + let builder = self.inner.read().expect("lock").clone(); + builder.build_with_fs_store(*node_entropy).await.map(Arc::new) } /// Builds a [`Node`] instance with a [VSS] backend and according to the options @@ -1247,14 +1271,14 @@ impl ArcedNodeBuilder { /// unrecoverable, i.e., if they remain unresolved after internal retries are exhausted. /// /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md - pub fn build_with_vss_store( + pub async fn build_with_vss_store( &self, node_entropy: Arc, vss_url: String, store_id: String, fixed_headers: HashMap, ) -> Result, BuildError> { - self.inner - .read() - .expect("lock") + let builder = self.inner.read().expect("lock").clone(); + builder .build_with_vss_store(*node_entropy, vss_url, store_id, fixed_headers) + .await .map(Arc::new) } @@ -1280,13 +1304,12 @@ impl ArcedNodeBuilder { /// /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md /// [LNURL-auth]: https://github.com/lnurl/luds/blob/luds/04.md - pub fn build_with_vss_store_and_lnurl_auth( + pub async fn build_with_vss_store_and_lnurl_auth( &self, node_entropy: Arc, vss_url: String, store_id: String, lnurl_auth_server_url: String, fixed_headers: HashMap, ) -> Result, BuildError> { - self.inner - .read() - .expect("lock") + let builder = self.inner.read().expect("lock").clone(); + builder .build_with_vss_store_and_lnurl_auth( *node_entropy, vss_url, @@ -1294,6 +1317,7 @@ impl ArcedNodeBuilder { lnurl_auth_server_url, fixed_headers, ) + .await .map(Arc::new) } @@ -1309,14 +1333,14 @@ impl ArcedNodeBuilder { /// unrecoverable, i.e., if they remain unresolved after internal retries are exhausted. /// /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md - pub fn build_with_vss_store_and_fixed_headers( + pub async fn build_with_vss_store_and_fixed_headers( &self, node_entropy: Arc, vss_url: String, store_id: String, fixed_headers: HashMap, ) -> Result, BuildError> { - self.inner - .read() - .expect("lock") + let builder = self.inner.read().expect("lock").clone(); + builder .build_with_vss_store_and_fixed_headers(*node_entropy, vss_url, store_id, fixed_headers) + .await .map(Arc::new) } @@ -1331,30 +1355,31 @@ impl ArcedNodeBuilder { /// unrecoverable, i.e., if they remain unresolved after internal retries are exhausted. /// /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md - pub fn build_with_vss_store_and_header_provider( + pub async fn build_with_vss_store_and_header_provider( &self, node_entropy: Arc, vss_url: String, store_id: String, header_provider: Arc, ) -> Result, BuildError> { let adapter = Arc::new(crate::ffi::VssHeaderProviderAdapter::new(header_provider)); - self.inner - .read() - .expect("lock") + let builder = self.inner.read().expect("lock").clone(); + builder .build_with_vss_store_and_header_provider(*node_entropy, vss_url, store_id, adapter) + .await .map(Arc::new) } /// Builds a [`Node`] instance according to the options previously configured. // Note that the generics here don't actually work for Uniffi, but we don't currently expose // this so its not needed. - pub fn build_with_store( + pub async fn build_with_store( &self, node_entropy: Arc, kv_store: S, ) -> Result, BuildError> { - self.inner.read().expect("lock").build_with_store(*node_entropy, kv_store).map(Arc::new) + let builder = self.inner.read().expect("lock").clone(); + builder.build_with_store(*node_entropy, kv_store).await.map(Arc::new) } } /// Builds a [`Node`] instance according to the options previously configured. -fn build_with_store_internal( +async fn build_with_store_internal( config: Arc, chain_data_source_config: Option<&ChainDataSourceConfig>, gossip_source_config: Option<&GossipSourceConfig>, liquidity_source_config: Option<&LiquiditySourceConfig>, @@ -1395,31 +1420,28 @@ fn build_with_store_internal( let kv_store_ref = Arc::clone(&kv_store); let logger_ref = Arc::clone(&logger); - let (payment_store_res, node_metris_res, pending_payment_store_res) = - runtime.block_on(async move { - tokio::join!( - read_all_objects( - &*kv_store_ref, - PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE, - PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE, - Arc::clone(&logger_ref), - ), - read_node_metrics(&*kv_store_ref, Arc::clone(&logger_ref)), - read_all_objects( - &*kv_store_ref, - PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE, - PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE, - Arc::clone(&logger_ref), - ) - ) - }); + let (payment_store_res, node_metris_res, pending_payment_store_res) = tokio::join!( + read_all_objects( + &*kv_store_ref, + PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE, + PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE, + Arc::clone(&logger_ref), + ), + read_node_metrics(&*kv_store_ref, Arc::clone(&logger_ref)), + read_all_objects( + &*kv_store_ref, + PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE, + PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE, + Arc::clone(&logger_ref), + ) + ); // Initialize the status fields. let node_metrics = match node_metris_res { - Ok(metrics) => Arc::new(PersistedNodeMetrics::new(metrics)), + Ok(metrics) => Arc::new(AsyncRwLock::new(metrics)), Err(e) => { if e.kind() == std::io::ErrorKind::NotFound { - Arc::new(PersistedNodeMetrics::new(NodeMetrics::default())) + Arc::new(AsyncRwLock::new(NodeMetrics::default())) } else { log_error!(logger, "Failed to read node metrics from store: {}", e); return Err(BuildError::ReadFailed); @@ -1477,7 +1499,7 @@ fn build_with_store_internal( rpc_password, rest_client_config, }) => match rest_client_config { - Some(rest_client_config) => runtime.block_on(async { + Some(rest_client_config) => { ChainSource::new_bitcoind_rest( rpc_host.clone(), *rpc_port, @@ -1492,8 +1514,8 @@ fn build_with_store_internal( Arc::clone(&node_metrics), ) .await - }), - None => runtime.block_on(async { + }, + None => { ChainSource::new_bitcoind_rpc( rpc_host.clone(), *rpc_port, @@ -1507,7 +1529,7 @@ fn build_with_store_internal( Arc::clone(&node_metrics), ) .await - }), + }, }, None => { @@ -1540,16 +1562,13 @@ fn build_with_store_internal( let change_descriptor = Bip84(xprv, KeychainKind::Internal); let mut wallet_persister = KVStoreWalletPersister::new(Arc::clone(&kv_store), Arc::clone(&logger)); - let wallet_opt = runtime - .block_on(async { - BdkWallet::load() - .descriptor(KeychainKind::External, Some(descriptor.clone())) - .descriptor(KeychainKind::Internal, Some(change_descriptor.clone())) - .extract_keys() - .check_network(config.network) - .load_wallet_async(&mut wallet_persister) - .await - }) + let wallet_opt = BdkWallet::load() + .descriptor(KeychainKind::External, Some(descriptor.clone())) + .descriptor(KeychainKind::Internal, Some(change_descriptor.clone())) + .extract_keys() + .check_network(config.network) + .load_wallet_async(&mut wallet_persister) + .await .map_err(|e| match e { bdk_wallet::LoadWithPersistError::InvalidChangeSet( bdk_wallet::LoadError::Mismatch(bdk_wallet::LoadMismatch::Network { @@ -1573,13 +1592,10 @@ fn build_with_store_internal( let bdk_wallet = match wallet_opt { Some(wallet) => wallet, None => { - let mut wallet = runtime - .block_on(async { - BdkWallet::create(descriptor, change_descriptor) - .network(config.network) - .create_wallet_async(&mut wallet_persister) - .await - }) + let mut wallet = BdkWallet::create(descriptor, change_descriptor) + .network(config.network) + .create_wallet_async(&mut wallet_persister) + .await .map_err(|e| { log_error!(logger, "Failed to set up wallet: {}", e); BuildError::WalletSetupFailed @@ -1664,12 +1680,10 @@ fn build_with_store_internal( // Read ChannelMonitors and the NetworkGraph let kv_store_ref = Arc::clone(&kv_store); let logger_ref = Arc::clone(&logger); - let (monitor_read_res, network_graph_res) = runtime.block_on(async { - tokio::join!( - monitor_reader.read_all_channel_monitors_with_updates_parallel(), - read_network_graph(&*kv_store_ref, logger_ref), - ) - }); + let (monitor_read_res, network_graph_res) = tokio::join!( + monitor_reader.read_all_channel_monitors_with_updates_parallel(), + read_network_graph(&*kv_store_ref, logger_ref), + ); // Read ChannelMonitor state from store let channel_monitors = match monitor_read_res { @@ -1732,21 +1746,19 @@ fn build_with_store_internal( sweeper_bytes_res, event_queue_res, peer_info_res, - ) = runtime.block_on(async move { - tokio::join!( - read_scorer(&*kv_store_ref, network_graph_ref, Arc::clone(&logger_ref)), - read_external_pathfinding_scores_from_cache(&*kv_store_ref, Arc::clone(&logger_ref)), - KVStore::read( - &*kv_store_ref, - CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE, - CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE, - CHANNEL_MANAGER_PERSISTENCE_KEY, - ), - output_sweeper_future, - read_event_queue(Arc::clone(&kv_store_ref), Arc::clone(&logger_ref)), - read_peer_info(Arc::clone(&kv_store_ref), Arc::clone(&logger_ref)), - ) - }); + ) = tokio::join!( + read_scorer(&*kv_store_ref, network_graph_ref, Arc::clone(&logger_ref)), + read_external_pathfinding_scores_from_cache(&*kv_store_ref, Arc::clone(&logger_ref)), + KVStore::read( + &*kv_store_ref, + CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE, + CHANNEL_MANAGER_PERSISTENCE_KEY, + ), + output_sweeper_future, + read_event_queue(Arc::clone(&kv_store_ref), Arc::clone(&logger_ref)), + read_peer_info(Arc::clone(&kv_store_ref), Arc::clone(&logger_ref)), + ); let local_scorer = match scorer_res { Ok(scorer) => scorer, @@ -2016,8 +2028,7 @@ fn build_with_store_internal( liquidity_source_builder.lsps2_service(promise_secret, config.clone()) }); - let liquidity_source = runtime - .block_on(async move { liquidity_source_builder.build().await.map(Arc::new) })?; + let liquidity_source = liquidity_source_builder.build().await.map(Arc::new)?; let custom_message_handler = Arc::new(NodeCustomMessageHandler::new_liquidity(Arc::clone(&liquidity_source))); (Some(liquidity_source), custom_message_handler) diff --git a/src/chain/bitcoind.rs b/src/chain/bitcoind.rs index 6bfa8ffd27..a044cecf05 100644 --- a/src/chain/bitcoind.rs +++ b/src/chain/bitcoind.rs @@ -29,6 +29,7 @@ use lightning_block_sync::{ SpvClient, }; use serde::Serialize; +use tokio::sync::RwLock as AsyncRwLock; use super::WalletSyncStatus; use crate::config::{ @@ -42,7 +43,7 @@ use crate::fee_estimator::{ use crate::io::utils::update_and_persist_node_metrics; use crate::logger::{log_bytes, log_debug, log_error, log_info, log_trace, LdkLogger, Logger}; use crate::types::{ChainMonitor, ChannelManager, DynStore, Sweeper, Wallet}; -use crate::{Error, PersistedNodeMetrics}; +use crate::{Error, NodeMetrics}; const CHAIN_POLLING_INTERVAL_SECS: u64 = 2; const CHAIN_POLLING_TIMEOUT_SECS: u64 = 10; @@ -55,14 +56,14 @@ pub(super) struct BitcoindChainSource { kv_store: Arc, config: Arc, logger: Arc, - node_metrics: Arc, + node_metrics: Arc>, } impl BitcoindChainSource { pub(crate) fn new_rpc( rpc_host: String, rpc_port: u16, rpc_user: String, rpc_password: String, fee_estimator: Arc, kv_store: Arc, config: Arc, - logger: Arc, node_metrics: Arc, + logger: Arc, node_metrics: Arc>, ) -> Self { let api_client = Arc::new(BitcoindClient::new_rpc( rpc_host.clone(), @@ -89,7 +90,7 @@ impl BitcoindChainSource { rpc_host: String, rpc_port: u16, rpc_user: String, rpc_password: String, fee_estimator: Arc, kv_store: Arc, config: Arc, rest_client_config: BitcoindRestClientConfig, logger: Arc, - node_metrics: Arc, + node_metrics: Arc>, ) -> Self { let api_client = Arc::new(BitcoindClient::new_rest( rest_client_config.rest_host, @@ -435,11 +436,12 @@ impl BitcoindChainSource { evicted_txids.len(), elapsed_ms, ); - onchain_wallet.apply_mempool_txs(unconfirmed_txs, evicted_txids).unwrap_or_else( - |e| { + onchain_wallet + .apply_mempool_txs(unconfirmed_txs, evicted_txids) + .await + .unwrap_or_else(|e| { log_error!(self.logger, "Failed to apply mempool transactions: {:?}", e); - }, - ); + }); }, Err(e) => { log_error!(self.logger, "Failed to poll for mempool transactions: {:?}", e); diff --git a/src/chain/electrum.rs b/src/chain/electrum.rs index ad0ef1b7ba..01c0780eca 100644 --- a/src/chain/electrum.rs +++ b/src/chain/electrum.rs @@ -22,6 +22,7 @@ use electrum_client::{ use lightning::chain::{Confirm, Filter, WatchedOutput}; use lightning::util::ser::Writeable; use lightning_transaction_sync::ElectrumSyncClient; +use tokio::sync::RwLock as AsyncRwLock; use super::WalletSyncStatus; use crate::config::{Config, ElectrumSyncConfig, BDK_CLIENT_STOP_GAP}; @@ -34,7 +35,7 @@ use crate::io::utils::update_and_persist_node_metrics; use crate::logger::{log_bytes, log_debug, log_error, log_trace, LdkLogger, Logger}; use crate::runtime::Runtime; use crate::types::{ChainMonitor, ChannelManager, DynStore, Sweeper, Wallet}; -use crate::PersistedNodeMetrics; +use crate::NodeMetrics; const BDK_ELECTRUM_CLIENT_BATCH_SIZE: usize = 5; const ELECTRUM_CLIENT_NUM_RETRIES: u8 = 3; @@ -49,14 +50,14 @@ pub(super) struct ElectrumChainSource { kv_store: Arc, config: Arc, logger: Arc, - node_metrics: Arc, + node_metrics: Arc>, } impl ElectrumChainSource { pub(super) fn new( server_url: String, sync_config: ElectrumSyncConfig, fee_estimator: Arc, kv_store: Arc, config: Arc, - logger: Arc, node_metrics: Arc, + logger: Arc, node_metrics: Arc>, ) -> Self { let electrum_runtime_status = RwLock::new(ElectrumRuntimeStatus::new()); let onchain_wallet_sync_status = Mutex::new(WalletSyncStatus::Completed); @@ -127,7 +128,7 @@ impl ElectrumChainSource { // If this is our first sync, do a full scan with the configured gap limit. // Otherwise just do an incremental sync. let incremental_sync = - self.node_metrics.read().expect("lock").latest_onchain_wallet_sync_timestamp.is_some(); + self.node_metrics.read().await.latest_onchain_wallet_sync_timestamp.is_some(); let cached_txs = onchain_wallet.get_cached_txs(); @@ -168,7 +169,7 @@ impl ElectrumChainSource { update_res: Result, now: Instant, ) -> Result<(), Error> { match update_res { - Ok(update) => match onchain_wallet.apply_update(update) { + Ok(update) => match onchain_wallet.apply_update(update).await { Ok(()) => { log_debug!( self.logger, diff --git a/src/chain/esplora.rs b/src/chain/esplora.rs index eb23a395d3..8f99d2b58e 100644 --- a/src/chain/esplora.rs +++ b/src/chain/esplora.rs @@ -15,6 +15,7 @@ use esplora_client::AsyncClient as EsploraAsyncClient; use lightning::chain::{Confirm, Filter, WatchedOutput}; use lightning::util::ser::Writeable; use lightning_transaction_sync::EsploraSyncClient; +use tokio::sync::RwLock as AsyncRwLock; use super::WalletSyncStatus; use crate::config::{Config, EsploraSyncConfig, BDK_CLIENT_CONCURRENCY, BDK_CLIENT_STOP_GAP}; @@ -25,7 +26,7 @@ use crate::fee_estimator::{ use crate::io::utils::update_and_persist_node_metrics; use crate::logger::{log_bytes, log_debug, log_error, log_trace, LdkLogger, Logger}; use crate::types::{ChainMonitor, ChannelManager, DynStore, Sweeper, Wallet}; -use crate::{Error, PersistedNodeMetrics}; +use crate::{Error, NodeMetrics}; pub(super) struct EsploraChainSource { pub(super) sync_config: EsploraSyncConfig, @@ -37,14 +38,14 @@ pub(super) struct EsploraChainSource { kv_store: Arc, config: Arc, logger: Arc, - node_metrics: Arc, + node_metrics: Arc>, } impl EsploraChainSource { pub(crate) fn new( server_url: String, headers: HashMap, sync_config: EsploraSyncConfig, fee_estimator: Arc, kv_store: Arc, config: Arc, - logger: Arc, node_metrics: Arc, + logger: Arc, node_metrics: Arc>, ) -> Result { let mut client_builder = esplora_client::Builder::new(&server_url); client_builder = @@ -103,14 +104,14 @@ impl EsploraChainSource { // If this is our first sync, do a full scan with the configured gap limit. // Otherwise just do an incremental sync. let incremental_sync = - self.node_metrics.read().expect("lock").latest_onchain_wallet_sync_timestamp.is_some(); + self.node_metrics.read().await.latest_onchain_wallet_sync_timestamp.is_some(); macro_rules! get_and_apply_wallet_update { ($sync_future: expr) => {{ let now = Instant::now(); match $sync_future.await { Ok(res) => match res { - Ok(update) => match onchain_wallet.apply_update(update) { + Ok(update) => match onchain_wallet.apply_update(update).await { Ok(()) => { log_debug!( self.logger, diff --git a/src/chain/mod.rs b/src/chain/mod.rs index 92c4bdb641..b152abd9e8 100644 --- a/src/chain/mod.rs +++ b/src/chain/mod.rs @@ -15,6 +15,7 @@ use std::time::Duration; use bitcoin::{Script, Txid}; use lightning::chain::{BlockLocator, Filter}; +use tokio::sync::RwLock as AsyncRwLock; use crate::chain::bitcoind::{BitcoindChainSource, UtxoSourceClient}; use crate::chain::electrum::ElectrumChainSource; @@ -27,7 +28,7 @@ use crate::fee_estimator::OnchainFeeEstimator; use crate::logger::{log_debug, log_info, log_trace, LdkLogger, Logger}; use crate::runtime::Runtime; use crate::types::{Broadcaster, ChainMonitor, ChannelManager, DynStore, Sweeper, Wallet}; -use crate::{Error, PersistedNodeMetrics}; +use crate::{Error, NodeMetrics}; pub(crate) enum WalletSyncStatus { Completed, @@ -100,7 +101,7 @@ impl ChainSource { server_url: String, headers: HashMap, sync_config: EsploraSyncConfig, fee_estimator: Arc, tx_broadcaster: Arc, kv_store: Arc, config: Arc, logger: Arc, - node_metrics: Arc, + node_metrics: Arc>, ) -> Result<(Self, Option), ()> { let esplora_chain_source = EsploraChainSource::new( server_url, @@ -121,7 +122,7 @@ impl ChainSource { server_url: String, sync_config: ElectrumSyncConfig, fee_estimator: Arc, tx_broadcaster: Arc, kv_store: Arc, config: Arc, logger: Arc, - node_metrics: Arc, + node_metrics: Arc>, ) -> (Self, Option) { let electrum_chain_source = ElectrumChainSource::new( server_url, @@ -141,7 +142,7 @@ impl ChainSource { rpc_host: String, rpc_port: u16, rpc_user: String, rpc_password: String, fee_estimator: Arc, tx_broadcaster: Arc, kv_store: Arc, config: Arc, logger: Arc, - node_metrics: Arc, + node_metrics: Arc>, ) -> (Self, Option) { let bitcoind_chain_source = BitcoindChainSource::new_rpc( rpc_host, @@ -164,7 +165,7 @@ impl ChainSource { rpc_host: String, rpc_port: u16, rpc_user: String, rpc_password: String, fee_estimator: Arc, tx_broadcaster: Arc, kv_store: Arc, config: Arc, rest_client_config: BitcoindRestClientConfig, - logger: Arc, node_metrics: Arc, + logger: Arc, node_metrics: Arc>, ) -> (Self, Option) { let bitcoind_chain_source = BitcoindChainSource::new_rest( rpc_host, diff --git a/src/data_store.rs b/src/data_store.rs index 70abfcc3fd..f7248118c1 100644 --- a/src/data_store.rs +++ b/src/data_store.rs @@ -7,10 +7,11 @@ use std::collections::{hash_map, HashMap}; use std::ops::Deref; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use lightning::util::persist::KVStore; use lightning::util::ser::{Readable, Writeable}; +use tokio::sync::RwLock; use crate::logger::{log_error, LdkLogger}; use crate::types::DynStore; @@ -44,8 +45,7 @@ pub(crate) struct DataStore where L::Target: LdkLogger, { - objects: Mutex>, - mutation_lock: tokio::sync::Mutex<()>, + objects: RwLock>, primary_namespace: String, secondary_namespace: String, kv_store: Arc, @@ -61,55 +61,42 @@ where kv_store: Arc, logger: L, ) -> Self { let objects = - Mutex::new(HashMap::from_iter(objects.into_iter().map(|obj| (obj.id(), obj)))); - Self { - objects, - mutation_lock: tokio::sync::Mutex::new(()), - primary_namespace, - secondary_namespace, - kv_store, - logger, - } + RwLock::new(HashMap::from_iter(objects.into_iter().map(|obj| (obj.id(), obj)))); + Self { objects, primary_namespace, secondary_namespace, kv_store, logger } } pub(crate) async fn insert(&self, object: SO) -> Result { - let _guard = self.mutation_lock.lock().await; - + let mut locked_objects = self.objects.write().await; self.persist(&object).await?; - let mut locked_objects = self.objects.lock().expect("lock"); let updated = locked_objects.insert(object.id(), object).is_some(); Ok(updated) } pub(crate) async fn insert_or_update(&self, object: SO) -> Result { - let _guard = self.mutation_lock.lock().await; - let (updated, data_to_persist) = { - let mut locked_objects = self.objects.lock().expect("lock"); - match locked_objects.entry(object.id()) { - hash_map::Entry::Occupied(mut e) => { - let update = object.to_update(); - let updated = e.get_mut().update(update); - let data_to_persist = - if updated { Some(Self::encode_object(e.get())) } else { None }; - (updated, data_to_persist) - }, - hash_map::Entry::Vacant(e) => { - let data_to_persist = Self::encode_object(&object); - e.insert(object); - (true, Some(data_to_persist)) - }, - } - }; + let mut locked_objects = self.objects.write().await; - if let Some((store_key, data)) = data_to_persist { - self.persist_encoded(store_key, data).await?; + let updated; + match locked_objects.entry(object.id()) { + hash_map::Entry::Occupied(mut e) => { + let update = object.to_update(); + updated = e.get_mut().update(update); + if updated { + self.persist(&e.get()).await?; + } + }, + hash_map::Entry::Vacant(e) => { + e.insert(object.clone()); + self.persist(&object).await?; + updated = true; + }, } + Ok(updated) } pub(crate) async fn remove(&self, id: &SO::Id) -> Result<(), Error> { - let _guard = self.mutation_lock.lock().await; - let removed = { self.objects.lock().expect("lock").remove(id).is_some() }; + let mut locked_objects = self.objects.write().await; + let removed = locked_objects.remove(id).is_some(); if removed { let store_key = id.encode_to_hex_str(); KVStore::remove( @@ -135,43 +122,28 @@ where Ok(()) } - /// Returns the current in-memory object for `id`. - /// - /// The async mutation lock serializes writers, but this synchronous reader cannot wait on it. - /// Until store reads are async, callers may temporarily see in-memory state that is either - /// still being persisted or has not yet caught up to a write in progress. - pub(crate) fn get(&self, id: &SO::Id) -> Option { - self.objects.lock().expect("lock").get(id).cloned() + pub(crate) async fn get(&self, id: &SO::Id) -> Option { + self.objects.read().await.get(id).cloned() } pub(crate) async fn update(&self, update: SO::Update) -> Result { - let _guard = self.mutation_lock.lock().await; - let (res, data_to_persist) = { - let mut locked_objects = self.objects.lock().expect("lock"); - if let Some(object) = locked_objects.get_mut(&update.id()) { - let updated = object.update(update); - if updated { - (DataStoreUpdateResult::Updated, Some(Self::encode_object(object))) - } else { - (DataStoreUpdateResult::Unchanged, None) - } + let mut locked_objects = self.objects.write().await; + + if let Some(object) = locked_objects.get_mut(&update.id()) { + let updated = object.update(update); + if updated { + self.persist(&object).await?; + Ok(DataStoreUpdateResult::Updated) } else { - (DataStoreUpdateResult::NotFound, None) + Ok(DataStoreUpdateResult::Unchanged) } - }; - if let Some((store_key, data)) = data_to_persist { - self.persist_encoded(store_key, data).await?; + } else { + Ok(DataStoreUpdateResult::NotFound) } - Ok(res) } - /// Returns in-memory objects matching `f`. - /// - /// The async mutation lock serializes writers, but this synchronous reader cannot wait on it. - /// Until store reads are async, callers may temporarily see in-memory state that is either - /// still being persisted or has not yet caught up to a write in progress. - pub(crate) fn list_filter bool>(&self, f: F) -> Vec { - self.objects.lock().expect("lock").values().filter(f).cloned().collect::>() + pub(crate) async fn list_filter bool>(&self, f: F) -> Vec { + self.objects.read().await.values().filter(f).cloned().collect::>() } async fn persist(&self, object: &SO) -> Result<(), Error> { @@ -206,13 +178,8 @@ where Ok(()) } - /// Returns whether the in-memory store contains `id`. - /// - /// The async mutation lock serializes writers, but this synchronous reader cannot wait on it. - /// Until store reads are async, callers may temporarily see in-memory state that is either - /// still being persisted or has not yet caught up to a write in progress. - pub(crate) fn contains_key(&self, id: &SO::Id) -> bool { - self.objects.lock().expect("lock").contains_key(id) + pub(crate) async fn contains_key(&self, id: &SO::Id) -> bool { + self.objects.read().await.contains_key(id) } } @@ -296,7 +263,7 @@ mod tests { ); let id = TestObjectId { id: [42u8; 4] }; - assert!(data_store.get(&id).is_none()); + assert!(data_store.get(&id).await.is_none()); let store_key = id.encode_to_hex_str(); @@ -308,7 +275,7 @@ mod tests { // Check we successfully store an object and return `false` let object = TestObject { id, data: [23u8; 3] }; assert_eq!(Ok(false), data_store.insert(object.clone()).await); - assert_eq!(Some(object), data_store.get(&id)); + assert_eq!(Some(object), data_store.get(&id).await); assert!(KVStore::read(&*store, &primary_namespace, &secondary_namespace, &store_key) .await .is_ok()); @@ -317,12 +284,12 @@ mod tests { let mut override_object = object.clone(); override_object.data = [24u8; 3]; assert_eq!(Ok(true), data_store.insert(override_object).await); - assert_eq!(Some(override_object), data_store.get(&id)); + assert_eq!(Some(override_object), data_store.get(&id).await); // Check update returns `Updated` let update = TestObjectUpdate { id, data: [25u8; 3] }; assert_eq!(Ok(DataStoreUpdateResult::Updated), data_store.update(update).await); - assert_eq!(data_store.get(&id).unwrap().data, [25u8; 3]); + assert_eq!(data_store.get(&id).await.unwrap().data, [25u8; 3]); // Check no-op update yields `Unchanged` let update = TestObjectUpdate { id, data: [25u8; 3] }; diff --git a/src/event.rs b/src/event.rs index 86ee7bb05a..f345c38883 100644 --- a/src/event.rs +++ b/src/event.rs @@ -630,12 +630,16 @@ where // Sign the final funding transaction and broadcast it. let channel_amount = Amount::from_sat(channel_value_satoshis); - match self.wallet.create_funding_transaction( - output_script, - channel_amount, - confirmation_target, - locktime, - ) { + match self + .wallet + .create_funding_transaction( + output_script, + channel_amount, + confirmation_target, + locktime, + ) + .await + { Ok(final_tx) => { let needs_manual_broadcast = self.liquidity_source.as_ref().map_or(false, |ls| { @@ -724,7 +728,7 @@ where .. } => { let payment_id = PaymentId(payment_hash.0); - let payment_info = self.payment_store.get(&payment_id); + let payment_info = self.payment_store.get(&payment_id).await; if let Some(info) = payment_info.as_ref() { if info.direction == PaymentDirection::Outbound { log_info!( @@ -1142,7 +1146,7 @@ where }, }; - self.payment_store.get(&payment_id).map(|payment| { + self.payment_store.get(&payment_id).await.map(|payment| { let amount_msat = payment.amount_msat.expect( "outbound payments should record their amount before they can succeed", ); @@ -1524,28 +1528,26 @@ where }, }; - let peer_to_store = { + let is_new_inbound_pending_channel = self + .channel_manager + .list_channels_with_counterparty(&counterparty_node_id) + .into_iter() + .any(|c| c.channel_id == channel_id && !c.is_outbound); + let peer_to_store = if is_new_inbound_pending_channel + && self.peer_store.get_peer(&counterparty_node_id).await.is_none() + { let network_graph = self.network_graph.read_only(); - let channels = - self.channel_manager.list_channels_with_counterparty(&counterparty_node_id); - channels - .into_iter() - .find(|c| c.channel_id == channel_id) - .filter(|pending_channel| { - !pending_channel.is_outbound - && self.peer_store.get_peer(&counterparty_node_id).is_none() - }) - .and_then(|_| { - network_graph - .nodes() - .get(&NodeId::from_pubkey(&counterparty_node_id)) - .and_then(|node_info| node_info.announcement_info.as_ref()) - .and_then(|ann_info| ann_info.addresses().first()) - .map(|address| PeerInfo { - node_id: counterparty_node_id, - address: address.clone(), - }) + network_graph + .nodes() + .get(&NodeId::from_pubkey(&counterparty_node_id)) + .and_then(|node_info| node_info.announcement_info.as_ref()) + .and_then(|ann_info| ann_info.addresses().first()) + .map(|address| PeerInfo { + node_id: counterparty_node_id, + address: address.clone(), }) + } else { + None }; if let Some(peer) = peer_to_store { self.peer_store.add_peer(peer).await.unwrap_or_else(|e| { @@ -1646,7 +1648,7 @@ where }) .collect(), }; - if let Err(e) = self.wallet.cancel_tx(&tx) { + if let Err(e) = self.wallet.cancel_tx(&tx).await { log_error!(self.logger, "Failed reclaiming unused addresses: {}", e); return Err(ReplayEvent()); } diff --git a/src/io/postgres_store/mod.rs b/src/io/postgres_store/mod.rs index c0770de5f0..486f985c7b 100644 --- a/src/io/postgres_store/mod.rs +++ b/src/io/postgres_store/mod.rs @@ -22,7 +22,6 @@ use tokio_postgres::{Config, Error as PgError}; use self::pool::{make_config_connection, ClientConnection, PgTlsConnector, SmallPool}; use crate::io::utils::check_namespace_key_validity; use crate::logger::{log_debug, log_info, LdkLogger, Logger}; -use crate::runtime::StoreRuntime; mod migrations; mod pool; @@ -39,9 +38,6 @@ const SCHEMA_VERSION: u16 = 1; // The number of entries returned per page in paginated list operations. const PAGE_SIZE: usize = 50; -// Keep this small while still allowing progress if one runtime worker blocks on sync store access. -const INTERNAL_RUNTIME_WORKERS: usize = 2; - fn sql_identifier(identifier: &str) -> io::Result { if identifier.is_empty() || identifier.contains('\0') { return Err(io::Error::new( @@ -92,8 +88,6 @@ macro_rules! query_with_retry { /// A [`KVStore`] implementation that writes to and reads from a [PostgreSQL] database. /// -/// Maintains an internal runtime for the underlying tokio-postgres connection drivers. -/// /// [PostgreSQL]: https://www.postgresql.org pub struct PostgresStore { inner: Arc, @@ -101,9 +95,6 @@ pub struct PostgresStore { // Version counter to ensure that writes are applied in the correct order. It is assumed that read and list // operations aren't sensitive to the order of execution. next_write_version: AtomicU64, - - // A store-internal runtime that drives PostgreSQL I/O independently from the node runtime. - internal_runtime: Option>, } // tokio::sync::Mutex (used for the DB client) contains UnsafeCell which opts out of @@ -146,21 +137,12 @@ impl PostgresStore { connection_string: String, db_name: Option, kv_table_name: Option, certificate_pem: Option, logger: Option>, ) -> io::Result { - let internal_runtime = Arc::new(StoreRuntime::new( - "ldk-node-postgres-runtime", - INTERNAL_RUNTIME_WORKERS, - "PostgreSQL", - )?); let tls = Self::build_tls_connector(certificate_pem)?; - let task = internal_runtime.spawn(async move { - PostgresStoreInner::new(connection_string, db_name, kv_table_name, tls, logger).await - }); - let inner = task.await.map_err(|e| { - io::Error::new(io::ErrorKind::Other, format!("PostgreSQL runtime task failed: {}", e)) - })??; + let inner = + PostgresStoreInner::new(connection_string, db_name, kv_table_name, tls, logger).await?; let inner = Arc::new(inner); let next_write_version = AtomicU64::new(1); - Ok(Self { inner, next_write_version, internal_runtime: Some(internal_runtime) }) + Ok(Self { inner, next_write_version }) } fn build_tls_connector(certificate_pem: Option) -> io::Result { @@ -203,20 +185,6 @@ impl PostgresStore { (inner_lock_ref, version) } - - fn internal_runtime(&self) -> Arc { - Arc::clone(self.internal_runtime.as_ref().expect("PostgreSQL runtime must be available")) - } -} - -impl Drop for PostgresStore { - fn drop(&mut self) { - if let Some(internal_runtime) = self.internal_runtime.take() { - if let Ok(internal_runtime) = Arc::try_unwrap(internal_runtime) { - internal_runtime.shutdown_background(); - } - } - } } impl KVStore for PostgresStore { @@ -227,18 +195,7 @@ impl KVStore for PostgresStore { let secondary_namespace = secondary_namespace.to_string(); let key = key.to_string(); let inner = Arc::clone(&self.inner); - let runtime = self.internal_runtime(); - async move { - let task = runtime.spawn(async move { - inner.read_internal(&primary_namespace, &secondary_namespace, &key).await - }); - task.await.map_err(|e| { - io::Error::new( - io::ErrorKind::Other, - format!("PostgreSQL runtime task failed: {}", e), - ) - })? - } + async move { inner.read_internal(&primary_namespace, &secondary_namespace, &key).await } } fn write( @@ -250,27 +207,18 @@ impl KVStore for PostgresStore { let secondary_namespace = secondary_namespace.to_string(); let key = key.to_string(); let inner = Arc::clone(&self.inner); - let runtime = self.internal_runtime(); async move { - let task = runtime.spawn(async move { - inner - .write_internal( - inner_lock_ref, - locking_key, - version, - &primary_namespace, - &secondary_namespace, - &key, - buf, - ) - .await - }); - task.await.map_err(|e| { - io::Error::new( - io::ErrorKind::Other, - format!("PostgreSQL runtime task failed: {}", e), + inner + .write_internal( + inner_lock_ref, + locking_key, + version, + &primary_namespace, + &secondary_namespace, + &key, + buf, ) - })? + .await } } @@ -283,26 +231,17 @@ impl KVStore for PostgresStore { let secondary_namespace = secondary_namespace.to_string(); let key = key.to_string(); let inner = Arc::clone(&self.inner); - let runtime = self.internal_runtime(); async move { - let task = runtime.spawn(async move { - inner - .remove_internal( - inner_lock_ref, - locking_key, - version, - &primary_namespace, - &secondary_namespace, - &key, - ) - .await - }); - task.await.map_err(|e| { - io::Error::new( - io::ErrorKind::Other, - format!("PostgreSQL runtime task failed: {}", e), + inner + .remove_internal( + inner_lock_ref, + locking_key, + version, + &primary_namespace, + &secondary_namespace, + &key, ) - })? + .await } } @@ -312,18 +251,7 @@ impl KVStore for PostgresStore { let primary_namespace = primary_namespace.to_string(); let secondary_namespace = secondary_namespace.to_string(); let inner = Arc::clone(&self.inner); - let runtime = self.internal_runtime(); - async move { - let task = runtime.spawn(async move { - inner.list_internal(&primary_namespace, &secondary_namespace).await - }); - task.await.map_err(|e| { - io::Error::new( - io::ErrorKind::Other, - format!("PostgreSQL runtime task failed: {}", e), - ) - })? - } + async move { inner.list_internal(&primary_namespace, &secondary_namespace).await } } } @@ -334,19 +262,10 @@ impl PaginatedKVStore for PostgresStore { let primary_namespace = primary_namespace.to_string(); let secondary_namespace = secondary_namespace.to_string(); let inner = Arc::clone(&self.inner); - let runtime = self.internal_runtime(); async move { - let task = runtime.spawn(async move { - inner - .list_paginated_internal(&primary_namespace, &secondary_namespace, page_token) - .await - }); - task.await.map_err(|e| { - io::Error::new( - io::ErrorKind::Other, - format!("PostgreSQL runtime task failed: {}", e), - ) - })? + inner + .list_paginated_internal(&primary_namespace, &secondary_namespace, page_token) + .await } } } @@ -899,7 +818,7 @@ mod tests { async fn test_postgres_store() { let store_0 = create_test_store("test_pg_store_0").await; let store_1 = create_test_store("test_pg_store_1").await; - do_test_store(&store_0, &store_1); + do_test_store(&store_0, &store_1).await; cleanup_store(&store_0).await; cleanup_store(&store_1).await; } diff --git a/src/io/sqlite_store/mod.rs b/src/io/sqlite_store/mod.rs index 076aeef9bd..cc28ccb28b 100644 --- a/src/io/sqlite_store/mod.rs +++ b/src/io/sqlite_store/mod.rs @@ -676,7 +676,7 @@ mod tests { Some("test_table".to_string()), ) .unwrap(); - do_test_store(&store_0, &store_1) + do_test_store(&store_0, &store_1).await } #[tokio::test] diff --git a/src/io/test_utils.rs b/src/io/test_utils.rs index aadb4b79a8..4e8e90deef 100644 --- a/src/io/test_utils.rs +++ b/src/io/test_utils.rs @@ -8,7 +8,7 @@ use std::future::Future; use std::panic::RefUnwindSafe; use std::path::PathBuf; -use std::sync::Arc; +use std::sync::Mutex; use lightning::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate}; use lightning::chain::{chainmonitor, BlockLocator, ChannelMonitorUpdateStatus}; @@ -35,71 +35,128 @@ use rand::{rng, Rng}; #[path = "in_memory_store.rs"] mod in_memory_store; -use crate::logger::Logger; -use crate::runtime::Runtime; +enum PendingMonitorPersistence { + Write { monitor_name: MonitorName, monitor: Vec }, + Archive { monitor_name: MonitorName }, +} pub(crate) struct TestMonitorUpdatePersister<'a, K> { store: &'a K, - runtime: Runtime, + pending_updates: Mutex>, entropy_source: &'a test_utils::TestKeysInterface, signer_provider: &'a test_utils::TestKeysInterface, } impl TestMonitorUpdatePersister<'_, K> { - pub(crate) fn read_all_channel_monitors_with_updates( + async fn flush_pending_monitor_updates(&self) -> Result<(), io::Error> { + loop { + let pending_updates = { + let mut pending_updates = self.pending_updates.lock().unwrap(); + if pending_updates.is_empty() { + return Ok(()); + } + std::mem::take(&mut *pending_updates) + }; + + for update in pending_updates { + match update { + PendingMonitorPersistence::Write { monitor_name, monitor } => { + KVStore::write( + self.store, + CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, + &monitor_name.to_string(), + monitor, + ) + .await?; + }, + PendingMonitorPersistence::Archive { monitor_name } => { + let key = monitor_name.to_string(); + let monitor = match KVStore::read( + self.store, + CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, + &key, + ) + .await + { + Ok(monitor) => monitor, + Err(_) => continue, + }; + + if KVStore::write( + self.store, + ARCHIVED_CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, + ARCHIVED_CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, + &key, + monitor, + ) + .await + .is_ok() + { + let _ = KVStore::remove( + self.store, + CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, + &key, + true, + ) + .await; + } + }, + } + } + } + } + + pub(crate) async fn read_all_channel_monitors_with_updates( &self, ) -> Result)>, io::Error> { - self.runtime.block_on(async { - let stored_keys = KVStore::list( + self.flush_pending_monitor_updates().await?; + + let stored_keys = KVStore::list( + self.store, + CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, + ) + .await?; + + let mut res = Vec::with_capacity(stored_keys.len()); + for stored_key in stored_keys { + let data = KVStore::read( self.store, CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, + &stored_key, ) .await?; - - let mut res = Vec::with_capacity(stored_keys.len()); - for stored_key in stored_keys { - let data = KVStore::read( - self.store, - CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, - CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, - &stored_key, - ) - .await?; - match )>>::read( - &mut io::Cursor::new(data), - (self.entropy_source, self.signer_provider), - ) { - Ok(Some((best_block, channel_monitor))) => { - res.push((best_block, channel_monitor)); - }, - Ok(None) => {}, - Err(_) => { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "Failed to read ChannelMonitor", - )); - }, - } + match )>>::read( + &mut io::Cursor::new(data), + (self.entropy_source, self.signer_provider), + ) { + Ok(Some((best_block, channel_monitor))) => { + res.push((best_block, channel_monitor)); + }, + Ok(None) => {}, + Err(_) => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "Failed to read ChannelMonitor", + )); + }, } - Ok(res) - }) + } + Ok(res) } fn write_monitor( &self, monitor_name: MonitorName, monitor: &ChannelMonitor, ) -> ChannelMonitorUpdateStatus { - let write_res = self.runtime.block_on(KVStore::write( - self.store, - CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, - CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, - &monitor_name.to_string(), - monitor.encode(), - )); - match write_res { - Ok(()) => ChannelMonitorUpdateStatus::Completed, - Err(_) => ChannelMonitorUpdateStatus::UnrecoverableError, - } + self.pending_updates + .lock() + .unwrap() + .push(PendingMonitorPersistence::Write { monitor_name, monitor: monitor.encode() }); + ChannelMonitorUpdateStatus::Completed } } @@ -120,40 +177,10 @@ impl chainmonitor::Persist } fn archive_persisted_channel(&self, monitor_name: MonitorName) { - let key = monitor_name.to_string(); - self.runtime.block_on(async { - let monitor = match KVStore::read( - self.store, - CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, - CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, - &key, - ) - .await - { - Ok(monitor) => monitor, - Err(_) => return, - }; - - if KVStore::write( - self.store, - ARCHIVED_CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, - ARCHIVED_CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, - &key, - monitor, - ) - .await - .is_ok() - { - let _ = KVStore::remove( - self.store, - CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, - CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, - &key, - true, - ) - .await; - } - }); + self.pending_updates + .lock() + .unwrap() + .push(PendingMonitorPersistence::Archive { monitor_name }); } } @@ -248,11 +275,9 @@ pub(crate) async fn do_read_write_remove_list_persist( store: &'a K, chanmon_cfg: &'a TestChanMonCfg, _max_pending_updates: u64, ) -> TestMonitorUpdatePersister<'a, K> { - let runtime = - Runtime::new(Arc::new(Logger::new_log_facade())).expect("Failed to setup runtime"); TestMonitorUpdatePersister { store, - runtime, + pending_updates: Mutex::new(Vec::new()), entropy_source: &chanmon_cfg.keys_manager, signer_provider: &chanmon_cfg.keys_manager, } @@ -273,7 +298,7 @@ pub(crate) fn create_chain_monitor<'a, K: KVStore + Sync>( // Integration-test the given KVStore implementation. Test relaying a few payments and check that // the persisted data is updated the appropriate number of times. -pub(crate) fn do_test_store(store_0: &K, store_1: &K) { +pub(crate) async fn do_test_store(store_0: &K, store_1: &K) { // This value is used later to limit how many iterations we perform. let persister_0_max_pending_updates = 7; // Intentionally set this to a smaller value to test a different alignment. @@ -297,20 +322,24 @@ pub(crate) fn do_test_store(store_0: &K, store_1: &K) { // Check that the persisted channel data is empty before any channels are // open. - let mut persisted_chan_data_0 = persister_0.read_all_channel_monitors_with_updates().unwrap(); + let mut persisted_chan_data_0 = + persister_0.read_all_channel_monitors_with_updates().await.unwrap(); assert_eq!(persisted_chan_data_0.len(), 0); - let mut persisted_chan_data_1 = persister_1.read_all_channel_monitors_with_updates().unwrap(); + let mut persisted_chan_data_1 = + persister_1.read_all_channel_monitors_with_updates().await.unwrap(); assert_eq!(persisted_chan_data_1.len(), 0); // Helper to make sure the channel is on the expected update ID. macro_rules! check_persisted_data { ($expected_update_id:expr) => { - persisted_chan_data_0 = persister_0.read_all_channel_monitors_with_updates().unwrap(); + persisted_chan_data_0 = + persister_0.read_all_channel_monitors_with_updates().await.unwrap(); assert_eq!(persisted_chan_data_0.len(), 1); for (_, mon) in persisted_chan_data_0.iter() { assert_eq!(mon.get_latest_update_id(), $expected_update_id); } - persisted_chan_data_1 = persister_1.read_all_channel_monitors_with_updates().unwrap(); + persisted_chan_data_1 = + persister_1.read_all_channel_monitors_with_updates().await.unwrap(); assert_eq!(persisted_chan_data_1.len(), 1); for (_, mon) in persisted_chan_data_1.iter() { assert_eq!(mon.get_latest_update_id(), $expected_update_id); diff --git a/src/io/utils.rs b/src/io/utils.rs index 4657688f51..b07bf14a5b 100644 --- a/src/io/utils.rs +++ b/src/io/utils.rs @@ -37,6 +37,7 @@ use lightning::util::ser::{Readable, ReadableArgs, Writeable}; use lightning_persister::fs_store::v1::FilesystemStore; use lightning_persister::fs_store::v2::{FilesystemStoreV2, FilesystemStoreV2Error}; use lightning_types::string::PrintableString; +use tokio::sync::RwLock; use super::*; use crate::chain::ChainSource; @@ -49,7 +50,7 @@ use crate::logger::{log_error, LdkLogger, Logger}; use crate::peer_store::PeerStore; use crate::types::{Broadcaster, DynStore, KeysManager, Sweeper}; use crate::wallet::ser::{ChangeSetDeserWrapper, ChangeSetSerWrapper}; -use crate::{BuildError, Error, EventQueue, NodeMetrics, PersistedNodeMetrics}; +use crate::{BuildError, Error, EventQueue, NodeMetrics}; pub const EXTERNAL_PATHFINDING_SCORES_CACHE_KEY: &str = "external_pathfinding_scores_cache"; @@ -337,18 +338,15 @@ where /// Take a write lock on `node_metrics`, apply `update`, and persist the result to `kv_store`. pub(crate) async fn update_and_persist_node_metrics( - node_metrics: &PersistedNodeMetrics, kv_store: &DynStore, logger: L, + node_metrics: &RwLock, kv_store: &DynStore, logger: L, update: impl FnOnce(&mut NodeMetrics), ) -> Result<(), Error> where L::Target: LdkLogger, { - let _guard = node_metrics.lock_mutation().await; - let data = { - let mut locked_node_metrics = node_metrics.write().expect("lock"); - update(&mut *locked_node_metrics); - locked_node_metrics.encode() - }; + let mut locked_node_metrics = node_metrics.write().await; + update(&mut *locked_node_metrics); + let data = locked_node_metrics.encode(); KVStore::write( &*kv_store, NODE_METRICS_PRIMARY_NAMESPACE, diff --git a/src/io/vss_store.rs b/src/io/vss_store.rs index 6c3535627a..5541e6c257 100644 --- a/src/io/vss_store.rs +++ b/src/io/vss_store.rs @@ -45,7 +45,6 @@ use vss_client::util::storable_builder::{EntropySource, StorableBuilder}; use crate::entropy::NodeEntropy; use crate::io::utils::check_namespace_key_validity; use crate::lnurl_auth::LNURL_AUTH_HARDENED_CHILD_INDEX; -use crate::runtime::StoreRuntime; type CustomRetryPolicy = FilteredRetryPolicy< JitteredRetryPolicy< @@ -54,7 +53,7 @@ type CustomRetryPolicy = FilteredRetryPolicy< Box bool + 'static + Send + Sync>, >; -#[derive(Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq)] enum VssSchemaVersion { // The initial schema version. // This used an empty `aad` and unobfuscated `primary_namespace`/`secondary_namespace`s in the @@ -74,10 +73,6 @@ const VSS_HARDENED_CHILD_INDEX: u32 = 877; const VSS_SIGS_AUTH_HARDENED_CHILD_INDEX: u32 = 139; const VSS_SCHEMA_VERSION_KEY: &str = "vss_schema_version"; -// We set this to a small number of threads that would still allow to make some progress if one -// would hit a blocking case -const INTERNAL_RUNTIME_WORKERS: usize = 2; - /// A [`KVStore`] implementation that writes to and reads from a [VSS] backend. /// /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md @@ -86,8 +81,6 @@ pub struct VssStore { // Version counter to ensure that writes are applied in the correct order. It is assumed that read and list // operations aren't sensitive to the order of execution. next_version: AtomicU64, - // A VSS-internal runtime that drives VSS I/O independently from the node runtime. - internal_runtime: Option>, } impl VssStore { @@ -96,49 +89,20 @@ impl VssStore { header_provider: Arc, ) -> io::Result { let next_version = AtomicU64::new(1); - let internal_runtime = - Arc::new(StoreRuntime::new("ldk-node-vss-runtime", INTERNAL_RUNTIME_WORKERS, "VSS")?); let (data_encryption_key, obfuscation_master_key) = derive_data_encryption_and_obfuscation_keys(&vss_seed); let key_obfuscator = KeyObfuscator::new(obfuscation_master_key); - let setup_key_obfuscator = KeyObfuscator::new(obfuscation_master_key); let mut entropy_seed = [0u8; 32]; getrandom::fill(&mut entropy_seed).expect("Failed to generate random bytes"); let entropy_source = RandomBytes::new(entropy_seed); - let setup_entropy_source = RandomBytes::new(entropy_seed); - - let setup_retry_policy = retry_policy(); - let setup_client = VssClient::new_with_headers( - base_url.clone(), - setup_retry_policy, - Arc::clone(&header_provider), - ); let async_retry_policy = retry_policy(); let async_client = VssClient::new_with_headers(base_url, async_retry_policy, header_provider); - let setup_store_id = store_id.clone(); - let runtime_handle = internal_runtime.handle().clone(); - let schema_version = std::thread::spawn(move || { - runtime_handle.block_on(async { - determine_and_write_schema_version( - &setup_client, - &setup_store_id, - data_encryption_key, - &setup_key_obfuscator, - &setup_entropy_source, - ) - .await - }) - }) - .join() - .map_err(|_| io::Error::new(io::ErrorKind::Other, "VSS schema setup task panicked"))??; - let inner = Arc::new(VssStoreInner::new( - schema_version, async_client, store_id, data_encryption_key, @@ -146,12 +110,13 @@ impl VssStore { entropy_source, )); - Ok(Self { inner, next_version, internal_runtime: Some(internal_runtime) }) + Ok(Self { inner, next_version }) } - fn internal_runtime(&self) -> Arc { - Arc::clone(self.internal_runtime.as_ref().expect("VSS runtime must be available")) + pub(crate) async fn setup_schema_version(&self) -> io::Result<()> { + self.inner.schema_version().await.map(|_| ()) } + /// Returns a [`VssStoreBuilder`] allowing to build a [`VssStore`]. pub fn builder( node_entropy: NodeEntropy, vss_url: String, store_id: String, network: Network, @@ -194,16 +159,10 @@ impl KVStore for VssStore { let secondary_namespace = secondary_namespace.to_string(); let key = key.to_string(); let inner = Arc::clone(&self.inner); - let runtime = self.internal_runtime(); async move { - let task = runtime.spawn(async move { - inner - .read_internal(&inner.async_client, primary_namespace, secondary_namespace, key) - .await - }); - task.await.map_err(|e| { - io::Error::new(io::ErrorKind::Other, format!("VSS runtime task failed: {}", e)) - })? + inner + .read_internal(&inner.async_client, primary_namespace, secondary_namespace, key) + .await } } fn write( @@ -215,25 +174,19 @@ impl KVStore for VssStore { let secondary_namespace = secondary_namespace.to_string(); let key = key.to_string(); let inner = Arc::clone(&self.inner); - let runtime = self.internal_runtime(); async move { - let task = runtime.spawn(async move { - inner - .write_internal( - &inner.async_client, - inner_lock_ref, - locking_key, - version, - primary_namespace, - secondary_namespace, - key, - buf, - ) - .await - }); - task.await.map_err(|e| { - io::Error::new(io::ErrorKind::Other, format!("VSS runtime task failed: {}", e)) - })? + inner + .write_internal( + &inner.async_client, + inner_lock_ref, + locking_key, + version, + primary_namespace, + secondary_namespace, + key, + buf, + ) + .await } } fn remove( @@ -245,7 +198,6 @@ impl KVStore for VssStore { let secondary_namespace = secondary_namespace.to_string(); let key = key.to_string(); let inner = Arc::clone(&self.inner); - let runtime = self.internal_runtime(); let fut = async move { inner .remove_internal( @@ -261,15 +213,10 @@ impl KVStore for VssStore { }; async move { if lazy { - runtime.spawn(async move { - let _ = fut.await; - }); + tokio::task::spawn(async move { fut.await }); Ok(()) } else { - let task = runtime.spawn(fut); - task.await.map_err(|e| { - io::Error::new(io::ErrorKind::Other, format!("VSS runtime task failed: {}", e)) - })? + fut.await } } } @@ -279,34 +226,15 @@ impl KVStore for VssStore { let primary_namespace = primary_namespace.to_string(); let secondary_namespace = secondary_namespace.to_string(); let inner = Arc::clone(&self.inner); - let runtime = self.internal_runtime(); async move { - let task = runtime.spawn(async move { - inner - .list_internal(&inner.async_client, primary_namespace, secondary_namespace) - .await - }); - task.await.map_err(|e| { - io::Error::new(io::ErrorKind::Other, format!("VSS runtime task failed: {}", e)) - })? - } - } -} - -impl Drop for VssStore { - fn drop(&mut self) { - if let Some(runtime) = self.internal_runtime.take() { - if let Ok(runtime) = Arc::try_unwrap(runtime) { - runtime.shutdown_background(); - } + inner.list_internal(&inner.async_client, primary_namespace, secondary_namespace).await } } } struct VssStoreInner { - schema_version: VssSchemaVersion, - // A secondary client that will only be used for async persistence via `KVStore`, to ensure TCP - // connections aren't shared between our outer and the internal runtime. + schema_version: tokio::sync::OnceCell, + // Client used for async persistence via `KVStore`. async_client: VssClient, store_id: String, data_encryption_key: [u8; 32], @@ -319,13 +247,12 @@ struct VssStoreInner { impl VssStoreInner { pub(crate) fn new( - schema_version: VssSchemaVersion, async_client: VssClient, - store_id: String, data_encryption_key: [u8; 32], key_obfuscator: KeyObfuscator, - entropy_source: RandomBytes, + async_client: VssClient, store_id: String, + data_encryption_key: [u8; 32], key_obfuscator: KeyObfuscator, entropy_source: RandomBytes, ) -> Self { let locks = Mutex::new(HashMap::new()); Self { - schema_version, + schema_version: tokio::sync::OnceCell::new(), async_client, store_id, data_encryption_key, @@ -340,12 +267,33 @@ impl VssStoreInner { Arc::clone(&outer_lock.entry(locking_key).or_default()) } + async fn schema_version(&self) -> io::Result { + let schema_version = self + .schema_version + .get_or_try_init(|| async { + determine_and_write_schema_version( + &self.async_client, + &self.store_id, + self.data_encryption_key, + &self.key_obfuscator, + &self.entropy_source, + ) + .await + }) + .await?; + Ok(*schema_version) + } + fn build_obfuscated_key( - &self, primary_namespace: &str, secondary_namespace: &str, key: &str, + &self, schema_version: VssSchemaVersion, primary_namespace: &str, + secondary_namespace: &str, key: &str, ) -> String { - if self.schema_version == VssSchemaVersion::V1 { - let obfuscated_prefix = - self.build_obfuscated_prefix(primary_namespace, secondary_namespace); + if schema_version == VssSchemaVersion::V1 { + let obfuscated_prefix = self.build_obfuscated_prefix( + schema_version, + primary_namespace, + secondary_namespace, + ); let obfuscated_key = self.key_obfuscator.obfuscate(key); format!("{}#{}", obfuscated_prefix, obfuscated_key) } else { @@ -360,9 +308,9 @@ impl VssStoreInner { } fn build_obfuscated_prefix( - &self, primary_namespace: &str, secondary_namespace: &str, + &self, schema_version: VssSchemaVersion, primary_namespace: &str, secondary_namespace: &str, ) -> String { - if self.schema_version == VssSchemaVersion::V1 { + if schema_version == VssSchemaVersion::V1 { let prefix = format!("{}#{}", primary_namespace, secondary_namespace); self.key_obfuscator.obfuscate(&prefix) } else { @@ -371,8 +319,10 @@ impl VssStoreInner { } } - fn extract_key(&self, unified_key: &str) -> io::Result { - let mut parts = if self.schema_version == VssSchemaVersion::V1 { + fn extract_key( + &self, schema_version: VssSchemaVersion, unified_key: &str, + ) -> io::Result { + let mut parts = if schema_version == VssSchemaVersion::V1 { let mut parts = unified_key.splitn(2, '#'); let _obfuscated_namespace = parts.next(); parts @@ -392,12 +342,13 @@ impl VssStoreInner { } async fn list_all_keys( - &self, client: &VssClient, primary_namespace: &str, - secondary_namespace: &str, + &self, client: &VssClient, schema_version: VssSchemaVersion, + primary_namespace: &str, secondary_namespace: &str, ) -> io::Result> { let mut page_token = None; let mut keys = vec![]; - let key_prefix = self.build_obfuscated_prefix(primary_namespace, secondary_namespace); + let key_prefix = + self.build_obfuscated_prefix(schema_version, primary_namespace, secondary_namespace); while page_token != Some("".to_string()) { let request = ListKeyVersionsRequest { store_id: self.store_id.clone(), @@ -415,7 +366,7 @@ impl VssStoreInner { })?; for kv in response.key_versions { - keys.push(self.extract_key(&kv.key)?); + keys.push(self.extract_key(schema_version, &kv.key)?); } page_token = response.next_page_token; } @@ -428,7 +379,13 @@ impl VssStoreInner { ) -> io::Result> { check_namespace_key_validity(&primary_namespace, &secondary_namespace, Some(&key), "read")?; - let store_key = self.build_obfuscated_key(&primary_namespace, &secondary_namespace, &key); + let schema_version = self.schema_version().await?; + let store_key = self.build_obfuscated_key( + schema_version, + &primary_namespace, + &secondary_namespace, + &key, + ); let request = GetObjectRequest { store_id: self.store_id.clone(), key: store_key.clone() }; let resp = client.get_object(&request).await.map_err(|e| { let msg = format!( @@ -454,8 +411,7 @@ impl VssStoreInner { })?; let storable_builder = StorableBuilder::new(VssEntropySource(&self.entropy_source)); - let aad = - if self.schema_version == VssSchemaVersion::V1 { store_key.as_bytes() } else { &[] }; + let aad = if schema_version == VssSchemaVersion::V1 { store_key.as_bytes() } else { &[] }; let decrypted = storable_builder.deconstruct(storable, &self.data_encryption_key, aad)?.0; Ok(decrypted) } @@ -472,11 +428,22 @@ impl VssStoreInner { "write", )?; - let store_key = self.build_obfuscated_key(&primary_namespace, &secondary_namespace, &key); + let schema_version = match self.schema_version().await { + Ok(schema_version) => schema_version, + Err(e) => { + self.clean_locks(&inner_lock_ref, locking_key); + return Err(e); + }, + }; + let store_key = self.build_obfuscated_key( + schema_version, + &primary_namespace, + &secondary_namespace, + &key, + ); let vss_version = -1; let storable_builder = StorableBuilder::new(VssEntropySource(&self.entropy_source)); - let aad = - if self.schema_version == VssSchemaVersion::V1 { store_key.as_bytes() } else { &[] }; + let aad = if schema_version == VssSchemaVersion::V1 { store_key.as_bytes() } else { &[] }; let storable = storable_builder.build(buf.to_vec(), vss_version, &self.data_encryption_key, aad); let request = PutObjectRequest { @@ -516,8 +483,19 @@ impl VssStoreInner { "remove", )?; - let obfuscated_key = - self.build_obfuscated_key(&primary_namespace, &secondary_namespace, &key); + let schema_version = match self.schema_version().await { + Ok(schema_version) => schema_version, + Err(e) => { + self.clean_locks(&inner_lock_ref, locking_key); + return Err(e); + }, + }; + let obfuscated_key = self.build_obfuscated_key( + schema_version, + &primary_namespace, + &secondary_namespace, + &key, + ); let key_value = KeyValue { key: obfuscated_key, version: -1, value: vec![] }; self.execute_locked_write(inner_lock_ref, locking_key, version, async move || { @@ -543,8 +521,9 @@ impl VssStoreInner { ) -> io::Result> { check_namespace_key_validity(&primary_namespace, &secondary_namespace, None, "list")?; + let schema_version = self.schema_version().await?; let keys = self - .list_all_keys(client, &primary_namespace, &secondary_namespace) + .list_all_keys(client, schema_version, &primary_namespace, &secondary_namespace) .await .map_err(|e| { let msg = format!( diff --git a/src/lib.rs b/src/lib.rs index 7465dfabf5..ebb102cb5d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,6 +16,16 @@ //! Lightning node with an integrated on-chain wallet. While minimalism is at its core, LDK Node //! aims to be sufficiently modular and configurable to be useful for a variety of use cases. //! +//! ## Runtime Assumptions +//! +//! LDK Node's Rust API is async and expects its futures to be polled by a Tokio runtime. When a +//! node is built from within an existing Tokio context, LDK Node reuses that runtime handle for +//! internally spawned work; otherwise it creates an owned multi-thread Tokio runtime for its +//! background tasks. Callers should keep the runtime threads available to drive network, timer, and +//! persistence futures while node operations are in progress. Long synchronous work should run +//! outside those worker threads, or on a dedicated blocking pool, so it does not prevent LDK Node's +//! I/O and storage tasks from making progress. +//! //! ## Getting Started //! //! The primary abstraction of the library is the [`Node`], which can be retrieved by setting up @@ -34,7 +44,8 @@ //! use ldk_node::lightning_invoice::Bolt11Invoice; //! use ldk_node::Builder; //! -//! fn main() { +//! #[tokio::main] +//! async fn main() { //! let mut builder = Builder::new(); //! builder.set_network(Network::Testnet); //! builder.set_chain_source_esplora("https://blockstream.info/testnet/api".to_string(), None); @@ -44,26 +55,26 @@ //! //! let mnemonic = generate_entropy_mnemonic(None); //! let node_entropy = NodeEntropy::from_bip39_mnemonic(mnemonic, None); -//! let node = builder.build(node_entropy).unwrap(); +//! let node = builder.build(node_entropy).await.unwrap(); //! -//! node.start().unwrap(); +//! node.start().await.unwrap(); //! -//! let funding_address = node.onchain_payment().new_address(); +//! let funding_address = node.onchain_payment().new_address().await.unwrap(); //! //! // .. fund address .. //! //! let node_id = PublicKey::from_str("NODE_ID").unwrap(); //! let node_addr = SocketAddress::from_str("IP_ADDR:PORT").unwrap(); -//! node.open_channel(node_id, node_addr, 10000, None, None).unwrap(); +//! node.open_channel(node_id, node_addr, 10000, None, None).await.unwrap(); //! -//! let event = node.wait_next_event(); +//! let event = node.wait_next_event().await; //! println!("EVENT: {:?}", event); -//! node.event_handled(); +//! node.event_handled().await.unwrap(); //! //! let invoice = Bolt11Invoice::from_str("INVOICE_STR").unwrap(); -//! node.bolt11_payment().send(&invoice, None).unwrap(); +//! node.bolt11_payment().send(&invoice, None).await.unwrap(); //! -//! node.stop().unwrap(); +//! node.stop().await.unwrap(); //! } //! # } //! ``` @@ -175,6 +186,7 @@ use payment::{ use peer_store::{PeerInfo, PeerStore}; use runtime::Runtime; pub use tokio; +use tokio::sync::RwLock as AsyncRwLock; use types::{ Broadcaster, BumpTransactionEventHandler, ChainMonitor, ChannelManager, DynStore, Graph, HRNResolver, KeysManager, OnionMessenger, PaymentStore, PeerManager, Router, Scorer, Sweeper, @@ -243,7 +255,7 @@ pub struct Node { payment_store: Arc, lnurl_auth: Arc, is_running: Arc>, - node_metrics: Arc, + node_metrics: Arc>, om_mailbox: Option>, async_payments_role: Option, hrn_resolver: HRNResolver, @@ -260,11 +272,14 @@ impl Node { /// /// After this returns, the [`Node`] instance can be controlled via the provided API methods in /// a thread-safe manner. - pub fn start(&self) -> Result<(), Error> { + pub async fn start(&self) -> Result<(), Error> { // Acquire a run lock and hold it until we're setup. - let mut is_running_lock = self.is_running.write().expect("lock"); - if *is_running_lock { - return Err(Error::AlreadyRunning); + { + let mut is_running_lock = self.is_running.write().expect("lock"); + if *is_running_lock { + return Err(Error::AlreadyRunning); + } + *is_running_lock = true; } log_info!( @@ -274,34 +289,35 @@ impl Node { self.config.network ); - // Start up any runtime-dependant chain sources (e.g. Electrum) - self.chain_source.start(Arc::clone(&self.runtime)).map_err(|e| { - log_error!(self.logger, "Failed to start chain syncing: {}", e); - e - })?; - - // Block to ensure we update our fee rate cache once on startup - let chain_source = Arc::clone(&self.chain_source); - self.runtime.block_on(async move { chain_source.update_fee_rate_estimates().await })?; + let startup_res = async { + // Start up any runtime-dependant chain sources (e.g. Electrum) + self.chain_source.start(Arc::clone(&self.runtime)).map_err(|e| { + log_error!(self.logger, "Failed to start chain syncing: {}", e); + e + })?; - // Spawn background task continuously syncing onchain, lightning, and fee rate cache. - let stop_sync_receiver = self.stop_sender.subscribe(); - let chain_source = Arc::clone(&self.chain_source); - let sync_wallet = Arc::clone(&self.wallet); - let sync_cman = Arc::clone(&self.channel_manager); - let sync_cmon = Arc::clone(&self.chain_monitor); - let sync_sweeper = Arc::clone(&self.output_sweeper); - self.runtime.spawn_background_task(async move { - chain_source - .continuously_sync_wallets( - stop_sync_receiver, - sync_wallet, - sync_cman, - sync_cmon, - sync_sweeper, - ) - .await; - }); + // Ensure we update our fee rate cache once on startup. + let chain_source = Arc::clone(&self.chain_source); + chain_source.update_fee_rate_estimates().await?; + + // Spawn background task continuously syncing onchain, lightning, and fee rate cache. + let stop_sync_receiver = self.stop_sender.subscribe(); + let chain_source = Arc::clone(&self.chain_source); + let sync_wallet = Arc::clone(&self.wallet); + let sync_cman = Arc::clone(&self.channel_manager); + let sync_cmon = Arc::clone(&self.chain_monitor); + let sync_sweeper = Arc::clone(&self.output_sweeper); + self.runtime.spawn_background_task(async move { + chain_source + .continuously_sync_wallets( + stop_sync_receiver, + sync_wallet, + sync_cman, + sync_cmon, + sync_sweeper, + ) + .await; + }); if self.gossip_source.is_rgs() { let gossip_source = Arc::clone(&self.gossip_source); @@ -361,7 +377,7 @@ impl Node { let logger = Arc::clone(&listening_logger); let listening_addrs = listening_addresses.clone(); - let listeners = self.runtime.block_on(async move { + let listeners = async move { let mut bind_addrs = Vec::with_capacity(listening_addrs.len()); for listening_addr in &listening_addrs { @@ -400,7 +416,8 @@ impl Node { } Ok(listeners) - })?; + } + .await?; for listener in listeners { let logger = Arc::clone(&listening_logger); @@ -459,27 +476,32 @@ impl Node { interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); loop { tokio::select! { - _ = stop_connect.changed() => { - log_debug!( - connect_logger, - "Stopping reconnecting known peers." - ); - return; - } - _ = interval.tick() => { - let pm_peers = connect_pm - .list_peers() - .iter() - .map(|peer| peer.counterparty_node_id) - .collect::>(); - - for peer_info in connect_peer_store.list_peers().iter().filter(|info| !pm_peers.contains(&info.node_id)) { - let _ = connect_cm.do_connect_peer( - peer_info.node_id, - peer_info.address.clone(), - ).await; - } + _ = stop_connect.changed() => { + log_debug!( + connect_logger, + "Stopping reconnecting known peers." + ); + return; + } + _ = interval.tick() => { + let pm_peers = connect_pm + .list_peers() + .iter() + .map(|peer| peer.counterparty_node_id) + .collect::>(); + + for peer_info in connect_peer_store + .list_peers() + .await + .iter() + .filter(|info| !pm_peers.contains(&info.node_id)) + { + let _ = connect_cm.do_connect_peer( + peer_info.node_id, + peer_info.address.clone(), + ).await; } + } } } }); @@ -506,11 +528,11 @@ impl Node { log_debug!( bcast_logger, "Stopping broadcasting node announcements.", - ); + ); return; } _ = interval.tick() => { - let skip_broadcast = match bcast_node_metrics.read().expect("lock").latest_node_announcement_broadcast_timestamp { + let skip_broadcast = match bcast_node_metrics.read().await.latest_node_announcement_broadcast_timestamp { Some(latest_bcast_time_secs) => { // Skip if the time hasn't elapsed yet. let next_bcast_unix_time = SystemTime::UNIX_EPOCH + Duration::from_secs(latest_bcast_time_secs) + NODE_ANN_BCAST_INTERVAL; @@ -695,18 +717,27 @@ impl Node { }); } - log_info!(self.logger, "Startup complete."); - *is_running_lock = true; - Ok(()) + log_info!(self.logger, "Startup complete."); + Ok(()) + } + .await; + + if startup_res.is_err() { + *self.is_running.write().expect("lock") = false; + } + startup_res } /// Disconnects all peers, stops all running background tasks, and shuts down [`Node`]. /// /// After this returns most API methods will return [`Error::NotRunning`]. - pub fn stop(&self) -> Result<(), Error> { - let mut is_running_lock = self.is_running.write().expect("lock"); - if !*is_running_lock { - return Err(Error::NotRunning); + pub async fn stop(&self) -> Result<(), Error> { + { + let mut is_running_lock = self.is_running.write().expect("lock"); + if !*is_running_lock { + return Err(Error::NotRunning); + } + *is_running_lock = false; } log_info!(self.logger, "Shutting down LDK Node with node ID {}...", self.node_id()); @@ -727,14 +758,14 @@ impl Node { }); // Cancel cancellable background tasks - self.runtime.abort_cancellable_background_tasks(); + self.runtime.abort_cancellable_background_tasks().await; // Disconnect all peers. self.peer_manager.disconnect_all_peers(); log_debug!(self.logger, "Disconnected all network peers."); // Wait until non-cancellable background tasks (mod LDK's background processor) are done. - self.runtime.wait_on_background_tasks(); + self.runtime.wait_on_background_tasks().await; // Stop any runtime-dependant chain sources. self.chain_source.stop(); @@ -756,22 +787,21 @@ impl Node { }); // Finally, wait until background processing stopped, at least until a timeout is reached. - self.runtime.wait_on_background_processor_task(); + self.runtime.wait_on_background_processor_task().await; #[cfg(tokio_unstable)] self.runtime.log_metrics(); log_info!(self.logger, "Shutdown complete."); - *is_running_lock = false; Ok(()) } /// Returns the status of the [`Node`]. - pub fn status(&self) -> NodeStatus { + pub async fn status(&self) -> NodeStatus { let is_running = *self.is_running.read().expect("lock"); let network = self.config.network; let current_best_block = self.channel_manager.current_best_block().into(); - let locked_node_metrics = self.node_metrics.read().expect("lock"); + let locked_node_metrics = self.node_metrics.read().await; let latest_lightning_wallet_sync_timestamp = locked_node_metrics.latest_lightning_wallet_sync_timestamp; let latest_onchain_wallet_sync_timestamp = @@ -831,27 +861,21 @@ impl Node { /// Returns the next event in the event queue. /// - /// Will block the current thread until the next event is available. + /// Will asynchronously wait until the next event is available. /// /// **Note:** this will always return the same event until handling is confirmed via [`Node::event_handled`]. /// /// **Caution:** Users must handle events as quickly as possible to prevent a large event backlog, /// which can increase the memory footprint of [`Node`]. - pub fn wait_next_event(&self) -> Event { - let fut = self.event_queue.next_event_async(); - // We use our runtime for the sync variant to ensure `tokio::task::block_in_place` is - // always called if we'd ever hit this in an outer runtime context. - self.runtime.block_on(fut) + pub async fn wait_next_event(&self) -> Event { + self.event_queue.next_event_async().await } /// Confirm the last retrieved event handled. /// /// **Note:** This **MUST** be called after each event has been handled. - pub fn event_handled(&self) -> Result<(), Error> { - // We use our runtime for the sync variant to ensure `tokio::task::block_in_place` is - // always called if we'd ever hit this in an outer runtime context. - let fut = self.event_queue.event_handled(); - self.runtime.block_on(fut).map_err(|e| { + pub async fn event_handled(&self) -> Result<(), Error> { + self.event_queue.event_handled().await.map_err(|e| { log_error!( self.logger, "Couldn't mark event handled due to persistence failure: {}", @@ -1051,21 +1075,19 @@ impl Node { /// Authenticates the user via [LNURL-auth] for the given LNURL string. /// /// [LNURL-auth]: https://github.com/lnurl/luds/blob/luds/04.md - pub fn lnurl_auth(&self, lnurl: String) -> Result<(), Error> { + pub async fn lnurl_auth(&self, lnurl: String) -> Result<(), Error> { let auth = Arc::clone(&self.lnurl_auth); - self.runtime.block_on(async move { - let res = tokio::time::timeout( - Duration::from_secs(LNURL_AUTH_TIMEOUT_SECS), - auth.authenticate(&lnurl), - ) - .await; + let res = tokio::time::timeout( + Duration::from_secs(LNURL_AUTH_TIMEOUT_SECS), + auth.authenticate(&lnurl), + ) + .await; - match res { - Ok(Ok(())) => Ok(()), - Ok(Err(e)) => Err(e), - Err(_) => Err(Error::LnurlAuthTimeout), - } - }) + match res { + Ok(Ok(())) => Ok(()), + Ok(Err(e)) => Err(e), + Err(_) => Err(Error::LnurlAuthTimeout), + } } /// Returns a liquidity handler allowing to request channels via the [bLIP-51 / LSPS1] protocol. @@ -1074,7 +1096,6 @@ impl Node { #[cfg(not(feature = "uniffi"))] pub fn lsps1_liquidity(&self) -> LSPS1Liquidity { LSPS1Liquidity::new( - Arc::clone(&self.runtime), Arc::clone(&self.wallet), Arc::clone(&self.connection_manager), self.liquidity_source.clone(), @@ -1088,7 +1109,6 @@ impl Node { #[cfg(feature = "uniffi")] pub fn lsps1_liquidity(&self) -> Arc { Arc::new(LSPS1Liquidity::new( - Arc::clone(&self.runtime), Arc::clone(&self.wallet), Arc::clone(&self.connection_manager), self.liquidity_source.clone(), @@ -1104,7 +1124,7 @@ impl Node { /// Connect to a node on the peer-to-peer network. /// /// If `persist` is set to `true`, we'll remember the peer and reconnect to it on restart. - pub fn connect( + pub async fn connect( &self, node_id: PublicKey, address: SocketAddress, persist: bool, ) -> Result<(), Error> { if !*self.is_running.read().expect("lock") { @@ -1117,16 +1137,12 @@ impl Node { let con_addr = peer_info.address.clone(); let con_cm = Arc::clone(&self.connection_manager); - // We need to use our main runtime here as a local runtime might not be around to poll - // connection futures going forward. - self.runtime.block_on(async move { - con_cm.connect_peer_if_necessary(con_node_id, con_addr).await - })?; + con_cm.connect_peer_if_necessary(con_node_id, con_addr).await?; log_info!(self.logger, "Connected to peer {}@{}. ", peer_info.node_id, peer_info.address); if persist { - self.runtime.block_on(self.peer_store.add_peer(peer_info))?; + self.peer_store.add_peer(peer_info).await?; } Ok(()) @@ -1136,14 +1152,14 @@ impl Node { /// /// Will also remove the peer from the peer store, i.e., after this has been called we won't /// try to reconnect on restart. - pub fn disconnect(&self, counterparty_node_id: PublicKey) -> Result<(), Error> { + pub async fn disconnect(&self, counterparty_node_id: PublicKey) -> Result<(), Error> { if !*self.is_running.read().expect("lock") { return Err(Error::NotRunning); } log_info!(self.logger, "Disconnecting peer {}..", counterparty_node_id); - match self.runtime.block_on(self.peer_store.remove_peer(&counterparty_node_id)) { + match self.peer_store.remove_peer(&counterparty_node_id).await { Ok(()) => {}, Err(e) => { log_error!(self.logger, "Failed to remove peer {}: {}", counterparty_node_id, e) @@ -1154,7 +1170,7 @@ impl Node { Ok(()) } - fn open_channel_inner( + async fn open_channel_inner( &self, node_id: PublicKey, address: SocketAddress, channel_amount_sats: FundingAmount, push_to_counterparty_msat: Option, channel_config: Option, announce_for_forwarding: bool, disable_counterparty_reserve: bool, @@ -1169,11 +1185,7 @@ impl Node { let con_addr = peer_info.address.clone(); let con_cm = Arc::clone(&self.connection_manager); - // We need to use our main runtime here as a local runtime might not be around to poll - // connection futures going forward. - self.runtime.block_on(async move { - con_cm.connect_peer_if_necessary(con_node_id, con_addr).await - })?; + con_cm.connect_peer_if_necessary(con_node_id, con_addr).await?; let channel_amount_sats = match channel_amount_sats { FundingAmount::Exact { amount_sats } => { @@ -1260,7 +1272,7 @@ impl Node { zero_reserve_string, peer_info.node_id ); - self.runtime.block_on(self.peer_store.add_peer(peer_info))?; + self.peer_store.add_peer(peer_info).await?; Ok(UserChannelId(user_channel_id)) }, Err(e) => { @@ -1334,7 +1346,7 @@ impl Node { /// Returns a [`UserChannelId`] allowing to locally keep track of the channel. /// /// [`AnchorChannelsConfig::per_channel_reserve_sats`]: crate::config::AnchorChannelsConfig::per_channel_reserve_sats - pub fn open_channel( + pub async fn open_channel( &self, node_id: PublicKey, address: SocketAddress, channel_amount_sats: u64, push_to_counterparty_msat: Option, channel_config: Option, ) -> Result { @@ -1347,6 +1359,7 @@ impl Node { false, false, ) + .await } /// Connect to a node and open a new announced channel. @@ -1370,7 +1383,7 @@ impl Node { /// Returns a [`UserChannelId`] allowing to locally keep track of the channel. /// /// [`AnchorChannelsConfig::per_channel_reserve_sats`]: crate::config::AnchorChannelsConfig::per_channel_reserve_sats - pub fn open_announced_channel( + pub async fn open_announced_channel( &self, node_id: PublicKey, address: SocketAddress, channel_amount_sats: u64, push_to_counterparty_msat: Option, channel_config: Option, ) -> Result { @@ -1388,6 +1401,7 @@ impl Node { true, false, ) + .await } /// Connect to a node and open a new unannounced channel, using all available on-chain funds @@ -1404,7 +1418,7 @@ impl Node { /// Returns a [`UserChannelId`] allowing to locally keep track of the channel. /// /// [`AnchorChannelsConfig::per_channel_reserve_sats`]: crate::config::AnchorChannelsConfig::per_channel_reserve_sats - pub fn open_channel_with_all( + pub async fn open_channel_with_all( &self, node_id: PublicKey, address: SocketAddress, push_to_counterparty_msat: Option, channel_config: Option, ) -> Result { @@ -1417,6 +1431,7 @@ impl Node { false, false, ) + .await } /// Connect to a node and open a new announced channel, using all available on-chain funds @@ -1437,7 +1452,7 @@ impl Node { /// Returns a [`UserChannelId`] allowing to locally keep track of the channel. /// /// [`AnchorChannelsConfig::per_channel_reserve_sats`]: crate::config::AnchorChannelsConfig::per_channel_reserve_sats - pub fn open_announced_channel_with_all( + pub async fn open_announced_channel_with_all( &self, node_id: PublicKey, address: SocketAddress, push_to_counterparty_msat: Option, channel_config: Option, ) -> Result { @@ -1455,6 +1470,7 @@ impl Node { true, false, ) + .await } /// Connect to a node and open a new unannounced channel, in which the target node can @@ -1476,7 +1492,7 @@ impl Node { /// Returns a [`UserChannelId`] allowing to locally keep track of the channel. /// /// [`AnchorChannelsConfig::per_channel_reserve_sats`]: crate::config::AnchorChannelsConfig::per_channel_reserve_sats - pub fn open_0reserve_channel( + pub async fn open_0reserve_channel( &self, node_id: PublicKey, address: SocketAddress, channel_amount_sats: u64, push_to_counterparty_msat: Option, channel_config: Option, ) -> Result { @@ -1489,6 +1505,7 @@ impl Node { false, true, ) + .await } /// Connect to a node and open a new unannounced channel, using all available on-chain funds @@ -1505,7 +1522,7 @@ impl Node { /// entirely shifted to one side, therefore allowing to receive payments from the getgo. /// /// Returns a [`UserChannelId`] allowing to locally keep track of the channel. - pub fn open_0reserve_channel_with_all( + pub async fn open_0reserve_channel_with_all( &self, node_id: PublicKey, address: SocketAddress, push_to_counterparty_msat: Option, channel_config: Option, ) -> Result { @@ -1518,9 +1535,10 @@ impl Node { false, true, ) + .await } - fn splice_in_inner( + async fn splice_in_inner( &self, user_channel_id: &UserChannelId, counterparty_node_id: PublicKey, splice_amount_sats: FundingAmount, ) -> Result<(), Error> { @@ -1605,14 +1623,14 @@ impl Node { return Err(Error::ChannelSplicingFailed); } - let contribution = self - .runtime - .block_on(funding_template.splice_in( + let contribution = funding_template + .splice_in( Amount::from_sat(splice_amount_sats), min_feerate, max_feerate, Arc::clone(&self.wallet), - )) + ) + .await .map_err(|e| { log_error!(self.logger, "Failed to splice channel: {}", e); Error::ChannelSplicingFailed @@ -1650,7 +1668,7 @@ impl Node { /// /// This API is experimental. Currently, a splice-in will be marked as an outbound payment, but /// this classification may change in the future. - pub fn splice_in( + pub async fn splice_in( &self, user_channel_id: &UserChannelId, counterparty_node_id: PublicKey, splice_amount_sats: u64, ) -> Result<(), Error> { @@ -1659,6 +1677,7 @@ impl Node { counterparty_node_id, FundingAmount::Exact { amount_sats: splice_amount_sats }, ) + .await } /// Add all available on-chain funds into an existing channel. @@ -1674,10 +1693,10 @@ impl Node { /// /// This API is experimental. Currently, a splice-in will be marked as an outbound payment, but /// this classification may change in the future. - pub fn splice_in_with_all( + pub async fn splice_in_with_all( &self, user_channel_id: &UserChannelId, counterparty_node_id: PublicKey, ) -> Result<(), Error> { - self.splice_in_inner(user_channel_id, counterparty_node_id, FundingAmount::Max) + self.splice_in_inner(user_channel_id, counterparty_node_id, FundingAmount::Max).await } /// Remove funds from an existing channel, sending them to an on-chain address. @@ -1768,7 +1787,7 @@ impl Node { /// this method must be called manually to keep wallets in sync with the chain state. /// /// [`EsploraSyncConfig::background_sync_config`]: crate::config::EsploraSyncConfig::background_sync_config - pub fn sync_wallets(&self) -> Result<(), Error> { + pub async fn sync_wallets(&self) -> Result<(), Error> { if !*self.is_running.read().expect("lock") { return Err(Error::NotRunning); } @@ -1778,37 +1797,35 @@ impl Node { let sync_cman = Arc::clone(&self.channel_manager); let sync_cmon = Arc::clone(&self.chain_monitor); let sync_sweeper = Arc::clone(&self.output_sweeper); - self.runtime.block_on(async move { - if chain_source.is_transaction_based() { - chain_source.update_fee_rate_estimates().await?; - chain_source - .sync_lightning_wallet(sync_cman, sync_cmon, Arc::clone(&sync_sweeper)) - .await?; - chain_source.sync_onchain_wallet(sync_wallet).await?; - } else { - chain_source.update_fee_rate_estimates().await?; - chain_source - .poll_and_update_listeners( - sync_wallet, - sync_cman, - sync_cmon, - Arc::clone(&sync_sweeper), - ) - .await?; - } - let _ = sync_sweeper.regenerate_and_broadcast_spend_if_necessary().await; - Ok(()) - }) + if chain_source.is_transaction_based() { + chain_source.update_fee_rate_estimates().await?; + chain_source + .sync_lightning_wallet(sync_cman, sync_cmon, Arc::clone(&sync_sweeper)) + .await?; + chain_source.sync_onchain_wallet(sync_wallet).await?; + } else { + chain_source.update_fee_rate_estimates().await?; + chain_source + .poll_and_update_listeners( + sync_wallet, + sync_cman, + sync_cmon, + Arc::clone(&sync_sweeper), + ) + .await?; + } + let _ = sync_sweeper.regenerate_and_broadcast_spend_if_necessary().await; + Ok(()) } /// Close a previously opened channel. /// /// Will attempt to close a channel coopertively. If this fails, users might need to resort to /// [`Node::force_close_channel`]. - pub fn close_channel( + pub async fn close_channel( &self, user_channel_id: &UserChannelId, counterparty_node_id: PublicKey, ) -> Result<(), Error> { - self.close_channel_internal(user_channel_id, counterparty_node_id, false, None) + self.close_channel_internal(user_channel_id, counterparty_node_id, false, None).await } /// Force-close a previously opened channel. @@ -1824,14 +1841,14 @@ impl Node { /// for more information). /// /// [`AnchorChannelsConfig::trusted_peers_no_reserve`]: crate::config::AnchorChannelsConfig::trusted_peers_no_reserve - pub fn force_close_channel( + pub async fn force_close_channel( &self, user_channel_id: &UserChannelId, counterparty_node_id: PublicKey, reason: Option, ) -> Result<(), Error> { - self.close_channel_internal(user_channel_id, counterparty_node_id, true, reason) + self.close_channel_internal(user_channel_id, counterparty_node_id, true, reason).await } - fn close_channel_internal( + async fn close_channel_internal( &self, user_channel_id: &UserChannelId, counterparty_node_id: PublicKey, force: bool, force_close_reason: Option, ) -> Result<(), Error> { @@ -1866,7 +1883,7 @@ impl Node { // Check if this was the last open channel, if so, forget the peer. if open_channels.len() == 1 { - self.runtime.block_on(self.peer_store.remove_peer(&counterparty_node_id))?; + self.peer_store.remove_peer(&counterparty_node_id).await?; } } @@ -1898,13 +1915,13 @@ impl Node { /// Retrieve the details of a specific payment with the given id. /// /// Returns `Some` if the payment was known and `None` otherwise. - pub fn payment(&self, payment_id: &PaymentId) -> Option { - self.payment_store.get(payment_id) + pub async fn payment(&self, payment_id: &PaymentId) -> Option { + self.payment_store.get(payment_id).await } /// Remove the payment with the given id from the store. - pub fn remove_payment(&self, payment_id: &PaymentId) -> Result<(), Error> { - self.runtime.block_on(self.payment_store.remove(&payment_id)) + pub async fn remove_payment(&self, payment_id: &PaymentId) -> Result<(), Error> { + self.payment_store.remove(&payment_id).await } /// Retrieves an overview of all known balances. @@ -1978,19 +1995,19 @@ impl Node { /// # let node = builder.build(node_entropy.into()).unwrap(); /// node.list_payments_with_filter(|p| p.direction == PaymentDirection::Outbound); /// ``` - pub fn list_payments_with_filter bool>( + pub async fn list_payments_with_filter bool>( &self, f: F, ) -> Vec { - self.payment_store.list_filter(f) + self.payment_store.list_filter(f).await } /// Retrieves all payments. - pub fn list_payments(&self) -> Vec { - self.payment_store.list_filter(|_| true) + pub async fn list_payments(&self) -> Vec { + self.payment_store.list_filter(|_| true).await } /// Retrieves a list of known peers. - pub fn list_peers(&self) -> Vec { + pub async fn list_peers(&self) -> Vec { let mut peers = Vec::new(); // First add all connected peers, preferring to list the connected address if available. @@ -1998,7 +2015,7 @@ impl Node { let connected_peers_len = connected_peers.len(); for connected_peer in connected_peers { let node_id = connected_peer.counterparty_node_id; - let stored_peer = self.peer_store.get_peer(&node_id); + let stored_peer = self.peer_store.get_peer(&node_id).await; let stored_addr_opt = stored_peer.as_ref().map(|p| p.address.clone()); let address = match (connected_peer.socket_address, stored_addr_opt) { (Some(con_addr), _) => con_addr, @@ -2013,7 +2030,7 @@ impl Node { } // Now add all known-but-offline peers, too. - for p in self.peer_store.list_peers() { + for p in self.peer_store.list_peers().await { if peers.iter().take(connected_peers_len).any(|d| d.node_id == p.node_id) { continue; } @@ -2061,22 +2078,22 @@ impl Node { /// Exports the current state of the scorer. The result can be shared with and merged by light nodes that only have /// a limited view of the network. - pub fn export_pathfinding_scores(&self) -> Result, Error> { - self.runtime - .block_on(KVStore::read( - &*self.kv_store, - lightning::util::persist::SCORER_PERSISTENCE_PRIMARY_NAMESPACE, - lightning::util::persist::SCORER_PERSISTENCE_SECONDARY_NAMESPACE, - lightning::util::persist::SCORER_PERSISTENCE_KEY, - )) - .map_err(|e| { - log_error!( - self.logger, - "Failed to access store while exporting pathfinding scores: {}", - e - ); - Error::PersistenceFailed - }) + pub async fn export_pathfinding_scores(&self) -> Result, Error> { + KVStore::read( + &*self.kv_store, + lightning::util::persist::SCORER_PERSISTENCE_PRIMARY_NAMESPACE, + lightning::util::persist::SCORER_PERSISTENCE_SECONDARY_NAMESPACE, + lightning::util::persist::SCORER_PERSISTENCE_KEY, + ) + .await + .map_err(|e| { + log_error!( + self.logger, + "Failed to access store while exporting pathfinding scores: {}", + e + ); + Error::PersistenceFailed + }) } /// Return the features used in node announcement. @@ -2104,7 +2121,15 @@ impl Node { impl Drop for Node { fn drop(&mut self) { - let _ = self.stop(); + if let Ok(mut is_running_lock) = self.is_running.write() { + if *is_running_lock { + *is_running_lock = false; + let _ = self.stop_sender.send(()); + let _ = self.background_processor_stop_sender.send(()); + self.peer_manager.disconnect_all_peers(); + self.chain_source.stop(); + } + } } } @@ -2187,42 +2212,6 @@ impl Default for NodeMetrics { } } -pub(crate) struct PersistedNodeMetrics { - metrics: RwLock, - mutation_lock: tokio::sync::Mutex<()>, -} - -impl PersistedNodeMetrics { - pub(crate) fn new(metrics: NodeMetrics) -> Self { - Self { metrics: RwLock::new(metrics), mutation_lock: tokio::sync::Mutex::new(()) } - } - - /// Returns the current in-memory metrics. - /// - /// The async mutation lock serializes persistence updates, but this synchronous reader cannot - /// wait on it. Until metrics reads are async, callers may observe metrics changes that are - /// still being persisted. - pub(crate) fn read( - &self, - ) -> std::sync::LockResult> { - self.metrics.read() - } - - /// Returns the in-memory metrics write lock. - /// - /// Persistence updates should go through `update_and_persist_node_metrics` so writers are - /// serialized by the async mutation lock. - pub(crate) fn write( - &self, - ) -> std::sync::LockResult> { - self.metrics.write() - } - - pub(crate) async fn lock_mutation(&self) -> tokio::sync::MutexGuard<'_, ()> { - self.mutation_lock.lock().await - } -} - impl_writeable_tlv_based!(NodeMetrics, { (0, latest_lightning_wallet_sync_timestamp, option), (1, latest_pathfinding_scores_sync_timestamp, option), diff --git a/src/liquidity.rs b/src/liquidity.rs index 3cd6d110da..00f71d1a7f 100644 --- a/src/liquidity.rs +++ b/src/liquidity.rs @@ -44,7 +44,6 @@ use crate::connection::ConnectionManager; use crate::logger::{log_debug, log_error, log_info, LdkLogger, Logger}; use crate::payment::store::LSPS2Parameters; use crate::payment::PaymentMetadata; -use crate::runtime::Runtime; use crate::types::{ Broadcaster, ChannelManager, DynStore, KeysManager, LiquidityManager, PeerManager, Wallet, }; @@ -1510,7 +1509,6 @@ pub(crate) struct LSPS2BuyResponse { #[derive(Clone)] #[cfg_attr(feature = "uniffi", derive(uniffi::Object))] pub struct LSPS1Liquidity { - runtime: Arc, wallet: Arc, connection_manager: Arc>>, liquidity_source: Option>>>, @@ -1519,11 +1517,10 @@ pub struct LSPS1Liquidity { impl LSPS1Liquidity { pub(crate) fn new( - runtime: Arc, wallet: Arc, - connection_manager: Arc>>, + wallet: Arc, connection_manager: Arc>>, liquidity_source: Option>>>, logger: Arc, ) -> Self { - Self { runtime, wallet, connection_manager, liquidity_source, logger } + Self { wallet, connection_manager, liquidity_source, logger } } } @@ -1533,7 +1530,7 @@ impl LSPS1Liquidity { /// /// The channel will be opened after one of the returned payment options has successfully been /// paid. - pub fn request_channel( + pub async fn request_channel( &self, lsp_balance_sat: u64, client_balance_sat: u64, channel_expiry_blocks: u32, announce_channel: bool, ) -> Result { @@ -1547,34 +1544,30 @@ impl LSPS1Liquidity { let con_addr = lsp_address.clone(); let con_cm = Arc::clone(&self.connection_manager); - // We need to use our main runtime here as a local runtime might not be around to poll - // connection futures going forward. - self.runtime.block_on(async move { - con_cm.connect_peer_if_necessary(con_node_id, con_addr).await - })?; + con_cm.connect_peer_if_necessary(con_node_id, con_addr).await?; log_info!(self.logger, "Connected to LSP {}@{}. ", lsp_node_id, lsp_address); - let refund_address = self.wallet.get_new_address()?; + let refund_address = self.wallet.get_new_address().await?; let liquidity_source = Arc::clone(&liquidity_source); - let response = self.runtime.block_on(async move { - liquidity_source - .lsps1_request_channel( - lsp_balance_sat, - client_balance_sat, - channel_expiry_blocks, - announce_channel, - refund_address, - ) - .await - })?; + let response = liquidity_source + .lsps1_request_channel( + lsp_balance_sat, + client_balance_sat, + channel_expiry_blocks, + announce_channel, + refund_address, + ) + .await?; Ok(response) } /// Connects to the configured LSP and checks for the status of a previously-placed order. - pub fn check_order_status(&self, order_id: LSPS1OrderId) -> Result { + pub async fn check_order_status( + &self, order_id: LSPS1OrderId, + ) -> Result { let liquidity_source = self.liquidity_source.as_ref().ok_or(Error::LiquiditySourceUnavailable)?; @@ -1585,16 +1578,10 @@ impl LSPS1Liquidity { let con_addr = lsp_address.clone(); let con_cm = Arc::clone(&self.connection_manager); - // We need to use our main runtime here as a local runtime might not be around to poll - // connection futures going forward. - self.runtime.block_on(async move { - con_cm.connect_peer_if_necessary(con_node_id, con_addr).await - })?; + con_cm.connect_peer_if_necessary(con_node_id, con_addr).await?; let liquidity_source = Arc::clone(&liquidity_source); - let response = self - .runtime - .block_on(async move { liquidity_source.lsps1_check_order_status(order_id).await })?; + let response = liquidity_source.lsps1_check_order_status(order_id).await?; Ok(response) } } diff --git a/src/payment/bolt11.rs b/src/payment/bolt11.rs index 068269997f..a4c0eb510b 100644 --- a/src/payment/bolt11.rs +++ b/src/payment/bolt11.rs @@ -36,7 +36,6 @@ use crate::payment::store::{ PaymentStatus, }; use crate::peer_store::{PeerInfo, PeerStore}; -use crate::runtime::Runtime; use crate::types::{ChannelManager, PaymentStore}; #[cfg(not(feature = "uniffi"))] @@ -67,7 +66,6 @@ impl_writeable_tlv_based!(PaymentMetadata, { /// [`Node::bolt11_payment`]: crate::Node::bolt11_payment #[cfg_attr(feature = "uniffi", derive(uniffi::Object))] pub struct Bolt11Payment { - runtime: Arc, channel_manager: Arc, connection_manager: Arc>>, liquidity_source: Option>>>, @@ -80,14 +78,13 @@ pub struct Bolt11Payment { impl Bolt11Payment { pub(crate) fn new( - runtime: Arc, channel_manager: Arc, + _runtime: Arc, channel_manager: Arc, connection_manager: Arc>>, liquidity_source: Option>>>, payment_store: Arc, peer_store: Arc>>, config: Arc, is_running: Arc>, logger: Arc, ) -> Self { Self { - runtime, channel_manager, connection_manager, liquidity_source, @@ -99,7 +96,7 @@ impl Bolt11Payment { } } - pub(crate) fn receive_inner( + pub(crate) async fn receive_inner( &self, amount_msat: Option, invoice_description: &LdkBolt11InvoiceDescription, expiry_secs: u32, manual_claim_payment_hash: Option, ) -> Result { @@ -158,12 +155,12 @@ impl Bolt11Payment { PaymentDirection::Inbound, PaymentStatus::Pending, ); - self.runtime.block_on(self.payment_store.insert(payment))?; + self.payment_store.insert(payment).await?; Ok(invoice) } - fn receive_via_jit_channel_inner( + async fn receive_via_jit_channel_inner( &self, amount_msat: Option, description: &LdkBolt11InvoiceDescription, expiry_secs: u32, max_total_lsp_fee_limit_msat: Option, max_proportional_lsp_fee_limit_ppm_msat: Option, payment_hash: Option, @@ -180,16 +177,12 @@ impl Bolt11Payment { let con_addr = peer_info.address.clone(); let con_cm = Arc::clone(&self.connection_manager); - // We need to use our main runtime here as a local runtime might not be around to poll - // connection futures going forward. - self.runtime.block_on(async move { - con_cm.connect_peer_if_necessary(con_node_id, con_addr).await - })?; + con_cm.connect_peer_if_necessary(con_node_id, con_addr).await?; log_info!(self.logger, "Connected to LSP {}@{}. ", peer_info.node_id, peer_info.address); let liquidity_source = Arc::clone(&liquidity_source); - let invoice = self.runtime.block_on(async move { + let invoice = async move { if let Some(amount_msat) = amount_msat { liquidity_source .lsps2_receive_to_jit_channel( @@ -210,7 +203,8 @@ impl Bolt11Payment { ) .await } - })?; + } + .await?; // Register payment in payment store. let payment_hash = invoice.payment_hash(); @@ -239,10 +233,10 @@ impl Bolt11Payment { PaymentDirection::Inbound, PaymentStatus::Pending, ); - self.runtime.block_on(self.payment_store.insert(payment))?; + self.payment_store.insert(payment).await?; // Persist LSP peer to make sure we reconnect on restart. - self.runtime.block_on(self.peer_store.add_peer(peer_info))?; + self.peer_store.add_peer(peer_info).await?; Ok(invoice) } @@ -285,7 +279,7 @@ impl Bolt11Payment { /// /// If `route_parameters` are provided they will override the default as well as the /// node-wide parameters configured via [`Config::route_parameters`] on a per-field basis. - pub fn send( + pub async fn send( &self, invoice: &Bolt11Invoice, route_parameters: Option, ) -> Result { if !*self.is_running.read().expect("lock") { @@ -295,7 +289,7 @@ impl Bolt11Payment { let invoice = maybe_deref(invoice); let payment_hash = invoice.payment_hash(); let payment_id = PaymentId(invoice.payment_hash().0); - if let Some(payment) = self.payment_store.get(&payment_id) { + if let Some(payment) = self.payment_store.get(&payment_id).await { if payment.status == PaymentStatus::Pending || payment.status == PaymentStatus::Succeeded { @@ -341,7 +335,7 @@ impl Bolt11Payment { PaymentStatus::Pending, ); - self.runtime.block_on(self.payment_store.insert(payment))?; + self.payment_store.insert(payment).await?; Ok(payment_id) }, @@ -371,7 +365,7 @@ impl Bolt11Payment { PaymentStatus::Failed, ); - self.runtime.block_on(self.payment_store.insert(payment))?; + self.payment_store.insert(payment).await?; Err(Error::PaymentSendingFailed) }, } @@ -388,7 +382,7 @@ impl Bolt11Payment { /// /// If `route_parameters` are provided they will override the default as well as the /// node-wide parameters configured via [`Config::route_parameters`] on a per-field basis. - pub fn send_using_amount( + pub async fn send_using_amount( &self, invoice: &Bolt11Invoice, amount_msat: u64, route_parameters: Option, ) -> Result { @@ -408,7 +402,7 @@ impl Bolt11Payment { let payment_hash = invoice.payment_hash(); let payment_id = PaymentId(invoice.payment_hash().0); - if let Some(payment) = self.payment_store.get(&payment_id) { + if let Some(payment) = self.payment_store.get(&payment_id).await { if payment.status == PaymentStatus::Pending || payment.status == PaymentStatus::Succeeded { @@ -457,7 +451,7 @@ impl Bolt11Payment { PaymentDirection::Outbound, PaymentStatus::Pending, ); - self.runtime.block_on(self.payment_store.insert(payment))?; + self.payment_store.insert(payment).await?; Ok(payment_id) }, @@ -488,7 +482,7 @@ impl Bolt11Payment { PaymentStatus::Failed, ); - self.runtime.block_on(self.payment_store.insert(payment))?; + self.payment_store.insert(payment).await?; Err(Error::PaymentSendingFailed) }, } @@ -512,7 +506,7 @@ impl Bolt11Payment { /// [`receive_variable_amount_for_hash`]: Self::receive_variable_amount_for_hash /// [`PaymentClaimable`]: crate::Event::PaymentClaimable /// [`PaymentReceived`]: crate::Event::PaymentReceived - pub fn claim_for_hash( + pub async fn claim_for_hash( &self, payment_hash: PaymentHash, claimable_amount_msat: u64, preimage: PaymentPreimage, ) -> Result<(), Error> { let payment_id = PaymentId(payment_hash.0); @@ -528,7 +522,7 @@ impl Bolt11Payment { return Err(Error::InvalidPaymentPreimage); } - if let Some(details) = self.payment_store.get(&payment_id) { + if let Some(details) = self.payment_store.get(&payment_id).await { // For payments requested via `receive*_via_jit_channel_for_hash()` // `skimmed_fee_msat` held by LSP must be taken into account. let skimmed_fee_msat = match details.kind { @@ -574,7 +568,7 @@ impl Bolt11Payment { /// [`receive_for_hash`]: Self::receive_for_hash /// [`receive_variable_amount_for_hash`]: Self::receive_variable_amount_for_hash /// [`PaymentClaimable`]: crate::Event::PaymentClaimable - pub fn fail_for_hash(&self, payment_hash: PaymentHash) -> Result<(), Error> { + pub async fn fail_for_hash(&self, payment_hash: PaymentHash) -> Result<(), Error> { let payment_id = PaymentId(payment_hash.0); let update = PaymentDetailsUpdate { @@ -582,7 +576,7 @@ impl Bolt11Payment { ..PaymentDetailsUpdate::new(payment_id) }; - match self.runtime.block_on(self.payment_store.update(update)) { + match self.payment_store.update(update).await { Ok(DataStoreUpdateResult::Updated) | Ok(DataStoreUpdateResult::Unchanged) => (), Ok(DataStoreUpdateResult::NotFound) => { log_error!( @@ -611,11 +605,12 @@ impl Bolt11Payment { /// given. /// /// The inbound payment will be automatically claimed upon arrival. - pub fn receive( + pub async fn receive( &self, amount_msat: u64, description: &Bolt11InvoiceDescription, expiry_secs: u32, ) -> Result { let description = maybe_try_convert_enum(description)?; - let invoice = self.receive_inner(Some(amount_msat), &description, expiry_secs, None)?; + let invoice = + self.receive_inner(Some(amount_msat), &description, expiry_secs, None).await?; Ok(maybe_wrap(invoice)) } @@ -633,13 +628,14 @@ impl Bolt11Payment { /// [`PaymentClaimable`]: crate::Event::PaymentClaimable /// [`claim_for_hash`]: Self::claim_for_hash /// [`fail_for_hash`]: Self::fail_for_hash - pub fn receive_for_hash( + pub async fn receive_for_hash( &self, amount_msat: u64, description: &Bolt11InvoiceDescription, expiry_secs: u32, payment_hash: PaymentHash, ) -> Result { let description = maybe_try_convert_enum(description)?; - let invoice = - self.receive_inner(Some(amount_msat), &description, expiry_secs, Some(payment_hash))?; + let invoice = self + .receive_inner(Some(amount_msat), &description, expiry_secs, Some(payment_hash)) + .await?; Ok(maybe_wrap(invoice)) } @@ -647,11 +643,11 @@ impl Bolt11Payment { /// amount is to be determined by the user, also known as a "zero-amount" invoice. /// /// The inbound payment will be automatically claimed upon arrival. - pub fn receive_variable_amount( + pub async fn receive_variable_amount( &self, description: &Bolt11InvoiceDescription, expiry_secs: u32, ) -> Result { let description = maybe_try_convert_enum(description)?; - let invoice = self.receive_inner(None, &description, expiry_secs, None)?; + let invoice = self.receive_inner(None, &description, expiry_secs, None).await?; Ok(maybe_wrap(invoice)) } @@ -669,11 +665,12 @@ impl Bolt11Payment { /// [`PaymentClaimable`]: crate::Event::PaymentClaimable /// [`claim_for_hash`]: Self::claim_for_hash /// [`fail_for_hash`]: Self::fail_for_hash - pub fn receive_variable_amount_for_hash( + pub async fn receive_variable_amount_for_hash( &self, description: &Bolt11InvoiceDescription, expiry_secs: u32, payment_hash: PaymentHash, ) -> Result { let description = maybe_try_convert_enum(description)?; - let invoice = self.receive_inner(None, &description, expiry_secs, Some(payment_hash))?; + let invoice = + self.receive_inner(None, &description, expiry_secs, Some(payment_hash)).await?; Ok(maybe_wrap(invoice)) } @@ -687,19 +684,21 @@ impl Bolt11Payment { /// channel to us. We'll use its cheapest offer otherwise. /// /// [LSPS2]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md - pub fn receive_via_jit_channel( + pub async fn receive_via_jit_channel( &self, amount_msat: u64, description: &Bolt11InvoiceDescription, expiry_secs: u32, max_total_lsp_fee_limit_msat: Option, ) -> Result { let description = maybe_try_convert_enum(description)?; - let invoice = self.receive_via_jit_channel_inner( - Some(amount_msat), - &description, - expiry_secs, - max_total_lsp_fee_limit_msat, - None, - None, - )?; + let invoice = self + .receive_via_jit_channel_inner( + Some(amount_msat), + &description, + expiry_secs, + max_total_lsp_fee_limit_msat, + None, + None, + ) + .await?; Ok(maybe_wrap(invoice)) } @@ -726,19 +725,21 @@ impl Bolt11Payment { /// [`claim_for_hash`]: Self::claim_for_hash /// [`fail_for_hash`]: Self::fail_for_hash /// [`counterparty_skimmed_fee_msat`]: crate::payment::PaymentKind::Bolt11::counterparty_skimmed_fee_msat - pub fn receive_via_jit_channel_for_hash( + pub async fn receive_via_jit_channel_for_hash( &self, amount_msat: u64, description: &Bolt11InvoiceDescription, expiry_secs: u32, max_total_lsp_fee_limit_msat: Option, payment_hash: PaymentHash, ) -> Result { let description = maybe_try_convert_enum(description)?; - let invoice = self.receive_via_jit_channel_inner( - Some(amount_msat), - &description, - expiry_secs, - max_total_lsp_fee_limit_msat, - None, - Some(payment_hash), - )?; + let invoice = self + .receive_via_jit_channel_inner( + Some(amount_msat), + &description, + expiry_secs, + max_total_lsp_fee_limit_msat, + None, + Some(payment_hash), + ) + .await?; Ok(maybe_wrap(invoice)) } @@ -753,19 +754,21 @@ impl Bolt11Payment { /// We'll use its cheapest offer otherwise. /// /// [LSPS2]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md - pub fn receive_variable_amount_via_jit_channel( + pub async fn receive_variable_amount_via_jit_channel( &self, description: &Bolt11InvoiceDescription, expiry_secs: u32, max_proportional_lsp_fee_limit_ppm_msat: Option, ) -> Result { let description = maybe_try_convert_enum(description)?; - let invoice = self.receive_via_jit_channel_inner( - None, - &description, - expiry_secs, - None, - max_proportional_lsp_fee_limit_ppm_msat, - None, - )?; + let invoice = self + .receive_via_jit_channel_inner( + None, + &description, + expiry_secs, + None, + max_proportional_lsp_fee_limit_ppm_msat, + None, + ) + .await?; Ok(maybe_wrap(invoice)) } @@ -793,19 +796,21 @@ impl Bolt11Payment { /// [`claim_for_hash`]: Self::claim_for_hash /// [`fail_for_hash`]: Self::fail_for_hash /// [`counterparty_skimmed_fee_msat`]: crate::payment::PaymentKind::Bolt11::counterparty_skimmed_fee_msat - pub fn receive_variable_amount_via_jit_channel_for_hash( + pub async fn receive_variable_amount_via_jit_channel_for_hash( &self, description: &Bolt11InvoiceDescription, expiry_secs: u32, max_proportional_lsp_fee_limit_ppm_msat: Option, payment_hash: PaymentHash, ) -> Result { let description = maybe_try_convert_enum(description)?; - let invoice = self.receive_via_jit_channel_inner( - None, - &description, - expiry_secs, - None, - max_proportional_lsp_fee_limit_ppm_msat, - Some(payment_hash), - )?; + let invoice = self + .receive_via_jit_channel_inner( + None, + &description, + expiry_secs, + None, + max_proportional_lsp_fee_limit_ppm_msat, + Some(payment_hash), + ) + .await?; Ok(maybe_wrap(invoice)) } diff --git a/src/payment/bolt12.rs b/src/payment/bolt12.rs index d79aca6c24..6658a18c30 100644 --- a/src/payment/bolt12.rs +++ b/src/payment/bolt12.rs @@ -29,7 +29,6 @@ use crate::error::Error; use crate::ffi::{maybe_deref, maybe_wrap}; use crate::logger::{log_error, log_info, LdkLogger, Logger}; use crate::payment::store::{PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus}; -use crate::runtime::Runtime; use crate::types::{ChannelManager, KeysManager, PaymentStore}; #[cfg(not(feature = "uniffi"))] @@ -60,7 +59,6 @@ type HumanReadableName = Arc; /// [`Node::bolt12_payment`]: crate::Node::bolt12_payment #[cfg_attr(feature = "uniffi", derive(uniffi::Object))] pub struct Bolt12Payment { - runtime: Arc, channel_manager: Arc, keys_manager: Arc, payment_store: Arc, @@ -72,13 +70,12 @@ pub struct Bolt12Payment { impl Bolt12Payment { pub(crate) fn new( - runtime: Arc, channel_manager: Arc, + _runtime: Arc, channel_manager: Arc, keys_manager: Arc, payment_store: Arc, config: Arc, is_running: Arc>, logger: Arc, async_payments_role: Option, ) -> Self { Self { - runtime, channel_manager, keys_manager, payment_store, @@ -89,7 +86,7 @@ impl Bolt12Payment { } } - pub(crate) fn send_using_amount_inner( + pub(crate) async fn send_using_amount_inner( &self, offer: &Offer, amount_msat: u64, quantity: Option, payer_note: Option, route_parameters: Option, hrn: Option, ) -> Result { @@ -167,7 +164,7 @@ impl Bolt12Payment { PaymentDirection::Outbound, PaymentStatus::Pending, ); - self.runtime.block_on(self.payment_store.insert(payment))?; + self.payment_store.insert(payment).await?; Ok(payment_id) }, @@ -192,7 +189,7 @@ impl Bolt12Payment { PaymentDirection::Outbound, PaymentStatus::Failed, ); - self.runtime.block_on(self.payment_store.insert(payment))?; + self.payment_store.insert(payment).await?; Err(Error::PaymentSendingFailed) }, } @@ -264,7 +261,7 @@ impl Bolt12Payment { /// /// If `route_parameters` are provided they will override the default as well as the /// node-wide parameters configured via [`Config::route_parameters`] on a per-field basis. - pub fn send( + pub async fn send( &self, offer: &Offer, quantity: Option, payer_note: Option, route_parameters: Option, ) -> Result { @@ -329,7 +326,7 @@ impl Bolt12Payment { PaymentDirection::Outbound, PaymentStatus::Pending, ); - self.runtime.block_on(self.payment_store.insert(payment))?; + self.payment_store.insert(payment).await?; Ok(payment_id) }, @@ -354,7 +351,7 @@ impl Bolt12Payment { PaymentDirection::Outbound, PaymentStatus::Failed, ); - self.runtime.block_on(self.payment_store.insert(payment))?; + self.payment_store.insert(payment).await?; Err(Error::InvoiceRequestCreationFailed) }, } @@ -374,18 +371,20 @@ impl Bolt12Payment { /// /// If `route_parameters` are provided they will override the default as well as the /// node-wide parameters configured via [`Config::route_parameters`] on a per-field basis. - pub fn send_using_amount( + pub async fn send_using_amount( &self, offer: &Offer, amount_msat: u64, quantity: Option, payer_note: Option, route_parameters: Option, ) -> Result { - let payment_id = self.send_using_amount_inner( - offer, - amount_msat, - quantity, - payer_note, - route_parameters, - None, - )?; + let payment_id = self + .send_using_amount_inner( + offer, + amount_msat, + quantity, + payer_note, + route_parameters, + None, + ) + .await?; Ok(payment_id) } @@ -430,7 +429,7 @@ impl Bolt12Payment { /// /// [`Refund`]: lightning::offers::refund::Refund /// [`Bolt12Invoice`]: lightning::offers::invoice::Bolt12Invoice - pub fn request_refund_payment(&self, refund: &Refund) -> Result { + pub async fn request_refund_payment(&self, refund: &Refund) -> Result { if !*self.is_running.read().expect("lock") { return Err(Error::NotRunning); } @@ -461,7 +460,7 @@ impl Bolt12Payment { PaymentStatus::Pending, ); - self.runtime.block_on(self.payment_store.insert(payment))?; + self.payment_store.insert(payment).await?; Ok(maybe_wrap(invoice)) } @@ -472,7 +471,7 @@ impl Bolt12Payment { /// node-wide parameters configured via [`Config::route_parameters`] on a per-field basis. /// /// [`Refund`]: lightning::offers::refund::Refund - pub fn initiate_refund( + pub async fn initiate_refund( &self, amount_msat: u64, expiry_secs: u32, quantity: Option, payer_note: Option, route_parameters: Option, ) -> Result { @@ -530,7 +529,7 @@ impl Bolt12Payment { PaymentStatus::Pending, ); - self.runtime.block_on(self.payment_store.insert(payment))?; + self.payment_store.insert(payment).await?; Ok(maybe_wrap(refund)) } diff --git a/src/payment/onchain.rs b/src/payment/onchain.rs index 9d00968fcc..93a12754fd 100644 --- a/src/payment/onchain.rs +++ b/src/payment/onchain.rs @@ -62,8 +62,8 @@ impl OnchainPayment { #[cfg_attr(feature = "uniffi", uniffi::export)] impl OnchainPayment { /// Retrieve a new on-chain/funding address. - pub fn new_address(&self) -> Result { - let funding_address = self.wallet.get_new_address()?; + pub async fn new_address(&self) -> Result { + let funding_address = self.wallet.get_new_address().await?; log_info!(self.logger, "Generated new funding address: {}", funding_address); Ok(funding_address) } @@ -77,7 +77,7 @@ impl OnchainPayment { /// a reasonable estimate from the configured chain source. /// /// [`BalanceDetails::total_anchor_channels_reserve_sats`]: crate::BalanceDetails::total_anchor_channels_reserve_sats - pub fn send_to_address( + pub async fn send_to_address( &self, address: &bitcoin::Address, amount_sats: u64, fee_rate: Option, ) -> Result { if !*self.is_running.read().expect("lock") { @@ -89,7 +89,7 @@ impl OnchainPayment { let send_amount = OnchainSendAmount::ExactRetainingReserve { amount_sats, cur_anchor_reserve_sats }; let fee_rate_opt = maybe_map_fee_rate_opt!(fee_rate); - self.wallet.send_to_address(address, send_amount, fee_rate_opt) + self.wallet.send_to_address(address, send_amount, fee_rate_opt).await } /// Send an on-chain payment to the given address, draining the available funds. @@ -107,7 +107,7 @@ impl OnchainPayment { /// we'll retrieve an estimate from the configured chain source. /// /// [`BalanceDetails::spendable_onchain_balance_sats`]: crate::balance::BalanceDetails::spendable_onchain_balance_sats - pub fn send_all_to_address( + pub async fn send_all_to_address( &self, address: &bitcoin::Address, retain_reserves: bool, fee_rate: Option, ) -> Result { if !*self.is_running.read().expect("lock") { @@ -123,7 +123,7 @@ impl OnchainPayment { }; let fee_rate_opt = maybe_map_fee_rate_opt!(fee_rate); - self.wallet.send_to_address(address, send_amount, fee_rate_opt) + self.wallet.send_to_address(address, send_amount, fee_rate_opt).await } /// Attempt to bump the fee of an unconfirmed transaction using Replace-by-Fee (RBF). @@ -135,10 +135,10 @@ impl OnchainPayment { /// higher fee, resulting in faster confirmation potential. /// /// Returns the [`Txid`] of the new replacement transaction if successful. - pub fn bump_fee_rbf( + pub async fn bump_fee_rbf( &self, payment_id: PaymentId, fee_rate: Option, ) -> Result { let fee_rate_opt = maybe_map_fee_rate_opt!(fee_rate); - self.wallet.bump_fee_rbf(payment_id, fee_rate_opt) + self.wallet.bump_fee_rbf(payment_id, fee_rate_opt).await } } diff --git a/src/payment/spontaneous.rs b/src/payment/spontaneous.rs index 45dab644d4..3e62ac591e 100644 --- a/src/payment/spontaneous.rs +++ b/src/payment/spontaneous.rs @@ -22,7 +22,6 @@ use crate::config::{Config, LDK_PAYMENT_RETRY_TIMEOUT}; use crate::error::Error; use crate::logger::{log_error, log_info, LdkLogger, Logger}; use crate::payment::store::{PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus}; -use crate::runtime::Runtime; use crate::types::{ChannelManager, CustomTlvRecord, KeysManager, PaymentStore}; // The default `final_cltv_expiry_delta` we apply when not set. @@ -35,7 +34,6 @@ const LDK_DEFAULT_FINAL_CLTV_EXPIRY_DELTA: u32 = 144; /// [`Node::spontaneous_payment`]: crate::Node::spontaneous_payment #[cfg_attr(feature = "uniffi", derive(uniffi::Object))] pub struct SpontaneousPayment { - runtime: Arc, channel_manager: Arc, keys_manager: Arc, payment_store: Arc, @@ -46,14 +44,14 @@ pub struct SpontaneousPayment { impl SpontaneousPayment { pub(crate) fn new( - runtime: Arc, channel_manager: Arc, + _runtime: Arc, channel_manager: Arc, keys_manager: Arc, payment_store: Arc, config: Arc, is_running: Arc>, logger: Arc, ) -> Self { - Self { runtime, channel_manager, keys_manager, payment_store, config, is_running, logger } + Self { channel_manager, keys_manager, payment_store, config, is_running, logger } } - fn send_inner( + async fn send_inner( &self, amount_msat: u64, node_id: PublicKey, route_parameters: Option, custom_tlvs: Option>, preimage: Option, @@ -68,7 +66,7 @@ impl SpontaneousPayment { let payment_hash = PaymentHash::from(payment_preimage); let payment_id = PaymentId(payment_hash.0); - if let Some(payment) = self.payment_store.get(&payment_id) { + if let Some(payment) = self.payment_store.get(&payment_id).await { if payment.status == PaymentStatus::Pending || payment.status == PaymentStatus::Succeeded { @@ -132,7 +130,7 @@ impl SpontaneousPayment { PaymentDirection::Outbound, PaymentStatus::Pending, ); - self.runtime.block_on(self.payment_store.insert(payment))?; + self.payment_store.insert(payment).await?; Ok(payment_id) }, @@ -155,7 +153,7 @@ impl SpontaneousPayment { PaymentStatus::Failed, ); - self.runtime.block_on(self.payment_store.insert(payment))?; + self.payment_store.insert(payment).await?; Err(Error::PaymentSendingFailed) }, } @@ -170,35 +168,36 @@ impl SpontaneousPayment { /// /// If `route_parameters` are provided they will override the default as well as the /// node-wide parameters configured via [`Config::route_parameters`] on a per-field basis. - pub fn send( + pub async fn send( &self, amount_msat: u64, node_id: PublicKey, route_parameters: Option, ) -> Result { - self.send_inner(amount_msat, node_id, route_parameters, None, None) + self.send_inner(amount_msat, node_id, route_parameters, None, None).await } /// Send a spontaneous payment including a list of custom TLVs. - pub fn send_with_custom_tlvs( + pub async fn send_with_custom_tlvs( &self, amount_msat: u64, node_id: PublicKey, route_parameters: Option, custom_tlvs: Vec, ) -> Result { - self.send_inner(amount_msat, node_id, route_parameters, Some(custom_tlvs), None) + self.send_inner(amount_msat, node_id, route_parameters, Some(custom_tlvs), None).await } /// Send a spontaneous payment with custom preimage - pub fn send_with_preimage( + pub async fn send_with_preimage( &self, amount_msat: u64, node_id: PublicKey, preimage: PaymentPreimage, route_parameters: Option, ) -> Result { - self.send_inner(amount_msat, node_id, route_parameters, None, Some(preimage)) + self.send_inner(amount_msat, node_id, route_parameters, None, Some(preimage)).await } /// Send a spontaneous payment with custom preimage including a list of custom TLVs. - pub fn send_with_preimage_and_custom_tlvs( + pub async fn send_with_preimage_and_custom_tlvs( &self, amount_msat: u64, node_id: PublicKey, custom_tlvs: Vec, preimage: PaymentPreimage, route_parameters: Option, ) -> Result { self.send_inner(amount_msat, node_id, route_parameters, Some(custom_tlvs), Some(preimage)) + .await } /// Sends payment probes over all paths of a route that would be used to pay the given diff --git a/src/payment/unified.rs b/src/payment/unified.rs index 3708afe8e6..73a70a569c 100644 --- a/src/payment/unified.rs +++ b/src/payment/unified.rs @@ -126,10 +126,10 @@ impl UnifiedPayment { /// /// [BOLT 11]: https://github.com/lightning/bolts/blob/master/11-payment-encoding.md /// [BOLT 12]: https://github.com/lightning/bolts/blob/master/12-offer-encoding.md - pub fn receive( + pub async fn receive( &self, amount_sats: u64, description: &str, expiry_sec: u32, ) -> Result { - let onchain_address = self.onchain_payment.new_address()?; + let onchain_address = self.onchain_payment.new_address().await?; let amount_msats = amount_sats * 1_000; @@ -145,12 +145,11 @@ impl UnifiedPayment { let invoice_description = Bolt11InvoiceDescription::Direct( Description::new(description.to_string()).map_err(|_| Error::InvoiceCreationFailed)?, ); - let bolt11_invoice = match self.bolt11_invoice.receive_inner( - Some(amount_msats), - &invoice_description, - expiry_sec, - None, - ) { + let bolt11_invoice = match self + .bolt11_invoice + .receive_inner(Some(amount_msats), &invoice_description, expiry_sec, None) + .await + { Ok(invoice) => Some(invoice), Err(e) => { log_error!(self.logger, "Failed to create invoice {}", e); @@ -286,15 +285,15 @@ impl UnifiedPayment { }; let payment_result = if let Ok(hrn) = HumanReadableName::from_encoded(uri_str) { - let hrn = maybe_wrap(hrn.clone()); - self.bolt12_payment.send_using_amount_inner(&offer, amount_msat.unwrap_or(0), None, None, route_parameters, Some(hrn)) - } else if let Some(amount_msat) = amount_msat { - self.bolt12_payment.send_using_amount(&offer, amount_msat, None, None, route_parameters) - } else { - self.bolt12_payment.send(&offer, None, None, route_parameters) - } - .map_err(|e| { - log_error!(self.logger, "Failed to send BOLT12 offer: {:?}. This is part of a unified payment. Falling back to the BOLT11 invoice.", e); + let hrn = maybe_wrap(hrn.clone()); + self.bolt12_payment.send_using_amount_inner(&offer, amount_msat.unwrap_or(0), None, None, route_parameters, Some(hrn)).await + } else if let Some(amount_msat) = amount_msat { + self.bolt12_payment.send_using_amount(&offer, amount_msat, None, None, route_parameters).await + } else { + self.bolt12_payment.send(&offer, None, None, route_parameters).await + } + .map_err(|e| { + log_error!(self.logger, "Failed to send BOLT12 offer: {:?}. This is part of a unified payment. Falling back to the BOLT11 invoice.", e); e }); @@ -304,11 +303,11 @@ impl UnifiedPayment { }, PaymentMethod::LightningBolt11(invoice) => { let invoice = maybe_wrap(invoice.clone()); - let payment_result = self.bolt11_invoice.send(&invoice, route_parameters) - .map_err(|e| { - log_error!(self.logger, "Failed to send BOLT11 invoice: {:?}. This is part of a unified payment. Falling back to the on-chain transaction.", e); - e - }); + let payment_result = + self.bolt11_invoice.send(&invoice, route_parameters).await.map_err(|e| { + log_error!(self.logger, "Failed to send BOLT11 invoice: {:?}. This is part of a unified payment. Falling back to the on-chain transaction.", e); + e + }); if let Ok(payment_id) = payment_result { return Ok(UnifiedPaymentResult::Bolt11 { payment_id }); @@ -328,7 +327,8 @@ impl UnifiedPayment { Error::InvalidAmount })?; - let txid = self.onchain_payment.send_to_address(&address, amt_sats, None)?; + let txid = + self.onchain_payment.send_to_address(&address, amt_sats, None).await?; return Ok(UnifiedPaymentResult::Onchain { txid }); }, } diff --git a/src/peer_store.rs b/src/peer_store.rs index 8037f93471..cccf2d3b50 100644 --- a/src/peer_store.rs +++ b/src/peer_store.rs @@ -7,12 +7,13 @@ use std::collections::HashMap; use std::ops::Deref; -use std::sync::{Arc, RwLock}; +use std::sync::Arc; use bitcoin::secp256k1::PublicKey; use lightning::impl_writeable_tlv_based; use lightning::util::persist::KVStore; use lightning::util::ser::{Readable, ReadableArgs, Writeable, Writer}; +use tokio::sync::RwLock; use crate::io::{ PEER_INFO_PERSISTENCE_KEY, PEER_INFO_PERSISTENCE_PRIMARY_NAMESPACE, @@ -27,7 +28,6 @@ where L::Target: LdkLogger, { peers: RwLock>, - mutation_lock: tokio::sync::Mutex<()>, kv_store: Arc, logger: L, } @@ -38,52 +38,37 @@ where { pub(crate) fn new(kv_store: Arc, logger: L) -> Self { let peers = RwLock::new(HashMap::new()); - let mutation_lock = tokio::sync::Mutex::new(()); - Self { peers, mutation_lock, kv_store, logger } + Self { peers, kv_store, logger } } pub(crate) async fn add_peer(&self, peer_info: PeerInfo) -> Result<(), Error> { - let _guard = self.mutation_lock.lock().await; - let data = { - let mut locked_peers = self.peers.write().expect("lock"); - if locked_peers.contains_key(&peer_info.node_id) { - return Ok(()); - } - locked_peers.insert(peer_info.node_id, peer_info); - PeerStoreSerWrapper(&locked_peers).encode() - }; - self.persist_peers(data).await + let mut locked_peers = self.peers.write().await; + if locked_peers.contains_key(&peer_info.node_id) { + return Ok(()); + } + + locked_peers.insert(peer_info.node_id, peer_info); + self.persist_peers(&*locked_peers).await } pub(crate) async fn remove_peer(&self, node_id: &PublicKey) -> Result<(), Error> { - let _guard = self.mutation_lock.lock().await; - let data = { - let mut locked_peers = self.peers.write().expect("lock"); - locked_peers.remove(node_id); - PeerStoreSerWrapper(&locked_peers).encode() - }; - self.persist_peers(data).await + let mut locked_peers = self.peers.write().await; + locked_peers.remove(node_id); + self.persist_peers(&*locked_peers).await } - /// Returns the current in-memory peer set. - /// - /// The async mutation lock serializes `add_peer` and `remove_peer`, but this synchronous - /// reader cannot wait on it. Until peer-store reads are async, callers may observe peer - /// changes that are still being persisted. - pub(crate) fn list_peers(&self) -> Vec { - self.peers.read().expect("lock").values().cloned().collect() + pub(crate) async fn list_peers(&self) -> Vec { + self.peers.read().await.values().cloned().collect() } - /// Returns the current in-memory peer info for `node_id`. - /// - /// The async mutation lock serializes `add_peer` and `remove_peer`, but this synchronous - /// reader cannot wait on it. Until peer-store reads are async, callers may observe peer - /// changes that are still being persisted. - pub(crate) fn get_peer(&self, node_id: &PublicKey) -> Option { - self.peers.read().expect("lock").get(node_id).cloned() + pub(crate) async fn get_peer(&self, node_id: &PublicKey) -> Option { + self.peers.read().await.get(node_id).cloned() } - async fn persist_peers(&self, data: Vec) -> Result<(), Error> { + async fn persist_peers( + &self, locked_peers: &HashMap, + ) -> Result<(), Error> { + let data = PeerStoreSerWrapper(&*locked_peers).encode(); KVStore::write( &*self.kv_store, PEER_INFO_PERSISTENCE_PRIMARY_NAMESPACE, @@ -118,8 +103,7 @@ where let (kv_store, logger) = args; let read_peers: PeerStoreDeserWrapper = Readable::read(reader)?; let peers: RwLock> = RwLock::new(read_peers.0); - let mutation_lock = tokio::sync::Mutex::new(()); - Ok(Self { peers, mutation_lock, kv_store, logger }) + Ok(Self { peers, kv_store, logger }) } } @@ -210,9 +194,9 @@ mod tests { let deser_peer_store = PeerStore::read(&mut &persisted_bytes[..], (Arc::clone(&store), logger)).unwrap(); - let peers = deser_peer_store.list_peers(); + let peers = deser_peer_store.list_peers().await; assert_eq!(peers.len(), 1); assert_eq!(peers[0], expected_peer_info); - assert_eq!(deser_peer_store.get_peer(&node_id), Some(expected_peer_info)); + assert_eq!(deser_peer_store.get_peer(&node_id).await, Some(expected_peer_info)); } } diff --git a/src/runtime.rs b/src/runtime.rs index 9673d0eb7a..a72dc0a274 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -6,7 +6,6 @@ // accordance with one or both of these licenses. use std::future::Future; -use std::io; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; use std::time::Duration; @@ -39,15 +38,11 @@ impl Runtime { let id = ATOMIC_ID.fetch_add(1, Ordering::SeqCst); format!("ldk-node-runtime-{}", id) }); - // Eager driver handoff lets Tokio move the I/O driver to another worker sooner - // when this runtime's current worker enters `block_in_place` via `block_on`. - // That marginally reduces the chance that a synchronous caller blocks the same - // worker that would otherwise drive the I/O resource it is waiting on. It does - // not solve the issue completely: it only applies to node runtimes we build - // ourselves under `tokio_unstable`, does not affect externally supplied runtime - // handles, and cannot guarantee that every persistence driver task needed by the - // blocked future is already polling elsewhere. See the `StoreRuntime` docs below - // for the full deadlock scenario and the temporary store-runtime isolation. + // Eager driver handoff lets Tokio move the I/O driver to another worker sooner when + // a worker enters Tokio-managed blocking sections in dependencies. This is a + // low-risk scheduler hint for runtimes we build ourselves under `tokio_unstable`, + // but it does not affect externally supplied runtime handles and is not a substitute + // for exposing async APIs all the way to callers. #[cfg(tokio_unstable)] runtime_builder.enable_eager_driver_handoff(); let rt = runtime_builder.build()?; @@ -128,74 +123,57 @@ impl Runtime { handle.spawn_blocking(func) } - pub fn block_on(&self, future: F) -> F::Output { - // While we generally decided not to overthink via which call graph users would enter our - // runtime context, we'd still try to reuse whatever current context would be present - // during `block_on`, as this is the context `block_in_place` would operate on. So we try - // to detect the outer context here, and otherwise use whatever was set during - // initialization. - let handle = tokio::runtime::Handle::try_current().unwrap_or(self.handle().clone()); - // Since it seems to make a difference to `tokio` (see - // https://docs.rs/tokio/latest/tokio/time/fn.timeout.html#panics) we make sure the futures - // are always put in an `async` / `.await` closure. - tokio::task::block_in_place(move || handle.block_on(async { future.await })) - } - - pub fn abort_cancellable_background_tasks(&self) { + pub async fn abort_cancellable_background_tasks(&self) { let mut tasks = core::mem::take(&mut *self.cancellable_background_tasks.lock().expect("lock")); debug_assert!(tasks.len() > 0, "Expected some cancellable background_tasks"); tasks.abort_all(); - self.block_on(async { while let Some(_) = tasks.join_next().await {} }) + while let Some(_) = tasks.join_next().await {} } - pub fn wait_on_background_tasks(&self) { + pub async fn wait_on_background_tasks(&self) { let mut tasks = core::mem::take(&mut *self.background_tasks.lock().expect("lock")); debug_assert!(tasks.len() > 0, "Expected some background_tasks"); - self.block_on(async { - loop { - let timeout_fut = tokio::time::timeout( - Duration::from_secs(BACKGROUND_TASK_SHUTDOWN_TIMEOUT_SECS), - tasks.join_next_with_id(), - ); - match timeout_fut.await { - Ok(Some(Ok((id, _)))) => { - log_trace!(self.logger, "Stopped background task with id {}", id); - }, - Ok(Some(Err(e))) => { - tasks.abort_all(); - log_trace!(self.logger, "Stopping background task failed: {}", e); - break; - }, - Ok(None) => { - log_debug!(self.logger, "Stopped all background tasks"); - break; - }, - Err(e) => { - tasks.abort_all(); - log_error!(self.logger, "Stopping background task timed out: {}", e); - break; - }, - } + loop { + let timeout_fut = tokio::time::timeout( + Duration::from_secs(BACKGROUND_TASK_SHUTDOWN_TIMEOUT_SECS), + tasks.join_next_with_id(), + ); + match timeout_fut.await { + Ok(Some(Ok((id, _)))) => { + log_trace!(self.logger, "Stopped background task with id {}", id); + }, + Ok(Some(Err(e))) => { + tasks.abort_all(); + log_trace!(self.logger, "Stopping background task failed: {}", e); + break; + }, + Ok(None) => { + log_debug!(self.logger, "Stopped all background tasks"); + break; + }, + Err(e) => { + tasks.abort_all(); + log_error!(self.logger, "Stopping background task timed out: {}", e); + break; + }, } - }) + } } - pub fn wait_on_background_processor_task(&self) { - if let Some(background_processor_task) = - self.background_processor_task.lock().expect("lock").take() - { + pub async fn wait_on_background_processor_task(&self) { + let background_processor_task = + { self.background_processor_task.lock().expect("lock").take() }; + if let Some(background_processor_task) = background_processor_task { let abort_handle = background_processor_task.abort_handle(); // Since it seems to make a difference to `tokio` (see // https://docs.rs/tokio/latest/tokio/time/fn.timeout.html#panics) we make sure the futures // are always put in an `async` / `.await` closure. - let timeout_res = self.block_on(async { - tokio::time::timeout( - Duration::from_secs(LDK_EVENT_HANDLER_SHUTDOWN_TIMEOUT_SECS), - background_processor_task, - ) - .await - }); + let timeout_res = tokio::time::timeout( + Duration::from_secs(LDK_EVENT_HANDLER_SHUTDOWN_TIMEOUT_SECS), + background_processor_task, + ) + .await; match timeout_res { Ok(stop_res) => match stop_res { @@ -243,90 +221,6 @@ enum RuntimeMode { Handle(tokio::runtime::Handle), } -/// Runtime used by async store backends while ldk-node still exposes synchronous APIs. -/// -/// This is a temporary bridge for store implementations that need Tokio-driven I/O, such as VSS -/// and PostgreSQL. Many public ldk-node methods are still synchronous, so they call -/// [`Runtime::block_on`] when they need to wait for async persistence. If that persistence work is -/// driven by the same Tokio runtime as the synchronous caller, a blocking call can deadlock in a -/// narrow but realistic scheduler state. -/// -/// The failure mode is that `block_on` parks the current worker with `block_in_place` while it -/// waits for an async store operation. Suppose that store operation is waiting for an I/O future, -/// and the connection driver or I/O driver task that can make the future progress is assigned to -/// the same worker thread that just entered `block_in_place`. The blocked sync caller is waiting -/// for the persistence future to complete, while the persistence future is waiting for an I/O task -/// that cannot be polled because its worker is occupied by the blocking caller. With no worker -/// driving that I/O resource, neither side can make progress. -/// -/// A simple example is a synchronous node API calling `block_on(store.write(...))` for a -/// tokio-postgres-backed store. The write future may wait for the postgres connection task or -/// socket readiness. If the runtime worker that should poll that connection task is also the -/// worker currently blocked in the synchronous API, the write cannot complete, and the synchronous -/// API cannot unblock. -/// -/// `StoreRuntime` gives each such store backend its own small runtime, workers, and I/O driver. -/// Synchronous node APIs may still block the node runtime while waiting for persistence, but the -/// persistence tasks they wait on are driven independently and can continue polling sockets and -/// connection drivers. -/// -/// Once ldk-node switches the remaining store-backed APIs to be fully async, callers will await -/// persistence directly and these `block_on` bridges will be disallowed. At that point the store -/// runtimes should be removed again and store I/O can run on the node runtime directly. -pub(crate) struct StoreRuntime { - runtime: Option, -} - -impl StoreRuntime { - pub(crate) fn new( - thread_name_prefix: &'static str, worker_threads: usize, runtime_name: &'static str, - ) -> io::Result { - let runtime = tokio::runtime::Builder::new_multi_thread() - .enable_all() - .thread_name_fn(move || { - static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0); - let id = ATOMIC_ID.fetch_add(1, Ordering::SeqCst); - format!("{}-{}", thread_name_prefix, id) - }) - .worker_threads(worker_threads) - .max_blocking_threads(worker_threads) - .build() - .map_err(|e| { - io::Error::new( - io::ErrorKind::Other, - format!("Failed to build {runtime_name} runtime: {e}"), - ) - })?; - Ok(Self { runtime: Some(runtime) }) - } - - pub(crate) fn handle(&self) -> &tokio::runtime::Handle { - self.runtime.as_ref().expect("store runtime must be available").handle() - } - - pub(crate) fn spawn(&self, future: F) -> JoinHandle - where - F: Future + Send + 'static, - F::Output: Send + 'static, - { - self.handle().spawn(future) - } - - pub(crate) fn shutdown_background(mut self) { - if let Some(runtime) = self.runtime.take() { - runtime.shutdown_background(); - } - } -} - -impl Drop for StoreRuntime { - fn drop(&mut self) { - if let Some(runtime) = self.runtime.take() { - runtime.shutdown_background(); - } - } -} - pub(crate) struct RuntimeSpawner { runtime: Arc, } diff --git a/src/scoring.rs b/src/scoring.rs index 401d9c3f1f..266ca01b03 100644 --- a/src/scoring.rs +++ b/src/scoring.rs @@ -4,6 +4,7 @@ use std::time::SystemTime; use lightning::routing::scoring::ChannelLiquidities; use lightning::util::ser::Readable; use lightning::{log_error, log_info, log_trace}; +use tokio::sync::RwLock as AsyncRwLock; use crate::config::{ EXTERNAL_PATHFINDING_SCORES_MAX_SIZE, EXTERNAL_PATHFINDING_SCORES_SYNC_INTERVAL, @@ -13,14 +14,14 @@ use crate::io::utils::write_external_pathfinding_scores_to_cache; use crate::logger::LdkLogger; use crate::runtime::Runtime; use crate::types::DynStore; -use crate::{update_and_persist_node_metrics, Logger, PersistedNodeMetrics, Scorer}; +use crate::{update_and_persist_node_metrics, Logger, NodeMetrics, Scorer}; /// Start a background task that periodically downloads scores via an external url and merges them into the local /// pathfinding scores. pub fn setup_background_pathfinding_scores_sync( - url: String, scorer: Arc>, node_metrics: Arc, - kv_store: Arc, logger: Arc, runtime: Arc, - mut stop_receiver: tokio::sync::watch::Receiver<()>, + url: String, scorer: Arc>, + node_metrics: Arc>, kv_store: Arc, logger: Arc, + runtime: Arc, mut stop_receiver: tokio::sync::watch::Receiver<()>, ) { log_info!(logger, "External scores background syncing enabled from {}", url); @@ -51,7 +52,7 @@ pub fn setup_background_pathfinding_scores_sync( } async fn sync_external_scores( - logger: &Logger, scorer: &Mutex, node_metrics: &PersistedNodeMetrics, + logger: &Logger, scorer: &Mutex, node_metrics: &AsyncRwLock, kv_store: Arc, url: &String, ) -> () { let request = bitreq::get(url) diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index 76f2aa9ce6..bec06474f5 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -11,12 +11,13 @@ use std::str::FromStr; use std::sync::{Arc, Mutex}; use bdk_chain::spk_client::{FullScanRequest, SyncRequest}; +use bdk_chain::Merge; use bdk_wallet::descriptor::ExtendedDescriptor; use bdk_wallet::error::{BuildFeeBumpError, CreateTxError}; use bdk_wallet::event::WalletEvent; #[allow(deprecated)] use bdk_wallet::SignOptions; -use bdk_wallet::{Balance, KeychainKind, PersistedWallet, Update}; +use bdk_wallet::{AsyncWalletPersister, Balance, ChangeSet, KeychainKind, PersistedWallet, Update}; use bitcoin::address::NetworkUnchecked; use bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR; use bitcoin::blockdata::locktime::absolute::LockTime; @@ -50,6 +51,7 @@ use lightning::util::wallet_utils::{ }; use lightning_invoice::RawBolt11Invoice; use persist::KVStoreWalletPersister; +use tokio::sync::Mutex as AsyncMutex; use crate::config::Config; use crate::fee_estimator::{ConfirmationTarget, FeeEstimator, OnchainFeeEstimator}; @@ -78,10 +80,13 @@ pub(crate) mod ser; const DUST_LIMIT_SATS: u64 = 546; +#[derive(Clone)] pub(crate) struct Wallet { // A BDK on-chain wallet. - inner: Mutex>, - persister: Mutex, + inner: Arc>>, + persister: Arc>, + pending_changes: Arc>, + pending_persist_lock: Arc>, broadcaster: Arc, fee_estimator: Arc, chain_source: Arc, @@ -100,11 +105,15 @@ impl Wallet { payment_store: Arc, runtime: Arc, config: Arc, logger: Arc, pending_payment_store: Arc, ) -> Self { - let inner = Mutex::new(wallet); - let persister = Mutex::new(wallet_persister); + let inner = Arc::new(Mutex::new(wallet)); + let persister = Arc::new(AsyncMutex::new(wallet_persister)); + let pending_changes = Arc::new(Mutex::new(ChangeSet::default())); + let pending_persist_lock = Arc::new(AsyncMutex::new(())); Self { inner, persister, + pending_changes, + pending_persist_lock, broadcaster, fee_estimator, chain_source, @@ -116,6 +125,56 @@ impl Wallet { } } + fn queue_staged_wallet_changes_from( + &self, locked_wallet: &mut PersistedWallet, + ) -> bool { + let Some(changes) = locked_wallet.take_staged() else { + return false; + }; + self.pending_changes.lock().expect("lock").merge(changes); + true + } + + fn queue_staged_wallet_changes(&self) -> bool { + let mut locked_wallet = self.inner.lock().expect("lock"); + self.queue_staged_wallet_changes_from(&mut locked_wallet) + } + + async fn persist_queued_wallet_changes(&self) -> Result<(), std::io::Error> { + let _lock = self.pending_persist_lock.lock().await; + + loop { + let changes = { + let mut pending_changes = self.pending_changes.lock().expect("lock"); + if pending_changes.is_empty() { + return Ok(()); + } + core::mem::take(&mut *pending_changes) + }; + + let persist_res = { + let mut locked_persister = self.persister.lock().await; + KVStoreWalletPersister::persist(&mut *locked_persister, &changes).await + }; + + if let Err(e) = persist_res { + self.pending_changes.lock().expect("lock").merge(changes); + return Err(e); + } + } + } + + fn spawn_persist_wallet(&self) { + let wallet = self.clone(); + let runtime = Arc::clone(&self.runtime); + let logger = Arc::clone(&self.logger); + runtime.spawn_background_task(async move { + if let Err(e) = wallet.persist_queued_wallet_changes().await { + log_error!(logger, "Failed to persist on-chain wallet: {}", e); + } + }); + } + pub(crate) fn get_full_scan_request(&self) -> FullScanRequest { self.inner.lock().expect("lock").start_full_scan().build() } @@ -151,72 +210,80 @@ impl Wallet { BlockLocator { block_hash: checkpoint.hash(), height: checkpoint.height(), previous_blocks } } - pub(crate) fn apply_update(&self, update: impl Into) -> Result<(), Error> { - let mut locked_wallet = self.inner.lock().expect("lock"); - match locked_wallet.apply_update_events(update) { - Ok(events) => { - self.update_payment_store(&mut *locked_wallet, events).map_err(|e| { - log_error!(self.logger, "Failed to update payment store: {}", e); - Error::PersistenceFailed - })?; + pub(crate) async fn apply_update(&self, update: impl Into) -> Result<(), Error> { + let events = { + let mut locked_wallet = self.inner.lock().expect("lock"); + match locked_wallet.apply_update_events(update) { + Ok(events) => events, + Err(e) => { + log_error!(self.logger, "Sync failed due to chain connection error: {}", e); + return Err(Error::WalletOperationFailed); + }, + } + }; - let mut locked_persister = self.persister.lock().expect("lock"); - self.runtime.block_on(locked_wallet.persist_async(&mut locked_persister)).map_err( - |e| { - log_error!(self.logger, "Failed to persist wallet: {}", e); - Error::PersistenceFailed - }, - )?; + self.queue_staged_wallet_changes(); + self.update_payment_store(events).await.map_err(|e| { + log_error!(self.logger, "Failed to update payment store: {}", e); + Error::PersistenceFailed + })?; - Ok(()) - }, - Err(e) => { - log_error!(self.logger, "Sync failed due to chain connection error: {}", e); - Err(Error::WalletOperationFailed) - }, - } + self.persist_queued_wallet_changes().await.map_err(|e| { + log_error!(self.logger, "Failed to persist wallet: {}", e); + Error::PersistenceFailed + })?; + + Ok(()) } - pub(crate) fn apply_mempool_txs( + pub(crate) async fn apply_mempool_txs( &self, unconfirmed_txs: Vec<(Transaction, u64)>, evicted_txids: Vec<(Txid, u64)>, ) -> Result<(), Error> { if unconfirmed_txs.is_empty() && evicted_txids.is_empty() { return Ok(()); } - let mut locked_wallet = self.inner.lock().expect("lock"); - - let chain_tip1 = locked_wallet.latest_checkpoint().block_id(); - let wallet_txs1 = locked_wallet - .transactions() - .map(|wtx| (wtx.tx_node.txid, (wtx.tx_node.tx.clone(), wtx.chain_position))) - .collect::, bdk_chain::ChainPosition), - >>(); - - locked_wallet.apply_unconfirmed_txs(unconfirmed_txs); - locked_wallet.apply_evicted_txs(evicted_txids); - - let chain_tip2 = locked_wallet.latest_checkpoint().block_id(); - let wallet_txs2 = locked_wallet - .transactions() - .map(|wtx| (wtx.tx_node.txid, (wtx.tx_node.tx.clone(), wtx.chain_position))) - .collect::, bdk_chain::ChainPosition), - >>(); + let events = { + let mut locked_wallet = self.inner.lock().expect("lock"); - let events = - wallet_events(&mut *locked_wallet, chain_tip1, chain_tip2, wallet_txs1, wallet_txs2); + let chain_tip1 = locked_wallet.latest_checkpoint().block_id(); + let wallet_txs1 = locked_wallet + .transactions() + .map(|wtx| (wtx.tx_node.txid, (wtx.tx_node.tx.clone(), wtx.chain_position))) + .collect::, bdk_chain::ChainPosition), + >>(); + + locked_wallet.apply_unconfirmed_txs(unconfirmed_txs); + locked_wallet.apply_evicted_txs(evicted_txids); + + let chain_tip2 = locked_wallet.latest_checkpoint().block_id(); + let wallet_txs2 = locked_wallet + .transactions() + .map(|wtx| (wtx.tx_node.txid, (wtx.tx_node.tx.clone(), wtx.chain_position))) + .collect::, bdk_chain::ChainPosition), + >>(); + + let events = wallet_events( + &mut *locked_wallet, + chain_tip1, + chain_tip2, + wallet_txs1, + wallet_txs2, + ); + self.queue_staged_wallet_changes_from(&mut locked_wallet); + events + }; - self.update_payment_store(&mut *locked_wallet, events).map_err(|e| { + self.update_payment_store(events).await.map_err(|e| { log_error!(self.logger, "Failed to update payment store: {}", e); Error::PersistenceFailed })?; - let mut locked_persister = self.persister.lock().expect("lock"); - self.runtime.block_on(locked_wallet.persist_async(&mut locked_persister)).map_err(|e| { + self.persist_queued_wallet_changes().await.map_err(|e| { log_error!(self.logger, "Failed to persist wallet: {}", e); Error::PersistenceFailed })?; @@ -224,10 +291,7 @@ impl Wallet { Ok(()) } - fn update_payment_store<'a>( - &self, locked_wallet: &'a mut PersistedWallet, - mut events: Vec, - ) -> Result<(), Error> { + async fn update_payment_store(&self, mut events: Vec) -> Result<(), Error> { if events.is_empty() { return Ok(()); } @@ -255,14 +319,7 @@ impl Wallet { for event in events { match event { WalletEvent::TxConfirmed { txid, tx, block_time, .. } => { - let cur_height = locked_wallet.latest_checkpoint().height(); let confirmation_height = block_time.block_id.height; - let payment_status = if cur_height >= confirmation_height + ANTI_REORG_DELAY - 1 - { - PaymentStatus::Succeeded - } else { - PaymentStatus::Pending - }; let confirmation_status = ConfirmationStatus::Confirmed { block_hash: block_time.block_id.hash, @@ -272,31 +329,42 @@ impl Wallet { let payment_id = self .find_payment_by_txid(txid) + .await .unwrap_or_else(|| PaymentId(txid.to_byte_array())); - let payment = self.create_payment_from_tx( - locked_wallet, - txid, - payment_id, - &tx, - payment_status, - confirmation_status, - ); + let payment = { + let locked_wallet = self.inner.lock().expect("lock"); + let cur_height = locked_wallet.latest_checkpoint().height(); + let payment_status = + if cur_height >= confirmation_height + ANTI_REORG_DELAY - 1 { + PaymentStatus::Succeeded + } else { + PaymentStatus::Pending + }; + + self.create_payment_from_tx( + &locked_wallet, + txid, + payment_id, + &tx, + payment_status, + confirmation_status, + ) + }; - self.runtime.block_on(self.payment_store.insert_or_update(payment.clone()))?; + self.payment_store.insert_or_update(payment.clone()).await?; - if payment_status == PaymentStatus::Pending { + if payment.status == PaymentStatus::Pending { let pending_payment = self.create_pending_payment_from_tx(payment, Vec::new()); - self.runtime.block_on( - self.pending_payment_store.insert_or_update(pending_payment), - )?; + self.pending_payment_store.insert_or_update(pending_payment).await?; } }, WalletEvent::ChainTipChanged { new_tip, .. } => { - let pending_payments: Vec = - self.pending_payment_store.list_filter(|p| { + let pending_payments: Vec = self + .pending_payment_store + .list_filter(|p| { debug_assert!( p.details.status == PaymentStatus::Pending, "Non-pending payment {:?} found in pending store", @@ -304,7 +372,8 @@ impl Wallet { ); p.details.status == PaymentStatus::Pending && matches!(p.details.kind, PaymentKind::Onchain { .. }) - }); + }) + .await; let mut unconfirmed_outbound_txids: Vec = Vec::new(); @@ -317,11 +386,8 @@ impl Wallet { let payment_id = payment.details.id; if new_tip.height >= height + ANTI_REORG_DELAY - 1 { payment.details.status = PaymentStatus::Succeeded; - self.runtime.block_on( - self.payment_store.insert_or_update(payment.details), - )?; - self.runtime - .block_on(self.pending_payment_store.remove(&payment_id))?; + self.payment_store.insert_or_update(payment.details).await?; + self.pending_payment_store.remove(&payment_id).await?; } }, PaymentKind::Onchain { @@ -338,6 +404,7 @@ impl Wallet { let txs_to_broadcast: Vec = unconfirmed_outbound_txids .iter() .filter_map(|txid| { + let locked_wallet = self.inner.lock().expect("lock"); locked_wallet.tx_details(*txid).map(|d| (*d.tx).clone()) }) .collect(); @@ -365,24 +432,27 @@ impl Wallet { WalletEvent::TxUnconfirmed { txid, tx, old_block_time: None } => { let payment_id = self .find_payment_by_txid(txid) + .await .unwrap_or_else(|| PaymentId(txid.to_byte_array())); - let payment = self.create_payment_from_tx( - locked_wallet, - txid, - payment_id, - &tx, - PaymentStatus::Pending, - ConfirmationStatus::Unconfirmed, - ); + let payment = { + let locked_wallet = self.inner.lock().expect("lock"); + self.create_payment_from_tx( + &locked_wallet, + txid, + payment_id, + &tx, + PaymentStatus::Pending, + ConfirmationStatus::Unconfirmed, + ) + }; let pending_payment = self.create_pending_payment_from_tx(payment.clone(), Vec::new()); - self.runtime.block_on(self.payment_store.insert_or_update(payment))?; - self.runtime - .block_on(self.pending_payment_store.insert_or_update(pending_payment))?; + self.payment_store.insert_or_update(payment).await?; + self.pending_payment_store.insert_or_update(pending_payment).await?; }, WalletEvent::TxReplaced { txid, conflicts, .. } => { - let Some(payment_id) = self.find_payment_by_txid(txid) else { + let Some(payment_id) = self.find_payment_by_txid(txid).await else { log_error!( self.logger, "Could not find payment for replaced transaction {}. Skipping.", @@ -400,36 +470,37 @@ impl Wallet { // the payment store with the replacement txid before the next sync cycle, so we // can safely fetch it here. debug_assert!( - self.payment_store.get(&payment_id).is_some(), + self.payment_store.get(&payment_id).await.is_some(), "Payment {:?} expected in store during WalletEvent::TxReplaced but not found", payment_id, ); let payment = - self.payment_store.get(&payment_id).ok_or(Error::InvalidPaymentId)?; + self.payment_store.get(&payment_id).await.ok_or(Error::InvalidPaymentId)?; let pending_payment_details = self.create_pending_payment_from_tx(payment, conflict_txids.clone()); - self.runtime.block_on( - self.pending_payment_store.insert_or_update(pending_payment_details), - )?; + self.pending_payment_store.insert_or_update(pending_payment_details).await?; }, WalletEvent::TxDropped { txid, tx } => { let payment_id = self .find_payment_by_txid(txid) + .await .unwrap_or_else(|| PaymentId(txid.to_byte_array())); - let payment = self.create_payment_from_tx( - locked_wallet, - txid, - payment_id, - &tx, - PaymentStatus::Pending, - ConfirmationStatus::Unconfirmed, - ); + let payment = { + let locked_wallet = self.inner.lock().expect("lock"); + self.create_payment_from_tx( + &locked_wallet, + txid, + payment_id, + &tx, + PaymentStatus::Pending, + ConfirmationStatus::Unconfirmed, + ) + }; let pending_payment = self.create_pending_payment_from_tx(payment.clone(), Vec::new()); - self.runtime.block_on(self.payment_store.insert_or_update(payment))?; - self.runtime - .block_on(self.pending_payment_store.insert_or_update(pending_payment))?; + self.payment_store.insert_or_update(payment).await?; + self.pending_payment_store.insert_or_update(pending_payment).await?; }, _ => { continue; @@ -441,84 +512,105 @@ impl Wallet { } #[allow(deprecated)] - pub(crate) fn create_funding_transaction( + pub(crate) async fn create_funding_transaction( &self, output_script: ScriptBuf, amount: Amount, confirmation_target: ConfirmationTarget, locktime: LockTime, ) -> Result { let fee_rate = self.fee_estimator.estimate_fee_rate(confirmation_target); - let mut locked_wallet = self.inner.lock().expect("lock"); - let mut tx_builder = locked_wallet.build_tx(); + let tx = { + let mut locked_wallet = self.inner.lock().expect("lock"); + let mut tx_builder = locked_wallet.build_tx(); - tx_builder.add_recipient(output_script, amount).fee_rate(fee_rate).nlocktime(locktime); + tx_builder.add_recipient(output_script, amount).fee_rate(fee_rate).nlocktime(locktime); - let mut psbt = match tx_builder.finish() { - Ok(psbt) => { - log_trace!(self.logger, "Created funding PSBT: {:?}", psbt); - psbt - }, - Err(err) => { - log_error!(self.logger, "Failed to create funding transaction: {}", err); - return Err(err.into()); - }, - }; + let mut psbt = match tx_builder.finish() { + Ok(psbt) => { + log_trace!(self.logger, "Created funding PSBT: {:?}", psbt); + psbt + }, + Err(err) => { + log_error!(self.logger, "Failed to create funding transaction: {}", err); + return Err(err.into()); + }, + }; - match locked_wallet.sign(&mut psbt, SignOptions::default()) { - Ok(finalized) => { - if !finalized { - return Err(Error::OnchainTxCreationFailed); - } - }, - Err(err) => { - log_error!(self.logger, "Failed to create funding transaction: {}", err); - return Err(err.into()); - }, - } + match locked_wallet.sign(&mut psbt, SignOptions::default()) { + Ok(finalized) => { + if !finalized { + return Err(Error::OnchainTxCreationFailed); + } + }, + Err(err) => { + log_error!(self.logger, "Failed to create funding transaction: {}", err); + return Err(err.into()); + }, + } + + self.queue_staged_wallet_changes_from(&mut locked_wallet); + + psbt.extract_tx().map_err(|e| { + log_error!(self.logger, "Failed to extract transaction: {}", e); + e + })? + }; - let mut locked_persister = self.persister.lock().expect("lock"); - self.runtime.block_on(locked_wallet.persist_async(&mut locked_persister)).map_err(|e| { + self.persist_queued_wallet_changes().await.map_err(|e| { log_error!(self.logger, "Failed to persist wallet: {}", e); Error::PersistenceFailed })?; - let tx = psbt.extract_tx().map_err(|e| { - log_error!(self.logger, "Failed to extract transaction: {}", e); - e + Ok(tx) + } + + pub(crate) async fn get_new_address(&self) -> Result { + let address = self.get_new_address_inner(); + self.persist_queued_wallet_changes().await.map_err(|e| { + log_error!(self.logger, "Failed to persist wallet: {}", e); + Error::PersistenceFailed })?; + Ok(address) + } - Ok(tx) + fn get_new_address_sync(&self) -> bitcoin::Address { + let address = self.get_new_address_inner(); + self.spawn_persist_wallet(); + address } - pub(crate) fn get_new_address(&self) -> Result { + fn get_new_address_inner(&self) -> bitcoin::Address { let mut locked_wallet = self.inner.lock().expect("lock"); - let mut locked_persister = self.persister.lock().expect("lock"); let address_info = locked_wallet.reveal_next_address(KeychainKind::External); - self.runtime.block_on(locked_wallet.persist_async(&mut locked_persister)).map_err(|e| { + self.queue_staged_wallet_changes_from(&mut locked_wallet); + address_info.address + } + + pub(crate) async fn get_new_internal_address(&self) -> Result { + let address = self.get_new_internal_address_inner(); + self.persist_queued_wallet_changes().await.map_err(|e| { log_error!(self.logger, "Failed to persist wallet: {}", e); Error::PersistenceFailed })?; - Ok(address_info.address) + Ok(address) } - pub(crate) fn get_new_internal_address(&self) -> Result { + fn get_new_internal_address_inner(&self) -> bitcoin::Address { let mut locked_wallet = self.inner.lock().expect("lock"); - let mut locked_persister = self.persister.lock().expect("lock"); let address_info = locked_wallet.next_unused_address(KeychainKind::Internal); - self.runtime.block_on(locked_wallet.persist_async(&mut locked_persister)).map_err(|e| { - log_error!(self.logger, "Failed to persist wallet: {}", e); - Error::PersistenceFailed - })?; - Ok(address_info.address) + self.queue_staged_wallet_changes_from(&mut locked_wallet); + address_info.address } - pub(crate) fn cancel_tx(&self, tx: &Transaction) -> Result<(), Error> { - let mut locked_wallet = self.inner.lock().expect("lock"); - let mut locked_persister = self.persister.lock().expect("lock"); + pub(crate) async fn cancel_tx(&self, tx: &Transaction) -> Result<(), Error> { + { + let mut locked_wallet = self.inner.lock().expect("lock"); + locked_wallet.cancel_tx(tx); + self.queue_staged_wallet_changes_from(&mut locked_wallet); + } - locked_wallet.cancel_tx(tx); - self.runtime.block_on(locked_wallet.persist_async(&mut locked_persister)).map_err(|e| { + self.persist_queued_wallet_changes().await.map_err(|e| { log_error!(self.logger, "Failed to persist wallet: {}", e); Error::PersistenceFailed })?; @@ -721,7 +813,7 @@ impl Wallet { } #[allow(deprecated)] - pub(crate) fn send_to_address( + pub(crate) async fn send_to_address( &self, address: &bitcoin::Address, send_amount: OnchainSendAmount, fee_rate: Option, ) -> Result { @@ -855,13 +947,7 @@ impl Wallet { }, } - let mut locked_persister = self.persister.lock().expect("lock"); - self.runtime.block_on(locked_wallet.persist_async(&mut locked_persister)).map_err( - |e| { - log_error!(self.logger, "Failed to persist wallet: {}", e); - Error::PersistenceFailed - }, - )?; + self.queue_staged_wallet_changes_from(&mut locked_wallet); psbt.extract_tx().map_err(|e| { log_error!(self.logger, "Failed to extract transaction: {}", e); @@ -869,6 +955,11 @@ impl Wallet { })? }; + self.persist_queued_wallet_changes().await.map_err(|e| { + log_error!(self.logger, "Failed to persist wallet: {}", e); + Error::PersistenceFailed + })?; + self.broadcaster.broadcast_transactions(&[( &tx, lightning::chain::chaininterface::TransactionType::Sweep { channels: vec![] }, @@ -908,83 +999,90 @@ impl Wallet { Ok(txid) } - pub(crate) fn select_confirmed_utxos( + pub(crate) async fn select_confirmed_utxos( &self, must_spend: Vec, must_pay_to: &[TxOut], fee_rate: FeeRate, ) -> Result { - let mut locked_wallet = self.inner.lock().expect("lock"); - let mut locked_persister = self.persister.lock().expect("lock"); + let (coin_selection, should_persist) = { + let mut locked_wallet = self.inner.lock().expect("lock"); - debug_assert!(matches!( - locked_wallet.public_descriptor(KeychainKind::External), - ExtendedDescriptor::Wpkh(_) - )); - debug_assert!(matches!( - locked_wallet.public_descriptor(KeychainKind::Internal), - ExtendedDescriptor::Wpkh(_) - )); + debug_assert!(matches!( + locked_wallet.public_descriptor(KeychainKind::External), + ExtendedDescriptor::Wpkh(_) + )); + debug_assert!(matches!( + locked_wallet.public_descriptor(KeychainKind::Internal), + ExtendedDescriptor::Wpkh(_) + )); + + let mut tx_builder = locked_wallet.build_tx(); + tx_builder.only_witness_utxo(); + + for input in &must_spend { + let psbt_input = psbt::Input { + witness_utxo: Some(input.previous_utxo.clone()), + ..Default::default() + }; + let weight = ldk_to_bdk_satisfaction_weight(input.satisfaction_weight); + tx_builder.add_foreign_utxo(input.outpoint, psbt_input, weight).map_err(|_| ())?; + } - let mut tx_builder = locked_wallet.build_tx(); - tx_builder.only_witness_utxo(); + for output in must_pay_to { + tx_builder.add_recipient(output.script_pubkey.clone(), output.value); + } - for input in &must_spend { - let psbt_input = psbt::Input { - witness_utxo: Some(input.previous_utxo.clone()), - ..Default::default() - }; - let weight = ldk_to_bdk_satisfaction_weight(input.satisfaction_weight); - tx_builder.add_foreign_utxo(input.outpoint, psbt_input, weight).map_err(|_| ())?; - } + tx_builder.fee_rate(fee_rate); + tx_builder.exclude_unconfirmed(); - for output in must_pay_to { - tx_builder.add_recipient(output.script_pubkey.clone(), output.value); - } + let unsigned_tx = tx_builder + .finish() + .map_err(|e| { + log_error!(self.logger, "Failed to select confirmed UTXOs: {}", e); + })? + .unsigned_tx; + + let confirmed_utxos = unsigned_tx + .input + .iter() + .filter(|txin| { + must_spend.iter().all(|input| input.outpoint != txin.previous_output) + }) + .filter_map(|txin| { + locked_wallet + .tx_details(txin.previous_output.txid) + .map(|tx_details| tx_details.tx.deref().clone()) + .map(|prevtx| ConfirmedUtxo::new_p2wpkh(prevtx, txin.previous_output.vout)) + }) + .collect::, ()>>()?; - tx_builder.fee_rate(fee_rate); - tx_builder.exclude_unconfirmed(); + if unsigned_tx.output.len() > must_pay_to.len() + 1 { + log_error!( + self.logger, + "Unexpected number of change outputs during coin selection: {}", + unsigned_tx.output.len() - must_pay_to.len(), + ); + return Err(()); + } - let unsigned_tx = tx_builder - .finish() - .map_err(|e| { - log_error!(self.logger, "Failed to select confirmed UTXOs: {}", e); - })? - .unsigned_tx; + let change_output = unsigned_tx + .output + .into_iter() + .find(|txout| must_pay_to.iter().all(|output| output != txout)); + let should_persist = change_output.is_some(); - let confirmed_utxos = unsigned_tx - .input - .iter() - .filter(|txin| must_spend.iter().all(|input| input.outpoint != txin.previous_output)) - .filter_map(|txin| { - locked_wallet - .tx_details(txin.previous_output.txid) - .map(|tx_details| tx_details.tx.deref().clone()) - .map(|prevtx| ConfirmedUtxo::new_p2wpkh(prevtx, txin.previous_output.vout)) - }) - .collect::, ()>>()?; + if should_persist { + self.queue_staged_wallet_changes_from(&mut locked_wallet); + } - if unsigned_tx.output.len() > must_pay_to.len() + 1 { - log_error!( - self.logger, - "Unexpected number of change outputs during coin selection: {}", - unsigned_tx.output.len() - must_pay_to.len(), - ); - return Err(()); - } + (Ok(CoinSelection { confirmed_utxos, change_output }), should_persist) + }; - let change_output = unsigned_tx - .output - .into_iter() - .find(|txout| must_pay_to.iter().all(|output| output != txout)); - - if change_output.is_some() { - self.runtime.block_on(locked_wallet.persist_async(&mut locked_persister)).map_err( - |e| { - log_error!(self.logger, "Failed to persist wallet: {}", e); - () - }, - )?; + if should_persist { + self.persist_queued_wallet_changes().await.map_err(|e| { + log_error!(self.logger, "Failed to persist wallet: {}", e); + })?; } - Ok(CoinSelection { confirmed_utxos, change_output }) + coin_selection } fn list_confirmed_utxos_inner(&self) -> Result, ()> { @@ -1081,16 +1179,19 @@ impl Wallet { } #[allow(deprecated)] - fn get_change_script_inner(&self) -> Result { - let mut locked_wallet = self.inner.lock().expect("lock"); - let mut locked_persister = self.persister.lock().expect("lock"); + async fn get_change_script_inner(&self) -> Result { + let script_pubkey = { + let mut locked_wallet = self.inner.lock().expect("lock"); + let address_info = locked_wallet.next_unused_address(KeychainKind::Internal); + self.queue_staged_wallet_changes_from(&mut locked_wallet); + address_info.address.script_pubkey() + }; - let address_info = locked_wallet.next_unused_address(KeychainKind::Internal); - self.runtime.block_on(locked_wallet.persist_async(&mut locked_persister)).map_err(|e| { + self.persist_queued_wallet_changes().await.map_err(|e| { log_error!(self.logger, "Failed to persist wallet: {}", e); () })?; - Ok(address_info.address.script_pubkey()) + Ok(script_pubkey) } #[allow(deprecated)] @@ -1216,9 +1317,9 @@ impl Wallet { PendingPaymentDetails::new(payment, conflicting_txids) } - fn find_payment_by_txid(&self, target_txid: Txid) -> Option { + async fn find_payment_by_txid(&self, target_txid: Txid) -> Option { let direct_payment_id = PaymentId(target_txid.to_byte_array()); - if self.pending_payment_store.contains_key(&direct_payment_id) { + if self.pending_payment_store.contains_key(&direct_payment_id).await { return Some(direct_payment_id); } @@ -1228,6 +1329,7 @@ impl Wallet { matches!(p.details.kind, PaymentKind::Onchain { txid, .. } if txid == target_txid) || p.conflicting_txids.contains(&target_txid) }) + .await .first() { return Some(replaced_details.details.id); @@ -1237,10 +1339,10 @@ impl Wallet { } #[allow(deprecated)] - pub(crate) fn bump_fee_rbf( + pub(crate) async fn bump_fee_rbf( &self, payment_id: PaymentId, fee_rate: Option, ) -> Result { - let payment = self.payment_store.get(&payment_id).ok_or_else(|| { + let payment = self.payment_store.get(&payment_id).await.ok_or_else(|| { log_error!(self.logger, "Payment {} not found in payment store", payment_id); Error::InvalidPaymentId })?; @@ -1280,163 +1382,189 @@ impl Wallet { }, }; - let mut locked_wallet = self.inner.lock().expect("lock"); - - debug_assert!( - locked_wallet.tx_details(txid).is_some(), - "Transaction {} expected in wallet but not found", - txid, - ); - let old_tx = locked_wallet - .tx_details(txid) - .ok_or_else(|| { - log_error!(self.logger, "Transaction {} not found in wallet", txid); - Error::InvalidPaymentId - })? - .tx - .deref() - .clone(); + let (fee_bumped_tx, new_txid, new_payment, pending_payment_store) = { + let mut locked_wallet = self.inner.lock().expect("lock"); - let old_fee_rate = locked_wallet.calculate_fee_rate(&old_tx).map_err(|e| { - log_error!(self.logger, "Failed to calculate fee rate of transaction {}: {}", txid, e); - Error::WalletOperationFailed - })?; + debug_assert!( + locked_wallet.tx_details(txid).is_some(), + "Transaction {} expected in wallet but not found", + txid, + ); + let old_tx = locked_wallet + .tx_details(txid) + .ok_or_else(|| { + log_error!(self.logger, "Transaction {} not found in wallet", txid); + Error::InvalidPaymentId + })? + .tx + .deref() + .clone(); + + let old_fee_rate = locked_wallet.calculate_fee_rate(&old_tx).map_err(|e| { + log_error!( + self.logger, + "Failed to calculate fee rate of transaction {}: {}", + txid, + e + ); + Error::WalletOperationFailed + })?; - // BIP 125 requires the replacement to pay a higher fee rate than the original. - // The minimum increase is the incremental relay fee. - let min_required_fee_rate_sat_per_kwu = - old_fee_rate.to_sat_per_kwu() + INCREMENTAL_RELAY_FEE_SAT_PER_1000_WEIGHT as u64; + // BIP 125 requires the replacement to pay a higher fee rate than the original. + // The minimum increase is the incremental relay fee. + let min_required_fee_rate_sat_per_kwu = + old_fee_rate.to_sat_per_kwu() + INCREMENTAL_RELAY_FEE_SAT_PER_1000_WEIGHT as u64; + + let confirmation_target = ConfirmationTarget::OnchainPayment; + let estimated_fee_rate = self.fee_estimator.estimate_fee_rate(confirmation_target); + + // Use the higher of minimum RBF requirement or current network estimate + let final_fee_rate_sat_per_kwu = + min_required_fee_rate_sat_per_kwu.max(estimated_fee_rate.to_sat_per_kwu()); + let final_fee_rate = + fee_rate.unwrap_or_else(|| FeeRate::from_sat_per_kwu(final_fee_rate_sat_per_kwu)); + + let mut psbt = { + let mut builder = locked_wallet.build_fee_bump(txid).map_err(|e| { + log_error!(self.logger, "BDK fee bump failed for {}: {:?}", txid, e); + match e { + BuildFeeBumpError::TransactionNotFound(_) => Error::InvalidPaymentId, + BuildFeeBumpError::TransactionConfirmed(_) => { + log_error!(self.logger, "Payment {} is already confirmed", payment_id); + Error::InvalidPaymentId + }, + BuildFeeBumpError::IrreplaceableTransaction(_) => { + Error::OnchainTxCreationFailed + }, + BuildFeeBumpError::FeeRateUnavailable => { + Error::FeerateEstimationUpdateFailed + }, + BuildFeeBumpError::UnknownUtxo(_) => Error::OnchainTxCreationFailed, + BuildFeeBumpError::InvalidOutputIndex(_) => Error::OnchainTxCreationFailed, + } + })?; - let confirmation_target = ConfirmationTarget::OnchainPayment; - let estimated_fee_rate = self.fee_estimator.estimate_fee_rate(confirmation_target); - - // Use the higher of minimum RBF requirement or current network estimate - let final_fee_rate_sat_per_kwu = - min_required_fee_rate_sat_per_kwu.max(estimated_fee_rate.to_sat_per_kwu()); - let final_fee_rate = - fee_rate.unwrap_or_else(|| FeeRate::from_sat_per_kwu(final_fee_rate_sat_per_kwu)); - - let mut psbt = { - let mut builder = locked_wallet.build_fee_bump(txid).map_err(|e| { - log_error!(self.logger, "BDK fee bump failed for {}: {:?}", txid, e); - match e { - BuildFeeBumpError::TransactionNotFound(_) => Error::InvalidPaymentId, - BuildFeeBumpError::TransactionConfirmed(_) => { - log_error!(self.logger, "Payment {} is already confirmed", payment_id); - Error::InvalidPaymentId - }, - BuildFeeBumpError::IrreplaceableTransaction(_) => { - Error::OnchainTxCreationFailed - }, - BuildFeeBumpError::FeeRateUnavailable => Error::FeerateEstimationUpdateFailed, - BuildFeeBumpError::UnknownUtxo(_) => Error::OnchainTxCreationFailed, - BuildFeeBumpError::InvalidOutputIndex(_) => Error::OnchainTxCreationFailed, - } - })?; + builder.fee_rate(final_fee_rate); - builder.fee_rate(final_fee_rate); + match builder.finish() { + Ok(psbt) => Ok(psbt), + Err(CreateTxError::FeeRateTooLow { required: required_fee_rate }) => { + if fee_rate.is_some() { + log_error!( + self.logger, + "Provided fee rate {} is too low for RBF fee bump of txid {}, required minimum fee rate: {}", + fee_rate.expect("fee rate is set"), + txid, + required_fee_rate + ); + return Err(Error::InvalidFeeRate); + } - match builder.finish() { - Ok(psbt) => Ok(psbt), - Err(CreateTxError::FeeRateTooLow { required: required_fee_rate }) => { - if fee_rate.is_some() { - log_error!( + log_info!( self.logger, - "Provided fee rate {} is too low for RBF fee bump of txid {}, required minimum fee rate: {}", - fee_rate.expect("fee rate is set"), - txid, + "BDK requires higher fee rate: {}", required_fee_rate ); - return Err(Error::InvalidFeeRate); - } - log_info!(self.logger, "BDK requires higher fee rate: {}", required_fee_rate); + // BDK may require a higher fee rate than our estimate due to + // differences in UTXO selection or transaction weight calculations. + // We cap the retry at 1.5x our target fee rate as a safety bound + // to avoid overpaying. + let max_allowed_fee_rate = FeeRate::from_sat_per_kwu( + final_fee_rate_sat_per_kwu.saturating_mul(3).saturating_div(2), + ); + if required_fee_rate > max_allowed_fee_rate { + log_error!( self.logger, "BDK required fee rate {} exceeds sanity cap {} (1.5x our estimate) for tx {}", required_fee_rate, max_allowed_fee_rate, txid ); + return Err(Error::InvalidFeeRate); + } - // BDK may require a higher fee rate than our estimate due to - // differences in UTXO selection or transaction weight calculations. - // We cap the retry at 1.5x our target fee rate as a safety bound - // to avoid overpaying. - let max_allowed_fee_rate = FeeRate::from_sat_per_kwu( - final_fee_rate_sat_per_kwu.saturating_mul(3).saturating_div(2), - ); - if required_fee_rate > max_allowed_fee_rate { - log_error!( self.logger, "BDK required fee rate {} exceeds sanity cap {} (1.5x our estimate) for tx {}", required_fee_rate, max_allowed_fee_rate, txid ); - return Err(Error::InvalidFeeRate); - } + let mut builder = locked_wallet.build_fee_bump(txid).map_err(|e| { + log_error!( + self.logger, + "BDK fee bump retry failed for {}: {:?}", + txid, + e + ); + Error::InvalidFeeRate + })?; - let mut builder = locked_wallet.build_fee_bump(txid).map_err(|e| { - log_error!(self.logger, "BDK fee bump retry failed for {}: {:?}", txid, e); - Error::InvalidFeeRate - })?; + builder.fee_rate(required_fee_rate); + builder.finish().map_err(|e| { + log_error!( + self.logger, + "Failed to finish PSBT with required fee rate: {:?}", + e + ); + Error::InvalidFeeRate + }) + }, + Err(e) => { + log_error!(self.logger, "Failed to create fee bump PSBT: {:?}", e); + Err(Error::InvalidFeeRate) + }, + }? + }; - builder.fee_rate(required_fee_rate); - builder.finish().map_err(|e| { + match locked_wallet.sign(&mut psbt, SignOptions::default()) { + Ok(finalized) => { + if !finalized { log_error!( self.logger, - "Failed to finish PSBT with required fee rate: {:?}", - e + "Failed to finalize signing for fee bump of {}", + txid ); - Error::InvalidFeeRate - }) + return Err(Error::OnchainTxCreationFailed); + } }, - Err(e) => { - log_error!(self.logger, "Failed to create fee bump PSBT: {:?}", e); - Err(Error::InvalidFeeRate) + Err(err) => { + log_error!( + self.logger, + "Failed to sign fee bump transaction for {}: {}", + txid, + err + ); + return Err(err.into()); }, - }? - }; + } - match locked_wallet.sign(&mut psbt, SignOptions::default()) { - Ok(finalized) => { - if !finalized { - log_error!(self.logger, "Failed to finalize signing for fee bump of {}", txid); - return Err(Error::OnchainTxCreationFailed); - } - }, - Err(err) => { + self.queue_staged_wallet_changes_from(&mut locked_wallet); + + let fee_bumped_tx = psbt.extract_tx().map_err(|e| { log_error!( self.logger, - "Failed to sign fee bump transaction for {}: {}", + "Failed to extract fee bump transaction for {}: {}", txid, - err + e ); - return Err(err.into()); - }, - } + e + })?; - let mut locked_persister = self.persister.lock().expect("lock"); - self.runtime.block_on(locked_wallet.persist_async(&mut locked_persister)).map_err(|e| { + let new_txid = fee_bumped_tx.compute_txid(); + let new_payment = self.create_payment_from_tx( + &locked_wallet, + new_txid, + payment.id, + &fee_bumped_tx, + PaymentStatus::Pending, + ConfirmationStatus::Unconfirmed, + ); + let pending_payment_store = + self.create_pending_payment_from_tx(new_payment.clone(), Vec::new()); + (fee_bumped_tx, new_txid, new_payment, pending_payment_store) + }; + + self.persist_queued_wallet_changes().await.map_err(|e| { log_error!(self.logger, "Failed to persist wallet after fee bump of {}: {}", txid, e); Error::PersistenceFailed })?; - let fee_bumped_tx = psbt.extract_tx().map_err(|e| { - log_error!(self.logger, "Failed to extract fee bump transaction for {}: {}", txid, e); - e - })?; - - let new_txid = fee_bumped_tx.compute_txid(); - self.broadcaster.broadcast_transactions(&[( &fee_bumped_tx, lightning::chain::chaininterface::TransactionType::Sweep { channels: vec![] }, )]); - let new_payment = self.create_payment_from_tx( - &locked_wallet, - new_txid, - payment.id, - &fee_bumped_tx, - PaymentStatus::Pending, - ConfirmationStatus::Unconfirmed, - ); - - let pending_payment_store = - self.create_pending_payment_from_tx(new_payment.clone(), Vec::new()); - - self.runtime - .block_on(self.pending_payment_store.insert_or_update(pending_payment_store))?; - self.runtime.block_on(self.payment_store.insert_or_update(new_payment))?; + self.pending_payment_store.insert_or_update(pending_payment_store).await?; + self.payment_store.insert_or_update(new_payment).await?; log_info!(self.logger, "RBF successful: replaced {} with {}", txid, new_txid); @@ -1456,64 +1584,67 @@ impl Listen for Wallet { } fn block_connected(&self, block: &bitcoin::Block, height: u32) { - let mut locked_wallet = self.inner.lock().expect("lock"); + let events = { + let mut locked_wallet = self.inner.lock().expect("lock"); - let pre_checkpoint = locked_wallet.latest_checkpoint(); - if pre_checkpoint.height() != height - 1 - || pre_checkpoint.hash() != block.header.prev_blockhash - { - log_debug!( - self.logger, - "Detected reorg while applying a connected block to on-chain wallet: new block with hash {} at height {}", - block.header.block_hash(), - height - ); - } + let pre_checkpoint = locked_wallet.latest_checkpoint(); + if pre_checkpoint.height() != height - 1 + || pre_checkpoint.hash() != block.header.prev_blockhash + { + log_debug!( + self.logger, + "Detected reorg while applying a connected block to on-chain wallet: new block with hash {} at height {}", + block.header.block_hash(), + height + ); + } - // In order to be able to reliably calculate fees the `Wallet` needs access to the previous - // ouput data. To this end, we here insert any ouputs of transactions that LDK is intersted - // in (e.g., funding transaction ouputs) into the wallet's transaction graph when we see - // them, so it is reliably able to calculate fees for subsequent spends. - // - // FIXME: technically, we should also do this for mempool transactions. However, at the - // current time fixing the edge case doesn't seem worth the additional conplexity / - // additional overhead.. - let registered_txids = self.chain_source.registered_txids(); - for tx in &block.txdata { - let txid = tx.compute_txid(); - if registered_txids.contains(&txid) { - for (vout, txout) in tx.output.iter().enumerate() { - let outpoint = OutPoint { txid, vout: vout as u32 }; - locked_wallet.insert_txout(outpoint, txout.clone()); + // In order to be able to reliably calculate fees the `Wallet` needs access to the previous + // ouput data. To this end, we here insert any ouputs of transactions that LDK is intersted + // in (e.g., funding transaction ouputs) into the wallet's transaction graph when we see + // them, so it is reliably able to calculate fees for subsequent spends. + // + // FIXME: technically, we should also do this for mempool transactions. However, at the + // current time fixing the edge case doesn't seem worth the additional conplexity / + // additional overhead.. + let registered_txids = self.chain_source.registered_txids(); + for tx in &block.txdata { + let txid = tx.compute_txid(); + if registered_txids.contains(&txid) { + for (vout, txout) in tx.output.iter().enumerate() { + let outpoint = OutPoint { txid, vout: vout as u32 }; + locked_wallet.insert_txout(outpoint, txout.clone()); + } } } - } - match locked_wallet.apply_block_events(block, height) { - Ok(events) => { - if let Err(e) = self.update_payment_store(&mut *locked_wallet, events) { - log_error!(self.logger, "Failed to update payment store: {}", e); + let events = match locked_wallet.apply_block_events(block, height) { + Ok(events) => events, + Err(e) => { + log_error!( + self.logger, + "Failed to apply connected block to on-chain wallet: {}", + e + ); return; - } - }, - Err(e) => { - log_error!( - self.logger, - "Failed to apply connected block to on-chain wallet: {}", - e - ); - return; - }, + }, + }; + self.queue_staged_wallet_changes_from(&mut locked_wallet); + events }; - let mut locked_persister = self.persister.lock().expect("lock"); - match self.runtime.block_on(locked_wallet.persist_async(&mut locked_persister)) { - Ok(_) => (), - Err(e) => { - log_error!(self.logger, "Failed to persist on-chain wallet: {}", e); + let wallet = self.clone(); + let runtime = Arc::clone(&self.runtime); + let logger = Arc::clone(&self.logger); + runtime.spawn_background_task(async move { + if let Err(e) = wallet.update_payment_store(events).await { + log_error!(logger, "Failed to update payment store: {}", e); return; - }, - }; + } + if let Err(e) = wallet.persist_queued_wallet_changes().await { + log_error!(logger, "Failed to persist on-chain wallet: {}", e); + } + }); } fn blocks_disconnected(&self, _fork_point_block: BlockLocator) { @@ -1531,7 +1662,7 @@ impl WalletSource for Wallet { } fn get_change_script<'a>(&'a self) -> impl Future> + Send + 'a { - async move { self.get_change_script_inner() } + async move { self.get_change_script_inner().await } } fn get_prevtx<'a>( @@ -1568,7 +1699,7 @@ impl CoinSelectionSource for Wallet { ) -> impl Future> + Send + 'a { debug_assert!(claim_id.is_none()); let fee_rate = FeeRate::from_sat_per_kwu(target_feerate_sat_per_1000_weight as u64); - async move { self.select_confirmed_utxos(must_spend, must_pay_to, fee_rate) } + async move { self.select_confirmed_utxos(must_spend, must_pay_to, fee_rate).await } } fn sign_psbt<'a>( @@ -1692,16 +1823,12 @@ impl SignerProvider for WalletKeysManager { } fn get_destination_script(&self, _channel_keys_id: [u8; 32]) -> Result { - let address = self.wallet.get_new_address().map_err(|e| { - log_error!(self.logger, "Failed to retrieve new address from wallet: {}", e); - })?; + let address = self.wallet.get_new_address_sync(); Ok(address.script_pubkey()) } fn get_shutdown_scriptpubkey(&self) -> Result { - let address = self.wallet.get_new_address().map_err(|e| { - log_error!(self.logger, "Failed to retrieve new address from wallet: {}", e); - })?; + let address = self.wallet.get_new_address_sync(); match address.witness_program() { Some(program) => ShutdownScript::new_witness_program(&program).map_err(|e| { @@ -1725,6 +1852,7 @@ impl ChangeDestinationSource for WalletKeysManager { async move { self.wallet .get_new_internal_address() + .await .map_err(|e| { log_error!(self.logger, "Failed to retrieve new address from wallet: {}", e); }) diff --git a/tests/common/mod.rs b/tests/common/mod.rs index d7775e67b3..c8e195e2de 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -83,7 +83,7 @@ macro_rules! expect_event { match event { ref e @ Event::$event_type { .. } => { println!("{} got event {:?}", $node.node_id(), e); - $node.event_handled().unwrap(); + $node.event_handled().await.unwrap(); }, ref e => { panic!("{} got unexpected event!: {:?}", std::stringify!($node), e); @@ -108,7 +108,7 @@ macro_rules! expect_channel_pending_event { ref e @ Event::ChannelPending { funding_txo, counterparty_node_id, .. } => { println!("{} got event {:?}", $node.node_id(), e); assert_eq!(counterparty_node_id, $counterparty_node_id); - $node.event_handled().unwrap(); + $node.event_handled().await.unwrap(); funding_txo }, ref e => { @@ -134,7 +134,7 @@ macro_rules! expect_channel_ready_event { ref e @ Event::ChannelReady { user_channel_id, counterparty_node_id, .. } => { println!("{} got event {:?}", $node.node_id(), e); assert_eq!(counterparty_node_id, Some($counterparty_node_id)); - $node.event_handled().unwrap(); + $node.event_handled().await.unwrap(); user_channel_id }, ref e => { @@ -162,7 +162,7 @@ macro_rules! expect_channel_ready_events { ref e @ Event::ChannelReady { counterparty_node_id, .. } => { println!("{} got event {:?}", $node.node_id(), e); ids.push(counterparty_node_id); - $node.event_handled().unwrap(); + $node.event_handled().await.unwrap(); }, ref e => { panic!("{} got unexpected event!: {:?}", std::stringify!($node), e); @@ -196,7 +196,7 @@ macro_rules! expect_splice_negotiated_event { ref e @ Event::SpliceNegotiated { new_funding_txo, counterparty_node_id, .. } => { println!("{} got event {:?}", $node.node_id(), e); assert_eq!(counterparty_node_id, $counterparty_node_id); - $node.event_handled().unwrap(); + $node.event_handled().await.unwrap(); new_funding_txo }, ref e => { @@ -222,11 +222,11 @@ macro_rules! expect_payment_received_event { ref e @ Event::PaymentReceived { payment_id, amount_msat, .. } => { println!("{} got event {:?}", $node.node_id(), e); assert_eq!(amount_msat, $amount_msat); - let payment = $node.payment(&payment_id.unwrap()).unwrap(); + let payment = $node.payment(&payment_id.unwrap()).await.unwrap(); if !matches!(payment.kind, ldk_node::payment::PaymentKind::Onchain { .. }) { assert_eq!(payment.fee_paid_msat, None); } - $node.event_handled().unwrap(); + $node.event_handled().await.unwrap(); payment_id }, ref e => { @@ -262,7 +262,7 @@ macro_rules! expect_payment_claimable_event { assert_eq!(payment_hash, $payment_hash); assert_eq!(payment_id, $payment_id); assert_eq!(claimable_amount_msat, $claimable_amount_msat); - $node.event_handled().unwrap(); + $node.event_handled().await.unwrap(); claimable_amount_msat }, ref e => { @@ -290,10 +290,10 @@ macro_rules! expect_payment_successful_event { if let Some(fee_msat) = $fee_paid_msat { assert_eq!(fee_paid_msat, fee_msat); } - let payment = $node.payment(&$payment_id.unwrap()).unwrap(); + let payment = $node.payment(&$payment_id.unwrap()).await.unwrap(); assert_eq!(payment.fee_paid_msat, fee_paid_msat); assert_eq!(payment_id, $payment_id); - $node.event_handled().unwrap(); + $node.event_handled().await.unwrap(); }, ref e => { panic!("{} got unexpected event!: {:?}", std::stringify!(node_b), e); @@ -473,8 +473,8 @@ pub(crate) use setup_builder; #[cfg(any(cln_test, lnd_test, eclair_test))] pub(crate) mod scenarios; -pub(crate) fn setup_two_nodes( - chain_source: &TestChainSource, allow_0conf: bool, anchor_channels: bool, +pub(crate) async fn setup_two_nodes( + chain_source: &TestChainSource<'_>, allow_0conf: bool, anchor_channels: bool, anchors_trusted_no_reserve: bool, ) -> (TestNode, TestNode) { setup_two_nodes_with_store( @@ -484,10 +484,11 @@ pub(crate) fn setup_two_nodes( anchors_trusted_no_reserve, TestStoreType::TestSyncStore, ) + .await } -pub(crate) fn setup_two_nodes_with_store( - chain_source: &TestChainSource, allow_0conf: bool, anchor_channels: bool, +pub(crate) async fn setup_two_nodes_with_store( + chain_source: &TestChainSource<'_>, allow_0conf: bool, anchor_channels: bool, anchors_trusted_no_reserve: bool, store_type: TestStoreType, ) -> (TestNode, TestNode) { println!("== Node A =="); @@ -499,7 +500,7 @@ pub(crate) fn setup_two_nodes_with_store( HumanReadableNamesConfig { resolution_config: HRNResolverConfig::Blip32 }; } - let node_a = setup_node(chain_source, config_a); + let node_a = setup_node(chain_source, config_a).await; println!("\n== Node B =="); let mut config_b = random_config(anchor_channels); @@ -526,11 +527,11 @@ pub(crate) fn setup_two_nodes_with_store( .trusted_peers_no_reserve .push(node_a.node_id()); } - let node_b = setup_node(chain_source, config_b); + let node_b = setup_node(chain_source, config_b).await; (node_a, node_b) } -pub(crate) fn setup_node(chain_source: &TestChainSource, config: TestConfig) -> TestNode { +pub(crate) async fn setup_node(chain_source: &TestChainSource<'_>, config: TestConfig) -> TestNode { setup_builder!(builder, config.node_config); match chain_source { TestChainSource::Esplora(electrsd) => { @@ -593,11 +594,11 @@ pub(crate) fn setup_node(chain_source: &TestChainSource, config: TestConfig) -> let node = match config.store_type { TestStoreType::TestSyncStore => { let kv_store = TestSyncStore::new(config.node_config.storage_dir_path.into()); - builder.build_with_store(config.node_entropy.into(), kv_store).unwrap() + builder.build_with_store(config.node_entropy.into(), kv_store).await.unwrap() }, - TestStoreType::Sqlite => builder.build(config.node_entropy.into()).unwrap(), + TestStoreType::Sqlite => builder.build(config.node_entropy.into()).await.unwrap(), TestStoreType::FilesystemStore => { - builder.build_with_fs_store(config.node_entropy.into()).unwrap() + builder.build_with_fs_store(config.node_entropy.into()).await.unwrap() }, }; @@ -605,9 +606,7 @@ pub(crate) fn setup_node(chain_source: &TestChainSource, config: TestConfig) -> builder.set_wallet_recovery_mode(); } - node.start().unwrap(); - assert!(node.status().is_running); - assert!(node.status().latest_fee_rate_cache_update_timestamp.is_some()); + node.start().await.unwrap(); node } @@ -836,6 +835,7 @@ pub async fn open_channel_push_amt( push_amount_msat, None, ) + .await .unwrap(); } else { node_a @@ -846,9 +846,10 @@ pub async fn open_channel_push_amt( push_amount_msat, None, ) + .await .unwrap(); } - assert!(node_a.list_peers().iter().find(|c| { c.node_id == node_b.node_id() }).is_some()); + assert!(node_a.list_peers().await.iter().find(|c| { c.node_id == node_b.node_id() }).is_some()); let funding_txo_a = expect_channel_pending_event!(node_a, node_b.node_id()); let funding_txo_b = expect_channel_pending_event!(node_b, node_a.node_id()); @@ -869,6 +870,7 @@ pub async fn open_channel_with_all( None, None, ) + .await .unwrap(); } else { node_a @@ -878,9 +880,10 @@ pub async fn open_channel_with_all( None, None, ) + .await .unwrap(); } - assert!(node_a.list_peers().iter().find(|c| { c.node_id == node_b.node_id() }).is_some()); + assert!(node_a.list_peers().await.iter().find(|c| { c.node_id == node_b.node_id() }).is_some()); let funding_txo_a = expect_channel_pending_event!(node_a, node_b.node_id()); let funding_txo_b = expect_channel_pending_event!(node_b, node_a.node_id()); @@ -893,7 +896,7 @@ pub async fn open_channel_with_all( pub async fn splice_in_with_all( node_a: &TestNode, node_b: &TestNode, user_channel_id: &UserChannelId, electrsd: &ElectrsD, ) { - node_a.splice_in_with_all(user_channel_id, node_b.node_id()).unwrap(); + node_a.splice_in_with_all(user_channel_id, node_b.node_id()).await.unwrap(); let splice_txo = expect_splice_negotiated_event!(node_a, node_b.node_id()); expect_splice_negotiated_event!(node_b, node_a.node_id()); @@ -904,8 +907,8 @@ pub(crate) async fn do_channel_full_cycle( node_a: TestNode, node_b: TestNode, bitcoind: &BitcoindClient, electrsd: &E, allow_0conf: bool, disable_node_b_reserve: bool, expect_anchor_channel: bool, force_close: bool, ) { - let addr_a = node_a.onchain_payment().new_address().unwrap(); - let addr_b = node_b.onchain_payment().new_address().unwrap(); + let addr_a = node_a.onchain_payment().new_address().await.unwrap(); + let addr_b = node_b.onchain_payment().new_address().await.unwrap(); let premine_amount_sat = if expect_anchor_channel { 2_125_000 } else { 2_100_000 }; @@ -916,8 +919,8 @@ pub(crate) async fn do_channel_full_cycle( Amount::from_sat(premine_amount_sat), ) .await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); assert_eq!(node_a.list_balances().spendable_onchain_balance_sats, premine_amount_sat); assert_eq!(node_b.list_balances().spendable_onchain_balance_sats, premine_amount_sat); @@ -926,6 +929,7 @@ pub(crate) async fn do_channel_full_cycle( node_a .list_payments_with_filter(|p| p.direction == PaymentDirection::Inbound && matches!(p.kind, PaymentKind::Onchain { .. })) + .await .len(), 1 ); @@ -933,6 +937,7 @@ pub(crate) async fn do_channel_full_cycle( node_a .list_payments_with_filter(|p| p.direction == PaymentDirection::Outbound && matches!(p.kind, PaymentKind::Onchain { .. })) + .await .len(), 0 ); @@ -940,6 +945,7 @@ pub(crate) async fn do_channel_full_cycle( node_b .list_payments_with_filter(|p| p.direction == PaymentDirection::Inbound && matches!(p.kind, PaymentKind::Onchain { .. })) + .await .len(), 1 ); @@ -947,6 +953,7 @@ pub(crate) async fn do_channel_full_cycle( node_b .list_payments_with_filter(|p| p.direction == PaymentDirection::Outbound && matches!(p.kind, PaymentKind::Onchain { .. })) + .await .len(), 0 ); @@ -967,6 +974,7 @@ pub(crate) async fn do_channel_full_cycle( Some(push_msat), None, ) + .await .unwrap(); } else { node_a @@ -977,11 +985,12 @@ pub(crate) async fn do_channel_full_cycle( Some(push_msat), None, ) + .await .unwrap(); } - assert_eq!(node_a.list_peers().first().unwrap().node_id, node_b.node_id()); - assert!(node_a.list_peers().first().unwrap().is_persisted); + assert_eq!(node_a.list_peers().await.first().unwrap().node_id, node_b.node_id()); + assert!(node_a.list_peers().await.first().unwrap().is_persisted); let funding_txo_a = expect_channel_pending_event!(node_a, node_b.node_id()); let funding_txo_b = expect_channel_pending_event!(node_b, node_a.node_id()); assert_eq!(funding_txo_a, funding_txo_b); @@ -992,14 +1001,15 @@ pub(crate) async fn do_channel_full_cycle( generate_blocks_and_wait(&bitcoind, electrsd, 6).await; } - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); // Check we now see the channel funding transaction as outbound. assert_eq!( node_a .list_payments_with_filter(|p| p.direction == PaymentDirection::Outbound && matches!(p.kind, PaymentKind::Onchain { .. })) + .await .len(), 1 ); @@ -1069,32 +1079,46 @@ pub(crate) async fn do_channel_full_cycle( let invoice = node_b .bolt11_payment() .receive(invoice_amount_1_msat, &invoice_description.clone().into(), 9217) + .await .unwrap(); println!("\nA send"); - let payment_id = node_a.bolt11_payment().send(&invoice, None).unwrap(); - assert_eq!(node_a.bolt11_payment().send(&invoice, None), Err(NodeError::DuplicatePayment)); + let payment_id = node_a.bolt11_payment().send(&invoice, None).await.unwrap(); + assert_eq!( + node_a.bolt11_payment().send(&invoice, None).await, + Err(NodeError::DuplicatePayment) + ); - assert!(!node_a.list_payments_with_filter(|p| p.id == payment_id).is_empty()); + assert!(!node_a.list_payments_with_filter(|p| p.id == payment_id).await.is_empty()); - let outbound_payments_a = node_a.list_payments_with_filter(|p| { - p.direction == PaymentDirection::Outbound && matches!(p.kind, PaymentKind::Bolt11 { .. }) - }); + let outbound_payments_a = node_a + .list_payments_with_filter(|p| { + p.direction == PaymentDirection::Outbound + && matches!(p.kind, PaymentKind::Bolt11 { .. }) + }) + .await; assert_eq!(outbound_payments_a.len(), 1); - let inbound_payments_a = node_a.list_payments_with_filter(|p| { - p.direction == PaymentDirection::Inbound && matches!(p.kind, PaymentKind::Bolt11 { .. }) - }); + let inbound_payments_a = node_a + .list_payments_with_filter(|p| { + p.direction == PaymentDirection::Inbound && matches!(p.kind, PaymentKind::Bolt11 { .. }) + }) + .await; assert_eq!(inbound_payments_a.len(), 0); - let outbound_payments_b = node_b.list_payments_with_filter(|p| { - p.direction == PaymentDirection::Outbound && matches!(p.kind, PaymentKind::Bolt11 { .. }) - }); + let outbound_payments_b = node_b + .list_payments_with_filter(|p| { + p.direction == PaymentDirection::Outbound + && matches!(p.kind, PaymentKind::Bolt11 { .. }) + }) + .await; assert_eq!(outbound_payments_b.len(), 0); - let inbound_payments_b = node_b.list_payments_with_filter(|p| { - p.direction == PaymentDirection::Inbound && matches!(p.kind, PaymentKind::Bolt11 { .. }) - }); + let inbound_payments_b = node_b + .list_payments_with_filter(|p| { + p.direction == PaymentDirection::Inbound && matches!(p.kind, PaymentKind::Bolt11 { .. }) + }) + .await; assert_eq!(inbound_payments_b.len(), 1); // Verify bolt12_invoice is None for BOLT11 payments @@ -1102,59 +1126,67 @@ pub(crate) async fn do_channel_full_cycle( ref e @ Event::PaymentSuccessful { ref bolt12_invoice, .. } => { println!("{} got event {:?}", node_a.node_id(), e); assert!(bolt12_invoice.is_none(), "bolt12_invoice should be None for BOLT11 payments"); - node_a.event_handled().unwrap(); + node_a.event_handled().await.unwrap(); }, ref e => { panic!("{} got unexpected event!: {:?}", std::stringify!(node_a), e); }, } expect_event!(node_b, PaymentReceived); - assert_eq!(node_a.payment(&payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_a.payment(&payment_id).unwrap().direction, PaymentDirection::Outbound); - assert_eq!(node_a.payment(&payment_id).unwrap().amount_msat, Some(invoice_amount_1_msat)); - assert!(matches!(node_a.payment(&payment_id).unwrap().kind, PaymentKind::Bolt11 { .. })); - assert_eq!(node_b.payment(&payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_b.payment(&payment_id).unwrap().direction, PaymentDirection::Inbound); - assert_eq!(node_b.payment(&payment_id).unwrap().amount_msat, Some(invoice_amount_1_msat)); - assert!(matches!(node_b.payment(&payment_id).unwrap().kind, PaymentKind::Bolt11 { .. })); + assert_eq!(node_a.payment(&payment_id).await.unwrap().status, PaymentStatus::Succeeded); + assert_eq!(node_a.payment(&payment_id).await.unwrap().direction, PaymentDirection::Outbound); + assert_eq!(node_a.payment(&payment_id).await.unwrap().amount_msat, Some(invoice_amount_1_msat)); + assert!(matches!(node_a.payment(&payment_id).await.unwrap().kind, PaymentKind::Bolt11 { .. })); + assert_eq!(node_b.payment(&payment_id).await.unwrap().status, PaymentStatus::Succeeded); + assert_eq!(node_b.payment(&payment_id).await.unwrap().direction, PaymentDirection::Inbound); + assert_eq!(node_b.payment(&payment_id).await.unwrap().amount_msat, Some(invoice_amount_1_msat)); + assert!(matches!(node_b.payment(&payment_id).await.unwrap().kind, PaymentKind::Bolt11 { .. })); // Assert we fail duplicate outbound payments and check the status hasn't changed. - assert_eq!(Err(NodeError::DuplicatePayment), node_a.bolt11_payment().send(&invoice, None)); - assert_eq!(node_a.payment(&payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_a.payment(&payment_id).unwrap().direction, PaymentDirection::Outbound); - assert_eq!(node_a.payment(&payment_id).unwrap().amount_msat, Some(invoice_amount_1_msat)); - assert_eq!(node_b.payment(&payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_b.payment(&payment_id).unwrap().direction, PaymentDirection::Inbound); - assert_eq!(node_b.payment(&payment_id).unwrap().amount_msat, Some(invoice_amount_1_msat)); + assert_eq!( + Err(NodeError::DuplicatePayment), + node_a.bolt11_payment().send(&invoice, None).await + ); + assert_eq!(node_a.payment(&payment_id).await.unwrap().status, PaymentStatus::Succeeded); + assert_eq!(node_a.payment(&payment_id).await.unwrap().direction, PaymentDirection::Outbound); + assert_eq!(node_a.payment(&payment_id).await.unwrap().amount_msat, Some(invoice_amount_1_msat)); + assert_eq!(node_b.payment(&payment_id).await.unwrap().status, PaymentStatus::Succeeded); + assert_eq!(node_b.payment(&payment_id).await.unwrap().direction, PaymentDirection::Inbound); + assert_eq!(node_b.payment(&payment_id).await.unwrap().amount_msat, Some(invoice_amount_1_msat)); // Test under-/overpayment let invoice_amount_2_msat = 2500_000; let invoice = node_b .bolt11_payment() .receive(invoice_amount_2_msat, &invoice_description.clone().into(), 9217) + .await .unwrap(); let underpaid_amount = invoice_amount_2_msat - 1; assert_eq!( Err(NodeError::InvalidAmount), - node_a.bolt11_payment().send_using_amount(&invoice, underpaid_amount, None) + node_a.bolt11_payment().send_using_amount(&invoice, underpaid_amount, None).await ); println!("\nB overpaid receive"); let invoice = node_b .bolt11_payment() .receive(invoice_amount_2_msat, &invoice_description.clone().into(), 9217) + .await .unwrap(); let overpaid_amount_msat = invoice_amount_2_msat + 100; println!("\nA overpaid send"); - let payment_id = - node_a.bolt11_payment().send_using_amount(&invoice, overpaid_amount_msat, None).unwrap(); + let payment_id = node_a + .bolt11_payment() + .send_using_amount(&invoice, overpaid_amount_msat, None) + .await + .unwrap(); expect_event!(node_a, PaymentSuccessful); let received_amount = match node_b.next_event_async().await { ref e @ Event::PaymentReceived { amount_msat, .. } => { println!("{} got event {:?}", std::stringify!(node_b), e); - node_b.event_handled().unwrap(); + node_b.event_handled().await.unwrap(); amount_msat }, ref e => { @@ -1162,37 +1194,39 @@ pub(crate) async fn do_channel_full_cycle( }, }; assert_eq!(received_amount, overpaid_amount_msat); - assert_eq!(node_a.payment(&payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_a.payment(&payment_id).unwrap().direction, PaymentDirection::Outbound); - assert_eq!(node_a.payment(&payment_id).unwrap().amount_msat, Some(overpaid_amount_msat)); - assert!(matches!(node_a.payment(&payment_id).unwrap().kind, PaymentKind::Bolt11 { .. })); - assert_eq!(node_b.payment(&payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_b.payment(&payment_id).unwrap().direction, PaymentDirection::Inbound); - assert_eq!(node_b.payment(&payment_id).unwrap().amount_msat, Some(overpaid_amount_msat)); - assert!(matches!(node_b.payment(&payment_id).unwrap().kind, PaymentKind::Bolt11 { .. })); + assert_eq!(node_a.payment(&payment_id).await.unwrap().status, PaymentStatus::Succeeded); + assert_eq!(node_a.payment(&payment_id).await.unwrap().direction, PaymentDirection::Outbound); + assert_eq!(node_a.payment(&payment_id).await.unwrap().amount_msat, Some(overpaid_amount_msat)); + assert!(matches!(node_a.payment(&payment_id).await.unwrap().kind, PaymentKind::Bolt11 { .. })); + assert_eq!(node_b.payment(&payment_id).await.unwrap().status, PaymentStatus::Succeeded); + assert_eq!(node_b.payment(&payment_id).await.unwrap().direction, PaymentDirection::Inbound); + assert_eq!(node_b.payment(&payment_id).await.unwrap().amount_msat, Some(overpaid_amount_msat)); + assert!(matches!(node_b.payment(&payment_id).await.unwrap().kind, PaymentKind::Bolt11 { .. })); // Test "zero-amount" invoice payment println!("\nB receive_variable_amount_payment"); let variable_amount_invoice = node_b .bolt11_payment() .receive_variable_amount(&invoice_description.clone().into(), 9217) + .await .unwrap(); let determined_amount_msat = 2345_678; assert_eq!( Err(NodeError::InvalidInvoice), - node_a.bolt11_payment().send(&variable_amount_invoice, None) + node_a.bolt11_payment().send(&variable_amount_invoice, None).await ); println!("\nA send_using_amount"); let payment_id = node_a .bolt11_payment() .send_using_amount(&variable_amount_invoice, determined_amount_msat, None) + .await .unwrap(); expect_event!(node_a, PaymentSuccessful); let received_amount = match node_b.next_event_async().await { ref e @ Event::PaymentReceived { amount_msat, .. } => { println!("{} got event {:?}", std::stringify!(node_b), e); - node_b.event_handled().unwrap(); + node_b.event_handled().await.unwrap(); amount_msat }, ref e => { @@ -1200,14 +1234,20 @@ pub(crate) async fn do_channel_full_cycle( }, }; assert_eq!(received_amount, determined_amount_msat); - assert_eq!(node_a.payment(&payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_a.payment(&payment_id).unwrap().direction, PaymentDirection::Outbound); - assert_eq!(node_a.payment(&payment_id).unwrap().amount_msat, Some(determined_amount_msat)); - assert!(matches!(node_a.payment(&payment_id).unwrap().kind, PaymentKind::Bolt11 { .. })); - assert_eq!(node_b.payment(&payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_b.payment(&payment_id).unwrap().direction, PaymentDirection::Inbound); - assert_eq!(node_b.payment(&payment_id).unwrap().amount_msat, Some(determined_amount_msat)); - assert!(matches!(node_b.payment(&payment_id).unwrap().kind, PaymentKind::Bolt11 { .. })); + assert_eq!(node_a.payment(&payment_id).await.unwrap().status, PaymentStatus::Succeeded); + assert_eq!(node_a.payment(&payment_id).await.unwrap().direction, PaymentDirection::Outbound); + assert_eq!( + node_a.payment(&payment_id).await.unwrap().amount_msat, + Some(determined_amount_msat) + ); + assert!(matches!(node_a.payment(&payment_id).await.unwrap().kind, PaymentKind::Bolt11 { .. })); + assert_eq!(node_b.payment(&payment_id).await.unwrap().status, PaymentStatus::Succeeded); + assert_eq!(node_b.payment(&payment_id).await.unwrap().direction, PaymentDirection::Inbound); + assert_eq!( + node_b.payment(&payment_id).await.unwrap().amount_msat, + Some(determined_amount_msat) + ); + assert!(matches!(node_b.payment(&payment_id).await.unwrap().kind, PaymentKind::Bolt11 { .. })); // Test claiming manually registered payments. let invoice_amount_3_msat = 5_532_000; @@ -1221,8 +1261,9 @@ pub(crate) async fn do_channel_full_cycle( 9217, manual_payment_hash, ) + .await .unwrap(); - let manual_payment_id = node_a.bolt11_payment().send(&manual_invoice, None).unwrap(); + let manual_payment_id = node_a.bolt11_payment().send(&manual_invoice, None).await.unwrap(); let claimable_amount_msat = expect_payment_claimable_event!( node_b, @@ -1233,23 +1274,36 @@ pub(crate) async fn do_channel_full_cycle( node_b .bolt11_payment() .claim_for_hash(manual_payment_hash, claimable_amount_msat, manual_preimage) + .await .unwrap(); expect_payment_received_event!(node_b, claimable_amount_msat); expect_payment_successful_event!(node_a, Some(manual_payment_id), None); - assert_eq!(node_a.payment(&manual_payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_a.payment(&manual_payment_id).unwrap().direction, PaymentDirection::Outbound); + assert_eq!(node_a.payment(&manual_payment_id).await.unwrap().status, PaymentStatus::Succeeded); assert_eq!( - node_a.payment(&manual_payment_id).unwrap().amount_msat, + node_a.payment(&manual_payment_id).await.unwrap().direction, + PaymentDirection::Outbound + ); + assert_eq!( + node_a.payment(&manual_payment_id).await.unwrap().amount_msat, Some(invoice_amount_3_msat) ); - assert!(matches!(node_a.payment(&manual_payment_id).unwrap().kind, PaymentKind::Bolt11 { .. })); - assert_eq!(node_b.payment(&manual_payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_b.payment(&manual_payment_id).unwrap().direction, PaymentDirection::Inbound); + assert!(matches!( + node_a.payment(&manual_payment_id).await.unwrap().kind, + PaymentKind::Bolt11 { .. } + )); + assert_eq!(node_b.payment(&manual_payment_id).await.unwrap().status, PaymentStatus::Succeeded); + assert_eq!( + node_b.payment(&manual_payment_id).await.unwrap().direction, + PaymentDirection::Inbound + ); assert_eq!( - node_b.payment(&manual_payment_id).unwrap().amount_msat, + node_b.payment(&manual_payment_id).await.unwrap().amount_msat, Some(invoice_amount_3_msat) ); - assert!(matches!(node_b.payment(&manual_payment_id).unwrap().kind, PaymentKind::Bolt11 { .. })); + assert!(matches!( + node_b.payment(&manual_payment_id).await.unwrap().kind, + PaymentKind::Bolt11 { .. } + )); // Test failing manually registered payments. let invoice_amount_4_msat = 5_532_000; @@ -1264,8 +1318,10 @@ pub(crate) async fn do_channel_full_cycle( 9217, manual_fail_payment_hash, ) + .await .unwrap(); - let manual_fail_payment_id = node_a.bolt11_payment().send(&manual_fail_invoice, None).unwrap(); + let manual_fail_payment_id = + node_a.bolt11_payment().send(&manual_fail_invoice, None).await.unwrap(); expect_payment_claimable_event!( node_b, @@ -1273,32 +1329,38 @@ pub(crate) async fn do_channel_full_cycle( manual_fail_payment_hash, invoice_amount_4_msat ); - node_b.bolt11_payment().fail_for_hash(manual_fail_payment_hash).unwrap(); + node_b.bolt11_payment().fail_for_hash(manual_fail_payment_hash).await.unwrap(); expect_event!(node_a, PaymentFailed); - assert_eq!(node_a.payment(&manual_fail_payment_id).unwrap().status, PaymentStatus::Failed); assert_eq!( - node_a.payment(&manual_fail_payment_id).unwrap().direction, + node_a.payment(&manual_fail_payment_id).await.unwrap().status, + PaymentStatus::Failed + ); + assert_eq!( + node_a.payment(&manual_fail_payment_id).await.unwrap().direction, PaymentDirection::Outbound ); assert_eq!( - node_a.payment(&manual_fail_payment_id).unwrap().amount_msat, + node_a.payment(&manual_fail_payment_id).await.unwrap().amount_msat, Some(invoice_amount_4_msat) ); assert!(matches!( - node_a.payment(&manual_fail_payment_id).unwrap().kind, + node_a.payment(&manual_fail_payment_id).await.unwrap().kind, PaymentKind::Bolt11 { .. } )); - assert_eq!(node_b.payment(&manual_fail_payment_id).unwrap().status, PaymentStatus::Failed); assert_eq!( - node_b.payment(&manual_fail_payment_id).unwrap().direction, + node_b.payment(&manual_fail_payment_id).await.unwrap().status, + PaymentStatus::Failed + ); + assert_eq!( + node_b.payment(&manual_fail_payment_id).await.unwrap().direction, PaymentDirection::Inbound ); assert_eq!( - node_b.payment(&manual_fail_payment_id).unwrap().amount_msat, + node_b.payment(&manual_fail_payment_id).await.unwrap().amount_msat, Some(invoice_amount_4_msat) ); assert!(matches!( - node_b.payment(&manual_fail_payment_id).unwrap().kind, + node_b.payment(&manual_fail_payment_id).await.unwrap().kind, PaymentKind::Bolt11 { .. } )); @@ -1309,13 +1371,14 @@ pub(crate) async fn do_channel_full_cycle( let keysend_payment_id = node_a .spontaneous_payment() .send_with_custom_tlvs(keysend_amount_msat, node_b.node_id(), None, custom_tlvs.clone()) + .await .unwrap(); expect_event!(node_a, PaymentSuccessful); let next_event = node_b.next_event_async().await; let (received_keysend_amount, received_custom_records) = match next_event { ref e @ Event::PaymentReceived { amount_msat, ref custom_records, .. } => { println!("{} got event {:?}", std::stringify!(node_b), e); - node_b.event_handled().unwrap(); + node_b.event_handled().await.unwrap(); (amount_msat, custom_records) }, ref e => { @@ -1323,38 +1386,58 @@ pub(crate) async fn do_channel_full_cycle( }, }; assert_eq!(received_keysend_amount, keysend_amount_msat); - assert_eq!(node_a.payment(&keysend_payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_a.payment(&keysend_payment_id).unwrap().direction, PaymentDirection::Outbound); - assert_eq!(node_a.payment(&keysend_payment_id).unwrap().amount_msat, Some(keysend_amount_msat)); + assert_eq!(node_a.payment(&keysend_payment_id).await.unwrap().status, PaymentStatus::Succeeded); + assert_eq!( + node_a.payment(&keysend_payment_id).await.unwrap().direction, + PaymentDirection::Outbound + ); + assert_eq!( + node_a.payment(&keysend_payment_id).await.unwrap().amount_msat, + Some(keysend_amount_msat) + ); assert!(matches!( - node_a.payment(&keysend_payment_id).unwrap().kind, + node_a.payment(&keysend_payment_id).await.unwrap().kind, PaymentKind::Spontaneous { .. } )); assert_eq!(received_custom_records, &custom_tlvs); - assert_eq!(node_b.payment(&keysend_payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_b.payment(&keysend_payment_id).unwrap().direction, PaymentDirection::Inbound); - assert_eq!(node_b.payment(&keysend_payment_id).unwrap().amount_msat, Some(keysend_amount_msat)); + assert_eq!(node_b.payment(&keysend_payment_id).await.unwrap().status, PaymentStatus::Succeeded); + assert_eq!( + node_b.payment(&keysend_payment_id).await.unwrap().direction, + PaymentDirection::Inbound + ); + assert_eq!( + node_b.payment(&keysend_payment_id).await.unwrap().amount_msat, + Some(keysend_amount_msat) + ); assert!(matches!( - node_b.payment(&keysend_payment_id).unwrap().kind, + node_b.payment(&keysend_payment_id).await.unwrap().kind, PaymentKind::Spontaneous { .. } )); assert_eq!( - node_a.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Bolt11 { .. })).len(), + node_a + .list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Bolt11 { .. })) + .await + .len(), 5 ); assert_eq!( - node_b.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Bolt11 { .. })).len(), + node_b + .list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Bolt11 { .. })) + .await + .len(), 6 ); assert_eq!( node_a .list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Spontaneous { .. })) + .await .len(), 1 ); assert_eq!( node_b .list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Spontaneous { .. })) + .await .len(), 1 ); @@ -1363,7 +1446,7 @@ pub(crate) async fn do_channel_full_cycle( generate_blocks_and_wait(&bitcoind, electrsd, 1).await; println!("\nB splices out to pay A"); - let addr_a = node_a.onchain_payment().new_address().unwrap(); + let addr_a = node_a.onchain_payment().new_address().await.unwrap(); let splice_out_sat = funding_amount_sat / 2; node_b.splice_out(&user_channel_id_b, node_a.node_id(), &addr_a, splice_out_sat).unwrap(); @@ -1371,8 +1454,8 @@ pub(crate) async fn do_channel_full_cycle( expect_splice_negotiated_event!(node_b, node_a.node_id()); generate_blocks_and_wait(&bitcoind, electrsd, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); expect_channel_ready_event!(node_a, node_b.node_id()); expect_channel_ready_event!(node_b, node_a.node_id()); @@ -1381,20 +1464,21 @@ pub(crate) async fn do_channel_full_cycle( node_a .list_payments_with_filter(|p| p.direction == PaymentDirection::Inbound && matches!(p.kind, PaymentKind::Onchain { .. })) + .await .len(), 2 ); println!("\nA splices in the splice-out payment from B"); let splice_in_sat = splice_out_sat; - node_a.splice_in(&user_channel_id_a, node_b.node_id(), splice_in_sat).unwrap(); + node_a.splice_in(&user_channel_id_a, node_b.node_id(), splice_in_sat).await.unwrap(); expect_splice_negotiated_event!(node_a, node_b.node_id()); expect_splice_negotiated_event!(node_b, node_a.node_id()); generate_blocks_and_wait(&bitcoind, electrsd, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); expect_channel_ready_event!(node_a, node_b.node_id()); expect_channel_ready_event!(node_b, node_a.node_id()); @@ -1403,6 +1487,7 @@ pub(crate) async fn do_channel_full_cycle( node_a .list_payments_with_filter(|p| p.direction == PaymentDirection::Outbound && matches!(p.kind, PaymentKind::Onchain { .. })) + .await .len(), 2 ); @@ -1431,11 +1516,19 @@ pub(crate) async fn do_channel_full_cycle( // This is a private channel, so node B can send 100% of the value over assert_eq!(node_b.list_channels()[0].next_outbound_htlc_limit_msat, node_b_capacity_msat); - node_b.spontaneous_payment().send(node_b_capacity_msat, node_a.node_id(), None).unwrap(); + node_b + .spontaneous_payment() + .send(node_b_capacity_msat, node_a.node_id(), None) + .await + .unwrap(); expect_event!(node_b, PaymentSuccessful); expect_event!(node_a, PaymentReceived); - node_a.spontaneous_payment().send(node_b_capacity_msat, node_b.node_id(), None).unwrap(); + node_a + .spontaneous_payment() + .send(node_b_capacity_msat, node_b.node_id(), None) + .await + .unwrap(); expect_event!(node_a, PaymentSuccessful); expect_event!(node_b, PaymentReceived); } @@ -1443,9 +1536,9 @@ pub(crate) async fn do_channel_full_cycle( println!("\nB close_channel (force: {})", force_close); tokio::time::sleep(Duration::from_secs(1)).await; if force_close { - node_a.force_close_channel(&user_channel_id_a, node_b.node_id(), None).unwrap(); + node_a.force_close_channel(&user_channel_id_a, node_b.node_id(), None).await.unwrap(); } else { - node_a.close_channel(&user_channel_id_a, node_b.node_id()).unwrap(); + node_a.close_channel(&user_channel_id_a, node_b.node_id()).await.unwrap(); // The cooperative shutdown may complete before we get to check, but if the channel // is still visible it must already be in a shutdown state. if let Some(channel) = @@ -1468,8 +1561,8 @@ pub(crate) async fn do_channel_full_cycle( wait_for_outpoint_spend(electrsd, funding_txo_b).await; generate_blocks_and_wait(&bitcoind, electrsd, 1).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); if force_close { // Check node_b properly sees all balances and sweeps them. @@ -1481,11 +1574,11 @@ pub(crate) async fn do_channel_full_cycle( .. } => { assert_eq!(counterparty_node_id, node_a.node_id()); - let cur_height = node_b.status().current_best_block.height; + let cur_height = node_b.status().await.current_best_block.height; let blocks_to_go = confirmation_height - cur_height; generate_blocks_and_wait(&bitcoind, electrsd, blocks_to_go as usize).await; - node_b.sync_wallets().unwrap(); - node_a.sync_wallets().unwrap(); + node_b.sync_wallets().await.unwrap(); + node_a.sync_wallets().await.unwrap(); }, _ => panic!("Unexpected balance state!"), } @@ -1497,8 +1590,8 @@ pub(crate) async fn do_channel_full_cycle( _ => panic!("Unexpected balance state!"), } generate_blocks_and_wait(&bitcoind, electrsd, 1).await; - node_b.sync_wallets().unwrap(); - node_a.sync_wallets().unwrap(); + node_b.sync_wallets().await.unwrap(); + node_a.sync_wallets().await.unwrap(); assert!(node_b.list_balances().lightning_balances.is_empty()); assert_eq!(node_b.list_balances().pending_balances_from_channel_closures.len(), 1); @@ -1507,8 +1600,8 @@ pub(crate) async fn do_channel_full_cycle( _ => panic!("Unexpected balance state!"), } generate_blocks_and_wait(&bitcoind, electrsd, 5).await; - node_b.sync_wallets().unwrap(); - node_a.sync_wallets().unwrap(); + node_b.sync_wallets().await.unwrap(); + node_a.sync_wallets().await.unwrap(); assert!(node_b.list_balances().lightning_balances.is_empty()); assert_eq!(node_b.list_balances().pending_balances_from_channel_closures.len(), 1); @@ -1522,11 +1615,11 @@ pub(crate) async fn do_channel_full_cycle( .. } => { assert_eq!(counterparty_node_id, node_b.node_id()); - let cur_height = node_a.status().current_best_block.height; + let cur_height = node_a.status().await.current_best_block.height; let blocks_to_go = confirmation_height - cur_height; generate_blocks_and_wait(&bitcoind, electrsd, blocks_to_go as usize).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); }, _ => panic!("Unexpected balance state!"), } @@ -1538,8 +1631,8 @@ pub(crate) async fn do_channel_full_cycle( _ => panic!("Unexpected balance state!"), } generate_blocks_and_wait(&bitcoind, electrsd, 1).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); assert!(node_a.list_balances().lightning_balances.is_empty()); assert_eq!(node_a.list_balances().pending_balances_from_channel_closures.len(), 1); @@ -1548,8 +1641,8 @@ pub(crate) async fn do_channel_full_cycle( _ => panic!("Unexpected balance state!"), } generate_blocks_and_wait(&bitcoind, electrsd, 5).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); } else { assert_eq!(node_a.list_balances().lightning_balances.len(), 1); assert!(node_a.list_balances().pending_balances_from_channel_closures.is_empty()); @@ -1560,7 +1653,7 @@ pub(crate) async fn do_channel_full_cycle( .. } => { assert_eq!(counterparty_node_id, node_b.node_id()); - let cur_height = node_a.status().current_best_block.height; + let cur_height = node_a.status().await.current_best_block.height; let blocks_to_go = confirmation_height - cur_height; blocks_to_go }, @@ -1576,7 +1669,7 @@ pub(crate) async fn do_channel_full_cycle( .. } => { assert_eq!(counterparty_node_id, node_a.node_id()); - let cur_height = node_b.status().current_best_block.height; + let cur_height = node_b.status().await.current_best_block.height; let blocks_to_go = confirmation_height - cur_height; blocks_to_go }, @@ -1586,8 +1679,8 @@ pub(crate) async fn do_channel_full_cycle( assert_eq!(node_a_blocks_to_go, node_b_blocks_to_go); generate_blocks_and_wait(&bitcoind, electrsd, node_a_blocks_to_go as usize).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); assert!(node_a.list_balances().lightning_balances.is_empty()); assert!(node_a.list_balances().pending_balances_from_channel_closures.is_empty()); @@ -1621,6 +1714,7 @@ pub(crate) async fn do_channel_full_cycle( node_a .list_payments_with_filter(|p| p.direction == PaymentDirection::Inbound && matches!(p.kind, PaymentKind::Onchain { .. })) + .await .len(), 3 ); @@ -1628,6 +1722,7 @@ pub(crate) async fn do_channel_full_cycle( node_b .list_payments_with_filter(|p| p.direction == PaymentDirection::Inbound && matches!(p.kind, PaymentKind::Onchain { .. })) + .await .len(), 2 ); @@ -1636,9 +1731,9 @@ pub(crate) async fn do_channel_full_cycle( assert_eq!(node_a.next_event(), None); assert_eq!(node_b.next_event(), None); - node_a.stop().unwrap(); + node_a.stop().await.unwrap(); println!("\nA stopped"); - node_b.stop().unwrap(); + node_b.stop().await.unwrap(); println!("\nB stopped"); } diff --git a/tests/common/scenarios/channel.rs b/tests/common/scenarios/channel.rs index 74e04127c6..d2b618828c 100644 --- a/tests/common/scenarios/channel.rs +++ b/tests/common/scenarios/channel.rs @@ -53,7 +53,7 @@ pub(crate) async fn cooperative_close( match initiator { Side::Ldk => { let ext_node_id = peer.get_node_id().await.unwrap(); - node.close_channel(user_channel_id, ext_node_id).unwrap(); + node.close_channel(user_channel_id, ext_node_id).await.unwrap(); }, Side::External => { peer.close_channel(ext_channel_id).await.unwrap(); @@ -75,7 +75,7 @@ pub(crate) async fn force_close( match initiator { Side::Ldk => { let ext_node_id = peer.get_node_id().await.unwrap(); - node.force_close_channel(user_channel_id, ext_node_id, None).unwrap(); + node.force_close_channel(user_channel_id, ext_node_id, None).await.unwrap(); expect_event!(node, ChannelClosed); generate_blocks_and_wait(bitcoind, electrs, 6).await; super::sync_wallets_with_retry(node).await; diff --git a/tests/common/scenarios/connectivity.rs b/tests/common/scenarios/connectivity.rs index e24419c76a..516d1fe8b0 100644 --- a/tests/common/scenarios/connectivity.rs +++ b/tests/common/scenarios/connectivity.rs @@ -23,7 +23,7 @@ pub(crate) async fn disconnect_by_side( ) -> Result<(), String> { let ext_node_id = peer.get_node_id().await.unwrap(); match side { - Side::Ldk => node.disconnect(ext_node_id).map_err(|e| format!("{:?}", e)), + Side::Ldk => node.disconnect(ext_node_id).await.map_err(|e| format!("{:?}", e)), Side::External => { peer.disconnect_peer(node.node_id()).await.map_err(|e| format!("{:?}", e)) }, @@ -34,10 +34,10 @@ pub(crate) async fn disconnect_by_side( pub(crate) async fn reconnect_and_wait( node: &Node, peer_id: PublicKey, addr: SocketAddress, context: &str, ) { - node.connect(peer_id, addr, true).unwrap(); + node.connect(peer_id, addr, true).await.unwrap(); let max_attempts = super::super::INTEROP_TIMEOUT_SECS; for i in 0..max_attempts { - if node.list_peers().iter().any(|p| p.node_id == peer_id && p.is_connected) { + if node.list_peers().await.iter().any(|p| p.node_id == peer_id && p.is_connected) { tokio::time::sleep(Duration::from_secs(2)).await; return; } @@ -59,7 +59,7 @@ pub(crate) async fn disconnect_during_payment( let parsed_invoice = Bolt11Invoice::from_str(&invoice_str).unwrap(); // If send() fails immediately, no event will arrive, so skip event wait below. - let send_ok = node.bolt11_payment().send(&parsed_invoice, None).is_ok(); + let send_ok = node.bolt11_payment().send(&parsed_invoice, None).await.is_ok(); // Disconnect may race with payment delivery; tolerate failure. let _ = disconnect_by_side(node, peer, disconnect_side).await; diff --git a/tests/common/scenarios/mod.rs b/tests/common/scenarios/mod.rs index 7cbf56b8e1..cf1ecc7bb1 100644 --- a/tests/common/scenarios/mod.rs +++ b/tests/common/scenarios/mod.rs @@ -130,7 +130,7 @@ pub(crate) async fn setup_interop_test( let ext_node_id = peer.get_node_id().await.unwrap(); let ext_addr = peer.get_listening_address().await.unwrap(); - node.connect(ext_node_id, ext_addr, true).unwrap(); + node.connect(ext_node_id, ext_addr, true).await.unwrap(); } /// Drive a scenario end-to-end: fund LDK + peer, run the scenario, stop the node. @@ -239,7 +239,7 @@ pub(crate) async fn splice_in_scenario( ) .await; let ext_node_id = peer.get_node_id().await.unwrap(); - node.splice_in(&user_ch, ext_node_id, 500_000).unwrap(); + node.splice_in(&user_ch, ext_node_id, 500_000).await.unwrap(); expect_splice_negotiated_event!(node, ext_node_id); generate_blocks_and_wait(bitcoind, electrs, 6).await; sync_wallets_with_retry(node).await; diff --git a/tests/common/scenarios/payment.rs b/tests/common/scenarios/payment.rs index 191f60abc1..2ff5065dc0 100644 --- a/tests/common/scenarios/payment.rs +++ b/tests/common/scenarios/payment.rs @@ -19,7 +19,7 @@ pub(crate) async fn send_bolt11_to_peer( ) { let invoice_str = peer.create_invoice(amount_msat, label).await.unwrap(); let parsed = Bolt11Invoice::from_str(&invoice_str).unwrap(); - node.bolt11_payment().send(&parsed, None).unwrap(); + node.bolt11_payment().send(&parsed, None).await.unwrap(); expect_event!(node, PaymentSuccessful); } @@ -37,6 +37,7 @@ pub(crate) async fn receive_bolt11_payment( ), 3600, ) + .await .unwrap(); let invoice_str = invoice.to_string(); retry_until_ok(10, "receive_bolt11_payment", || peer.pay_invoice(&invoice_str)).await; @@ -48,7 +49,7 @@ pub(crate) async fn send_keysend_to_peer( node: &Node, peer: &(impl ExternalNode + ?Sized), amount_msat: u64, ) { let peer_id = peer.get_node_id().await.unwrap(); - node.spontaneous_payment().send(amount_msat, peer_id, None).unwrap(); + node.spontaneous_payment().send(amount_msat, peer_id, None).await.unwrap(); expect_event!(node, PaymentSuccessful); } diff --git a/tests/integration_tests_hrn.rs b/tests/integration_tests_hrn.rs index 9102400398..497b9e61a2 100644 --- a/tests/integration_tests_hrn.rs +++ b/tests/integration_tests_hrn.rs @@ -24,9 +24,9 @@ async fn unified_send_to_hrn() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false).await; - let address_a = node_a.onchain_payment().new_address().unwrap(); + let address_a = node_a.onchain_payment().new_address().await.unwrap(); let premined_sats = 5_000_000; premine_and_distribute_funds( @@ -37,25 +37,26 @@ async fn unified_send_to_hrn() { ) .await; - node_a.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); open_channel(&node_a, &node_b, 4_000_000, true, &electrsd).await; generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); expect_channel_ready_event!(node_a, node_b.node_id()); expect_channel_ready_event!(node_b, node_a.node_id()); // Wait until node_b broadcasts a node announcement - while node_b.status().latest_node_announcement_broadcast_timestamp.is_none() { + while node_b.status().await.latest_node_announcement_broadcast_timestamp.is_none() { tokio::time::sleep(std::time::Duration::from_millis(10)).await; } // Sleep to make sure the node announcement propagates tokio::time::sleep(std::time::Duration::from_secs(1)).await; - let test_offer = node_b.bolt12_payment().receive(1000000, "test offer", None, None).unwrap(); + let test_offer = + node_b.bolt12_payment().receive(1000000, "test offer", None, None).await.unwrap(); let hrn_str = "matt@mattcorallo.com"; diff --git a/tests/integration_tests_postgres.rs b/tests/integration_tests_postgres.rs index b96b0c277c..8939134568 100644 --- a/tests/integration_tests_postgres.rs +++ b/tests/integration_tests_postgres.rs @@ -121,7 +121,7 @@ async fn postgres_node_restart() { bitcoin::Amount::from_sat(100_000), ) .await; - node.sync_wallets().unwrap(); + node.sync_wallets().await.unwrap(); let balance = node.list_balances().spendable_onchain_balance_sats; assert!(balance > 0); @@ -148,7 +148,7 @@ async fn postgres_node_restart() { .unwrap(); node.start().unwrap(); - node.sync_wallets().unwrap(); + node.sync_wallets().await.unwrap(); assert_eq!(expected_node_id, node.node_id()); assert_eq!(expected_balance_sats, node.list_balances().spendable_onchain_balance_sats); diff --git a/tests/integration_tests_rust.rs b/tests/integration_tests_rust.rs index 1ea6c45845..54ad381f3e 100644 --- a/tests/integration_tests_rust.rs +++ b/tests/integration_tests_rust.rs @@ -47,7 +47,7 @@ use log::LevelFilter; async fn channel_full_cycle() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false).await; do_channel_full_cycle( node_a, node_b, @@ -65,7 +65,7 @@ async fn channel_full_cycle() { async fn channel_full_cycle_force_close() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false).await; do_channel_full_cycle( node_a, node_b, @@ -83,7 +83,7 @@ async fn channel_full_cycle_force_close() { async fn channel_full_cycle_force_close_trusted_no_reserve() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, true); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, true).await; do_channel_full_cycle( node_a, node_b, @@ -101,7 +101,7 @@ async fn channel_full_cycle_force_close_trusted_no_reserve() { async fn channel_full_cycle_0conf() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, true, true, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, true, true, false).await; do_channel_full_cycle( node_a, node_b, @@ -119,7 +119,7 @@ async fn channel_full_cycle_0conf() { async fn channel_full_cycle_legacy_staticremotekey() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, false, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, false, false).await; do_channel_full_cycle( node_a, node_b, @@ -137,7 +137,7 @@ async fn channel_full_cycle_legacy_staticremotekey() { async fn channel_full_cycle_0reserve() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false).await; do_channel_full_cycle( node_a, node_b, @@ -155,7 +155,7 @@ async fn channel_full_cycle_0reserve() { async fn channel_full_cycle_0conf_0reserve() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, true, true, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, true, true, false).await; do_channel_full_cycle( node_a, node_b, @@ -173,10 +173,10 @@ async fn channel_full_cycle_0conf_0reserve() { async fn channel_open_fails_when_funds_insufficient() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false).await; - let addr_a = node_a.onchain_payment().new_address().unwrap(); - let addr_b = node_b.onchain_payment().new_address().unwrap(); + let addr_a = node_a.onchain_payment().new_address().await.unwrap(); + let addr_b = node_b.onchain_payment().new_address().await.unwrap(); let premine_amount_sat = 100_000; @@ -187,21 +187,23 @@ async fn channel_open_fails_when_funds_insufficient() { Amount::from_sat(premine_amount_sat), ) .await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); assert_eq!(node_a.list_balances().spendable_onchain_balance_sats, premine_amount_sat); assert_eq!(node_b.list_balances().spendable_onchain_balance_sats, premine_amount_sat); println!("\nA -- open_channel -> B"); assert_eq!( Err(NodeError::InsufficientFunds), - node_a.open_channel( - node_b.node_id(), - node_b.listening_addresses().unwrap().first().unwrap().clone(), - 120000, - None, - None, - ) + node_a + .open_channel( + node_b.node_id(), + node_b.listening_addresses().unwrap().first().unwrap().clone(), + 120000, + None, + None, + ) + .await ); } @@ -218,12 +220,15 @@ async fn multi_hop_sending() { sync_config.background_sync_config = None; setup_builder!(builder, config.node_config); builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config)); - let node = builder.build(config.node_entropy.into()).unwrap(); - node.start().unwrap(); + let node = builder.build(config.node_entropy.into()).await.unwrap(); + node.start().await.unwrap(); nodes.push(node); } - let addresses = nodes.iter().map(|n| n.onchain_payment().new_address().unwrap()).collect(); + let mut addresses = Vec::with_capacity(nodes.len()); + for node in &nodes { + addresses.push(node.onchain_payment().new_address().await.unwrap()); + } let premine_amount_sat = 5_000_000; premine_and_distribute_funds( &bitcoind.client, @@ -234,7 +239,7 @@ async fn multi_hop_sending() { .await; for n in &nodes { - n.sync_wallets().unwrap(); + n.sync_wallets().await.unwrap(); assert_eq!(n.list_balances().spendable_onchain_balance_sats, premine_amount_sat); assert_eq!(n.next_event(), None); } @@ -252,7 +257,7 @@ async fn multi_hop_sending() { // wallet picks up on the broadcast funding tx and doesn't double-spend itself. // // TODO: Remove once fixed in BDK. - nodes[1].sync_wallets().unwrap(); + nodes[1].sync_wallets().await.unwrap(); open_channel(&nodes[1], &nodes[3], 1_000_000, true, &electrsd).await; open_channel(&nodes[2], &nodes[4], 1_000_000, true, &electrsd).await; open_channel(&nodes[3], &nodes[4], 1_000_000, true, &electrsd).await; @@ -260,7 +265,7 @@ async fn multi_hop_sending() { generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; for n in &nodes { - n.sync_wallets().unwrap(); + n.sync_wallets().await.unwrap(); } expect_event!(nodes[0], ChannelReady); @@ -289,8 +294,9 @@ async fn multi_hop_sending() { let invoice = nodes[4] .bolt11_payment() .receive(2_500_000, &invoice_description.clone().into(), 9217) + .await .unwrap(); - nodes[0].bolt11_payment().send(&invoice, Some(route_params)).unwrap(); + nodes[0].bolt11_payment().send(&invoice, Some(route_params)).await.unwrap(); expect_event!(nodes[1], PaymentForwarded); @@ -318,14 +324,16 @@ async fn start_stop_reinit() { setup_builder!(builder, config.node_config); builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config)); - let node = - builder.build_with_store(config.node_entropy.into(), test_sync_store.clone()).unwrap(); - node.start().unwrap(); + let node = builder + .build_with_store(config.node_entropy.into(), test_sync_store.clone()) + .await + .unwrap(); + node.start().await.unwrap(); let expected_node_id = node.node_id(); - assert_eq!(node.start(), Err(NodeError::AlreadyRunning)); + assert_eq!(node.start().await, Err(NodeError::AlreadyRunning)); - let funding_address = node.onchain_payment().new_address().unwrap(); + let funding_address = node.onchain_payment().new_address().await.unwrap(); assert_eq!(node.list_balances().total_onchain_balance_sats, 0); @@ -338,28 +346,28 @@ async fn start_stop_reinit() { ) .await; - node.sync_wallets().unwrap(); + node.sync_wallets().await.unwrap(); assert_eq!(node.list_balances().spendable_onchain_balance_sats, expected_amount.to_sat()); let log_file = format!("{}/ldk_node.log", config.node_config.clone().storage_dir_path); assert!(std::path::Path::new(&log_file).exists()); - node.stop().unwrap(); - assert_eq!(node.stop(), Err(NodeError::NotRunning)); + node.stop().await.unwrap(); + assert_eq!(node.stop().await, Err(NodeError::NotRunning)); - node.start().unwrap(); - assert_eq!(node.start(), Err(NodeError::AlreadyRunning)); + node.start().await.unwrap(); + assert_eq!(node.start().await, Err(NodeError::AlreadyRunning)); - node.stop().unwrap(); - assert_eq!(node.stop(), Err(NodeError::NotRunning)); + node.stop().await.unwrap(); + assert_eq!(node.stop().await, Err(NodeError::NotRunning)); drop(node); setup_builder!(builder, config.node_config); builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config)); let reinitialized_node = - builder.build_with_store(config.node_entropy.into(), test_sync_store).unwrap(); - reinitialized_node.start().unwrap(); + builder.build_with_store(config.node_entropy.into(), test_sync_store).await.unwrap(); + reinitialized_node.start().await.unwrap(); assert_eq!(reinitialized_node.node_id(), expected_node_id); assert_eq!( @@ -367,23 +375,23 @@ async fn start_stop_reinit() { expected_amount.to_sat() ); - reinitialized_node.sync_wallets().unwrap(); + reinitialized_node.sync_wallets().await.unwrap(); assert_eq!( reinitialized_node.list_balances().spendable_onchain_balance_sats, expected_amount.to_sat() ); - reinitialized_node.stop().unwrap(); + reinitialized_node.stop().await.unwrap(); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn onchain_send_receive() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false).await; - let addr_a = node_a.onchain_payment().new_address().unwrap(); - let addr_b = node_b.onchain_payment().new_address().unwrap(); + let addr_a = node_a.onchain_payment().new_address().await.unwrap(); + let addr_b = node_b.onchain_payment().new_address().await.unwrap(); // This is a Bitcoin Testnet address. Sending funds to this address from the Regtest network will fail let static_address = "tb1q0d40e5rta4fty63z64gztf8c3v20cvet6v2jdh"; let unchecked_address = Address::::from_str(static_address).unwrap(); @@ -398,13 +406,13 @@ async fn onchain_send_receive() { ) .await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); assert_eq!(node_a.list_balances().spendable_onchain_balance_sats, premine_amount_sat); assert_eq!(node_b.list_balances().spendable_onchain_balance_sats, premine_amount_sat); - let node_a_payments = node_a.list_payments(); - let node_b_payments = node_b.list_payments(); + let node_a_payments = node_a.list_payments().await; + let node_b_payments = node_b.list_payments().await; for payments in [&node_a_payments, &node_b_payments] { assert_eq!(payments.len(), 1) } @@ -426,16 +434,16 @@ async fn onchain_send_receive() { open_channel(&node_b, &node_a, channel_amount_sat, true, &electrsd).await; generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); expect_channel_ready_event!(node_a, node_b.node_id()); expect_channel_ready_event!(node_b, node_a.node_id()); let node_a_payments = - node_a.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Onchain { .. })); + node_a.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Onchain { .. })).await; assert_eq!(node_a_payments.len(), 1); let node_b_payments = - node_b.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Onchain { .. })); + node_b.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Onchain { .. })).await; assert_eq!(node_b_payments.len(), 2); let onchain_fee_buffer_sat = 1000; @@ -450,28 +458,28 @@ async fn onchain_send_receive() { assert_eq!( Err(NodeError::InsufficientFunds), - node_a.onchain_payment().send_to_address(&addr_b, expected_node_a_balance + 1, None) + node_a.onchain_payment().send_to_address(&addr_b, expected_node_a_balance + 1, None).await ); assert_eq!( Err(NodeError::InvalidAddress), - node_a.onchain_payment().send_to_address(&addr_c, expected_node_a_balance + 1, None) + node_a.onchain_payment().send_to_address(&addr_c, expected_node_a_balance + 1, None).await ); assert_eq!( Err(NodeError::InvalidAddress), - node_a.onchain_payment().send_all_to_address(&addr_c, true, None) + node_a.onchain_payment().send_all_to_address(&addr_c, true, None).await ); let amount_to_send_sats = 54321; let txid = - node_b.onchain_payment().send_to_address(&addr_a, amount_to_send_sats, None).unwrap(); + node_b.onchain_payment().send_to_address(&addr_a, amount_to_send_sats, None).await.unwrap(); wait_for_tx(&electrsd.client, txid).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); let payment_id = PaymentId(txid.to_byte_array()); - let payment_a = node_a.payment(&payment_id).unwrap(); + let payment_a = node_a.payment(&payment_id).await.unwrap(); assert_eq!(payment_a.status, PaymentStatus::Pending); match payment_a.kind { PaymentKind::Onchain { status, .. } => { @@ -480,7 +488,7 @@ async fn onchain_send_receive() { _ => panic!("Unexpected payment kind"), } assert!(payment_a.fee_paid_msat > Some(0)); - let payment_b = node_b.payment(&payment_id).unwrap(); + let payment_b = node_b.payment(&payment_id).await.unwrap(); assert_eq!(payment_b.status, PaymentStatus::Pending); match payment_a.kind { PaymentKind::Onchain { status, .. } => { @@ -494,8 +502,8 @@ async fn onchain_send_receive() { assert_eq!(payment_a.fee_paid_msat, payment_b.fee_paid_msat); generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); let expected_node_a_balance = expected_node_a_balance + amount_to_send_sats; let expected_node_b_balance_lower = expected_node_b_balance_lower - amount_to_send_sats; @@ -505,13 +513,13 @@ async fn onchain_send_receive() { assert!(node_b.list_balances().spendable_onchain_balance_sats < expected_node_b_balance_upper); let node_a_payments = - node_a.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Onchain { .. })); + node_a.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Onchain { .. })).await; assert_eq!(node_a_payments.len(), 2); let node_b_payments = - node_b.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Onchain { .. })); + node_b.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Onchain { .. })).await; assert_eq!(node_b_payments.len(), 3); - let payment_a = node_a.payment(&payment_id).unwrap(); + let payment_a = node_a.payment(&payment_id).await.unwrap(); match payment_a.kind { PaymentKind::Onchain { txid: _txid, status } => { assert_eq!(_txid, txid); @@ -520,7 +528,7 @@ async fn onchain_send_receive() { _ => panic!("Unexpected payment kind"), } - let payment_b = node_a.payment(&payment_id).unwrap(); + let payment_b = node_a.payment(&payment_id).await.unwrap(); match payment_b.kind { PaymentKind::Onchain { txid: _txid, status } => { assert_eq!(_txid, txid); @@ -529,13 +537,13 @@ async fn onchain_send_receive() { _ => panic!("Unexpected payment kind"), } - let addr_b = node_b.onchain_payment().new_address().unwrap(); - let txid = node_a.onchain_payment().send_all_to_address(&addr_b, true, None).unwrap(); + let addr_b = node_b.onchain_payment().new_address().await.unwrap(); + let txid = node_a.onchain_payment().send_all_to_address(&addr_b, true, None).await.unwrap(); generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; wait_for_tx(&electrsd.client, txid).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); let expected_node_b_balance_lower = expected_node_b_balance_lower + expected_node_a_balance; let expected_node_b_balance_upper = expected_node_b_balance_upper + expected_node_a_balance; @@ -546,19 +554,19 @@ async fn onchain_send_receive() { assert!(node_b.list_balances().spendable_onchain_balance_sats < expected_node_b_balance_upper); let node_a_payments = - node_a.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Onchain { .. })); + node_a.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Onchain { .. })).await; assert_eq!(node_a_payments.len(), 3); let node_b_payments = - node_b.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Onchain { .. })); + node_b.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Onchain { .. })).await; assert_eq!(node_b_payments.len(), 4); - let addr_b = node_b.onchain_payment().new_address().unwrap(); - let txid = node_a.onchain_payment().send_all_to_address(&addr_b, false, None).unwrap(); + let addr_b = node_b.onchain_payment().new_address().await.unwrap(); + let txid = node_a.onchain_payment().send_all_to_address(&addr_b, false, None).await.unwrap(); generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; wait_for_tx(&electrsd.client, txid).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); let expected_node_b_balance_lower = expected_node_b_balance_lower + reserve_amount_sat; let expected_node_b_balance_upper = expected_node_b_balance_upper + reserve_amount_sat; @@ -570,10 +578,10 @@ async fn onchain_send_receive() { assert!(node_b.list_balances().spendable_onchain_balance_sats < expected_node_b_balance_upper); let node_a_payments = - node_a.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Onchain { .. })); + node_a.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Onchain { .. })).await; assert_eq!(node_a_payments.len(), 4); let node_b_payments = - node_b.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Onchain { .. })); + node_b.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Onchain { .. })).await; assert_eq!(node_b_payments.len(), 5); } @@ -581,11 +589,11 @@ async fn onchain_send_receive() { async fn onchain_send_all_retains_reserve() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false).await; // Setup nodes - let addr_a = node_a.onchain_payment().new_address().unwrap(); - let addr_b = node_b.onchain_payment().new_address().unwrap(); + let addr_a = node_a.onchain_payment().new_address().await.unwrap(); + let addr_b = node_b.onchain_payment().new_address().await.unwrap(); let premine_amount_sat = 1_000_000; let reserve_amount_sat = 25_000; @@ -598,19 +606,19 @@ async fn onchain_send_all_retains_reserve() { ) .await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); assert_eq!(node_a.list_balances().spendable_onchain_balance_sats, premine_amount_sat); assert_eq!(node_b.list_balances().spendable_onchain_balance_sats, premine_amount_sat); // Send all over, with 0 reserve as we don't have any channels open. - let txid = node_a.onchain_payment().send_all_to_address(&addr_b, true, None).unwrap(); + let txid = node_a.onchain_payment().send_all_to_address(&addr_b, true, None).await.unwrap(); wait_for_tx(&electrsd.client, txid).await; generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); // Check node a sent all and node b received it assert_eq!(node_a.list_balances().spendable_onchain_balance_sats, 0); assert!(((premine_amount_sat * 2 - onchain_fee_buffer_sat)..=(premine_amount_sat * 2)) @@ -626,15 +634,15 @@ async fn onchain_send_all_retains_reserve() { .unwrap(); wait_for_tx(&electrsd.client, txid).await; generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); assert_eq!(node_a.list_balances().spendable_onchain_balance_sats, reserve_amount_sat); // Open a channel. open_channel(&node_b, &node_a, premine_amount_sat, false, &electrsd).await; generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); expect_channel_ready_event!(node_a, node_b.node_id()); expect_channel_ready_event!(node_b, node_a.node_id()); @@ -645,13 +653,13 @@ async fn onchain_send_all_retains_reserve() { .contains(&node_b.list_balances().spendable_onchain_balance_sats)); // Send all over again, this time ensuring the reserve is accounted for - let txid = node_b.onchain_payment().send_all_to_address(&addr_a, true, None).unwrap(); + let txid = node_b.onchain_payment().send_all_to_address(&addr_a, true, None).await.unwrap(); wait_for_tx(&electrsd.client, txid).await; generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); // Check node b sent all and node a received it assert_eq!(node_b.list_balances().total_onchain_balance_sats, reserve_amount_sat); @@ -669,11 +677,11 @@ async fn onchain_wallet_recovery() { let original_config = random_config(true); let original_node_entropy = original_config.node_entropy; - let original_node = setup_node(&chain_source, original_config); + let original_node = setup_node(&chain_source, original_config).await; let premine_amount_sat = 100_000; - let addr_1 = original_node.onchain_payment().new_address().unwrap(); + let addr_1 = original_node.onchain_payment().new_address().await.unwrap(); premine_and_distribute_funds( &bitcoind.client, @@ -682,10 +690,10 @@ async fn onchain_wallet_recovery() { Amount::from_sat(premine_amount_sat), ) .await; - original_node.sync_wallets().unwrap(); + original_node.sync_wallets().await.unwrap(); assert_eq!(original_node.list_balances().spendable_onchain_balance_sats, premine_amount_sat); - let addr_2 = original_node.onchain_payment().new_address().unwrap(); + let addr_2 = original_node.onchain_payment().new_address().await.unwrap(); let txid = bitcoind .client @@ -698,32 +706,32 @@ async fn onchain_wallet_recovery() { generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 1).await; - original_node.sync_wallets().unwrap(); + original_node.sync_wallets().await.unwrap(); assert_eq!( original_node.list_balances().spendable_onchain_balance_sats, premine_amount_sat * 2 ); - original_node.stop().unwrap(); + original_node.stop().await.unwrap(); drop(original_node); // Now we start from scratch, only the seed remains the same. let mut recovered_config = random_config(true); recovered_config.node_entropy = original_node_entropy; recovered_config.recovery_mode = true; - let recovered_node = setup_node(&chain_source, recovered_config); + let recovered_node = setup_node(&chain_source, recovered_config).await; - recovered_node.sync_wallets().unwrap(); + recovered_node.sync_wallets().await.unwrap(); assert_eq!( recovered_node.list_balances().spendable_onchain_balance_sats, premine_amount_sat * 2 ); // Check we sync even when skipping some addresses. - let _addr_3 = recovered_node.onchain_payment().new_address().unwrap(); - let _addr_4 = recovered_node.onchain_payment().new_address().unwrap(); - let _addr_5 = recovered_node.onchain_payment().new_address().unwrap(); - let addr_6 = recovered_node.onchain_payment().new_address().unwrap(); + let _addr_3 = recovered_node.onchain_payment().new_address().await.unwrap(); + let _addr_4 = recovered_node.onchain_payment().new_address().await.unwrap(); + let _addr_5 = recovered_node.onchain_payment().new_address().await.unwrap(); + let addr_6 = recovered_node.onchain_payment().new_address().await.unwrap(); let txid = bitcoind .client @@ -736,7 +744,7 @@ async fn onchain_wallet_recovery() { generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 1).await; - recovered_node.sync_wallets().unwrap(); + recovered_node.sync_wallets().await.unwrap(); assert_eq!( recovered_node.list_balances().spendable_onchain_balance_sats, premine_amount_sat * 3 @@ -765,7 +773,7 @@ async fn run_rbf_test(is_insert_block: bool) { macro_rules! config_node { ($chain_source:expr, $anchor_channels:expr) => {{ let config_a = random_config($anchor_channels); - let node = setup_node(&$chain_source, config_a); + let node = setup_node(&$chain_source, config_a).await; node }}; } @@ -780,8 +788,10 @@ async fn run_rbf_test(is_insert_block: bool) { premine_blocks(bitcoind, electrs).await; // Helpers declaration before starting the test - let all_addrs = - nodes.iter().map(|node| node.onchain_payment().new_address().unwrap()).collect::>(); + let mut all_addrs = Vec::with_capacity(nodes.len()); + for node in &nodes { + all_addrs.push(node.onchain_payment().new_address().await.unwrap()); + } let amount_sat = 2_100_000; let mut txid; macro_rules! distribute_funds_all_nodes { @@ -799,7 +809,7 @@ async fn run_rbf_test(is_insert_block: bool) { ($expected_balance_sat:expr, $is_spendable:expr) => { let spend_balance = if $is_spendable { $expected_balance_sat } else { 0 }; for node in &nodes { - node.sync_wallets().unwrap(); + node.sync_wallets().await.unwrap(); let balances = node.list_balances(); assert_eq!(balances.spendable_onchain_balance_sats, spend_balance); assert_eq!(balances.total_onchain_balance_sats, $expected_balance_sat); @@ -868,10 +878,10 @@ async fn run_rbf_test(is_insert_block: bool) { // Check if it is possible to send all funds from the node let mut txids = Vec::new(); let addr = bitcoind.new_address().unwrap(); - nodes.iter().for_each(|node| { - let txid = node.onchain_payment().send_all_to_address(&addr, true, None).unwrap(); + for node in &nodes { + let txid = node.onchain_payment().send_all_to_address(&addr, true, None).await.unwrap(); txids.push(txid); - }); + } for txid in txids { wait_for_tx(electrs, txid).await; } @@ -884,7 +894,7 @@ async fn sign_verify_msg() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let config = random_config(true); let chain_source = random_chain_source(&bitcoind, &electrsd); - let node = setup_node(&chain_source, config); + let node = setup_node(&chain_source, config).await; // Tests arbitrary message signing and later verification let msg = "OK computer".as_bytes(); @@ -897,14 +907,14 @@ async fn sign_verify_msg() { async fn connection_multi_listen() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, false, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, false, false).await; let node_id_b = node_b.node_id(); let node_addrs_b = node_b.listening_addresses().unwrap(); for node_addr_b in &node_addrs_b { - node_a.connect(node_id_b, node_addr_b.clone(), false).unwrap(); - node_a.disconnect(node_id_b).unwrap(); + node_a.connect(node_id_b, node_addr_b.clone(), false).await.unwrap(); + node_a.disconnect(node_id_b).await.unwrap(); } } @@ -917,46 +927,46 @@ async fn connection_restart_behavior() { async fn do_connection_restart_behavior(persist: bool) { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, false, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, false, false).await; let node_id_a = node_a.node_id(); let node_id_b = node_b.node_id(); let node_addr_b = node_b.listening_addresses().unwrap().first().unwrap().clone(); - node_a.connect(node_id_b, node_addr_b, persist).unwrap(); + node_a.connect(node_id_b, node_addr_b, persist).await.unwrap(); - let peer_details_a = node_a.list_peers().first().unwrap().clone(); + let peer_details_a = node_a.list_peers().await.first().unwrap().clone(); assert_eq!(peer_details_a.node_id, node_id_b); assert_eq!(peer_details_a.is_persisted, persist); assert!(peer_details_a.is_connected); - let peer_details_b = node_b.list_peers().first().unwrap().clone(); + let peer_details_b = node_b.list_peers().await.first().unwrap().clone(); assert_eq!(peer_details_b.node_id, node_id_a); assert_eq!(peer_details_b.is_persisted, false); assert!(peer_details_a.is_connected); // Restart nodes. - node_a.stop().unwrap(); - node_b.stop().unwrap(); - node_b.start().unwrap(); - node_a.start().unwrap(); + node_a.stop().await.unwrap(); + node_b.stop().await.unwrap(); + node_b.start().await.unwrap(); + node_a.start().await.unwrap(); // Sleep a bit to allow for the reconnect to happen. tokio::time::sleep(std::time::Duration::from_secs(5)).await; if persist { - let peer_details_a = node_a.list_peers().first().unwrap().clone(); + let peer_details_a = node_a.list_peers().await.first().unwrap().clone(); assert_eq!(peer_details_a.node_id, node_id_b); assert_eq!(peer_details_a.is_persisted, persist); assert!(peer_details_a.is_connected); - let peer_details_b = node_b.list_peers().first().unwrap().clone(); + let peer_details_b = node_b.list_peers().await.first().unwrap().clone(); assert_eq!(peer_details_b.node_id, node_id_a); assert_eq!(peer_details_b.is_persisted, false); assert!(peer_details_a.is_connected); } else { - assert!(node_a.list_peers().is_empty()); - assert!(node_b.list_peers().is_empty()); + assert!(node_a.list_peers().await.is_empty()); + assert!(node_b.list_peers().await.is_empty()); } } @@ -964,7 +974,7 @@ async fn do_connection_restart_behavior(persist: bool) { async fn concurrent_connections_succeed() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false).await; let node_a = Arc::new(node_a); let node_b = Arc::new(node_b); @@ -974,16 +984,16 @@ async fn concurrent_connections_succeed() { let mut handles = Vec::new(); for _ in 0..10 { - let thread_node = Arc::clone(&node_a); - let thread_addr = node_addr_b.clone(); - let handle = std::thread::spawn(move || { - thread_node.connect(node_id_b, thread_addr, false).unwrap(); + let task_node = Arc::clone(&node_a); + let task_addr = node_addr_b.clone(); + let handle = tokio::spawn(async move { + task_node.connect(node_id_b, task_addr, false).await.unwrap(); }); handles.push(handle); } for h in handles { - h.join().unwrap(); + h.await.unwrap(); } } @@ -992,10 +1002,10 @@ async fn splice_channel() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false).await; - let address_a = node_a.onchain_payment().new_address().unwrap(); - let address_b = node_b.onchain_payment().new_address().unwrap(); + let address_a = node_a.onchain_payment().new_address().await.unwrap(); + let address_b = node_b.onchain_payment().new_address().await.unwrap(); let premine_amount_sat = 5_000_000; premine_and_distribute_funds( &bitcoind.client, @@ -1005,8 +1015,8 @@ async fn splice_channel() { ) .await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); assert_eq!(node_a.list_balances().total_onchain_balance_sats, premine_amount_sat); assert_eq!(node_b.list_balances().total_onchain_balance_sats, premine_amount_sat); @@ -1016,8 +1026,8 @@ async fn splice_channel() { // Open a channel with Node A contributing the funding generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); let user_channel_id_a = expect_channel_ready_event!(node_a, node_b.node_id()); let user_channel_id_b = expect_channel_ready_event!(node_b, node_a.node_id()); @@ -1037,11 +1047,11 @@ async fn splice_channel() { assert_eq!(node_b.list_balances().total_lightning_balance_sats, 0); // Test that splicing and payments fail when there are insufficient funds - let address = node_b.onchain_payment().new_address().unwrap(); + let address = node_b.onchain_payment().new_address().await.unwrap(); let amount_msat = 400_000_000; assert_eq!( - node_b.splice_in(&user_channel_id_b, node_b.node_id(), 5_000_000), + node_b.splice_in(&user_channel_id_b, node_b.node_id(), 5_000_000).await, Err(NodeError::ChannelSplicingFailed), ); assert_eq!( @@ -1049,20 +1059,20 @@ async fn splice_channel() { Err(NodeError::ChannelSplicingFailed), ); assert_eq!( - node_b.spontaneous_payment().send(amount_msat, node_a.node_id(), None), + node_b.spontaneous_payment().send(amount_msat, node_a.node_id(), None).await, Err(NodeError::PaymentSendingFailed) ); // Splice-in funds for Node B so that it has outbound liquidity to make a payment - node_b.splice_in(&user_channel_id_b, node_a.node_id(), 4_000_000).unwrap(); + node_b.splice_in(&user_channel_id_b, node_a.node_id(), 4_000_000).await.unwrap(); let txo = expect_splice_negotiated_event!(node_a, node_b.node_id()); expect_splice_negotiated_event!(node_b, node_a.node_id()); generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); expect_channel_ready_event!(node_a, node_b.node_id()); expect_channel_ready_event!(node_b, node_a.node_id()); @@ -1078,7 +1088,7 @@ async fn splice_channel() { // updating the BDK wallet dependency. See: https://github.com/bitcoindevkit/bdk_wallet/pull/479 let expected_splice_in_lightning_balance_sat = 4_000_003; - let payments = node_b.list_payments(); + let payments = node_b.list_payments().await; let payment = payments.into_iter().find(|p| p.id == PaymentId(txo.txid.to_byte_array())).unwrap(); assert_eq!(payment.fee_paid_msat, Some(expected_splice_in_fee_sat * 1_000)); @@ -1093,7 +1103,7 @@ async fn splice_channel() { ); let payment_id = - node_b.spontaneous_payment().send(amount_msat, node_a.node_id(), None).unwrap(); + node_b.spontaneous_payment().send(amount_msat, node_a.node_id(), None).await.unwrap(); expect_payment_successful_event!(node_b, Some(payment_id), None); expect_payment_received_event!(node_a, amount_msat); @@ -1111,7 +1121,7 @@ async fn splice_channel() { ); // Splice-out funds for Node A from the payment sent by Node B - let address = node_a.onchain_payment().new_address().unwrap(); + let address = node_a.onchain_payment().new_address().await.unwrap(); node_a.splice_out(&user_channel_id_a, node_b.node_id(), &address, amount_msat / 1000).unwrap(); let txo = expect_splice_negotiated_event!(node_a, node_b.node_id()); @@ -1119,15 +1129,15 @@ async fn splice_channel() { generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); expect_channel_ready_event!(node_a, node_b.node_id()); expect_channel_ready_event!(node_b, node_a.node_id()); let expected_splice_out_fee_sat = 183; - let payments = node_a.list_payments(); + let payments = node_a.list_payments().await; let payment = payments.into_iter().find(|p| p.id == PaymentId(txo.txid.to_byte_array())).unwrap(); assert_eq!(payment.fee_paid_msat, Some(expected_splice_out_fee_sat * 1_000)); @@ -1146,9 +1156,9 @@ async fn splice_channel() { async fn simple_bolt12_send_receive() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false).await; - let address_a = node_a.onchain_payment().new_address().unwrap(); + let address_a = node_a.onchain_payment().new_address().await.unwrap(); let premine_amount_sat = 5_000_000; premine_and_distribute_funds( &bitcoind.client, @@ -1158,19 +1168,19 @@ async fn simple_bolt12_send_receive() { ) .await; - node_a.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); open_channel(&node_a, &node_b, 4_000_000, true, &electrsd).await; generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); expect_channel_ready_event!(node_a, node_b.node_id()); expect_channel_ready_event!(node_b, node_a.node_id()); // Sleep until we broadcasted a node announcement. - while node_b.status().latest_node_announcement_broadcast_timestamp.is_none() { + while node_b.status().await.latest_node_announcement_broadcast_timestamp.is_none() { tokio::time::sleep(std::time::Duration::from_millis(10)).await; } @@ -1185,6 +1195,7 @@ async fn simple_bolt12_send_receive() { let payment_id = node_a .bolt12_payment() .send(&offer, expected_quantity, expected_payer_note.clone(), None) + .await .unwrap(); let event = node_a.next_event_async().await; @@ -1196,12 +1207,13 @@ async fn simple_bolt12_send_receive() { bolt12_invoice.is_some(), "bolt12_invoice should be present for BOLT12 payments" ); - node_a.event_handled().unwrap(); + node_a.event_handled().await.unwrap(); }, ref e => panic!("{} got unexpected event!: {:?}", "node_a", e), } - let node_a_payments = - node_a.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Bolt12Offer { .. })); + let node_a_payments = node_a + .list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Bolt12Offer { .. })) + .await; assert_eq!(node_a_payments.len(), 1); match node_a_payments.first().unwrap().kind { PaymentKind::Bolt12Offer { @@ -1227,8 +1239,9 @@ async fn simple_bolt12_send_receive() { assert_eq!(node_a_payments.first().unwrap().amount_msat, Some(expected_amount_msat)); expect_payment_received_event!(node_b, expected_amount_msat); - let node_b_payments = - node_b.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Bolt12Offer { .. })); + let node_b_payments = node_b + .list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Bolt12Offer { .. })) + .await; assert_eq!(node_b_payments.len(), 1); match node_b_payments.first().unwrap().kind { PaymentKind::Bolt12Offer { hash, preimage, secret, offer_id, .. } => { @@ -1253,6 +1266,7 @@ async fn simple_bolt12_send_receive() { assert!(node_a .bolt12_payment() .send_using_amount(&offer, less_than_offer_amount, None, None, None) + .await .is_err()); let payment_id = node_a .bolt12_payment() @@ -1263,12 +1277,15 @@ async fn simple_bolt12_send_receive() { expected_payer_note.clone(), None, ) + .await .unwrap(); expect_payment_successful_event!(node_a, Some(payment_id), None); - let node_a_payments = node_a.list_payments_with_filter(|p| { - matches!(p.kind, PaymentKind::Bolt12Offer { .. }) && p.id == payment_id - }); + let node_a_payments = node_a + .list_payments_with_filter(|p| { + matches!(p.kind, PaymentKind::Bolt12Offer { .. }) && p.id == payment_id + }) + .await; assert_eq!(node_a_payments.len(), 1); let payment_hash = match node_a_payments.first().unwrap().kind { PaymentKind::Bolt12Offer { @@ -1296,9 +1313,11 @@ async fn simple_bolt12_send_receive() { expect_payment_received_event!(node_b, expected_amount_msat); let node_b_payment_id = PaymentId(payment_hash.0); - let node_b_payments = node_b.list_payments_with_filter(|p| { - matches!(p.kind, PaymentKind::Bolt12Offer { .. }) && p.id == node_b_payment_id - }); + let node_b_payments = node_b + .list_payments_with_filter(|p| { + matches!(p.kind, PaymentKind::Bolt12Offer { .. }) && p.id == node_b_payment_id + }) + .await; assert_eq!(node_b_payments.len(), 1); match node_b_payments.first().unwrap().kind { PaymentKind::Bolt12Offer { hash, preimage, secret, offer_id, .. } => { @@ -1326,8 +1345,9 @@ async fn simple_bolt12_send_receive() { expected_payer_note.clone(), None, ) + .await .unwrap(); - let invoice = node_a.bolt12_payment().request_refund_payment(&refund).unwrap(); + let invoice = node_a.bolt12_payment().request_refund_payment(&refund).await.unwrap(); expect_payment_received_event!(node_a, overpaid_amount); let node_b_payment_id = node_b @@ -1335,14 +1355,17 @@ async fn simple_bolt12_send_receive() { matches!(p.kind, PaymentKind::Bolt12Refund { .. }) && p.amount_msat == Some(overpaid_amount) }) + .await .first() .unwrap() .id; expect_payment_successful_event!(node_b, Some(node_b_payment_id), None); - let node_b_payments = node_b.list_payments_with_filter(|p| { - matches!(p.kind, PaymentKind::Bolt12Refund { .. }) && p.id == node_b_payment_id - }); + let node_b_payments = node_b + .list_payments_with_filter(|p| { + matches!(p.kind, PaymentKind::Bolt12Refund { .. }) && p.id == node_b_payment_id + }) + .await; assert_eq!(node_b_payments.len(), 1); match node_b_payments.first().unwrap().kind { PaymentKind::Bolt12Refund { @@ -1366,9 +1389,11 @@ async fn simple_bolt12_send_receive() { assert_eq!(node_b_payments.first().unwrap().amount_msat, Some(overpaid_amount)); let node_a_payment_id = PaymentId(invoice.payment_hash().0); - let node_a_payments = node_a.list_payments_with_filter(|p| { - matches!(p.kind, PaymentKind::Bolt12Refund { .. }) && p.id == node_a_payment_id - }); + let node_a_payments = node_a + .list_payments_with_filter(|p| { + matches!(p.kind, PaymentKind::Bolt12Refund { .. }) && p.id == node_a_payment_id + }) + .await; assert_eq!(node_a_payments.len(), 1); match node_a_payments.first().unwrap().kind { PaymentKind::Bolt12Refund { hash, preimage, secret, .. } => { @@ -1394,32 +1419,32 @@ async fn async_payment() { config_sender.log_writer = TestLogWriter::Custom(Arc::new(MultiNodeLogger::new("sender ".to_string()))); config_sender.async_payments_role = Some(AsyncPaymentsRole::Client); - let node_sender = setup_node(&chain_source, config_sender); + let node_sender = setup_node(&chain_source, config_sender).await; let mut config_sender_lsp = random_config(true); config_sender_lsp.log_writer = TestLogWriter::Custom(Arc::new(MultiNodeLogger::new("sender_lsp ".to_string()))); config_sender_lsp.async_payments_role = Some(AsyncPaymentsRole::Server); - let node_sender_lsp = setup_node(&chain_source, config_sender_lsp); + let node_sender_lsp = setup_node(&chain_source, config_sender_lsp).await; let mut config_receiver_lsp = random_config(true); config_receiver_lsp.log_writer = TestLogWriter::Custom(Arc::new(MultiNodeLogger::new("receiver_lsp".to_string()))); config_receiver_lsp.async_payments_role = Some(AsyncPaymentsRole::Server); - let node_receiver_lsp = setup_node(&chain_source, config_receiver_lsp); + let node_receiver_lsp = setup_node(&chain_source, config_receiver_lsp).await; let mut config_receiver = random_config(true); config_receiver.node_config.listening_addresses = None; config_receiver.node_config.node_alias = None; config_receiver.log_writer = TestLogWriter::Custom(Arc::new(MultiNodeLogger::new("receiver ".to_string()))); - let node_receiver = setup_node(&chain_source, config_receiver); + let node_receiver = setup_node(&chain_source, config_receiver).await; - let address_sender = node_sender.onchain_payment().new_address().unwrap(); - let address_sender_lsp = node_sender_lsp.onchain_payment().new_address().unwrap(); - let address_receiver_lsp = node_receiver_lsp.onchain_payment().new_address().unwrap(); - let address_receiver = node_receiver.onchain_payment().new_address().unwrap(); + let address_sender = node_sender.onchain_payment().new_address().await.unwrap(); + let address_sender_lsp = node_sender_lsp.onchain_payment().new_address().await.unwrap(); + let address_receiver_lsp = node_receiver_lsp.onchain_payment().new_address().await.unwrap(); + let address_receiver = node_receiver.onchain_payment().new_address().await.unwrap(); let premine_amount_sat = 4_000_000; premine_and_distribute_funds( &bitcoind.client, @@ -1429,10 +1454,10 @@ async fn async_payment() { ) .await; - node_sender.sync_wallets().unwrap(); - node_sender_lsp.sync_wallets().unwrap(); - node_receiver_lsp.sync_wallets().unwrap(); - node_receiver.sync_wallets().unwrap(); + node_sender.sync_wallets().await.unwrap(); + node_sender_lsp.sync_wallets().await.unwrap(); + node_receiver_lsp.sync_wallets().await.unwrap(); + node_receiver.sync_wallets().await.unwrap(); open_channel(&node_sender, &node_sender_lsp, 400_000, false, &electrsd).await; open_channel(&node_sender_lsp, &node_receiver_lsp, 400_000, true, &electrsd).await; @@ -1448,10 +1473,10 @@ async fn async_payment() { generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_sender.sync_wallets().unwrap(); - node_sender_lsp.sync_wallets().unwrap(); - node_receiver_lsp.sync_wallets().unwrap(); - node_receiver.sync_wallets().unwrap(); + node_sender.sync_wallets().await.unwrap(); + node_sender_lsp.sync_wallets().await.unwrap(); + node_receiver_lsp.sync_wallets().await.unwrap(); + node_receiver.sync_wallets().await.unwrap(); expect_channel_ready_event!(node_sender, node_sender_lsp.node_id()); expect_channel_ready_events!( @@ -1502,15 +1527,18 @@ async fn async_payment() { tokio::time::sleep(std::time::Duration::from_millis(100)).await; }; - node_receiver.stop().unwrap(); + node_receiver.stop().await.unwrap(); - let payment_id = - node_sender.bolt12_payment().send_using_amount(&offer, 5_000, None, None, None).unwrap(); + let payment_id = node_sender + .bolt12_payment() + .send_using_amount(&offer, 5_000, None, None, None) + .await + .unwrap(); // Sleep to allow the payment reach a state where the htlc is held and waiting for the receiver to come online. tokio::time::sleep(std::time::Duration::from_millis(3000)).await; - node_receiver.start().unwrap(); + node_receiver.start().await.unwrap(); expect_payment_successful_event!(node_sender, Some(payment_id), None); } @@ -1544,10 +1572,10 @@ async fn test_node_announcement_propagation() { config_b.node_config.listening_addresses = Some(node_b_listening_addresses.clone()); config_b.node_config.announcement_addresses = None; - let node_a = setup_node(&chain_source, config_a); - let node_b = setup_node(&chain_source, config_b); + let node_a = setup_node(&chain_source, config_a).await; + let node_b = setup_node(&chain_source, config_b).await; - let address_a = node_a.onchain_payment().new_address().unwrap(); + let address_a = node_a.onchain_payment().new_address().await.unwrap(); let premine_amount_sat = 5_000_000; premine_and_distribute_funds( &bitcoind.client, @@ -1557,21 +1585,21 @@ async fn test_node_announcement_propagation() { ) .await; - node_a.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); // Open an announced channel from node_a to node_b open_channel(&node_a, &node_b, 4_000_000, true, &electrsd).await; generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); expect_channel_ready_event!(node_a, node_b.node_id()); expect_channel_ready_event!(node_b, node_a.node_id()); // Wait until node_b broadcasts a node announcement - while node_b.status().latest_node_announcement_broadcast_timestamp.is_none() { + while node_b.status().await.latest_node_announcement_broadcast_timestamp.is_none() { tokio::time::sleep(std::time::Duration::from_millis(10)).await; } @@ -1612,9 +1640,9 @@ async fn generate_bip21_uri() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false).await; - let address_a = node_a.onchain_payment().new_address().unwrap(); + let address_a = node_a.onchain_payment().new_address().await.unwrap(); let premined_sats = 5_000_000; let expected_amount_sats = 100_000; @@ -1625,6 +1653,7 @@ async fn generate_bip21_uri() { let initial_uni_payment = node_b .unified_payment() .receive(expected_amount_sats, "asdf", expiry_sec) + .await .expect("Failed to generate URI"); println!("Initial URI (no channels): {}", initial_uni_payment); @@ -1640,12 +1669,12 @@ async fn generate_bip21_uri() { ) .await; - node_a.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); open_channel(&node_a, &node_b, 4_000_000, true, &electrsd).await; generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); expect_channel_ready_event!(node_a, node_b.node_id()); expect_channel_ready_event!(node_b, node_a.node_id()); @@ -1654,6 +1683,7 @@ async fn generate_bip21_uri() { let uni_payment = node_b .unified_payment() .receive(expected_amount_sats, "asdf", expiry_sec) + .await .expect("Failed to generate URI"); println!("Generated URI: {}", uni_payment); @@ -1667,9 +1697,9 @@ async fn unified_send_receive_bip21_uri() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false).await; - let address_a = node_a.onchain_payment().new_address().unwrap(); + let address_a = node_a.onchain_payment().new_address().await.unwrap(); let premined_sats = 5_000_000; premine_and_distribute_funds( @@ -1680,18 +1710,18 @@ async fn unified_send_receive_bip21_uri() { ) .await; - node_a.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); open_channel(&node_a, &node_b, 4_000_000, true, &electrsd).await; generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); expect_channel_ready_event!(node_a, node_b.node_id()); expect_channel_ready_event!(node_b, node_a.node_id()); // Sleep until we broadcast a node announcement. - while node_b.status().latest_node_announcement_broadcast_timestamp.is_none() { + while node_b.status().await.latest_node_announcement_broadcast_timestamp.is_none() { tokio::time::sleep(std::time::Duration::from_millis(10)).await; } @@ -1701,7 +1731,8 @@ async fn unified_send_receive_bip21_uri() { let expected_amount_sats = 100_000; let expiry_sec = 4_000; - let uni_payment = node_b.unified_payment().receive(expected_amount_sats, "asdf", expiry_sec); + let uni_payment = + node_b.unified_payment().receive(expected_amount_sats, "asdf", expiry_sec).await; let uri_str = uni_payment.clone().unwrap(); let offer_payment_id: PaymentId = match node_a.unified_payment().send(&uri_str, None, None).await { @@ -1744,7 +1775,7 @@ async fn unified_send_receive_bip21_uri() { let expect_onchain_amount_sats = 800_000; let onchain_uni_payment = - node_b.unified_payment().receive(expect_onchain_amount_sats, "asdf", 4_000).unwrap(); + node_b.unified_payment().receive(expect_onchain_amount_sats, "asdf", 4_000).await.unwrap(); // Cut off any lightning part to fallback to on-chain only. let uri_str_without_lightning = onchain_uni_payment.split("&lightning=").next().unwrap(); @@ -1767,8 +1798,8 @@ async fn unified_send_receive_bip21_uri() { generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; wait_for_tx(&electrsd.client, txid).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); assert_eq!(node_b.list_balances().total_onchain_balance_sats, 800_000); assert_eq!(node_b.list_balances().total_lightning_balance_sats, 200_000); @@ -1808,8 +1839,8 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) { setup_builder!(service_builder, service_config.node_config); service_builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config)); service_builder.set_liquidity_provider_lsps2(lsps2_service_config); - let service_node = service_builder.build(service_config.node_entropy.into()).unwrap(); - service_node.start().unwrap(); + let service_node = service_builder.build(service_config.node_entropy.into()).await.unwrap(); + service_node.start().await.unwrap(); let service_node_id = service_node.node_id(); let service_addr = service_node.listening_addresses().unwrap().first().unwrap().clone(); @@ -1818,18 +1849,18 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) { setup_builder!(client_builder, client_config.node_config); client_builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config)); client_builder.set_liquidity_source_lsps2(service_node_id, service_addr, None); - let client_node = client_builder.build(client_config.node_entropy.into()).unwrap(); - client_node.start().unwrap(); + let client_node = client_builder.build(client_config.node_entropy.into()).await.unwrap(); + client_node.start().await.unwrap(); let payer_config = random_config(true); setup_builder!(payer_builder, payer_config.node_config); payer_builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config)); - let payer_node = payer_builder.build(payer_config.node_entropy.into()).unwrap(); - payer_node.start().unwrap(); + let payer_node = payer_builder.build(payer_config.node_entropy.into()).await.unwrap(); + payer_node.start().await.unwrap(); - let service_addr = service_node.onchain_payment().new_address().unwrap(); - let client_addr = client_node.onchain_payment().new_address().unwrap(); - let payer_addr = payer_node.onchain_payment().new_address().unwrap(); + let service_addr = service_node.onchain_payment().new_address().await.unwrap(); + let client_addr = client_node.onchain_payment().new_address().await.unwrap(); + let payer_addr = payer_node.onchain_payment().new_address().await.unwrap(); let premine_amount_sat = 10_000_000; @@ -1840,17 +1871,17 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) { Amount::from_sat(premine_amount_sat), ) .await; - service_node.sync_wallets().unwrap(); - client_node.sync_wallets().unwrap(); - payer_node.sync_wallets().unwrap(); + service_node.sync_wallets().await.unwrap(); + client_node.sync_wallets().await.unwrap(); + payer_node.sync_wallets().await.unwrap(); // Open a channel payer -> service that will allow paying the JIT invoice println!("Opening channel payer_node -> service_node!"); open_channel(&payer_node, &service_node, 5_000_000, false, &electrsd).await; generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - service_node.sync_wallets().unwrap(); - payer_node.sync_wallets().unwrap(); + service_node.sync_wallets().await.unwrap(); + payer_node.sync_wallets().await.unwrap(); expect_channel_ready_event!(payer_node, service_node.node_id()); expect_channel_ready_event!(service_node, payer_node.node_id()); @@ -1862,11 +1893,12 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) { let jit_invoice = client_node .bolt11_payment() .receive_via_jit_channel(jit_amount_msat, &invoice_description.into(), 1024, None) + .await .unwrap(); // Have the payer_node pay the invoice, therby triggering channel open service_node -> client_node. println!("Paying JIT invoice!"); - let payment_id = payer_node.bolt11_payment().send(&jit_invoice, None).unwrap(); + let payment_id = payer_node.bolt11_payment().send(&jit_invoice, None).await.unwrap(); expect_channel_pending_event!(service_node, client_node.node_id()); expect_channel_ready_event!(service_node, client_node.node_id()); expect_event!(service_node, PaymentForwarded); @@ -1878,7 +1910,7 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) { expect_payment_successful_event!(payer_node, Some(payment_id), None); let client_payment_id = expect_payment_received_event!(client_node, expected_received_amount_msat).unwrap(); - let client_payment = client_node.payment(&client_payment_id).unwrap(); + let client_payment = client_node.payment(&client_payment_id).await.unwrap(); match client_payment.kind { PaymentKind::Bolt11 { counterparty_skimmed_fee_msat, .. } => { assert_eq!(counterparty_skimmed_fee_msat, Some(service_fee_msat)); @@ -1897,13 +1929,16 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) { let invoice_description = Bolt11InvoiceDescription::Direct(Description::new(String::from("asdf")).unwrap()).into(); let amount_msat = 5_000_000; - let invoice = - client_node.bolt11_payment().receive(amount_msat, &invoice_description, 1024).unwrap(); + let invoice = client_node + .bolt11_payment() + .receive(amount_msat, &invoice_description, 1024) + .await + .unwrap(); // Have the payer_node pay the invoice, to check regular forwards service_node -> client_node // are working as expected. println!("Paying regular invoice!"); - let payment_id = payer_node.bolt11_payment().send(&invoice, None).unwrap(); + let payment_id = payer_node.bolt11_payment().send(&invoice, None).await.unwrap(); expect_payment_successful_event!(payer_node, Some(payment_id), None); expect_event!(service_node, PaymentForwarded); expect_payment_received_event!(client_node, amount_msat); @@ -1925,11 +1960,12 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) { None, manual_payment_hash, ) + .await .unwrap(); // Have the payer_node pay the invoice, therby triggering channel open service_node -> client_node. println!("Paying JIT invoice!"); - let payment_id = payer_node.bolt11_payment().send(&jit_invoice, None).unwrap(); + let payment_id = payer_node.bolt11_payment().send(&jit_invoice, None).await.unwrap(); expect_channel_pending_event!(service_node, client_node.node_id()); expect_channel_ready_event!(service_node, client_node.node_id()); expect_channel_pending_event!(client_node, service_node.node_id()); @@ -1947,13 +1983,14 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) { client_node .bolt11_payment() .claim_for_hash(manual_payment_hash, claimable_amount_msat, manual_preimage) + .await .unwrap(); expect_event!(service_node, PaymentForwarded); expect_payment_successful_event!(payer_node, Some(payment_id), None); let client_payment_id = expect_payment_received_event!(client_node, expected_received_amount_msat).unwrap(); - let client_payment = client_node.payment(&client_payment_id).unwrap(); + let client_payment = client_node.payment(&client_payment_id).await.unwrap(); match client_payment.kind { PaymentKind::Bolt11 { counterparty_skimmed_fee_msat, .. } => { assert_eq!(counterparty_skimmed_fee_msat, Some(service_fee_msat)); @@ -1978,11 +2015,12 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) { None, manual_payment_hash, ) + .await .unwrap(); // Have the payer_node pay the invoice, therby triggering channel open service_node -> client_node. println!("Paying JIT invoice!"); - let payment_id = payer_node.bolt11_payment().send(&jit_invoice, None).unwrap(); + let payment_id = payer_node.bolt11_payment().send(&jit_invoice, None).await.unwrap(); expect_channel_pending_event!(service_node, client_node.node_id()); expect_channel_ready_event!(service_node, client_node.node_id()); expect_channel_pending_event!(client_node, service_node.node_id()); @@ -1997,10 +2035,10 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) { expected_received_amount_msat ); println!("Failing payment!"); - client_node.bolt11_payment().fail_for_hash(manual_payment_hash).unwrap(); + client_node.bolt11_payment().fail_for_hash(manual_payment_hash).await.unwrap(); expect_event!(payer_node, PaymentFailed); - assert_eq!(client_node.payment(&payment_id).unwrap().status, PaymentStatus::Failed); + assert_eq!(client_node.payment(&payment_id).await.unwrap().status, PaymentStatus::Failed); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] @@ -2013,7 +2051,7 @@ async fn facade_logging() { config.log_writer = TestLogWriter::LogFacade; println!("== Facade logging starts =="); - let _node = setup_node(&chain_source, config); + let _node = setup_node(&chain_source, config).await; assert!(!logger.retrieve_logs().is_empty()); for (_, entry) in logger.retrieve_logs().iter().enumerate() { @@ -2025,9 +2063,9 @@ async fn facade_logging() { async fn spontaneous_send_with_custom_preimage() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false).await; - let address_a = node_a.onchain_payment().new_address().unwrap(); + let address_a = node_a.onchain_payment().new_address().await.unwrap(); let premine_sat = 1_000_000; premine_and_distribute_funds( &bitcoind.client, @@ -2036,12 +2074,12 @@ async fn spontaneous_send_with_custom_preimage() { Amount::from_sat(premine_sat), ) .await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); open_channel(&node_a, &node_b, 500_000, true, &electrsd).await; generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); expect_channel_ready_event!(node_a, node_b.node_id()); expect_channel_ready_event!(node_b, node_a.node_id()); @@ -2054,12 +2092,13 @@ async fn spontaneous_send_with_custom_preimage() { let payment_id = node_a .spontaneous_payment() .send_with_preimage(amount_msat, node_b.node_id(), custom_preimage, None) + .await .unwrap(); // check payment status and verify stored preimage expect_payment_successful_event!(node_a, Some(payment_id), None); let details: PaymentDetails = - node_a.list_payments_with_filter(|p| p.id == payment_id).first().unwrap().clone(); + node_a.list_payments_with_filter(|p| p.id == payment_id).await.first().unwrap().clone(); assert_eq!(details.status, PaymentStatus::Succeeded); if let PaymentKind::Spontaneous { preimage: Some(pi), .. } = details.kind { assert_eq!(pi.0, custom_bytes); @@ -2069,10 +2108,12 @@ async fn spontaneous_send_with_custom_preimage() { // Verify receiver side (node_b) expect_payment_received_event!(node_b, amount_msat); - let receiver_payments: Vec = node_b.list_payments_with_filter(|p| { - p.direction == PaymentDirection::Inbound - && matches!(p.kind, PaymentKind::Spontaneous { .. }) - }); + let receiver_payments: Vec = node_b + .list_payments_with_filter(|p| { + p.direction == PaymentDirection::Inbound + && matches!(p.kind, PaymentKind::Spontaneous { .. }) + }) + .await; assert_eq!(receiver_payments.len(), 1); let receiver_details = &receiver_payments[0]; @@ -2093,8 +2134,8 @@ async fn drop_in_async_context() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); let config = random_config(true); - let node = setup_node(&chain_source, config); - node.stop().unwrap(); + let node = setup_node(&chain_source, config).await; + node.stop().await.unwrap(); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] @@ -2127,8 +2168,8 @@ async fn lsps2_client_trusts_lsp() { setup_builder!(service_builder, service_config.node_config); service_builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config)); service_builder.set_liquidity_provider_lsps2(lsps2_service_config); - let service_node = service_builder.build(service_config.node_entropy.into()).unwrap(); - service_node.start().unwrap(); + let service_node = service_builder.build(service_config.node_entropy.into()).await.unwrap(); + service_node.start().await.unwrap(); let service_node_id = service_node.node_id(); let service_addr = service_node.listening_addresses().unwrap().first().unwrap().clone(); @@ -2136,19 +2177,19 @@ async fn lsps2_client_trusts_lsp() { setup_builder!(client_builder, client_config.node_config); client_builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config)); client_builder.set_liquidity_source_lsps2(service_node_id, service_addr.clone(), None); - let client_node = client_builder.build(client_config.node_entropy.into()).unwrap(); - client_node.start().unwrap(); + let client_node = client_builder.build(client_config.node_entropy.into()).await.unwrap(); + client_node.start().await.unwrap(); let client_node_id = client_node.node_id(); let payer_config = random_config(true); setup_builder!(payer_builder, payer_config.node_config); payer_builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config)); - let payer_node = payer_builder.build(payer_config.node_entropy.into()).unwrap(); - payer_node.start().unwrap(); + let payer_node = payer_builder.build(payer_config.node_entropy.into()).await.unwrap(); + payer_node.start().await.unwrap(); - let service_addr_onchain = service_node.onchain_payment().new_address().unwrap(); - let client_addr_onchain = client_node.onchain_payment().new_address().unwrap(); - let payer_addr_onchain = payer_node.onchain_payment().new_address().unwrap(); + let service_addr_onchain = service_node.onchain_payment().new_address().await.unwrap(); + let client_addr_onchain = client_node.onchain_payment().new_address().await.unwrap(); + let payer_addr_onchain = payer_node.onchain_payment().new_address().await.unwrap(); let premine_amount_sat = 10_000_000; @@ -2159,16 +2200,16 @@ async fn lsps2_client_trusts_lsp() { Amount::from_sat(premine_amount_sat), ) .await; - service_node.sync_wallets().unwrap(); - client_node.sync_wallets().unwrap(); - payer_node.sync_wallets().unwrap(); + service_node.sync_wallets().await.unwrap(); + client_node.sync_wallets().await.unwrap(); + payer_node.sync_wallets().await.unwrap(); println!("Premine complete!"); // Open a channel payer -> service that will allow paying the JIT invoice open_channel(&payer_node, &service_node, 5_000_000, false, &electrsd).await; generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - service_node.sync_wallets().unwrap(); - payer_node.sync_wallets().unwrap(); + service_node.sync_wallets().await.unwrap(); + payer_node.sync_wallets().await.unwrap(); expect_channel_ready_event!(payer_node, service_node.node_id()); expect_channel_ready_event!(service_node, payer_node.node_id()); @@ -2188,11 +2229,12 @@ async fn lsps2_client_trusts_lsp() { None, manual_payment_hash, ) + .await .unwrap(); // Have the payer_node pay the invoice, therby triggering channel open service_node -> client_node. println!("Paying JIT invoice!"); - let payment_id = payer_node.bolt11_payment().send(&res, None).unwrap(); + let payment_id = payer_node.bolt11_payment().send(&res, None).await.unwrap(); println!("Payment ID: {:?}", payment_id); let funding_txo = expect_channel_pending_event!(service_node, client_node.node_id()); expect_channel_ready_event!(service_node, client_node.node_id()); @@ -2206,8 +2248,8 @@ async fn lsps2_client_trusts_lsp() { let funding_tx_found = mempool.0.iter().any(|txid| *txid == funding_txo.txid); assert!(!funding_tx_found, "Funding transaction should NOT be broadcast yet"); - service_node.sync_wallets().unwrap(); - client_node.sync_wallets().unwrap(); + service_node.sync_wallets().await.unwrap(); + client_node.sync_wallets().await.unwrap(); assert_eq!( client_node .list_channels() @@ -2241,6 +2283,7 @@ async fn lsps2_client_trusts_lsp() { client_node .bolt11_payment() .claim_for_hash(manual_payment_hash, jit_amount_msat, manual_preimage) + .await .unwrap(); expect_payment_successful_event!(payer_node, Some(payment_id), None); @@ -2250,8 +2293,8 @@ async fn lsps2_client_trusts_lsp() { // Check the nodes pick up on the confirmed funding tx now. wait_for_tx(&electrsd.client, funding_txo.txid).await; generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - service_node.sync_wallets().unwrap(); - client_node.sync_wallets().unwrap(); + service_node.sync_wallets().await.unwrap(); + client_node.sync_wallets().await.unwrap(); assert_eq!( client_node .list_channels() @@ -2302,8 +2345,8 @@ async fn lsps2_lsp_trusts_client_but_client_does_not_claim() { setup_builder!(service_builder, service_config.node_config); service_builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config)); service_builder.set_liquidity_provider_lsps2(lsps2_service_config); - let service_node = service_builder.build(service_config.node_entropy.into()).unwrap(); - service_node.start().unwrap(); + let service_node = service_builder.build(service_config.node_entropy.into()).await.unwrap(); + service_node.start().await.unwrap(); let service_node_id = service_node.node_id(); let service_addr = service_node.listening_addresses().unwrap().first().unwrap().clone(); @@ -2312,20 +2355,20 @@ async fn lsps2_lsp_trusts_client_but_client_does_not_claim() { setup_builder!(client_builder, client_config.node_config); client_builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config)); client_builder.set_liquidity_source_lsps2(service_node_id, service_addr.clone(), None); - let client_node = client_builder.build(client_config.node_entropy.into()).unwrap(); - client_node.start().unwrap(); + let client_node = client_builder.build(client_config.node_entropy.into()).await.unwrap(); + client_node.start().await.unwrap(); let client_node_id = client_node.node_id(); let payer_config = random_config(true); setup_builder!(payer_builder, payer_config.node_config); payer_builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config)); - let payer_node = payer_builder.build(payer_config.node_entropy.into()).unwrap(); - payer_node.start().unwrap(); + let payer_node = payer_builder.build(payer_config.node_entropy.into()).await.unwrap(); + payer_node.start().await.unwrap(); - let service_addr_onchain = service_node.onchain_payment().new_address().unwrap(); - let client_addr_onchain = client_node.onchain_payment().new_address().unwrap(); - let payer_addr_onchain = payer_node.onchain_payment().new_address().unwrap(); + let service_addr_onchain = service_node.onchain_payment().new_address().await.unwrap(); + let client_addr_onchain = client_node.onchain_payment().new_address().await.unwrap(); + let payer_addr_onchain = payer_node.onchain_payment().new_address().await.unwrap(); let premine_amount_sat = 10_000_000; @@ -2336,16 +2379,16 @@ async fn lsps2_lsp_trusts_client_but_client_does_not_claim() { Amount::from_sat(premine_amount_sat), ) .await; - service_node.sync_wallets().unwrap(); - client_node.sync_wallets().unwrap(); - payer_node.sync_wallets().unwrap(); + service_node.sync_wallets().await.unwrap(); + client_node.sync_wallets().await.unwrap(); + payer_node.sync_wallets().await.unwrap(); println!("Premine complete!"); // Open a channel payer -> service that will allow paying the JIT invoice open_channel(&payer_node, &service_node, 5_000_000, false, &electrsd).await; generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - service_node.sync_wallets().unwrap(); - payer_node.sync_wallets().unwrap(); + service_node.sync_wallets().await.unwrap(); + payer_node.sync_wallets().await.unwrap(); expect_channel_ready_event!(payer_node, service_node.node_id()); expect_channel_ready_event!(service_node, payer_node.node_id()); @@ -2365,11 +2408,12 @@ async fn lsps2_lsp_trusts_client_but_client_does_not_claim() { None, manual_payment_hash, ) + .await .unwrap(); // Have the payer_node pay the invoice, therby triggering channel open service_node -> client_node. println!("Paying JIT invoice!"); - let _payment_id = payer_node.bolt11_payment().send(&res, None).unwrap(); + let _payment_id = payer_node.bolt11_payment().send(&res, None).await.unwrap(); let funding_txo = expect_channel_pending_event!(service_node, client_node.node_id()); expect_channel_ready_event!(service_node, client_node.node_id()); expect_channel_pending_event!(client_node, service_node.node_id()); @@ -2379,8 +2423,8 @@ async fn lsps2_lsp_trusts_client_but_client_does_not_claim() { // Check the nodes pick up on the confirmed funding tx now. wait_for_tx(&electrsd.client, funding_txo.txid).await; generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - service_node.sync_wallets().unwrap(); - client_node.sync_wallets().unwrap(); + service_node.sync_wallets().await.unwrap(); + client_node.sync_wallets().await.unwrap(); assert_eq!( client_node .list_channels() @@ -2415,14 +2459,14 @@ async fn payment_persistence_after_restart() { let payment_amount_msat = 1_000_000; // 1000 sats per payment { - let node_a = setup_node(&chain_source, config_a.clone()); + let node_a = setup_node(&chain_source, config_a.clone()).await; println!("\n== Node B =="); let config_b = random_config(true); - let node_b = setup_node(&chain_source, config_b); + let node_b = setup_node(&chain_source, config_b).await; - let addr_a = node_a.onchain_payment().new_address().unwrap(); - let addr_b = node_b.onchain_payment().new_address().unwrap(); + let addr_a = node_a.onchain_payment().new_address().await.unwrap(); + let addr_b = node_b.onchain_payment().new_address().await.unwrap(); // Premine sufficient funds for a large channel and many payments let premine_amount_sat = 10_000_000; @@ -2433,8 +2477,8 @@ async fn payment_persistence_after_restart() { Amount::from_sat(premine_amount_sat), ) .await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); assert_eq!(node_a.list_balances().spendable_onchain_balance_sats, premine_amount_sat); assert_eq!(node_b.list_balances().spendable_onchain_balance_sats, premine_amount_sat); @@ -2442,8 +2486,8 @@ async fn payment_persistence_after_restart() { let channel_amount_sat = 5_000_000; open_channel(&node_a, &node_b, channel_amount_sat, true, &electrsd).await; generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); expect_channel_ready_event!(node_a, node_b.node_id()); expect_channel_ready_event!(node_b, node_a.node_id()); @@ -2456,8 +2500,9 @@ async fn payment_persistence_after_restart() { let invoice = node_b .bolt11_payment() .receive(payment_amount_msat, &invoice_description.clone().into(), 3600) + .await .unwrap(); - let payment_id = node_a.bolt11_payment().send(&invoice, None).unwrap(); + let payment_id = node_a.bolt11_payment().send(&invoice, None).await.unwrap(); expect_event!(node_a, PaymentSuccessful); expect_event!(node_b, PaymentReceived); @@ -2466,31 +2511,36 @@ async fn payment_persistence_after_restart() { } // Verify payment succeeded - assert_eq!(node_a.payment(&payment_id).unwrap().status, PaymentStatus::Succeeded); + assert_eq!(node_a.payment(&payment_id).await.unwrap().status, PaymentStatus::Succeeded); } println!("All {} payments completed successfully", num_payments); // Verify node_a has 200 outbound Bolt11 payments before shutdown - let outbound_payments_before = node_a.list_payments_with_filter(|p| { - p.direction == PaymentDirection::Outbound - && matches!(p.kind, PaymentKind::Bolt11 { .. }) - }); + let outbound_payments_before = node_a + .list_payments_with_filter(|p| { + p.direction == PaymentDirection::Outbound + && matches!(p.kind, PaymentKind::Bolt11 { .. }) + }) + .await; assert_eq!(outbound_payments_before.len(), num_payments); // Shut down both nodes println!("\nShutting down nodes..."); - node_a.stop().unwrap(); - node_b.stop().unwrap(); + node_a.stop().await.unwrap(); + node_b.stop().await.unwrap(); } // Restart node_a with the same config println!("\nRestarting node A..."); - let restarted_node_a = setup_node(&chain_source, config_a); + let restarted_node_a = setup_node(&chain_source, config_a).await; // Assert all 200 payments are still in the store - let outbound_payments_after = restarted_node_a.list_payments_with_filter(|p| { - p.direction == PaymentDirection::Outbound && matches!(p.kind, PaymentKind::Bolt11 { .. }) - }); + let outbound_payments_after = restarted_node_a + .list_payments_with_filter(|p| { + p.direction == PaymentDirection::Outbound + && matches!(p.kind, PaymentKind::Bolt11 { .. }) + }) + .await; assert_eq!( outbound_payments_after.len(), num_payments, @@ -2516,7 +2566,7 @@ async fn payment_persistence_after_restart() { outbound_payments_after.len() ); - restarted_node_a.stop().unwrap(); + restarted_node_a.stop().await.unwrap(); } enum OldLdkVersion { @@ -2629,10 +2679,10 @@ async fn do_persistence_backwards_compatibility(version: OldLdkVersion) { let node_entropy = NodeEntropy::from_seed_bytes(seed_bytes.to_vec()).unwrap(); #[cfg(not(feature = "uniffi"))] let node_entropy = NodeEntropy::from_seed_bytes(seed_bytes); - let node_new = builder_new.build(node_entropy.into()).unwrap(); + let node_new = builder_new.build(node_entropy.into()).await.unwrap(); - node_new.start().unwrap(); - node_new.sync_wallets().unwrap(); + node_new.start().await.unwrap(); + node_new.sync_wallets().await.unwrap(); let new_balance = node_new.list_balances().spendable_onchain_balance_sats; let new_node_id = node_new.node_id(); @@ -2640,7 +2690,7 @@ async fn do_persistence_backwards_compatibility(version: OldLdkVersion) { assert_eq!(old_node_id, new_node_id); assert_eq!(old_balance, new_balance); - node_new.stop().unwrap(); + node_new.stop().await.unwrap(); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] @@ -2678,10 +2728,10 @@ async fn fs_store_persistence_backwards_compatibility() { let node_entropy = NodeEntropy::from_seed_bytes(seed_bytes.to_vec()).unwrap(); #[cfg(not(feature = "uniffi"))] let node_entropy = NodeEntropy::from_seed_bytes(seed_bytes); - let node_new = builder_new.build_with_fs_store(node_entropy.into()).unwrap(); + let node_new = builder_new.build_with_fs_store(node_entropy.into()).await.unwrap(); - node_new.start().unwrap(); - node_new.sync_wallets().unwrap(); + node_new.start().await.unwrap(); + node_new.sync_wallets().await.unwrap(); let new_balance = node_new.list_balances().spendable_onchain_balance_sats; let new_node_id = node_new.node_id(); @@ -2689,18 +2739,18 @@ async fn fs_store_persistence_backwards_compatibility() { assert_eq!(old_node_id, new_node_id); assert_eq!(old_balance, new_balance); - node_new.stop().unwrap(); + node_new.stop().await.unwrap(); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn onchain_fee_bump_rbf() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false).await; // Fund both nodes - let addr_a = node_a.onchain_payment().new_address().unwrap(); - let addr_b = node_b.onchain_payment().new_address().unwrap(); + let addr_a = node_a.onchain_payment().new_address().await.unwrap(); + let addr_b = node_b.onchain_payment().new_address().await.unwrap(); let premine_amount_sat = 500_000; premine_and_distribute_funds( @@ -2711,23 +2761,23 @@ async fn onchain_fee_bump_rbf() { ) .await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); // Send a transaction from node_b to node_a that we'll later bump let amount_to_send_sats = 100_000; let txid = - node_b.onchain_payment().send_to_address(&addr_a, amount_to_send_sats, None).unwrap(); + node_b.onchain_payment().send_to_address(&addr_a, amount_to_send_sats, None).await.unwrap(); wait_for_tx(&electrsd.client, txid).await; // Give the chain source time to index the unconfirmed transaction before syncing. // Without this, Esplora may not yet have the tx, causing sync to miss it and // leaving the BDK wallet graph empty. tokio::time::sleep(std::time::Duration::from_secs(5)).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); let payment_id = PaymentId(txid.to_byte_array()); - let original_payment = node_b.payment(&payment_id).unwrap(); + let original_payment = node_b.payment(&payment_id).await.unwrap(); let original_fee = original_payment.fee_paid_msat.unwrap(); // Non-existent payment id @@ -2736,27 +2786,27 @@ async fn onchain_fee_bump_rbf() { let invalid_payment_id = PaymentId(fake_txid.to_byte_array()); assert_eq!( Err(NodeError::InvalidPaymentId), - node_b.onchain_payment().bump_fee_rbf(invalid_payment_id, None) + node_b.onchain_payment().bump_fee_rbf(invalid_payment_id, None).await ); // Bump an inbound payment assert_eq!( Err(NodeError::InvalidPaymentId), - node_a.onchain_payment().bump_fee_rbf(payment_id, None) + node_a.onchain_payment().bump_fee_rbf(payment_id, None).await ); // Successful fee bump - let new_txid = node_b.onchain_payment().bump_fee_rbf(payment_id, None).unwrap(); + let new_txid = node_b.onchain_payment().bump_fee_rbf(payment_id, None).await.unwrap(); wait_for_tx(&electrsd.client, new_txid).await; // Give the chain source time to index the unconfirmed transaction before syncing. // Without this, Esplora may not yet have the tx, causing sync to miss it and // leaving the BDK wallet graph empty. tokio::time::sleep(std::time::Duration::from_secs(5)).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); // Verify fee increased and txid updated for node_b - let new_payment = node_b.payment(&payment_id).unwrap(); + let new_payment = node_b.payment(&payment_id).await.unwrap(); assert!( new_payment.fee_paid_msat > Some(original_fee), "Fee should increase after RBF bump. Original: {}, New: {}", @@ -2774,13 +2824,13 @@ async fn onchain_fee_bump_rbf() { } // Multiple consecutive bumps - let second_bump_txid = node_b.onchain_payment().bump_fee_rbf(payment_id, None).unwrap(); + let second_bump_txid = node_b.onchain_payment().bump_fee_rbf(payment_id, None).await.unwrap(); wait_for_tx(&electrsd.client, second_bump_txid).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); // Verify second bump payment exists and txid updated for node_b - let second_payment = node_b.payment(&payment_id).unwrap(); + let second_payment = node_b.payment(&payment_id).await.unwrap(); assert!( second_payment.fee_paid_msat > new_payment.fee_paid_msat, "Second bump should have higher fee than first bump" @@ -2797,16 +2847,16 @@ async fn onchain_fee_bump_rbf() { // Confirm the transaction and try to bump again (should fail) generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); assert_eq!( Err(NodeError::InvalidPaymentId), - node_b.onchain_payment().bump_fee_rbf(payment_id, None) + node_b.onchain_payment().bump_fee_rbf(payment_id, None).await ); // Verify final payment is confirmed - let final_payment = node_b.payment(&payment_id).unwrap(); + let final_payment = node_b.payment(&payment_id).await.unwrap(); assert_eq!(final_payment.status, PaymentStatus::Succeeded); match final_payment.kind { PaymentKind::Onchain { status, .. } => { @@ -2816,9 +2866,11 @@ async fn onchain_fee_bump_rbf() { } // Verify node A received the funds correctly - let node_a_received_payment = node_a.list_payments_with_filter(|p| { - p.id == payment_id && matches!(p.kind, PaymentKind::Onchain { .. }) - }); + let node_a_received_payment = node_a + .list_payments_with_filter(|p| { + p.id == payment_id && matches!(p.kind, PaymentKind::Onchain { .. }) + }) + .await; assert_eq!(node_a_received_payment.len(), 1); match &node_a_received_payment[0].kind { @@ -2838,10 +2890,10 @@ async fn onchain_fee_bump_rbf() { async fn open_channel_with_all_with_anchors() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false).await; - let addr_a = node_a.onchain_payment().new_address().unwrap(); - let addr_b = node_b.onchain_payment().new_address().unwrap(); + let addr_a = node_a.onchain_payment().new_address().await.unwrap(); + let addr_b = node_b.onchain_payment().new_address().await.unwrap(); let premine_amount_sat = 1_000_000; @@ -2852,16 +2904,16 @@ async fn open_channel_with_all_with_anchors() { Amount::from_sat(premine_amount_sat), ) .await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); assert_eq!(node_a.list_balances().spendable_onchain_balance_sats, premine_amount_sat); let funding_txo = open_channel_with_all(&node_a, &node_b, false, &electrsd).await; generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); let _user_channel_id_a = expect_channel_ready_event!(node_a, node_b.node_id()); let _user_channel_id_b = expect_channel_ready_event!(node_b, node_a.node_id()); @@ -2883,18 +2935,18 @@ async fn open_channel_with_all_with_anchors() { assert_eq!(channel.counterparty_node_id, node_b.node_id()); assert_eq!(channel.funding_txo.unwrap(), funding_txo); - node_a.stop().unwrap(); - node_b.stop().unwrap(); + node_a.stop().await.unwrap(); + node_b.stop().await.unwrap(); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn open_channel_with_all_without_anchors() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, false, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, false, false).await; - let addr_a = node_a.onchain_payment().new_address().unwrap(); - let addr_b = node_b.onchain_payment().new_address().unwrap(); + let addr_a = node_a.onchain_payment().new_address().await.unwrap(); + let addr_b = node_b.onchain_payment().new_address().await.unwrap(); let premine_amount_sat = 1_000_000; @@ -2905,16 +2957,16 @@ async fn open_channel_with_all_without_anchors() { Amount::from_sat(premine_amount_sat), ) .await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); assert_eq!(node_a.list_balances().spendable_onchain_balance_sats, premine_amount_sat); let funding_txo = open_channel_with_all(&node_a, &node_b, false, &electrsd).await; generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); let _user_channel_id_a = expect_channel_ready_event!(node_a, node_b.node_id()); let _user_channel_id_b = expect_channel_ready_event!(node_b, node_a.node_id()); @@ -2934,18 +2986,18 @@ async fn open_channel_with_all_without_anchors() { assert_eq!(channel.counterparty_node_id, node_b.node_id()); assert_eq!(channel.funding_txo.unwrap(), funding_txo); - node_a.stop().unwrap(); - node_b.stop().unwrap(); + node_a.stop().await.unwrap(); + node_b.stop().await.unwrap(); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn splice_in_with_all_balance() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = random_chain_source(&bitcoind, &electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false).await; - let addr_a = node_a.onchain_payment().new_address().unwrap(); - let addr_b = node_b.onchain_payment().new_address().unwrap(); + let addr_a = node_a.onchain_payment().new_address().await.unwrap(); + let addr_b = node_b.onchain_payment().new_address().await.unwrap(); let premine_amount_sat = 5_000_000; let channel_amount_sat = 1_000_000; @@ -2957,8 +3009,8 @@ async fn splice_in_with_all_balance() { Amount::from_sat(premine_amount_sat), ) .await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); assert_eq!(node_a.list_balances().spendable_onchain_balance_sats, premine_amount_sat); // Open a channel with a fixed amount first @@ -2966,8 +3018,8 @@ async fn splice_in_with_all_balance() { generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); let user_channel_id_a = expect_channel_ready_event!(node_a, node_b.node_id()); let _user_channel_id_b = expect_channel_ready_event!(node_b, node_a.node_id()); @@ -2985,8 +3037,8 @@ async fn splice_in_with_all_balance() { generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await; - node_a.sync_wallets().unwrap(); - node_b.sync_wallets().unwrap(); + node_a.sync_wallets().await.unwrap(); + node_b.sync_wallets().await.unwrap(); let _user_channel_id_a2 = expect_channel_ready_event!(node_a, node_b.node_id()); let _user_channel_id_b2 = expect_channel_ready_event!(node_b, node_a.node_id()); @@ -3012,6 +3064,6 @@ async fn splice_in_with_all_balance() { "Remaining balance {remaining_balance} should be close to the anchor reserve {anchor_reserve_sat}" ); - node_a.stop().unwrap(); - node_b.stop().unwrap(); + node_a.stop().await.unwrap(); + node_b.stop().await.unwrap(); } diff --git a/tests/integration_tests_vss.rs b/tests/integration_tests_vss.rs index 210e9a8b25..2ee5ae1ca0 100644 --- a/tests/integration_tests_vss.rs +++ b/tests/integration_tests_vss.rs @@ -96,7 +96,7 @@ async fn vss_node_restart() { bitcoin::Amount::from_sat(100_000), ) .await; - node.sync_wallets().unwrap(); + node.sync_wallets().await.unwrap(); let balance = node.list_balances().spendable_onchain_balance_sats; assert!(balance > 0); @@ -117,7 +117,7 @@ async fn vss_node_restart() { .unwrap(); node.start().unwrap(); - node.sync_wallets().unwrap(); + node.sync_wallets().await.unwrap(); assert_eq!(expected_node_id, node.node_id()); assert_eq!(expected_balance_sats, node.list_balances().spendable_onchain_balance_sats); diff --git a/tests/integration_tests_vss_no_auth.rs b/tests/integration_tests_vss_no_auth.rs index e4ad9cacba..eeea450e7f 100644 --- a/tests/integration_tests_vss_no_auth.rs +++ b/tests/integration_tests_vss_no_auth.rs @@ -53,17 +53,19 @@ async fn vss_v0_schema_backwards_compatibility() { bitcoin::Amount::from_sat(100_000), ) .await; - node_old.sync_wallets().unwrap(); + node_old.sync_wallets().await.unwrap(); let balance = node_old.list_balances().spendable_onchain_balance_sats; assert!(balance > 0); let node_id = node_old.node_id(); // Workaround necessary as v0.6.2's VSS runtime wasn't dropsafe in a tokio context. - tokio::task::block_in_place(move || { + tokio::task::spawn_blocking(move || { node_old.stop().unwrap(); drop(node_old); - }); + }) + .await + .unwrap(); (balance, node_id) }; @@ -81,10 +83,11 @@ async fn vss_v0_schema_backwards_compatibility() { store_id, HashMap::new(), ) + .await .unwrap(); - node_new.start().unwrap(); - node_new.sync_wallets().unwrap(); + node_new.start().await.unwrap(); + node_new.sync_wallets().await.unwrap(); let new_balance = node_new.list_balances().spendable_onchain_balance_sats; let new_node_id = node_new.node_id(); @@ -92,5 +95,5 @@ async fn vss_v0_schema_backwards_compatibility() { assert_eq!(old_node_id, new_node_id); assert_eq!(old_balance, new_balance); - node_new.stop().unwrap(); + node_new.stop().await.unwrap(); } diff --git a/tests/reorg_test.rs b/tests/reorg_test.rs index 295d9fdd24..d405fb2641 100644 --- a/tests/reorg_test.rs +++ b/tests/reorg_test.rs @@ -17,11 +17,12 @@ proptest! { #![proptest_config(proptest::test_runner::Config::with_cases(5))] #[test] fn reorg_test(reorg_depth in 1..=6usize, force_close in prop::bool::ANY) { - let rt = tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .unwrap(); - rt.block_on(async { + let rt = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap(); + let (test_done, test_result) = std::sync::mpsc::channel(); + rt.spawn(async move { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source_a = random_chain_source(&bitcoind, &electrsd); @@ -31,7 +32,7 @@ proptest! { macro_rules! config_node { ($chain_source: expr, $anchor_channels: expr) => {{ let config_a = random_config($anchor_channels); - let node = setup_node(&$chain_source, config_a); + let node = setup_node(&$chain_source, config_a).await; node }}; } @@ -50,15 +51,17 @@ proptest! { }}; } - let amount_sat = 2_100_000; - let addr_nodes = - nodes.iter().map(|node| node.onchain_payment().new_address().unwrap()).collect::>(); - premine_and_distribute_funds(bitcoind, electrs, addr_nodes, Amount::from_sat(amount_sat)).await; + let amount_sat = 2_100_000; + let mut addr_nodes = Vec::with_capacity(nodes.len()); + for node in &nodes { + addr_nodes.push(node.onchain_payment().new_address().await.unwrap()); + } + premine_and_distribute_funds(bitcoind, electrs, addr_nodes, Amount::from_sat(amount_sat)).await; macro_rules! sync_wallets { () => { for node in &nodes { - node.sync_wallets().unwrap(); + node.sync_wallets().await.unwrap(); } }; } @@ -88,7 +91,7 @@ proptest! { for _ in 0..$expected { match $node.next_event_async().await { Event::ChannelReady { user_channel_id, counterparty_node_id, .. } => { - $node.event_handled().unwrap(); + $node.event_handled().await.unwrap(); user_channels.insert(counterparty_node_id, user_channel_id); }, other => panic!("Unexpected event: {:?}", other), @@ -104,6 +107,7 @@ proptest! { node .list_payments_with_filter(|p| p.direction == PaymentDirection::Outbound && matches!(p.kind, PaymentKind::Onchain { .. })) + .await .len(), 1 ); @@ -126,9 +130,9 @@ proptest! { let funding = nodes_funding_tx.get(&node.node_id()).expect("Funding tx not exist"); if force_close { - node.force_close_channel(&user_channel_id, next_node.node_id(), None).unwrap(); + node.force_close_channel(&user_channel_id, next_node.node_id(), None).await.unwrap(); } else { - node.close_channel(&user_channel_id, next_node.node_id()).unwrap(); + node.close_channel(&user_channel_id, next_node.node_id()).await.unwrap(); } expect_event!(node, ChannelClosed); @@ -145,20 +149,21 @@ proptest! { if force_close { for node in &nodes { - node.sync_wallets().unwrap(); - // If there is no more balance, there is nothing to process here. - if node.list_balances().lightning_balances.len() < 1 { - return; - } + node.sync_wallets().await.unwrap(); + // If there is no more balance, there is nothing to process here. + if node.list_balances().lightning_balances.len() < 1 { + test_done.send(()).unwrap(); + return; + } match node.list_balances().lightning_balances[0] { LightningBalance::ClaimableAwaitingConfirmations { confirmation_height, .. } => { - let cur_height = node.status().current_best_block.height; + let cur_height = node.status().await.current_best_block.height; let blocks_to_go = confirmation_height - cur_height; generate_blocks_and_wait(bitcoind, electrs, blocks_to_go as usize).await; - node.sync_wallets().unwrap(); + node.sync_wallets().await.unwrap(); }, _ => panic!("Unexpected balance state for node_hub!"), } @@ -171,7 +176,7 @@ proptest! { } generate_blocks_and_wait(&bitcoind, electrs, 1).await; - node.sync_wallets().unwrap(); + node.sync_wallets().await.unwrap(); assert!(node.list_balances().lightning_balances.len() < 2); assert!(node.list_balances().pending_balances_from_channel_closures.len() > 0); match node.list_balances().pending_balances_from_channel_closures[0] { @@ -198,6 +203,8 @@ proptest! { assert_eq!(node.next_event(), None); }); - }) + test_done.send(()).unwrap(); + }); + test_result.recv().unwrap(); } }