diff --git a/benches/payments.rs b/benches/payments.rs index ba69e046d..6adc91123 100644 --- a/benches/payments.rs +++ b/benches/payments.rs @@ -35,12 +35,10 @@ 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( - amount_msat, - node_b.node_id(), - preimage, - None, - ); + let payment_id = node_a + .spontaneous_payment() + .send_with_preimage(amount_msat, node_b.node_id(), preimage, None) + .await; match payment_id { Ok(payment_id) => { @@ -110,6 +108,7 @@ async fn send_payments(node_a: Arc, node_b: Arc) -> std::time::Durat PaymentPreimage(preimage_bytes), None, ) + .await .ok() .unwrap(); diff --git a/bindings/ldk_node.udl b/bindings/ldk_node.udl index 6bd031379..d71d6359e 100644 --- a/bindings/ldk_node.udl +++ b/bindings/ldk_node.udl @@ -164,28 +164,28 @@ interface Node { OnchainPayment onchain_payment(); UnifiedPayment unified_payment(); LSPS1Liquidity lsps1_liquidity(); - [Throws=NodeError] + [Throws=NodeError, Async] void connect(PublicKey node_id, SocketAddress address, boolean persist); - [Throws=NodeError] + [Throws=NodeError, Async] void disconnect(PublicKey node_id); - [Throws=NodeError] + [Throws=NodeError, Async] UserChannelId open_channel(PublicKey node_id, SocketAddress address, u64 channel_amount_sats, u64? push_to_counterparty_msat, ChannelConfig? channel_config); - [Throws=NodeError] + [Throws=NodeError, Async] UserChannelId open_announced_channel(PublicKey node_id, SocketAddress address, u64 channel_amount_sats, u64? push_to_counterparty_msat, ChannelConfig? channel_config); - [Throws=NodeError] + [Throws=NodeError, Async] void splice_in([ByRef]UserChannelId user_channel_id, PublicKey counterparty_node_id, u64 splice_amount_sats); - [Throws=NodeError] + [Throws=NodeError, Async] void splice_out([ByRef]UserChannelId user_channel_id, PublicKey counterparty_node_id, [ByRef]Address address, u64 splice_amount_sats); - [Throws=NodeError] + [Throws=NodeError, Async] void close_channel([ByRef]UserChannelId user_channel_id, PublicKey counterparty_node_id); - [Throws=NodeError] + [Throws=NodeError, Async] 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] void sync_wallets(); PaymentDetails? payment([ByRef]PaymentId payment_id); - [Throws=NodeError] + [Throws=NodeError, Async] void remove_payment([ByRef]PaymentId payment_id); BalanceDetails list_balances(); sequence list_payments(); @@ -194,7 +194,7 @@ interface Node { 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(); }; @@ -205,9 +205,9 @@ interface Bolt11InvoiceDescription { }; interface Bolt11Payment { - [Throws=NodeError] + [Throws=NodeError, Async] PaymentId send([ByRef]Bolt11Invoice invoice, RouteParametersConfig? route_parameters); - [Throws=NodeError] + [Throws=NodeError, Async] PaymentId send_using_amount([ByRef]Bolt11Invoice invoice, u64 amount_msat, RouteParametersConfig? route_parameters); [Throws=NodeError] void send_probes([ByRef]Bolt11Invoice invoice, RouteParametersConfig? route_parameters); @@ -215,38 +215,38 @@ interface Bolt11Payment { void send_probes_using_amount([ByRef]Bolt11Invoice invoice, u64 amount_msat, RouteParametersConfig? route_parameters); [Throws=NodeError] void claim_for_hash(PaymentHash payment_hash, u64 claimable_amount_msat, PaymentPreimage preimage); - [Throws=NodeError] + [Throws=NodeError, Async] void fail_for_hash(PaymentHash payment_hash); - [Throws=NodeError] + [Throws=NodeError, Async] Bolt11Invoice receive(u64 amount_msat, [ByRef]Bolt11InvoiceDescription description, u32 expiry_secs); - [Throws=NodeError] + [Throws=NodeError, Async] Bolt11Invoice receive_for_hash(u64 amount_msat, [ByRef]Bolt11InvoiceDescription description, u32 expiry_secs, PaymentHash payment_hash); - [Throws=NodeError] + [Throws=NodeError, Async] Bolt11Invoice receive_variable_amount([ByRef]Bolt11InvoiceDescription description, u32 expiry_secs); - [Throws=NodeError] + [Throws=NodeError, Async] Bolt11Invoice receive_variable_amount_for_hash([ByRef]Bolt11InvoiceDescription description, u32 expiry_secs, PaymentHash payment_hash); - [Throws=NodeError] + [Throws=NodeError, Async] Bolt11Invoice receive_via_jit_channel(u64 amount_msat, [ByRef]Bolt11InvoiceDescription description, u32 expiry_secs, u64? max_lsp_fee_limit_msat); - [Throws=NodeError] + [Throws=NodeError, Async] Bolt11Invoice receive_via_jit_channel_for_hash(u64 amount_msat, [ByRef]Bolt11InvoiceDescription description, u32 expiry_secs, u64? max_lsp_fee_limit_msat, PaymentHash payment_hash); - [Throws=NodeError] + [Throws=NodeError, Async] Bolt11Invoice receive_variable_amount_via_jit_channel([ByRef]Bolt11InvoiceDescription description, u32 expiry_secs, u64? max_proportional_lsp_fee_limit_ppm_msat); - [Throws=NodeError] + [Throws=NodeError, Async] Bolt11Invoice receive_variable_amount_via_jit_channel_for_hash([ByRef]Bolt11InvoiceDescription description, u32 expiry_secs, u64? max_proportional_lsp_fee_limit_ppm_msat, PaymentHash payment_hash); }; interface Bolt12Payment { - [Throws=NodeError] + [Throws=NodeError, Async] PaymentId send([ByRef]Offer offer, u64? quantity, string? payer_note, RouteParametersConfig? route_parameters); - [Throws=NodeError] + [Throws=NodeError, Async] PaymentId send_using_amount([ByRef]Offer offer, u64 amount_msat, u64? quantity, string? payer_note, RouteParametersConfig? route_parameters); [Throws=NodeError] Offer receive(u64 amount_msat, [ByRef]string description, u32? expiry_secs, u64? quantity); [Throws=NodeError] Offer receive_variable_amount([ByRef]string description, u32? expiry_secs); - [Throws=NodeError] + [Throws=NodeError, Async] Bolt12Invoice request_refund_payment([ByRef]Refund refund); - [Throws=NodeError] + [Throws=NodeError, Async] Refund initiate_refund(u64 amount_msat, u32 expiry_secs, u64? quantity, string? payer_note, RouteParametersConfig? route_parameters); [Throws=NodeError] Offer receive_async(); @@ -257,24 +257,24 @@ interface Bolt12Payment { }; interface SpontaneousPayment { - [Throws=NodeError] + [Throws=NodeError, Async] PaymentId send(u64 amount_msat, PublicKey node_id, RouteParametersConfig? route_parameters); - [Throws=NodeError] + [Throws=NodeError, Async] PaymentId send_with_custom_tlvs(u64 amount_msat, PublicKey node_id, RouteParametersConfig? route_parameters, sequence custom_tlvs); - [Throws=NodeError] + [Throws=NodeError, Async] PaymentId send_with_preimage(u64 amount_msat, PublicKey node_id, PaymentPreimage preimage, RouteParametersConfig? route_parameters); - [Throws=NodeError] + [Throws=NodeError, Async] PaymentId send_with_preimage_and_custom_tlvs(u64 amount_msat, PublicKey node_id, sequence custom_tlvs, PaymentPreimage preimage, RouteParametersConfig? route_parameters); [Throws=NodeError] void send_probes(u64 amount_msat, PublicKey node_id); }; interface OnchainPayment { - [Throws=NodeError] + [Throws=NodeError, Async] Address new_address(); - [Throws=NodeError] + [Throws=NodeError, Async] Txid send_to_address([ByRef]Address address, u64 amount_sats, FeeRate? fee_rate); - [Throws=NodeError] + [Throws=NodeError, Async] Txid send_all_to_address([ByRef]Address address, boolean retain_reserve, FeeRate? fee_rate); }; @@ -289,7 +289,7 @@ interface FeeRate { }; interface UnifiedPayment { - [Throws=NodeError] + [Throws=NodeError, Async] string receive(u64 amount_sats, [ByRef]string message, u32 expiry_sec); [Throws=NodeError, Async] UnifiedPaymentResult send([ByRef]string uri_str, u64? amount_msat, RouteParametersConfig? route_parameters); diff --git a/src/builder.rs b/src/builder.rs index 5d8a5a7a9..abc294fc8 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -75,9 +75,9 @@ use crate::peer_store::PeerStore; use crate::runtime::{Runtime, RuntimeSpawner}; use crate::tx_broadcaster::TransactionBroadcaster; use crate::types::{ - AsyncPersister, ChainMonitor, ChannelManager, DynStore, DynStoreWrapper, GossipSync, Graph, - KeysManager, MessageRouter, OnionMessenger, PaymentStore, PeerManager, PendingPaymentStore, - Persister, SyncAndAsyncKVStore, + AsyncPersister, ChainMonitor, ChannelManager, DynStore, DynStoreRef, DynStoreWrapper, + GossipSync, Graph, KeysManager, MessageRouter, OnionMessenger, PaymentStore, PeerManager, + PendingPaymentStore, }; use crate::wallet::persist::KVStoreWalletPersister; use crate::wallet::Wallet; @@ -169,17 +169,17 @@ pub enum BuildError { RuntimeSetupFailed, /// We failed to read data from the [`KVStore`]. /// - /// [`KVStore`]: lightning::util::persist::KVStoreSync + /// [`KVStore`]: lightning::util::persist::KVStore ReadFailed, /// We failed to write data to the [`KVStore`]. /// - /// [`KVStore`]: lightning::util::persist::KVStoreSync + /// [`KVStore`]: lightning::util::persist::KVStore WriteFailed, /// We failed to access the given `storage_dir_path`. StoragePathAccessFailed, /// We failed to setup our [`KVStore`]. /// - /// [`KVStore`]: lightning::util::persist::KVStoreSync + /// [`KVStore`]: lightning::util::persist::KVStore KVStoreSetupFailed, /// We failed to setup the onchain wallet. WalletSetupFailed, @@ -655,7 +655,7 @@ impl NodeBuilder { } /// Builds a [`Node`] instance according to the options previously configured. - pub fn build_with_store( + pub fn build_with_store( &self, node_entropy: NodeEntropy, kv_store: S, ) -> Result { let logger = setup_logger(&self.log_writer_config, &self.config)?; @@ -1020,7 +1020,7 @@ impl ArcedNodeBuilder { /// 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 fn build_with_store( &self, node_entropy: Arc, kv_store: S, ) -> Result, BuildError> { self.inner.read().unwrap().build_with_store(*node_entropy, kv_store).map(Arc::new) @@ -1193,12 +1193,15 @@ 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 = BdkWallet::load() - .descriptor(KeychainKind::External, Some(descriptor.clone())) - .descriptor(KeychainKind::Internal, Some(change_descriptor.clone())) - .extract_keys() - .check_network(config.network) - .load_wallet(&mut wallet_persister) + let wallet_opt = runtime + .block_on( + 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), + ) .map_err(|e| match e { bdk_wallet::LoadWithPersistError::InvalidChangeSet( bdk_wallet::LoadError::Mismatch(bdk_wallet::LoadMismatch::Network { @@ -1222,9 +1225,12 @@ fn build_with_store_internal( let bdk_wallet = match wallet_opt { Some(wallet) => wallet, None => { - let mut wallet = BdkWallet::create(descriptor, change_descriptor) - .network(config.network) - .create_wallet(&mut wallet_persister) + let mut wallet = runtime + .block_on( + BdkWallet::create(descriptor, change_descriptor) + .network(config.network) + .create_wallet_async(&mut wallet_persister), + ) .map_err(|e| { log_error!(logger, "Failed to set up wallet: {}", e); BuildError::WalletSetupFailed @@ -1289,8 +1295,8 @@ fn build_with_store_internal( )); let peer_storage_key = keys_manager.get_peer_storage_key(); - let monitor_reader = Arc::new(AsyncPersister::new( - Arc::clone(&kv_store), + let persister = Arc::new(AsyncPersister::new( + DynStoreRef(Arc::clone(&kv_store)), RuntimeSpawner::new(Arc::clone(&runtime)), Arc::clone(&logger), PERSISTER_MAX_PENDING_UPDATES, @@ -1303,9 +1309,9 @@ 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 move { + let (monitor_read_res, network_graph_res) = runtime.block_on(async { tokio::join!( - monitor_reader.read_all_channel_monitors_with_updates_parallel(), + persister.read_all_channel_monitors_with_updates_parallel(), read_network_graph(&*kv_store_ref, logger_ref), ) }); @@ -1323,23 +1329,16 @@ fn build_with_store_internal( }, }; - let persister = Arc::new(Persister::new( - Arc::clone(&kv_store), - Arc::clone(&logger), - PERSISTER_MAX_PENDING_UPDATES, - Arc::clone(&keys_manager), - Arc::clone(&keys_manager), - Arc::clone(&tx_broadcaster), - Arc::clone(&fee_estimator), - )); + let persister = Arc::try_unwrap(persister) + .unwrap_or_else(|_| panic!("Arc should have no other references")); // Initialize the ChainMonitor - let chain_monitor: Arc = Arc::new(chainmonitor::ChainMonitor::new( + let chain_monitor: Arc = Arc::new(chainmonitor::ChainMonitor::new_async_beta( Some(Arc::clone(&chain_source)), Arc::clone(&tx_broadcaster), Arc::clone(&logger), Arc::clone(&fee_estimator), - Arc::clone(&persister), + persister, Arc::clone(&keys_manager), peer_storage_key, )); @@ -1565,12 +1564,16 @@ fn build_with_store_internal( { let mut locked_node_metrics = node_metrics.write().unwrap(); locked_node_metrics.latest_rgs_snapshot_timestamp = None; - write_node_metrics(&*locked_node_metrics, &*kv_store, Arc::clone(&logger)) - .map_err(|e| { - log_error!(logger, "Failed writing to store: {}", e); - BuildError::WriteFailed - })?; } + runtime + .block_on(async { + let snapshot = node_metrics.read().unwrap().clone(); + write_node_metrics(&snapshot, &*kv_store, Arc::clone(&logger)).await + }) + .map_err(|e| { + log_error!(logger, "Failed writing to store: {}", e); + BuildError::WriteFailed + })?; p2p_source }, GossipSourceConfig::RapidGossipSync(rgs_server) => { diff --git a/src/chain/bitcoind.rs b/src/chain/bitcoind.rs index 8a7167022..a26d46133 100644 --- a/src/chain/bitcoind.rs +++ b/src/chain/bitcoind.rs @@ -206,7 +206,11 @@ impl BitcoindChainSource { unix_time_secs_opt; locked_node_metrics.latest_onchain_wallet_sync_timestamp = unix_time_secs_opt; - write_node_metrics(&*locked_node_metrics, &*self.kv_store, &*self.logger) + } + { + let snapshot = self.node_metrics.read().unwrap().clone(); + write_node_metrics(&snapshot, &*self.kv_store, &*self.logger) + .await .unwrap_or_else(|e| { log_error!(self.logger, "Failed to persist node metrics: {}", e); }); @@ -440,11 +444,12 @@ impl BitcoindChainSource { evicted_txids.len(), now.elapsed().unwrap().as_millis() ); - 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); @@ -454,11 +459,15 @@ impl BitcoindChainSource { let unix_time_secs_opt = SystemTime::now().duration_since(UNIX_EPOCH).ok().map(|d| d.as_secs()); - let mut locked_node_metrics = self.node_metrics.write().unwrap(); - locked_node_metrics.latest_lightning_wallet_sync_timestamp = unix_time_secs_opt; - locked_node_metrics.latest_onchain_wallet_sync_timestamp = unix_time_secs_opt; - - write_node_metrics(&*locked_node_metrics, &*self.kv_store, &*self.logger)?; + { + let mut locked_node_metrics = self.node_metrics.write().unwrap(); + locked_node_metrics.latest_lightning_wallet_sync_timestamp = unix_time_secs_opt; + locked_node_metrics.latest_onchain_wallet_sync_timestamp = unix_time_secs_opt; + } + { + let snapshot = self.node_metrics.read().unwrap().clone(); + write_node_metrics(&snapshot, &*self.kv_store, &*self.logger).await?; + } Ok(()) } @@ -571,7 +580,10 @@ impl BitcoindChainSource { { let mut locked_node_metrics = self.node_metrics.write().unwrap(); locked_node_metrics.latest_fee_rate_cache_update_timestamp = unix_time_secs_opt; - write_node_metrics(&*locked_node_metrics, &*self.kv_store, &*self.logger)?; + } + { + let snapshot = self.node_metrics.read().unwrap().clone(); + write_node_metrics(&snapshot, &*self.kv_store, &*self.logger).await?; } Ok(()) diff --git a/src/chain/electrum.rs b/src/chain/electrum.rs index 7b08c3845..0be83e987 100644 --- a/src/chain/electrum.rs +++ b/src/chain/electrum.rs @@ -128,52 +128,48 @@ impl ElectrumChainSource { let incremental_sync = self.node_metrics.read().unwrap().latest_onchain_wallet_sync_timestamp.is_some(); - let apply_wallet_update = - |update_res: Result, now: Instant| match update_res { - Ok(update) => match onchain_wallet.apply_update(update) { - Ok(()) => { - log_debug!( - self.logger, - "{} of on-chain wallet finished in {}ms.", - if incremental_sync { "Incremental sync" } else { "Sync" }, - now.elapsed().as_millis() - ); - let unix_time_secs_opt = - SystemTime::now().duration_since(UNIX_EPOCH).ok().map(|d| d.as_secs()); - { - let mut locked_node_metrics = self.node_metrics.write().unwrap(); - locked_node_metrics.latest_onchain_wallet_sync_timestamp = - unix_time_secs_opt; - write_node_metrics( - &*locked_node_metrics, - &*self.kv_store, - &*self.logger, - )?; - } - Ok(()) - }, - Err(e) => Err(e), - }, - Err(e) => Err(e), - }; - let cached_txs = onchain_wallet.get_cached_txs(); - let res = if incremental_sync { + let update_res: Result<(BdkUpdate, Instant), Error> = if incremental_sync { let incremental_sync_request = onchain_wallet.get_incremental_sync_request(); let incremental_sync_fut = electrum_client .get_incremental_sync_wallet_update(incremental_sync_request, cached_txs); let now = Instant::now(); - let update_res = incremental_sync_fut.await.map(|u| u.into()); - apply_wallet_update(update_res, now) + incremental_sync_fut.await.map(|u| (u.into(), now)) } else { let full_scan_request = onchain_wallet.get_full_scan_request(); let full_scan_fut = electrum_client.get_full_scan_wallet_update(full_scan_request, cached_txs); let now = Instant::now(); - let update_res = full_scan_fut.await.map(|u| u.into()); - apply_wallet_update(update_res, now) + full_scan_fut.await.map(|u| (u.into(), now)) + }; + + let res = match update_res { + Ok((update, now)) => match onchain_wallet.apply_update(update).await { + Ok(()) => { + log_debug!( + self.logger, + "{} of on-chain wallet finished in {}ms.", + if incremental_sync { "Incremental sync" } else { "Sync" }, + now.elapsed().as_millis() + ); + let unix_time_secs_opt = + SystemTime::now().duration_since(UNIX_EPOCH).ok().map(|d| d.as_secs()); + { + let mut locked_node_metrics = self.node_metrics.write().unwrap(); + locked_node_metrics.latest_onchain_wallet_sync_timestamp = + unix_time_secs_opt; + } + { + let snapshot = self.node_metrics.read().unwrap().clone(); + write_node_metrics(&snapshot, &*self.kv_store, &*self.logger).await?; + } + Ok(()) + }, + Err(e) => Err(e), + }, + Err(e) => Err(e), }; res @@ -236,7 +232,10 @@ impl ElectrumChainSource { { let mut locked_node_metrics = self.node_metrics.write().unwrap(); locked_node_metrics.latest_lightning_wallet_sync_timestamp = unix_time_secs_opt; - write_node_metrics(&*locked_node_metrics, &*self.kv_store, &*self.logger)?; + } + { + let snapshot = self.node_metrics.read().unwrap().clone(); + write_node_metrics(&snapshot, &*self.kv_store, &*self.logger).await?; } } @@ -269,7 +268,10 @@ impl ElectrumChainSource { { let mut locked_node_metrics = self.node_metrics.write().unwrap(); locked_node_metrics.latest_fee_rate_cache_update_timestamp = unix_time_secs_opt; - write_node_metrics(&*locked_node_metrics, &*self.kv_store, &*self.logger)?; + } + { + let snapshot = self.node_metrics.read().unwrap().clone(); + write_node_metrics(&snapshot, &*self.kv_store, &*self.logger).await?; } Ok(()) diff --git a/src/chain/esplora.rs b/src/chain/esplora.rs index 245db72f6..826b83d11 100644 --- a/src/chain/esplora.rs +++ b/src/chain/esplora.rs @@ -108,7 +108,7 @@ impl EsploraChainSource { 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, @@ -123,11 +123,14 @@ impl EsploraChainSource { { let mut locked_node_metrics = self.node_metrics.write().unwrap(); locked_node_metrics.latest_onchain_wallet_sync_timestamp = unix_time_secs_opt; + } + { + let snapshot = self.node_metrics.read().unwrap().clone(); write_node_metrics( - &*locked_node_metrics, + &snapshot, &*self.kv_store, &*self.logger - )?; + ).await?; } Ok(()) }, @@ -262,7 +265,10 @@ impl EsploraChainSource { let mut locked_node_metrics = self.node_metrics.write().unwrap(); locked_node_metrics.latest_lightning_wallet_sync_timestamp = unix_time_secs_opt; - write_node_metrics(&*locked_node_metrics, &*self.kv_store, &*self.logger)?; + } + { + let snapshot = self.node_metrics.read().unwrap().clone(); + write_node_metrics(&snapshot, &*self.kv_store, &*self.logger).await?; } Ok(()) }, @@ -346,7 +352,10 @@ impl EsploraChainSource { { let mut locked_node_metrics = self.node_metrics.write().unwrap(); locked_node_metrics.latest_fee_rate_cache_update_timestamp = unix_time_secs_opt; - write_node_metrics(&*locked_node_metrics, &*self.kv_store, &*self.logger)?; + } + { + let snapshot = self.node_metrics.read().unwrap().clone(); + write_node_metrics(&snapshot, &*self.kv_store, &*self.logger).await?; } Ok(()) diff --git a/src/data_store.rs b/src/data_store.rs index ac5c78fb7..a18490b85 100644 --- a/src/data_store.rs +++ b/src/data_store.rs @@ -9,7 +9,7 @@ use std::collections::{hash_map, HashMap}; use std::ops::Deref; use std::sync::{Arc, Mutex}; -use lightning::util::persist::KVStoreSync; +use lightning::util::persist::KVStore; use lightning::util::ser::{Readable, Writeable}; use crate::logger::{log_error, LdkLogger}; @@ -64,47 +64,54 @@ where Self { objects, primary_namespace, secondary_namespace, kv_store, logger } } - pub(crate) fn insert(&self, object: SO) -> Result { - let mut locked_objects = self.objects.lock().unwrap(); - - self.persist(&object)?; - let updated = locked_objects.insert(object.id(), object).is_some(); + pub(crate) async fn insert(&self, object: SO) -> Result { + let updated = { + let mut locked_objects = self.objects.lock().unwrap(); + locked_objects.insert(object.id(), object.clone()).is_some() + }; + self.persist(&object).await?; Ok(updated) } - pub(crate) fn insert_or_update(&self, object: SO) -> Result { - let mut locked_objects = self.objects.lock().unwrap(); + pub(crate) async fn insert_or_update(&self, object: SO) -> Result { + let (updated, to_persist) = { + let mut locked_objects = self.objects.lock().unwrap(); + match locked_objects.entry(object.id()) { + hash_map::Entry::Occupied(mut e) => { + let update = object.to_update(); + let updated = e.get_mut().update(update); + if updated { + (true, Some(e.get().clone())) + } else { + (false, None) + } + }, + hash_map::Entry::Vacant(e) => { + e.insert(object.clone()); + (true, Some(object)) + }, + } + }; - 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())?; - } - }, - hash_map::Entry::Vacant(e) => { - e.insert(object.clone()); - self.persist(&object)?; - updated = true; - }, + if let Some(obj) = to_persist { + self.persist(&obj).await?; } Ok(updated) } - pub(crate) fn remove(&self, id: &SO::Id) -> Result<(), Error> { + pub(crate) async fn remove(&self, id: &SO::Id) -> Result<(), Error> { let removed = self.objects.lock().unwrap().remove(id).is_some(); if removed { let store_key = id.encode_to_hex_str(); - KVStoreSync::remove( + KVStore::remove( &*self.kv_store, &self.primary_namespace, &self.secondary_namespace, &store_key, false, ) + .await .map_err(|e| { log_error!( self.logger, @@ -124,36 +131,43 @@ where self.objects.lock().unwrap().get(id).cloned() } - pub(crate) fn update(&self, update: SO::Update) -> Result { - let mut locked_objects = self.objects.lock().unwrap(); - - if let Some(object) = locked_objects.get_mut(&update.id()) { - let updated = object.update(update); - if updated { - self.persist(&object)?; - Ok(DataStoreUpdateResult::Updated) + pub(crate) async fn update(&self, update: SO::Update) -> Result { + let (result, to_persist) = { + let mut locked_objects = self.objects.lock().unwrap(); + if let Some(object) = locked_objects.get_mut(&update.id()) { + let updated = object.update(update); + if updated { + (DataStoreUpdateResult::Updated, Some(object.clone())) + } else { + (DataStoreUpdateResult::Unchanged, None) + } } else { - Ok(DataStoreUpdateResult::Unchanged) + (DataStoreUpdateResult::NotFound, None) } - } else { - Ok(DataStoreUpdateResult::NotFound) + }; + + if let Some(obj) = to_persist { + self.persist(&obj).await?; } + + Ok(result) } pub(crate) fn list_filter bool>(&self, f: F) -> Vec { self.objects.lock().unwrap().values().filter(f).cloned().collect::>() } - fn persist(&self, object: &SO) -> Result<(), Error> { + async fn persist(&self, object: &SO) -> Result<(), Error> { let store_key = object.id().encode_to_hex_str(); let data = object.encode(); - KVStoreSync::write( + KVStore::write( &*self.kv_store, &self.primary_namespace, &self.secondary_namespace, &store_key, data, ) + .await .map_err(|e| { log_error!( self.logger, @@ -238,8 +252,8 @@ mod tests { (2, data, required), }); - #[test] - fn data_is_persisted() { + #[tokio::test] + async fn data_is_persisted() { let store: Arc = Arc::new(DynStoreWrapper(InMemoryStore::new())); let logger = Arc::new(TestLogger::new()); let primary_namespace = "datastore_test_primary".to_string(); @@ -258,47 +272,49 @@ mod tests { let store_key = id.encode_to_hex_str(); // Check we start empty. - assert!(KVStoreSync::read(&*store, &primary_namespace, &secondary_namespace, &store_key) + assert!(KVStore::read(&*store, &primary_namespace, &secondary_namespace, &store_key) + .await .is_err()); // 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())); + assert_eq!(Ok(false), data_store.insert(object.clone()).await); assert_eq!(Some(object), data_store.get(&id)); - assert!(KVStoreSync::read(&*store, &primary_namespace, &secondary_namespace, &store_key) + assert!(KVStore::read(&*store, &primary_namespace, &secondary_namespace, &store_key) + .await .is_ok()); // Test re-insertion returns `true` let mut override_object = object.clone(); override_object.data = [24u8; 3]; - assert_eq!(Ok(true), data_store.insert(override_object)); + assert_eq!(Ok(true), data_store.insert(override_object).await); assert_eq!(Some(override_object), data_store.get(&id)); // Check update returns `Updated` let update = TestObjectUpdate { id, data: [25u8; 3] }; - assert_eq!(Ok(DataStoreUpdateResult::Updated), data_store.update(update)); + assert_eq!(Ok(DataStoreUpdateResult::Updated), data_store.update(update).await); assert_eq!(data_store.get(&id).unwrap().data, [25u8; 3]); // Check no-op update yields `Unchanged` let update = TestObjectUpdate { id, data: [25u8; 3] }; - assert_eq!(Ok(DataStoreUpdateResult::Unchanged), data_store.update(update)); + assert_eq!(Ok(DataStoreUpdateResult::Unchanged), data_store.update(update).await); // Check bogus update yields `NotFound` let bogus_id = TestObjectId { id: [84u8; 4] }; let update = TestObjectUpdate { id: bogus_id, data: [12u8; 3] }; - assert_eq!(Ok(DataStoreUpdateResult::NotFound), data_store.update(update)); + assert_eq!(Ok(DataStoreUpdateResult::NotFound), data_store.update(update).await); // Check `insert_or_update` inserts unknown objects let iou_id = TestObjectId { id: [55u8; 4] }; let iou_object = TestObject { id: iou_id, data: [34u8; 3] }; - assert_eq!(Ok(true), data_store.insert_or_update(iou_object.clone())); + assert_eq!(Ok(true), data_store.insert_or_update(iou_object.clone()).await); // Check `insert_or_update` doesn't update the same object - assert_eq!(Ok(false), data_store.insert_or_update(iou_object.clone())); + assert_eq!(Ok(false), data_store.insert_or_update(iou_object.clone()).await); // Check `insert_or_update` updates if object changed let mut new_iou_object = iou_object; new_iou_object.data[0] += 1; - assert_eq!(Ok(true), data_store.insert_or_update(new_iou_object)); + assert_eq!(Ok(true), data_store.insert_or_update(new_iou_object).await); } } diff --git a/src/event.rs b/src/event.rs index e24059ec7..c6b7146aa 100644 --- a/src/event.rs +++ b/src/event.rs @@ -550,12 +550,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| { @@ -657,7 +661,7 @@ where status: Some(PaymentStatus::Failed), ..PaymentDetailsUpdate::new(payment_id) }; - match self.payment_store.update(update) { + match self.payment_store.update(update).await { Ok(_) => return Ok(()), Err(e) => { log_error!(self.logger, "Failed to access payment store: {}", e); @@ -681,7 +685,7 @@ where status: Some(PaymentStatus::Failed), ..PaymentDetailsUpdate::new(payment_id) }; - match self.payment_store.update(update) { + match self.payment_store.update(update).await { Ok(_) => return Ok(()), Err(e) => { log_error!(self.logger, "Failed to access payment store: {}", e); @@ -722,7 +726,7 @@ where status: Some(PaymentStatus::Failed), ..PaymentDetailsUpdate::new(payment_id) }; - match self.payment_store.update(update) { + match self.payment_store.update(update).await { Ok(_) => return Ok(()), Err(e) => { log_error!(self.logger, "Failed to access payment store: {}", e); @@ -739,7 +743,7 @@ where counterparty_skimmed_fee_msat: Some(Some(counterparty_skimmed_fee_msat)), ..PaymentDetailsUpdate::new(payment_id) }; - match self.payment_store.update(update) { + match self.payment_store.update(update).await { Ok(_) => (), Err(e) => { log_error!(self.logger, "Failed to access payment store: {}", e); @@ -829,7 +833,7 @@ where PaymentStatus::Pending, ); - match self.payment_store.insert(payment) { + match self.payment_store.insert(payment).await { Ok(false) => (), Ok(true) => { log_error!( @@ -870,7 +874,7 @@ where PaymentStatus::Pending, ); - match self.payment_store.insert(payment) { + match self.payment_store.insert(payment).await { Ok(false) => (), Ok(true) => { log_error!( @@ -910,7 +914,7 @@ where status: Some(PaymentStatus::Failed), ..PaymentDetailsUpdate::new(payment_id) }; - match self.payment_store.update(update) { + match self.payment_store.update(update).await { Ok(_) => return Ok(()), Err(e) => { log_error!(self.logger, "Failed to access payment store: {}", e); @@ -978,7 +982,7 @@ where }, }; - match self.payment_store.update(update) { + match self.payment_store.update(update).await { Ok(DataStoreUpdateResult::Updated) | Ok(DataStoreUpdateResult::Unchanged) => ( // No need to do anything if the idempotent update was applied, which might // be the result of a replayed event. @@ -1039,7 +1043,7 @@ where ..PaymentDetailsUpdate::new(payment_id) }; - match self.payment_store.update(update) { + match self.payment_store.update(update).await { Ok(_) => {}, Err(e) => { log_error!(self.logger, "Failed to access payment store: {}", e); @@ -1090,7 +1094,7 @@ where status: Some(PaymentStatus::Failed), ..PaymentDetailsUpdate::new(payment_id) }; - match self.payment_store.update(update) { + match self.payment_store.update(update).await { Ok(_) => {}, Err(e) => { log_error!(self.logger, "Failed to access payment store: {}", e); @@ -1411,36 +1415,40 @@ where }, }; - let network_graph = self.network_graph.read_only(); - let channels = - self.channel_manager.list_channels_with_counterparty(&counterparty_node_id); - if let Some(pending_channel) = - channels.into_iter().find(|c| c.channel_id == channel_id) - { - if !pending_channel.is_outbound - && self.peer_store.get_peer(&counterparty_node_id).is_none() - { - if let Some(address) = network_graph + let maybe_peer_info = { + let network_graph = self.network_graph.read_only(); + let channels = + self.channel_manager.list_channels_with_counterparty(&counterparty_node_id); + let should_add = channels + .into_iter() + .find(|c| c.channel_id == channel_id) + .map_or(false, |c| !c.is_outbound) + && self.peer_store.get_peer(&counterparty_node_id).is_none(); + + if should_add { + 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()) - { - let peer = PeerInfo { + .map(|address| PeerInfo { node_id: counterparty_node_id, address: address.clone(), - }; - - self.peer_store.add_peer(peer).unwrap_or_else(|e| { - log_error!( - self.logger, - "Failed to add peer {} to peer store: {}", - counterparty_node_id, - e - ); - }); - } + }) + } else { + None } + }; + + if let Some(peer) = maybe_peer_info { + self.peer_store.add_peer(peer).await.unwrap_or_else(|e| { + log_error!( + self.logger, + "Failed to add peer {} to peer store: {}", + counterparty_node_id, + e + ); + }); } }, LdkEvent::ChannelReady { @@ -1767,7 +1775,7 @@ where input: vec![], output: contributed_outputs, }; - 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/sqlite_store/migrations.rs b/src/io/sqlite_store/migrations.rs index ea809be08..e6e3c6259 100644 --- a/src/io/sqlite_store/migrations.rs +++ b/src/io/sqlite_store/migrations.rs @@ -76,8 +76,9 @@ pub(super) fn migrate_schema( mod tests { use std::fs; - use lightning::util::persist::KVStoreSync; + use lightning::util::persist::KVStore; use rusqlite::{named_params, Connection}; + use tokio::runtime::Runtime; use crate::io::sqlite_store::SqliteStore; use crate::io::test_utils::{do_read_write_remove_list_persist, random_storage_path}; @@ -160,7 +161,10 @@ mod tests { // Check we migrate the db just fine without losing our written data. let store = SqliteStore::new(temp_path, Some(db_file_name), Some(kv_table_name)).unwrap(); - let res = store.read(&test_namespace, "", &test_key).unwrap(); + let rt = Runtime::new().unwrap(); + let res = rt + .block_on(async { KVStore::read(&store, &test_namespace, "", &test_key).await }) + .unwrap(); assert_eq!(res, test_data); // Check we can continue to use the store just fine. diff --git a/src/io/sqlite_store/mod.rs b/src/io/sqlite_store/mod.rs index e4091b24e..cffb50b2b 100644 --- a/src/io/sqlite_store/mod.rs +++ b/src/io/sqlite_store/mod.rs @@ -14,7 +14,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::{Arc, Mutex}; use lightning::io; -use lightning::util::persist::{KVStore, KVStoreSync}; +use lightning::util::persist::KVStore; use lightning_types::string::PrintableString; use rusqlite::{named_params, Connection}; @@ -36,7 +36,7 @@ pub const DEFAULT_KV_TABLE_NAME: &str = "ldk_data"; // The current SQLite `user_version`, which we can use if we'd ever need to do a schema migration. const SCHEMA_USER_VERSION: u16 = 2; -/// A [`KVStoreSync`] implementation that writes to and reads from an [SQLite] database. +/// A [`KVStore`] implementation that writes to and reads from an [SQLite] database. /// /// [SQLite]: https://sqlite.org pub struct SqliteStore { @@ -179,49 +179,6 @@ impl KVStore for SqliteStore { } } -impl KVStoreSync for SqliteStore { - fn read( - &self, primary_namespace: &str, secondary_namespace: &str, key: &str, - ) -> io::Result> { - self.inner.read_internal(primary_namespace, secondary_namespace, key) - } - - fn write( - &self, primary_namespace: &str, secondary_namespace: &str, key: &str, buf: Vec, - ) -> io::Result<()> { - let locking_key = self.build_locking_key(primary_namespace, secondary_namespace, key); - let (inner_lock_ref, version) = self.get_new_version_and_lock_ref(locking_key.clone()); - self.inner.write_internal( - inner_lock_ref, - locking_key, - version, - primary_namespace, - secondary_namespace, - key, - buf, - ) - } - - fn remove( - &self, primary_namespace: &str, secondary_namespace: &str, key: &str, _lazy: bool, - ) -> io::Result<()> { - let locking_key = self.build_locking_key(primary_namespace, secondary_namespace, key); - let (inner_lock_ref, version) = self.get_new_version_and_lock_ref(locking_key.clone()); - self.inner.remove_internal( - inner_lock_ref, - locking_key, - version, - primary_namespace, - secondary_namespace, - key, - ) - } - - fn list(&self, primary_namespace: &str, secondary_namespace: &str) -> io::Result> { - self.inner.list_internal(primary_namespace, secondary_namespace) - } -} - struct SqliteStoreInner { connection: Arc>, data_dir: PathBuf, @@ -516,9 +473,7 @@ impl SqliteStoreInner { #[cfg(test)] mod tests { use super::*; - use crate::io::test_utils::{ - do_read_write_remove_list_persist, do_test_store, random_storage_path, - }; + use crate::io::test_utils::{do_read_write_remove_list_persist, random_storage_path}; impl Drop for SqliteStore { fn drop(&mut self) { @@ -541,25 +496,6 @@ mod tests { .unwrap(); do_read_write_remove_list_persist(&store); } - - #[test] - fn test_sqlite_store() { - let mut temp_path = random_storage_path(); - temp_path.push("test_sqlite_store"); - let store_0 = SqliteStore::new( - temp_path.clone(), - Some("test_db_0".to_string()), - Some("test_table".to_string()), - ) - .unwrap(); - let store_1 = SqliteStore::new( - temp_path, - Some("test_db_1".to_string()), - Some("test_table".to_string()), - ) - .unwrap(); - do_test_store(&store_0, &store_1) - } } #[cfg(ldk_bench)] diff --git a/src/io/test_utils.rs b/src/io/test_utils.rs index 9add2d6c1..77e1358d5 100644 --- a/src/io/test_utils.rs +++ b/src/io/test_utils.rs @@ -11,30 +11,11 @@ use std::panic::RefUnwindSafe; use std::path::PathBuf; use std::sync::Mutex; -use lightning::events::ClosureReason; -use lightning::ln::functional_test_utils::{ - check_added_monitors, check_closed_event, connect_block, create_announced_chan_between_nodes, - create_chanmon_cfgs, create_dummy_block, create_network, create_node_cfgs, - create_node_chanmgrs, send_payment, test_legacy_channel_config, TestChanMonCfg, -}; -use lightning::util::persist::{ - KVStore, KVStoreSync, MonitorUpdatingPersister, KVSTORE_NAMESPACE_KEY_MAX_LEN, -}; -use lightning::util::test_utils; -use lightning::{check_closed_broadcast, io}; +use lightning::io; +use lightning::util::persist::{KVStore, KVSTORE_NAMESPACE_KEY_MAX_LEN}; use rand::distr::Alphanumeric; use rand::{rng, Rng}; - -type TestMonitorUpdatePersister<'a, K> = MonitorUpdatingPersister< - &'a K, - &'a test_utils::TestLogger, - &'a test_utils::TestKeysInterface, - &'a test_utils::TestKeysInterface, - &'a test_utils::TestBroadcaster, - &'a test_utils::TestFeeEstimator, ->; - -const EXPECTED_UPDATES_PER_PAYMENT: u64 = 5; +use tokio::runtime::Runtime; pub struct InMemoryStore { persisted_bytes: Mutex>>>, @@ -128,30 +109,6 @@ impl KVStore for InMemoryStore { } } -impl KVStoreSync for InMemoryStore { - fn read( - &self, primary_namespace: &str, secondary_namespace: &str, key: &str, - ) -> io::Result> { - self.read_internal(primary_namespace, secondary_namespace, key) - } - - fn write( - &self, primary_namespace: &str, secondary_namespace: &str, key: &str, buf: Vec, - ) -> io::Result<()> { - self.write_internal(primary_namespace, secondary_namespace, key, buf) - } - - fn remove( - &self, primary_namespace: &str, secondary_namespace: &str, key: &str, lazy: bool, - ) -> io::Result<()> { - self.remove_internal(primary_namespace, secondary_namespace, key, lazy) - } - - fn list(&self, primary_namespace: &str, secondary_namespace: &str) -> io::Result> { - self.list_internal(primary_namespace, secondary_namespace) - } -} - unsafe impl Sync for InMemoryStore {} unsafe impl Send for InMemoryStore {} @@ -163,192 +120,51 @@ pub(crate) fn random_storage_path() -> PathBuf { temp_path } -pub(crate) fn do_read_write_remove_list_persist(kv_store: &K) { +pub(crate) fn do_read_write_remove_list_persist(kv_store: &K) { + let rt = Runtime::new().unwrap(); + rt.block_on(do_read_write_remove_list_persist_async(kv_store)); +} + +async fn do_read_write_remove_list_persist_async(kv_store: &K) { let data = vec![42u8; 32]; let primary_namespace = "testspace"; let secondary_namespace = "testsubspace"; let key = "testkey"; - // Test the basic KVStore operations. - kv_store.write(primary_namespace, secondary_namespace, key, data.clone()).unwrap(); + KVStore::write(kv_store, primary_namespace, secondary_namespace, key, data.clone()) + .await + .unwrap(); - // Test empty primary/secondary namespaces are allowed, but not empty primary namespace and non-empty - // secondary primary_namespace, and not empty key. - kv_store.write("", "", key, data.clone()).unwrap(); - let res = - std::panic::catch_unwind(|| kv_store.write("", secondary_namespace, key, data.clone())); - assert!(res.is_err()); - let res = std::panic::catch_unwind(|| { - kv_store.write(primary_namespace, secondary_namespace, "", data.clone()) - }); - assert!(res.is_err()); + KVStore::write(kv_store, "", "", key, data.clone()).await.unwrap(); - let listed_keys = kv_store.list(primary_namespace, secondary_namespace).unwrap(); + let listed_keys = + KVStore::list(kv_store, primary_namespace, secondary_namespace).await.unwrap(); assert_eq!(listed_keys.len(), 1); assert_eq!(listed_keys[0], key); - let read_data = kv_store.read(primary_namespace, secondary_namespace, key).unwrap(); + let read_data = + KVStore::read(kv_store, primary_namespace, secondary_namespace, key).await.unwrap(); assert_eq!(data, &*read_data); - kv_store.remove(primary_namespace, secondary_namespace, key, false).unwrap(); + KVStore::remove(kv_store, primary_namespace, secondary_namespace, key, false).await.unwrap(); - let listed_keys = kv_store.list(primary_namespace, secondary_namespace).unwrap(); + let listed_keys = + KVStore::list(kv_store, primary_namespace, secondary_namespace).await.unwrap(); assert_eq!(listed_keys.len(), 0); - // Ensure we have no issue operating with primary_namespace/secondary_namespace/key being KVSTORE_NAMESPACE_KEY_MAX_LEN let max_chars: String = std::iter::repeat('A').take(KVSTORE_NAMESPACE_KEY_MAX_LEN).collect(); - kv_store.write(&max_chars, &max_chars, &max_chars, data.clone()).unwrap(); + KVStore::write(kv_store, &max_chars, &max_chars, &max_chars, data.clone()).await.unwrap(); - let listed_keys = kv_store.list(&max_chars, &max_chars).unwrap(); + let listed_keys = KVStore::list(kv_store, &max_chars, &max_chars).await.unwrap(); assert_eq!(listed_keys.len(), 1); assert_eq!(listed_keys[0], max_chars); - let read_data = kv_store.read(&max_chars, &max_chars, &max_chars).unwrap(); + let read_data = KVStore::read(kv_store, &max_chars, &max_chars, &max_chars).await.unwrap(); assert_eq!(data, &*read_data); - kv_store.remove(&max_chars, &max_chars, &max_chars, false).unwrap(); + KVStore::remove(kv_store, &max_chars, &max_chars, &max_chars, false).await.unwrap(); - let listed_keys = kv_store.list(&max_chars, &max_chars).unwrap(); + let listed_keys = KVStore::list(kv_store, &max_chars, &max_chars).await.unwrap(); assert_eq!(listed_keys.len(), 0); } - -pub(crate) fn create_persister<'a, K: KVStoreSync + Sync>( - store: &'a K, chanmon_cfg: &'a TestChanMonCfg, max_pending_updates: u64, -) -> TestMonitorUpdatePersister<'a, K> { - MonitorUpdatingPersister::new( - store, - &chanmon_cfg.logger, - max_pending_updates, - &chanmon_cfg.keys_manager, - &chanmon_cfg.keys_manager, - &chanmon_cfg.tx_broadcaster, - &chanmon_cfg.fee_estimator, - ) -} - -pub(crate) fn create_chain_monitor<'a, K: KVStoreSync + Sync>( - chanmon_cfg: &'a TestChanMonCfg, persister: &'a TestMonitorUpdatePersister<'a, K>, -) -> test_utils::TestChainMonitor<'a> { - test_utils::TestChainMonitor::new( - Some(&chanmon_cfg.chain_source), - &chanmon_cfg.tx_broadcaster, - &chanmon_cfg.logger, - &chanmon_cfg.fee_estimator, - persister, - &chanmon_cfg.keys_manager, - ) -} - -// 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) { - // 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. - let persister_1_max_pending_updates = 3; - - let chanmon_cfgs = create_chanmon_cfgs(2); - - let persister_0 = create_persister(store_0, &chanmon_cfgs[0], persister_0_max_pending_updates); - let persister_1 = create_persister(store_1, &chanmon_cfgs[1], persister_1_max_pending_updates); - - let chain_mon_0 = create_chain_monitor(&chanmon_cfgs[0], &persister_0); - let chain_mon_1 = create_chain_monitor(&chanmon_cfgs[1], &persister_1); - - let mut node_cfgs = create_node_cfgs(2, &chanmon_cfgs); - node_cfgs[0].chain_monitor = chain_mon_0; - node_cfgs[1].chain_monitor = chain_mon_1; - let legacy_cfg = test_legacy_channel_config(); - let node_chanmgrs = - create_node_chanmgrs(2, &node_cfgs, &[Some(legacy_cfg.clone()), Some(legacy_cfg)]); - let nodes = create_network(2, &node_cfgs, &node_chanmgrs); - - // 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(); - assert_eq!(persisted_chan_data_0.len(), 0); - let mut persisted_chan_data_1 = persister_1.read_all_channel_monitors_with_updates().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(); - 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(); - 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); - } - }; - } - - // Create some initial channel and check that a channel was persisted. - let _ = create_announced_chan_between_nodes(&nodes, 0, 1); - check_persisted_data!(0); - - // Send a few payments and make sure the monitors are updated to the latest. - let expected_route = &[&nodes[1]][..]; - send_payment(&nodes[0], expected_route, 8_000_000); - check_persisted_data!(EXPECTED_UPDATES_PER_PAYMENT); - let expected_route = &[&nodes[0]][..]; - send_payment(&nodes[1], expected_route, 4_000_000); - check_persisted_data!(2 * EXPECTED_UPDATES_PER_PAYMENT); - - // Send a few more payments to try all the alignments of max pending updates with - // updates for a payment sent and received. - let mut sender = 0; - for i in 3..=persister_0_max_pending_updates * 2 { - let receiver; - if sender == 0 { - sender = 1; - receiver = 0; - } else { - sender = 0; - receiver = 1; - } - let expected_route = &[&nodes[receiver]][..]; - send_payment(&nodes[sender], expected_route, 21_000); - check_persisted_data!(i * EXPECTED_UPDATES_PER_PAYMENT); - } - - // Force close because cooperative close doesn't result in any persisted - // updates. - let message = "Channel force-closed".to_owned(); - nodes[0] - .node - .force_close_broadcasting_latest_txn( - &nodes[0].node.list_channels()[0].channel_id, - &nodes[1].node.get_our_node_id(), - message.clone(), - ) - .unwrap(); - check_closed_event( - &nodes[0], - 1, - ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(true), message }, - &[nodes[1].node.get_our_node_id()], - 100000, - ); - check_closed_broadcast!(nodes[0], true); - check_added_monitors(&nodes[0], 1); - - let node_txn = nodes[0].tx_broadcaster.txn_broadcast(); - assert_eq!(node_txn.len(), 1); - let txn = vec![node_txn[0].clone(), node_txn[0].clone()]; - let dummy_block = create_dummy_block(nodes[0].best_block_hash(), 42, txn); - connect_block(&nodes[1], &dummy_block); - - check_closed_broadcast!(nodes[1], true); - let reason = ClosureReason::CommitmentTxConfirmed; - let node_id_0 = nodes[0].node.get_our_node_id(); - check_closed_event(&nodes[1], 1, reason, &[node_id_0], 100000); - check_added_monitors(&nodes[1], 1); - - // Make sure everything is persisted as expected after close. - check_persisted_data!(persister_0_max_pending_updates * 2 * EXPECTED_UPDATES_PER_PAYMENT + 1); -} diff --git a/src/io/utils.rs b/src/io/utils.rs index d2f70377b..2719304cd 100644 --- a/src/io/utils.rs +++ b/src/io/utils.rs @@ -26,7 +26,7 @@ use lightning::routing::scoring::{ ChannelLiquidities, ProbabilisticScorer, ProbabilisticScoringDecayParameters, }; use lightning::util::persist::{ - KVStore, KVStoreSync, KVSTORE_NAMESPACE_KEY_ALPHABET, KVSTORE_NAMESPACE_KEY_MAX_LEN, + KVStore, KVSTORE_NAMESPACE_KEY_ALPHABET, KVSTORE_NAMESPACE_KEY_MAX_LEN, NETWORK_GRAPH_PERSISTENCE_KEY, NETWORK_GRAPH_PERSISTENCE_PRIMARY_NAMESPACE, NETWORK_GRAPH_PERSISTENCE_SECONDARY_NAMESPACE, OUTPUT_SWEEPER_PERSISTENCE_KEY, OUTPUT_SWEEPER_PERSISTENCE_PRIMARY_NAMESPACE, OUTPUT_SWEEPER_PERSISTENCE_SECONDARY_NAMESPACE, @@ -348,20 +348,21 @@ where }) } -pub(crate) fn write_node_metrics( +pub(crate) async fn write_node_metrics( node_metrics: &NodeMetrics, kv_store: &DynStore, logger: L, ) -> Result<(), Error> where L::Target: LdkLogger, { let data = node_metrics.encode(); - KVStoreSync::write( + KVStore::write( &*kv_store, NODE_METRICS_PRIMARY_NAMESPACE, NODE_METRICS_SECONDARY_NAMESPACE, NODE_METRICS_KEY, data, ) + .await .map_err(|e| { log_error!( logger, @@ -475,14 +476,15 @@ macro_rules! impl_read_write_change_set_type { $secondary_namespace:expr, $key:expr ) => { - pub(crate) fn $read_name( + pub(crate) async fn $read_name( kv_store: &DynStore, logger: L, ) -> Result, std::io::Error> where L::Target: LdkLogger, { let reader = - match KVStoreSync::read(&*kv_store, $primary_namespace, $secondary_namespace, $key) + match KVStore::read(&*kv_store, $primary_namespace, $secondary_namespace, $key) + .await { Ok(bytes) => bytes, Err(e) => { @@ -516,14 +518,15 @@ macro_rules! impl_read_write_change_set_type { } } - pub(crate) fn $write_name( + pub(crate) async fn $write_name( value: &$change_set_type, kv_store: &DynStore, logger: L, ) -> Result<(), std::io::Error> where L::Target: LdkLogger, { let data = ChangeSetSerWrapper(value).encode(); - KVStoreSync::write(&*kv_store, $primary_namespace, $secondary_namespace, $key, data) + KVStore::write(&*kv_store, $primary_namespace, $secondary_namespace, $key, data) + .await .map_err(|e| { log_error!( logger, @@ -594,36 +597,39 @@ impl_read_write_change_set_type!( ); // Reads the full BdkWalletChangeSet or returns default fields -pub(crate) fn read_bdk_wallet_change_set( +pub(crate) async fn read_bdk_wallet_change_set( kv_store: &DynStore, logger: &Logger, ) -> Result, std::io::Error> { let mut change_set = BdkWalletChangeSet::default(); // We require a descriptor and return `None` to signal creation of a new wallet otherwise. - if let Some(descriptor) = read_bdk_wallet_descriptor(kv_store, logger)? { + if let Some(descriptor) = read_bdk_wallet_descriptor(kv_store, logger).await? { change_set.descriptor = Some(descriptor); } else { return Ok(None); } // We require a change_descriptor and return `None` to signal creation of a new wallet otherwise. - if let Some(change_descriptor) = read_bdk_wallet_change_descriptor(kv_store, logger)? { + if let Some(change_descriptor) = read_bdk_wallet_change_descriptor(kv_store, logger).await? { change_set.change_descriptor = Some(change_descriptor); } else { return Ok(None); } // We require a network and return `None` to signal creation of a new wallet otherwise. - if let Some(network) = read_bdk_wallet_network(kv_store, logger)? { + if let Some(network) = read_bdk_wallet_network(kv_store, logger).await? { change_set.network = Some(network); } else { return Ok(None); } - read_bdk_wallet_local_chain(&*kv_store, logger)? + read_bdk_wallet_local_chain(&*kv_store, logger) + .await? .map(|local_chain| change_set.local_chain = local_chain); - read_bdk_wallet_tx_graph(&*kv_store, logger)?.map(|tx_graph| change_set.tx_graph = tx_graph); - read_bdk_wallet_indexer(&*kv_store, logger)?.map(|indexer| change_set.indexer = indexer); + read_bdk_wallet_tx_graph(&*kv_store, logger) + .await? + .map(|tx_graph| change_set.tx_graph = tx_graph); + read_bdk_wallet_indexer(&*kv_store, logger).await?.map(|indexer| change_set.indexer = indexer); Ok(Some(change_set)) } diff --git a/src/io/vss_store.rs b/src/io/vss_store.rs index b4fdc770a..c20848db6 100644 --- a/src/io/vss_store.rs +++ b/src/io/vss_store.rs @@ -23,7 +23,7 @@ use bitcoin::key::Secp256k1; use bitcoin::Network; use lightning::impl_writeable_tlv_based_enum; use lightning::io::{self, Error, ErrorKind}; -use lightning::util::persist::{KVStore, KVStoreSync}; +use lightning::util::persist::KVStore; use lightning::util::ser::{Readable, Writeable}; use prost::Message; use rand::RngCore; @@ -75,7 +75,7 @@ const VSS_SCHEMA_VERSION_KEY: &str = "vss_schema_version"; // would hit a blocking case const INTERNAL_RUNTIME_WORKERS: usize = 2; -/// A [`KVStore`]/[`KVStoreSync`] implementation that writes to and reads from a [VSS] backend. +/// A [`KVStore`] implementation that writes to and reads from a [VSS] backend. /// /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md pub struct VssStore { @@ -134,14 +134,12 @@ impl VssStore { }) })?; - let async_retry_policy = retry_policy(); - let async_client = - VssClient::new_with_headers(base_url, async_retry_policy, header_provider); + let retry_pol = retry_policy(); + let client = VssClient::new_with_headers(base_url, retry_pol, header_provider); let inner = Arc::new(VssStoreInner::new( schema_version, - blocking_client, - async_client, + client, store_id, data_encryption_key, key_obfuscator, @@ -183,111 +181,6 @@ impl VssStore { } } -impl KVStoreSync for VssStore { - fn read( - &self, primary_namespace: &str, secondary_namespace: &str, key: &str, - ) -> io::Result> { - let internal_runtime = self.internal_runtime.as_ref().ok_or_else(|| { - debug_assert!(false, "Failed to access internal runtime"); - let msg = format!("Failed to access internal runtime"); - Error::new(ErrorKind::Other, msg) - })?; - let primary_namespace = primary_namespace.to_string(); - let secondary_namespace = secondary_namespace.to_string(); - let key = key.to_string(); - let inner = Arc::clone(&self.inner); - let fut = async move { - inner - .read_internal(&inner.blocking_client, primary_namespace, secondary_namespace, key) - .await - }; - tokio::task::block_in_place(move || internal_runtime.block_on(fut)) - } - - fn write( - &self, primary_namespace: &str, secondary_namespace: &str, key: &str, buf: Vec, - ) -> io::Result<()> { - let internal_runtime = self.internal_runtime.as_ref().ok_or_else(|| { - debug_assert!(false, "Failed to access internal runtime"); - let msg = format!("Failed to access internal runtime"); - Error::new(ErrorKind::Other, msg) - })?; - let primary_namespace = primary_namespace.to_string(); - let secondary_namespace = secondary_namespace.to_string(); - let key = key.to_string(); - let inner = Arc::clone(&self.inner); - let locking_key = self.build_locking_key(&primary_namespace, &secondary_namespace, &key); - let (inner_lock_ref, version) = self.get_new_version_and_lock_ref(locking_key.clone()); - let fut = async move { - inner - .write_internal( - &inner.blocking_client, - inner_lock_ref, - locking_key, - version, - primary_namespace, - secondary_namespace, - key, - buf, - ) - .await - }; - tokio::task::block_in_place(move || internal_runtime.block_on(fut)) - } - - fn remove( - &self, primary_namespace: &str, secondary_namespace: &str, key: &str, lazy: bool, - ) -> io::Result<()> { - let internal_runtime = self.internal_runtime.as_ref().ok_or_else(|| { - debug_assert!(false, "Failed to access internal runtime"); - let msg = format!("Failed to access internal runtime"); - Error::new(ErrorKind::Other, msg) - })?; - let primary_namespace = primary_namespace.to_string(); - let secondary_namespace = secondary_namespace.to_string(); - let key = key.to_string(); - let inner = Arc::clone(&self.inner); - let locking_key = self.build_locking_key(&primary_namespace, &secondary_namespace, &key); - let (inner_lock_ref, version) = self.get_new_version_and_lock_ref(locking_key.clone()); - let fut = async move { - inner - .remove_internal( - &inner.blocking_client, - inner_lock_ref, - locking_key, - version, - primary_namespace, - secondary_namespace, - key, - ) - .await - }; - if lazy { - internal_runtime.spawn(async { fut.await }); - Ok(()) - } else { - tokio::task::block_in_place(move || internal_runtime.block_on(fut)) - } - } - - fn list(&self, primary_namespace: &str, secondary_namespace: &str) -> io::Result> { - let internal_runtime = self.internal_runtime.as_ref().ok_or_else(|| { - debug_assert!(false, "Failed to access internal runtime"); - let msg = format!("Failed to access internal runtime"); - Error::new(ErrorKind::Other, msg) - })?; - let primary_namespace = primary_namespace.to_string(); - let secondary_namespace = secondary_namespace.to_string(); - let inner = Arc::clone(&self.inner); - let fut = async move { - inner - .list_internal(&inner.blocking_client, primary_namespace, secondary_namespace) - .await - }; - tokio::task::block_in_place(move || internal_runtime.block_on(fut)) - } -} - impl KVStore for VssStore { fn read( &self, primary_namespace: &str, secondary_namespace: &str, key: &str, @@ -297,9 +190,7 @@ impl KVStore for VssStore { let key = key.to_string(); let inner = Arc::clone(&self.inner); async move { - inner - .read_internal(&inner.async_client, primary_namespace, secondary_namespace, key) - .await + inner.read_internal(&inner.client, primary_namespace, secondary_namespace, key).await } } fn write( @@ -314,7 +205,7 @@ impl KVStore for VssStore { async move { inner .write_internal( - &inner.async_client, + &inner.client, inner_lock_ref, locking_key, version, @@ -338,7 +229,7 @@ impl KVStore for VssStore { let fut = async move { inner .remove_internal( - &inner.async_client, + &inner.client, inner_lock_ref, locking_key, version, @@ -363,9 +254,7 @@ impl KVStore for VssStore { let primary_namespace = primary_namespace.to_string(); let secondary_namespace = secondary_namespace.to_string(); let inner = Arc::clone(&self.inner); - async move { - inner.list_internal(&inner.async_client, primary_namespace, secondary_namespace).await - } + async move { inner.list_internal(&inner.client, primary_namespace, secondary_namespace).await } } } @@ -378,10 +267,7 @@ impl Drop for VssStore { struct VssStoreInner { schema_version: VssSchemaVersion, - blocking_client: VssClient, - // 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. - async_client: VssClient, + client: VssClient, store_id: String, data_encryption_key: [u8; 32], key_obfuscator: KeyObfuscator, @@ -392,20 +278,11 @@ struct VssStoreInner { impl VssStoreInner { pub(crate) fn new( - schema_version: VssSchemaVersion, blocking_client: VssClient, - async_client: VssClient, store_id: String, + schema_version: VssSchemaVersion, client: VssClient, store_id: String, data_encryption_key: [u8; 32], key_obfuscator: KeyObfuscator, ) -> Self { let locks = Mutex::new(HashMap::new()); - Self { - schema_version, - blocking_client, - async_client, - store_id, - data_encryption_key, - key_obfuscator, - locks, - } + Self { schema_version, client, store_id, data_encryption_key, key_obfuscator, locks } } fn get_inner_lock_ref(&self, locking_key: String) -> Arc> { diff --git a/src/lib.rs b/src/lib.rs index 64a9ac1a2..58de84607 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -146,7 +146,7 @@ use lightning::ln::channelmanager::PaymentId; use lightning::ln::funding::SpliceContribution; use lightning::ln::msgs::SocketAddress; use lightning::routing::gossip::NodeAlias; -use lightning::util::persist::KVStoreSync; +use lightning::util::persist::KVStore; use lightning_background_processor::process_events_async; use liquidity::{LSPS1Liquidity, LiquiditySource}; use logger::{log_debug, log_error, log_info, log_trace, LdkLogger, Logger}; @@ -164,7 +164,7 @@ use types::{ HRNResolver, KeysManager, OnionMessenger, PaymentStore, PeerManager, Router, Scorer, Sweeper, Wallet, }; -pub use types::{ChannelDetails, CustomTlvRecord, PeerDetails, SyncAndAsyncKVStore, UserChannelId}; +pub use types::{ChannelDetails, CustomTlvRecord, PeerDetails, UserChannelId}; pub use { bip39, bitcoin, lightning, lightning_invoice, lightning_liquidity, lightning_types, tokio, vss_client, @@ -312,7 +312,10 @@ impl Node { { let mut locked_node_metrics = gossip_node_metrics.write().unwrap(); locked_node_metrics.latest_rgs_snapshot_timestamp = Some(updated_timestamp); - write_node_metrics(&*locked_node_metrics, &*gossip_sync_store, Arc::clone(&gossip_sync_logger)) + } + { + let snapshot = gossip_node_metrics.read().unwrap().clone(); + write_node_metrics(&snapshot, &*gossip_sync_store, Arc::clone(&gossip_sync_logger)).await .unwrap_or_else(|e| { log_error!(gossip_sync_logger, "Persistence failed: {}", e); }); @@ -525,14 +528,17 @@ impl Node { let unix_time_secs_opt = SystemTime::now().duration_since(UNIX_EPOCH).ok().map(|d| d.as_secs()); - { - let mut locked_node_metrics = bcast_node_metrics.write().unwrap(); - locked_node_metrics.latest_node_announcement_broadcast_timestamp = unix_time_secs_opt; - write_node_metrics(&*locked_node_metrics, &*bcast_store, Arc::clone(&bcast_logger)) - .unwrap_or_else(|e| { - log_error!(bcast_logger, "Persistence failed: {}", e); - }); - } + { + let mut locked_node_metrics = bcast_node_metrics.write().unwrap(); + locked_node_metrics.latest_node_announcement_broadcast_timestamp = unix_time_secs_opt; + } + { + let snapshot = bcast_node_metrics.read().unwrap().clone(); + write_node_metrics(&snapshot, &*bcast_store, Arc::clone(&bcast_logger)).await + .unwrap_or_else(|e| { + log_error!(bcast_logger, "Persistence failed: {}", e); + }); + } } else { debug_assert!(false, "We checked whether the node may announce, so node alias should always be set"); continue @@ -852,7 +858,6 @@ impl Node { #[cfg(not(feature = "uniffi"))] pub fn bolt11_payment(&self) -> Bolt11Payment { Bolt11Payment::new( - Arc::clone(&self.runtime), Arc::clone(&self.channel_manager), Arc::clone(&self.connection_manager), self.liquidity_source.clone(), @@ -870,7 +875,6 @@ impl Node { #[cfg(feature = "uniffi")] pub fn bolt11_payment(&self) -> Arc { Arc::new(Bolt11Payment::new( - Arc::clone(&self.runtime), Arc::clone(&self.channel_manager), Arc::clone(&self.connection_manager), self.liquidity_source.clone(), @@ -1040,7 +1044,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().unwrap() { @@ -1049,20 +1053,14 @@ impl Node { let peer_info = PeerInfo { node_id, address }; - let con_node_id = peer_info.node_id; - 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 - })?; + self.connection_manager + .connect_peer_if_necessary(peer_info.node_id, peer_info.address.clone()) + .await?; log_info!(self.logger, "Connected to peer {}@{}. ", peer_info.node_id, peer_info.address); if persist { - self.peer_store.add_peer(peer_info)?; + self.peer_store.add_peer(peer_info).await?; } Ok(()) @@ -1072,14 +1070,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().unwrap() { return Err(Error::NotRunning); } log_info!(self.logger, "Disconnecting peer {}..", counterparty_node_id); - match 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) @@ -1090,7 +1088,7 @@ impl Node { Ok(()) } - fn open_channel_inner( + async fn open_channel_inner( &self, node_id: PublicKey, address: SocketAddress, channel_amount_sats: u64, push_to_counterparty_msat: Option, channel_config: Option, announce_for_forwarding: bool, @@ -1101,15 +1099,9 @@ impl Node { let peer_info = PeerInfo { node_id, address }; - let con_node_id = peer_info.node_id; - 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 - })?; + self.connection_manager + .connect_peer_if_necessary(peer_info.node_id, peer_info.address.clone()) + .await?; // Check funds availability after connection (includes anchor reserve calculation) self.check_sufficient_funds_for_channel(channel_amount_sats, &node_id)?; @@ -1143,7 +1135,7 @@ impl Node { "Initiated channel creation with peer {}. ", peer_info.node_id ); - self.peer_store.add_peer(peer_info)?; + self.peer_store.add_peer(peer_info).await?; Ok(UserChannelId(user_channel_id)) }, Err(e) => { @@ -1215,7 +1207,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 { @@ -1227,6 +1219,7 @@ impl Node { channel_config, false, ) + .await } /// Connect to a node and open a new announced channel. @@ -1250,7 +1243,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 { @@ -1267,6 +1260,7 @@ impl Node { channel_config, true, ) + .await } /// Add funds from the on-chain wallet into an existing channel. @@ -1279,7 +1273,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> { @@ -1331,9 +1325,9 @@ impl Node { // insert channel's funding utxo into the wallet so we can later calculate fees // correctly when viewing this splice-in. - self.wallet.insert_txo(funding_txo.into_bitcoin_outpoint(), funding_output)?; + self.wallet.insert_txo(funding_txo.into_bitcoin_outpoint(), funding_output).await?; - let change_address = self.wallet.get_new_internal_address()?; + let change_address = self.wallet.get_new_internal_address().await?; let contribution = SpliceContribution::splice_in( Amount::from_sat(splice_amount_sats), @@ -1349,30 +1343,32 @@ impl Node { }, }; - self.channel_manager - .splice_channel( - &channel_details.channel_id, - &counterparty_node_id, - contribution, - funding_feerate_per_kw, - None, - ) - .map_err(|e| { - log_error!(self.logger, "Failed to splice channel: {:?}", e); - let tx = bitcoin::Transaction { - version: bitcoin::transaction::Version::TWO, - lock_time: bitcoin::absolute::LockTime::ZERO, - input: vec![], - output: vec![bitcoin::TxOut { - value: Amount::ZERO, - script_pubkey: change_address.script_pubkey(), - }], - }; - match self.wallet.cancel_tx(&tx) { - Ok(()) => Error::ChannelSplicingFailed, - Err(e) => e, - } - }) + let splice_res = self.channel_manager.splice_channel( + &channel_details.channel_id, + &counterparty_node_id, + contribution, + funding_feerate_per_kw, + None, + ); + + if let Err(e) = splice_res { + log_error!(self.logger, "Failed to splice channel: {:?}", e); + let tx = bitcoin::Transaction { + version: bitcoin::transaction::Version::TWO, + lock_time: bitcoin::absolute::LockTime::ZERO, + input: vec![], + output: vec![bitcoin::TxOut { + value: Amount::ZERO, + script_pubkey: change_address.script_pubkey(), + }], + }; + return match self.wallet.cancel_tx(&tx).await { + Ok(()) => Err(Error::ChannelSplicingFailed), + Err(e) => Err(e), + }; + } + + Ok(()) } else { log_error!( self.logger, @@ -1396,7 +1392,7 @@ impl Node { /// This API is experimental. Currently, a splice-out will be marked as an inbound payment if /// paid to an address associated with the on-chain wallet, but this classification may change /// in the future. - pub fn splice_out( + pub async fn splice_out( &self, user_channel_id: &UserChannelId, counterparty_node_id: PublicKey, address: &Address, splice_amount_sats: u64, ) -> Result<(), Error> { @@ -1436,7 +1432,7 @@ impl Node { Error::ChannelSplicingFailed })?; - self.wallet.insert_txo(funding_txo.into_bitcoin_outpoint(), funding_output)?; + self.wallet.insert_txo(funding_txo.into_bitcoin_outpoint(), funding_output).await?; self.channel_manager .splice_channel( @@ -1508,10 +1504,10 @@ impl Node { /// /// 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. @@ -1527,14 +1523,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> { @@ -1569,7 +1565,7 @@ impl Node { // Check if this was the last open channel, if so, forget the peer. if open_channels.len() == 1 { - self.peer_store.remove_peer(&counterparty_node_id)?; + self.peer_store.remove_peer(&counterparty_node_id).await?; } } @@ -1606,8 +1602,8 @@ impl Node { } /// Remove the payment with the given id from the store. - pub fn remove_payment(&self, payment_id: &PaymentId) -> Result<(), Error> { - 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. @@ -1764,13 +1760,14 @@ 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> { - KVStoreSync::read( + 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, diff --git a/src/liquidity.rs b/src/liquidity.rs index 30a8068ad..e175bb725 100644 --- a/src/liquidity.rs +++ b/src/liquidity.rs @@ -1491,10 +1491,10 @@ impl LSPS1Liquidity { log_info!(self.logger, "Connected to LSP {}@{}. ", lsp_node_id, lsp_address); - let refund_address = self.wallet.get_new_address()?; - + let wallet = Arc::clone(&self.wallet); let liquidity_source = Arc::clone(&liquidity_source); let response = self.runtime.block_on(async move { + let refund_address = wallet.get_new_address().await?; liquidity_source .lsps1_request_channel( lsp_balance_sat, diff --git a/src/payment/asynchronous/static_invoice_store.rs b/src/payment/asynchronous/static_invoice_store.rs index cd0e2ebd2..f5a3c9af8 100644 --- a/src/payment/asynchronous/static_invoice_store.rs +++ b/src/payment/asynchronous/static_invoice_store.rs @@ -15,7 +15,7 @@ use bitcoin::hashes::Hash; use lightning::blinded_path::message::BlindedMessagePath; use lightning::impl_writeable_tlv_based; use lightning::offers::static_invoice::StaticInvoice; -use lightning::util::persist::KVStoreSync; +use lightning::util::persist::KVStore; use lightning::util::ser::{Readable, Writeable}; use crate::hex_utils; @@ -78,12 +78,13 @@ impl StaticInvoiceStore { let (secondary_namespace, key) = Self::get_storage_location(invoice_slot, recipient_id); - KVStoreSync::read( + KVStore::read( &*self.kv_store, STATIC_INVOICE_STORE_PRIMARY_NAMESPACE, &secondary_namespace, &key, ) + .await .and_then(|data| { PersistedStaticInvoice::read(&mut &*data) .map(|persisted_invoice| { @@ -124,13 +125,14 @@ impl StaticInvoiceStore { // Static invoices will be persisted at "static_invoices//". // // Example: static_invoices/039058c6f2c0cb492c533b0a4d14ef77cc0f78abccced5287d84a1a2011cfb81/00001 - KVStoreSync::write( + KVStore::write( &*self.kv_store, STATIC_INVOICE_STORE_PRIMARY_NAMESPACE, &secondary_namespace, &key, buf, ) + .await } fn get_storage_location(invoice_slot: u16, recipient_id: &[u8]) -> (String, String) { diff --git a/src/payment/bolt11.rs b/src/payment/bolt11.rs index 56eb2f20b..419cb5dd7 100644 --- a/src/payment/bolt11.rs +++ b/src/payment/bolt11.rs @@ -35,7 +35,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"))] @@ -55,7 +54,6 @@ type Bolt11InvoiceDescription = crate::ffi::Bolt11InvoiceDescription; /// [BOLT 11]: https://github.com/lightning/bolts/blob/master/11-payment-encoding.md /// [`Node::bolt11_payment`]: crate::Node::bolt11_payment pub struct Bolt11Payment { - runtime: Arc, channel_manager: Arc, connection_manager: Arc>>, liquidity_source: Option>>>, @@ -68,14 +66,13 @@ pub struct Bolt11Payment { impl Bolt11Payment { pub(crate) fn new( - runtime: Arc, channel_manager: 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, @@ -91,7 +88,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().unwrap() { @@ -145,7 +142,7 @@ impl Bolt11Payment { PaymentStatus::Pending, ); - self.payment_store.insert(payment)?; + self.payment_store.insert(payment).await?; Ok(payment_id) }, @@ -174,7 +171,7 @@ impl Bolt11Payment { PaymentStatus::Failed, ); - self.payment_store.insert(payment)?; + self.payment_store.insert(payment).await?; Err(Error::PaymentSendingFailed) }, } @@ -191,7 +188,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 { @@ -259,7 +256,7 @@ impl Bolt11Payment { PaymentDirection::Outbound, PaymentStatus::Pending, ); - self.payment_store.insert(payment)?; + self.payment_store.insert(payment).await?; Ok(payment_id) }, @@ -289,7 +286,7 @@ impl Bolt11Payment { PaymentStatus::Failed, ); - self.payment_store.insert(payment)?; + self.payment_store.insert(payment).await?; Err(Error::PaymentSendingFailed) }, } @@ -375,7 +372,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 { @@ -383,7 +380,7 @@ impl Bolt11Payment { ..PaymentDetailsUpdate::new(payment_id) }; - match self.payment_store.update(update) { + match self.payment_store.update(update).await { Ok(DataStoreUpdateResult::Updated) | Ok(DataStoreUpdateResult::Unchanged) => (), Ok(DataStoreUpdateResult::NotFound) => { log_error!( @@ -412,11 +409,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)) } @@ -434,13 +432,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)) } @@ -448,11 +447,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)) } @@ -470,15 +469,16 @@ 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)) } - 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 { @@ -531,7 +531,7 @@ impl Bolt11Payment { PaymentDirection::Inbound, PaymentStatus::Pending, ); - self.payment_store.insert(payment)?; + self.payment_store.insert(payment).await?; Ok(invoice) } @@ -546,19 +546,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)) } @@ -585,19 +587,21 @@ impl Bolt11Payment { /// [`claim_for_hash`]: Self::claim_for_hash /// [`fail_for_hash`]: Self::fail_for_hash /// [`counterparty_skimmed_fee_msat`]: crate::payment::PaymentKind::Bolt11Jit::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)) } @@ -612,19 +616,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)) } @@ -652,23 +658,25 @@ impl Bolt11Payment { /// [`claim_for_hash`]: Self::claim_for_hash /// [`fail_for_hash`]: Self::fail_for_hash /// [`counterparty_skimmed_fee_msat`]: crate::payment::PaymentKind::Bolt11Jit::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)) } - 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, @@ -681,44 +689,35 @@ impl Bolt11Payment { let peer_info = PeerInfo { node_id, address }; - let con_node_id = peer_info.node_id; - 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 - })?; + self.connection_manager + .connect_peer_if_necessary(peer_info.node_id, peer_info.address.clone()) + .await?; log_info!(self.logger, "Connected to LSP {}@{}. ", peer_info.node_id, peer_info.address); - let liquidity_source = Arc::clone(&liquidity_source); let (invoice, lsp_total_opening_fee, lsp_prop_opening_fee) = - self.runtime.block_on(async move { - if let Some(amount_msat) = amount_msat { - liquidity_source - .lsps2_receive_to_jit_channel( - amount_msat, - description, - expiry_secs, - max_total_lsp_fee_limit_msat, - payment_hash, - ) - .await - .map(|(invoice, total_fee)| (invoice, Some(total_fee), None)) - } else { - liquidity_source - .lsps2_receive_variable_amount_to_jit_channel( - description, - expiry_secs, - max_proportional_lsp_fee_limit_ppm_msat, - payment_hash, - ) - .await - .map(|(invoice, prop_fee)| (invoice, None, Some(prop_fee))) - } - })?; + if let Some(amount_msat) = amount_msat { + liquidity_source + .lsps2_receive_to_jit_channel( + amount_msat, + description, + expiry_secs, + max_total_lsp_fee_limit_msat, + payment_hash, + ) + .await + .map(|(invoice, total_fee)| (invoice, Some(total_fee), None)) + } else { + liquidity_source + .lsps2_receive_variable_amount_to_jit_channel( + description, + expiry_secs, + max_proportional_lsp_fee_limit_ppm_msat, + payment_hash, + ) + .await + .map(|(invoice, prop_fee)| (invoice, None, Some(prop_fee))) + }?; // Register payment in payment store. let payment_hash = invoice.payment_hash(); @@ -745,10 +744,10 @@ impl Bolt11Payment { PaymentDirection::Inbound, PaymentStatus::Pending, ); - self.payment_store.insert(payment)?; + self.payment_store.insert(payment).await?; // Persist LSP peer to make sure we reconnect on restart. - self.peer_store.add_peer(peer_info)?; + self.peer_store.add_peer(peer_info).await?; Ok(invoice) } diff --git a/src/payment/bolt12.rs b/src/payment/bolt12.rs index ada4cd7e2..24565346e 100644 --- a/src/payment/bolt12.rs +++ b/src/payment/bolt12.rs @@ -84,7 +84,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 { @@ -151,7 +151,7 @@ impl Bolt12Payment { PaymentDirection::Outbound, PaymentStatus::Pending, ); - self.payment_store.insert(payment)?; + self.payment_store.insert(payment).await?; Ok(payment_id) }, @@ -176,7 +176,7 @@ impl Bolt12Payment { PaymentDirection::Outbound, PaymentStatus::Failed, ); - self.payment_store.insert(payment)?; + self.payment_store.insert(payment).await?; Err(Error::InvoiceRequestCreationFailed) }, } @@ -196,18 +196,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) } @@ -227,7 +229,7 @@ impl Bolt12Payment { /// If `hrn` is `Some`, the payment is initiated using [`ChannelManager::pay_for_offer_from_hrn`] /// for offers resolved from a Human-Readable Name ([`HumanReadableName`]). /// Otherwise, it falls back to the standard offer payment methods. - 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 { @@ -307,7 +309,7 @@ impl Bolt12Payment { PaymentDirection::Outbound, PaymentStatus::Pending, ); - self.payment_store.insert(payment)?; + self.payment_store.insert(payment).await?; Ok(payment_id) }, @@ -332,7 +334,7 @@ impl Bolt12Payment { PaymentDirection::Outbound, PaymentStatus::Failed, ); - self.payment_store.insert(payment)?; + self.payment_store.insert(payment).await?; Err(Error::PaymentSendingFailed) }, } @@ -416,7 +418,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().unwrap() { return Err(Error::NotRunning); } @@ -447,7 +449,7 @@ impl Bolt12Payment { PaymentStatus::Pending, ); - self.payment_store.insert(payment)?; + self.payment_store.insert(payment).await?; Ok(maybe_wrap(invoice)) } @@ -458,7 +460,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 { @@ -518,7 +520,7 @@ impl Bolt12Payment { PaymentStatus::Pending, ); - 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 695f96d43..1a7e58a1a 100644 --- a/src/payment/onchain.rs +++ b/src/payment/onchain.rs @@ -57,8 +57,8 @@ 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) } @@ -72,7 +72,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().unwrap() { @@ -84,7 +84,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. @@ -102,7 +102,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().unwrap() { @@ -118,6 +118,6 @@ 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 } } diff --git a/src/payment/spontaneous.rs b/src/payment/spontaneous.rs index 84f6c6412..555987759 100644 --- a/src/payment/spontaneous.rs +++ b/src/payment/spontaneous.rs @@ -54,38 +54,39 @@ 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 } - fn send_inner( + async fn send_inner( &self, amount_msat: u64, node_id: PublicKey, route_parameters: Option, custom_tlvs: Option>, preimage: Option, @@ -164,7 +165,7 @@ impl SpontaneousPayment { PaymentDirection::Outbound, PaymentStatus::Pending, ); - self.payment_store.insert(payment)?; + self.payment_store.insert(payment).await?; Ok(payment_id) }, @@ -187,7 +188,7 @@ impl SpontaneousPayment { PaymentStatus::Failed, ); - self.payment_store.insert(payment)?; + self.payment_store.insert(payment).await?; Err(Error::PaymentSendingFailed) }, } diff --git a/src/payment/unified.rs b/src/payment/unified.rs index 671af14ff..14381aa68 100644 --- a/src/payment/unified.rs +++ b/src/payment/unified.rs @@ -102,10 +102,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; @@ -121,12 +121,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); @@ -234,11 +233,11 @@ 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)) + 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) + self.bolt12_payment.send_using_amount(&offer, amount_msat, None, None, route_parameters).await } else { - self.bolt12_payment.send(&offer, None, None, route_parameters) + 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); @@ -251,7 +250,7 @@ impl UnifiedPayment { }, PaymentMethod::LightningBolt11(invoice) => { let invoice = maybe_wrap(invoice.clone()); - let payment_result = self.bolt11_invoice.send(&invoice, route_parameters) + 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 @@ -275,7 +274,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 ce8a9810e..b44eeef7d 100644 --- a/src/peer_store.rs +++ b/src/peer_store.rs @@ -11,7 +11,7 @@ use std::sync::{Arc, RwLock}; use bitcoin::secp256k1::PublicKey; use lightning::impl_writeable_tlv_based; -use lightning::util::persist::KVStoreSync; +use lightning::util::persist::KVStore; use lightning::util::ser::{Readable, ReadableArgs, Writeable, Writer}; use crate::io::{ @@ -40,22 +40,27 @@ where Self { peers, kv_store, logger } } - pub(crate) fn add_peer(&self, peer_info: PeerInfo) -> Result<(), Error> { - let mut locked_peers = self.peers.write().unwrap(); + pub(crate) async fn add_peer(&self, peer_info: PeerInfo) -> Result<(), Error> { + let peers_snapshot = { + let mut locked_peers = self.peers.write().unwrap(); - if locked_peers.contains_key(&peer_info.node_id) { - return Ok(()); - } + 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) + locked_peers.insert(peer_info.node_id, peer_info); + locked_peers.clone() + }; + self.persist_peers(&peers_snapshot).await } - pub(crate) fn remove_peer(&self, node_id: &PublicKey) -> Result<(), Error> { - let mut locked_peers = self.peers.write().unwrap(); - - locked_peers.remove(node_id); - self.persist_peers(&*locked_peers) + pub(crate) async fn remove_peer(&self, node_id: &PublicKey) -> Result<(), Error> { + let peers_snapshot = { + let mut locked_peers = self.peers.write().unwrap(); + locked_peers.remove(node_id); + locked_peers.clone() + }; + self.persist_peers(&peers_snapshot).await } pub(crate) fn list_peers(&self) -> Vec { @@ -66,15 +71,18 @@ where self.peers.read().unwrap().get(node_id).cloned() } - fn persist_peers(&self, locked_peers: &HashMap) -> Result<(), Error> { + async fn persist_peers( + &self, locked_peers: &HashMap, + ) -> Result<(), Error> { let data = PeerStoreSerWrapper(&*locked_peers).encode(); - KVStoreSync::write( + KVStore::write( &*self.kv_store, PEER_INFO_PERSISTENCE_PRIMARY_NAMESPACE, PEER_INFO_PERSISTENCE_SECONDARY_NAMESPACE, PEER_INFO_PERSISTENCE_KEY, data, ) + .await .map_err(|e| { log_error!( self.logger, @@ -158,8 +166,8 @@ mod tests { use crate::io::test_utils::InMemoryStore; use crate::types::DynStoreWrapper; - #[test] - fn peer_info_persistence() { + #[tokio::test] + async fn peer_info_persistence() { let store: Arc = Arc::new(DynStoreWrapper(InMemoryStore::new())); let logger = Arc::new(TestLogger::new()); let peer_store = PeerStore::new(Arc::clone(&store), Arc::clone(&logger)); @@ -170,22 +178,24 @@ mod tests { .unwrap(); let address = SocketAddress::from_str("127.0.0.1:9738").unwrap(); let expected_peer_info = PeerInfo { node_id, address }; - assert!(KVStoreSync::read( + assert!(KVStore::read( &*store, PEER_INFO_PERSISTENCE_PRIMARY_NAMESPACE, PEER_INFO_PERSISTENCE_SECONDARY_NAMESPACE, PEER_INFO_PERSISTENCE_KEY, ) + .await .is_err()); - peer_store.add_peer(expected_peer_info.clone()).unwrap(); + peer_store.add_peer(expected_peer_info.clone()).await.unwrap(); // Check we can read back what we persisted. - let persisted_bytes = KVStoreSync::read( + let persisted_bytes = KVStore::read( &*store, PEER_INFO_PERSISTENCE_PRIMARY_NAMESPACE, PEER_INFO_PERSISTENCE_SECONDARY_NAMESPACE, PEER_INFO_PERSISTENCE_KEY, ) + .await .unwrap(); let deser_peer_store = PeerStore::read(&mut &persisted_bytes[..], (Arc::clone(&store), logger)).unwrap(); diff --git a/src/scoring.rs b/src/scoring.rs index 3ed7b9d1e..bac522ad1 100644 --- a/src/scoring.rs +++ b/src/scoring.rs @@ -85,12 +85,17 @@ async fn sync_external_scores( let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); scorer.lock().unwrap().merge(liquidities, duration_since_epoch); - let mut locked_node_metrics = node_metrics.write().unwrap(); - locked_node_metrics.latest_pathfinding_scores_sync_timestamp = - Some(duration_since_epoch.as_secs()); - write_node_metrics(&*locked_node_metrics, &*kv_store, logger).unwrap_or_else(|e| { - log_error!(logger, "Persisting node metrics failed: {}", e); - }); + { + let mut locked_node_metrics = node_metrics.write().unwrap(); + locked_node_metrics.latest_pathfinding_scores_sync_timestamp = + Some(duration_since_epoch.as_secs()); + } + { + let snapshot = node_metrics.read().unwrap().clone(); + write_node_metrics(&snapshot, &*kv_store, logger).await.unwrap_or_else(|e| { + log_error!(logger, "Persisting node metrics failed: {}", e); + }); + } log_trace!(logger, "External scores merged successfully"); }, Err(e) => { diff --git a/src/types.rs b/src/types.rs index b5b1ffed7..e239a9027 100644 --- a/src/types.rs +++ b/src/types.rs @@ -23,9 +23,7 @@ use lightning::routing::gossip; use lightning::routing::router::DefaultRouter; use lightning::routing::scoring::{CombinedScorer, ProbabilisticScoringFeeParameters}; use lightning::sign::InMemorySigner; -use lightning::util::persist::{ - KVStore, KVStoreSync, MonitorUpdatingPersister, MonitorUpdatingPersisterAsync, -}; +use lightning::util::persist::{KVStore, MonitorUpdatingPersisterAsync}; use lightning::util::ser::{Readable, Writeable, Writer}; use lightning::util::sweep::OutputSweeper; use lightning_block_sync::gossip::GossipVerifier; @@ -42,153 +40,108 @@ use crate::message_handler::NodeCustomMessageHandler; use crate::payment::{PaymentDetails, PendingPaymentDetails}; use crate::runtime::RuntimeSpawner; -/// A supertrait that requires that a type implements both [`KVStore`] and [`KVStoreSync`] at the -/// same time. -pub trait SyncAndAsyncKVStore: KVStore + KVStoreSync {} - -impl SyncAndAsyncKVStore for T -where - T: KVStore, - T: KVStoreSync, -{ -} - pub(crate) trait DynStoreTrait: Send + Sync { - fn read_async( - &self, primary_namespace: &str, secondary_namespace: &str, key: &str, - ) -> Pin, bitcoin::io::Error>> + Send + 'static>>; - fn write_async( - &self, primary_namespace: &str, secondary_namespace: &str, key: &str, buf: Vec, - ) -> Pin> + Send + 'static>>; - fn remove_async( - &self, primary_namespace: &str, secondary_namespace: &str, key: &str, lazy: bool, - ) -> Pin> + Send + 'static>>; - fn list_async( - &self, primary_namespace: &str, secondary_namespace: &str, - ) -> Pin, bitcoin::io::Error>> + Send + 'static>>; - fn read( &self, primary_namespace: &str, secondary_namespace: &str, key: &str, - ) -> Result, bitcoin::io::Error>; + ) -> Pin, bitcoin::io::Error>> + Send + 'static>>; fn write( &self, primary_namespace: &str, secondary_namespace: &str, key: &str, buf: Vec, - ) -> Result<(), bitcoin::io::Error>; + ) -> Pin> + Send + 'static>>; fn remove( &self, primary_namespace: &str, secondary_namespace: &str, key: &str, lazy: bool, - ) -> Result<(), bitcoin::io::Error>; + ) -> Pin> + Send + 'static>>; fn list( &self, primary_namespace: &str, secondary_namespace: &str, - ) -> Result, bitcoin::io::Error>; + ) -> Pin, bitcoin::io::Error>> + Send + 'static>>; } impl<'a> KVStore for dyn DynStoreTrait + 'a { fn read( &self, primary_namespace: &str, secondary_namespace: &str, key: &str, ) -> impl Future, bitcoin::io::Error>> + Send + 'static { - DynStoreTrait::read_async(self, primary_namespace, secondary_namespace, key) + DynStoreTrait::read(self, primary_namespace, secondary_namespace, key) } fn write( &self, primary_namespace: &str, secondary_namespace: &str, key: &str, buf: Vec, ) -> impl Future> + Send + 'static { - DynStoreTrait::write_async(self, primary_namespace, secondary_namespace, key, buf) + DynStoreTrait::write(self, primary_namespace, secondary_namespace, key, buf) } fn remove( &self, primary_namespace: &str, secondary_namespace: &str, key: &str, lazy: bool, ) -> impl Future> + Send + 'static { - DynStoreTrait::remove_async(self, primary_namespace, secondary_namespace, key, lazy) + DynStoreTrait::remove(self, primary_namespace, secondary_namespace, key, lazy) } fn list( &self, primary_namespace: &str, secondary_namespace: &str, ) -> impl Future, bitcoin::io::Error>> + Send + 'static { - DynStoreTrait::list_async(self, primary_namespace, secondary_namespace) + DynStoreTrait::list(self, primary_namespace, secondary_namespace) } } -impl<'a> KVStoreSync for dyn DynStoreTrait + 'a { +pub(crate) type DynStore = dyn DynStoreTrait; + +#[derive(Clone)] +pub(crate) struct DynStoreRef(pub(crate) Arc); + +impl KVStore for DynStoreRef { fn read( &self, primary_namespace: &str, secondary_namespace: &str, key: &str, - ) -> Result, bitcoin::io::Error> { - DynStoreTrait::read(self, primary_namespace, secondary_namespace, key) + ) -> impl Future, bitcoin::io::Error>> + Send + 'static { + DynStoreTrait::read(&*self.0, primary_namespace, secondary_namespace, key) } fn write( &self, primary_namespace: &str, secondary_namespace: &str, key: &str, buf: Vec, - ) -> Result<(), bitcoin::io::Error> { - DynStoreTrait::write(self, primary_namespace, secondary_namespace, key, buf) + ) -> impl Future> + Send + 'static { + DynStoreTrait::write(&*self.0, primary_namespace, secondary_namespace, key, buf) } fn remove( &self, primary_namespace: &str, secondary_namespace: &str, key: &str, lazy: bool, - ) -> Result<(), bitcoin::io::Error> { - DynStoreTrait::remove(self, primary_namespace, secondary_namespace, key, lazy) + ) -> impl Future> + Send + 'static { + DynStoreTrait::remove(&*self.0, primary_namespace, secondary_namespace, key, lazy) } fn list( &self, primary_namespace: &str, secondary_namespace: &str, - ) -> Result, bitcoin::io::Error> { - DynStoreTrait::list(self, primary_namespace, secondary_namespace) + ) -> impl Future, bitcoin::io::Error>> + Send + 'static { + DynStoreTrait::list(&*self.0, primary_namespace, secondary_namespace) } } -pub(crate) type DynStore = dyn DynStoreTrait; - -pub(crate) struct DynStoreWrapper(pub(crate) T); +pub(crate) struct DynStoreWrapper(pub(crate) T); -impl DynStoreTrait for DynStoreWrapper { - fn read_async( +impl DynStoreTrait for DynStoreWrapper { + fn read( &self, primary_namespace: &str, secondary_namespace: &str, key: &str, ) -> Pin, bitcoin::io::Error>> + Send + 'static>> { Box::pin(KVStore::read(&self.0, primary_namespace, secondary_namespace, key)) } - fn write_async( + fn write( &self, primary_namespace: &str, secondary_namespace: &str, key: &str, buf: Vec, ) -> Pin> + Send + 'static>> { Box::pin(KVStore::write(&self.0, primary_namespace, secondary_namespace, key, buf)) } - fn remove_async( + fn remove( &self, primary_namespace: &str, secondary_namespace: &str, key: &str, lazy: bool, ) -> Pin> + Send + 'static>> { Box::pin(KVStore::remove(&self.0, primary_namespace, secondary_namespace, key, lazy)) } - fn list_async( + fn list( &self, primary_namespace: &str, secondary_namespace: &str, ) -> Pin, bitcoin::io::Error>> + Send + 'static>> { Box::pin(KVStore::list(&self.0, primary_namespace, secondary_namespace)) } - - fn read( - &self, primary_namespace: &str, secondary_namespace: &str, key: &str, - ) -> Result, bitcoin::io::Error> { - KVStoreSync::read(&self.0, primary_namespace, secondary_namespace, key) - } - - fn write( - &self, primary_namespace: &str, secondary_namespace: &str, key: &str, buf: Vec, - ) -> Result<(), bitcoin::io::Error> { - KVStoreSync::write(&self.0, primary_namespace, secondary_namespace, key, buf) - } - - fn remove( - &self, primary_namespace: &str, secondary_namespace: &str, key: &str, lazy: bool, - ) -> Result<(), bitcoin::io::Error> { - KVStoreSync::remove(&self.0, primary_namespace, secondary_namespace, key, lazy) - } - - fn list( - &self, primary_namespace: &str, secondary_namespace: &str, - ) -> Result, bitcoin::io::Error> { - KVStoreSync::list(&self.0, primary_namespace, secondary_namespace) - } } pub(crate) type AsyncPersister = MonitorUpdatingPersisterAsync< - Arc, + DynStoreRef, RuntimeSpawner, Arc, Arc, @@ -197,22 +150,21 @@ pub(crate) type AsyncPersister = MonitorUpdatingPersisterAsync< Arc, >; -pub type Persister = MonitorUpdatingPersister< - Arc, - Arc, - Arc, - Arc, - Arc, - Arc, ->; - pub(crate) type ChainMonitor = chainmonitor::ChainMonitor< InMemorySigner, Arc, Arc, Arc, Arc, - Arc, + chainmonitor::AsyncPersister< + DynStoreRef, + RuntimeSpawner, + Arc, + Arc, + Arc, + Arc, + Arc, + >, Arc, >; diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index f808e9a3f..09652b3a3 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -10,6 +10,8 @@ use std::ops::Deref; use std::str::FromStr; use std::sync::{Arc, Mutex}; +use bdk_wallet::AsyncWalletPersister; + use bdk_chain::spk_client::{FullScanRequest, SyncRequest}; use bdk_wallet::descriptor::ExtendedDescriptor; use bdk_wallet::event::WalletEvent; @@ -68,7 +70,7 @@ pub(crate) mod ser; pub(crate) struct Wallet { // A BDK on-chain wallet. inner: Mutex>, - persister: Mutex, + persister: tokio::sync::Mutex, broadcaster: Arc, fee_estimator: Arc, payment_store: Arc, @@ -85,7 +87,7 @@ impl Wallet { config: Arc, logger: Arc, pending_payment_store: Arc, ) -> Self { let inner = Mutex::new(wallet); - let persister = Mutex::new(wallet_persister); + let persister = tokio::sync::Mutex::new(wallet_persister); Self { inner, persister, @@ -98,6 +100,33 @@ impl Wallet { } } + async fn persist_wallet(&self) -> Result<(), Error> { + let change_set = { + let locked_wallet = self.inner.lock().unwrap(); + match locked_wallet.staged() { + Some(cs) => cs.clone(), + None => return Ok(()), + } + }; + + { + let mut locked_persister = self.persister.lock().await; + KVStoreWalletPersister::persist(&mut locked_persister, &change_set).await.map_err( + |e| { + log_error!(self.logger, "Failed to persist wallet: {}", e); + Error::PersistenceFailed + }, + )?; + } + + { + let mut locked_wallet = self.inner.lock().unwrap(); + locked_wallet.take_staged(); + } + + Ok(()) + } + pub(crate) fn get_full_scan_request(&self) -> FullScanRequest { self.inner.lock().unwrap().start_full_scan().build() } @@ -125,63 +154,50 @@ impl Wallet { BestBlock { block_hash: checkpoint.hash(), height: checkpoint.height() } } - pub(crate) fn apply_update(&self, update: impl Into) -> Result<(), Error> { - let mut locked_wallet = self.inner.lock().unwrap(); - match locked_wallet.apply_update_events(update) { - Ok(events) => { - let mut locked_persister = self.persister.lock().unwrap(); - locked_wallet.persist(&mut locked_persister).map_err(|e| { - log_error!(self.logger, "Failed to persist wallet: {}", e); - Error::PersistenceFailed - })?; - - self.update_payment_store(&mut *locked_wallet, events).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) - }, - } - } + pub(crate) async fn apply_update(&self, update: impl Into) -> Result<(), Error> { + let events = { + let mut locked_wallet = self.inner.lock().unwrap(); + 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); + }, + } + }; - pub(crate) fn apply_mempool_txs( - &self, unconfirmed_txs: Vec<(Transaction, u64)>, evicted_txids: Vec<(Txid, u64)>, - ) -> Result<(), Error> { - let mut locked_wallet = self.inner.lock().unwrap(); - locked_wallet.apply_unconfirmed_txs(unconfirmed_txs); - locked_wallet.apply_evicted_txs(evicted_txids); + self.persist_wallet().await?; - let mut locked_persister = self.persister.lock().unwrap(); - locked_wallet.persist(&mut locked_persister).map_err(|e| { - log_error!(self.logger, "Failed to persist wallet: {}", e); + self.update_payment_store(events).await.map_err(|e| { + log_error!(self.logger, "Failed to update payment store: {}", e); Error::PersistenceFailed })?; Ok(()) } - pub(crate) fn insert_txo(&self, outpoint: OutPoint, txout: TxOut) -> Result<(), Error> { - let mut locked_wallet = self.inner.lock().unwrap(); - locked_wallet.insert_txout(outpoint, txout); + pub(crate) async fn apply_mempool_txs( + &self, unconfirmed_txs: Vec<(Transaction, u64)>, evicted_txids: Vec<(Txid, u64)>, + ) -> Result<(), Error> { + { + let mut locked_wallet = self.inner.lock().unwrap(); + locked_wallet.apply_unconfirmed_txs(unconfirmed_txs); + locked_wallet.apply_evicted_txs(evicted_txids); + } - let mut locked_persister = self.persister.lock().unwrap(); - locked_wallet.persist(&mut locked_persister).map_err(|e| { - log_error!(self.logger, "Failed to persist wallet: {}", e); - Error::PersistenceFailed - })?; + self.persist_wallet().await + } - Ok(()) + pub(crate) async fn insert_txo(&self, outpoint: OutPoint, txout: TxOut) -> Result<(), Error> { + { + let mut locked_wallet = self.inner.lock().unwrap(); + locked_wallet.insert_txout(outpoint, txout); + } + + self.persist_wallet().await } - 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(()); } @@ -206,178 +222,206 @@ 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, - height: confirmation_height, - timestamp: block_time.confirmation_time, - }; - - let payment_id = self - .find_payment_by_txid(txid) - .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, - ); + // Collect all payment persistence operations while holding the wallet lock, + // then persist them after releasing the lock. + enum PersistOp { + InsertOrUpdatePayment(PaymentDetails), + InsertOrUpdatePending(PendingPaymentDetails), + RemovePending(PaymentId), + } - let pending_payment = - self.create_pending_payment_from_tx(payment.clone(), Vec::new()); + let ops = { + let locked_wallet = self.inner.lock().unwrap(); + let mut ops = Vec::new(); + + 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, + height: confirmation_height, + timestamp: block_time.confirmation_time, + }; + + let payment_id = self + .find_payment_by_txid(txid) + .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, + ); - self.payment_store.insert_or_update(payment)?; - self.pending_payment_store.insert_or_update(pending_payment)?; - }, - WalletEvent::ChainTipChanged { new_tip, .. } => { - // Get all payments that are Pending with Confirmed status - let pending_payments: Vec = - self.pending_payment_store.list_filter(|p| { - p.details.status == PaymentStatus::Pending - && matches!( - p.details.kind, - PaymentKind::Onchain { - status: ConfirmationStatus::Confirmed { .. }, - .. - } - ) - }); - - for mut payment in pending_payments { - if let PaymentKind::Onchain { - status: ConfirmationStatus::Confirmed { height, .. }, - .. - } = payment.details.kind - { - let payment_id = payment.details.id; - if new_tip.height >= height + ANTI_REORG_DELAY - 1 { - payment.details.status = PaymentStatus::Succeeded; - self.payment_store.insert_or_update(payment.details)?; - self.pending_payment_store.remove(&payment_id)?; + let pending_payment = + self.create_pending_payment_from_tx(payment.clone(), Vec::new()); + + ops.push(PersistOp::InsertOrUpdatePayment(payment)); + ops.push(PersistOp::InsertOrUpdatePending(pending_payment)); + }, + WalletEvent::ChainTipChanged { new_tip, .. } => { + let pending_payments: Vec = + self.pending_payment_store.list_filter(|p| { + p.details.status == PaymentStatus::Pending + && matches!( + p.details.kind, + PaymentKind::Onchain { + status: ConfirmationStatus::Confirmed { .. }, + .. + } + ) + }); + + for mut payment in pending_payments { + if let PaymentKind::Onchain { + status: ConfirmationStatus::Confirmed { height, .. }, + .. + } = payment.details.kind + { + let payment_id = payment.details.id; + if new_tip.height >= height + ANTI_REORG_DELAY - 1 { + payment.details.status = PaymentStatus::Succeeded; + ops.push(PersistOp::InsertOrUpdatePayment(payment.details)); + ops.push(PersistOp::RemovePending(payment_id)); + } } } - } - }, - WalletEvent::TxUnconfirmed { txid, tx, old_block_time: None } => { - let payment_id = self - .find_payment_by_txid(txid) - .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 pending_payment = - self.create_pending_payment_from_tx(payment.clone(), Vec::new()); - self.payment_store.insert_or_update(payment)?; - self.pending_payment_store.insert_or_update(pending_payment)?; - }, - WalletEvent::TxReplaced { txid, conflicts, tx, .. } => { - let payment_id = self - .find_payment_by_txid(txid) - .unwrap_or_else(|| PaymentId(txid.to_byte_array())); - - // Collect all conflict txids - let conflict_txids: Vec = - conflicts.iter().map(|(_, conflict_txid)| *conflict_txid).collect(); - - let payment = self.create_payment_from_tx( - locked_wallet, - txid, - payment_id, - &tx, - PaymentStatus::Pending, - ConfirmationStatus::Unconfirmed, - ); - let pending_payment_details = self - .create_pending_payment_from_tx(payment.clone(), conflict_txids.clone()); + }, + WalletEvent::TxUnconfirmed { txid, tx, old_block_time: None } => { + let payment_id = self + .find_payment_by_txid(txid) + .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 pending_payment = + self.create_pending_payment_from_tx(payment.clone(), Vec::new()); + ops.push(PersistOp::InsertOrUpdatePayment(payment)); + ops.push(PersistOp::InsertOrUpdatePending(pending_payment)); + }, + WalletEvent::TxReplaced { txid, conflicts, tx, .. } => { + let payment_id = self + .find_payment_by_txid(txid) + .unwrap_or_else(|| PaymentId(txid.to_byte_array())); + + let conflict_txids: Vec = + conflicts.iter().map(|(_, conflict_txid)| *conflict_txid).collect(); + + let payment = self.create_payment_from_tx( + &locked_wallet, + txid, + payment_id, + &tx, + PaymentStatus::Pending, + ConfirmationStatus::Unconfirmed, + ); + let pending_payment_details = self.create_pending_payment_from_tx( + payment.clone(), + conflict_txids.clone(), + ); - self.pending_payment_store.insert_or_update(pending_payment_details)?; + ops.push(PersistOp::InsertOrUpdatePending(pending_payment_details)); + }, + WalletEvent::TxDropped { txid, tx } => { + let payment_id = self + .find_payment_by_txid(txid) + .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 pending_payment = + self.create_pending_payment_from_tx(payment.clone(), Vec::new()); + ops.push(PersistOp::InsertOrUpdatePayment(payment)); + ops.push(PersistOp::InsertOrUpdatePending(pending_payment)); + }, + _ => { + continue; + }, + }; + } + ops + }; + + for op in ops { + match op { + PersistOp::InsertOrUpdatePayment(payment) => { + self.payment_store.insert_or_update(payment).await?; }, - WalletEvent::TxDropped { txid, tx } => { - let payment_id = self - .find_payment_by_txid(txid) - .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 pending_payment = - self.create_pending_payment_from_tx(payment.clone(), Vec::new()); - self.payment_store.insert_or_update(payment)?; - self.pending_payment_store.insert_or_update(pending_payment)?; + PersistOp::InsertOrUpdatePending(pending) => { + self.pending_payment_store.insert_or_update(pending).await?; }, - _ => { - continue; + PersistOp::RemovePending(id) => { + self.pending_payment_store.remove(&id).await?; }, - }; + } } Ok(()) } #[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().unwrap(); - let mut tx_builder = locked_wallet.build_tx(); + let psbt = { + let mut locked_wallet = self.inner.lock().unwrap(); + 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()); + }, + } - let mut locked_persister = self.persister.lock().unwrap(); - locked_wallet.persist(&mut locked_persister).map_err(|e| { - log_error!(self.logger, "Failed to persist wallet: {}", e); - Error::PersistenceFailed - })?; + psbt + }; + + self.persist_wallet().await?; let tx = psbt.extract_tx().map_err(|e| { log_error!(self.logger, "Failed to extract transaction: {}", e); @@ -387,41 +431,30 @@ impl Wallet { Ok(tx) } - pub(crate) fn get_new_address(&self) -> Result { - let mut locked_wallet = self.inner.lock().unwrap(); - let mut locked_persister = self.persister.lock().unwrap(); - - let address_info = locked_wallet.reveal_next_address(KeychainKind::External); - locked_wallet.persist(&mut locked_persister).map_err(|e| { - log_error!(self.logger, "Failed to persist wallet: {}", e); - Error::PersistenceFailed - })?; + pub(crate) async fn get_new_address(&self) -> Result { + let address_info = { + let mut locked_wallet = self.inner.lock().unwrap(); + locked_wallet.reveal_next_address(KeychainKind::External) + }; + self.persist_wallet().await?; Ok(address_info.address) } - pub(crate) fn get_new_internal_address(&self) -> Result { - let mut locked_wallet = self.inner.lock().unwrap(); - let mut locked_persister = self.persister.lock().unwrap(); - - let address_info = locked_wallet.next_unused_address(KeychainKind::Internal); - locked_wallet.persist(&mut locked_persister).map_err(|e| { - log_error!(self.logger, "Failed to persist wallet: {}", e); - Error::PersistenceFailed - })?; + pub(crate) async fn get_new_internal_address(&self) -> Result { + let address_info = { + let mut locked_wallet = self.inner.lock().unwrap(); + locked_wallet.next_unused_address(KeychainKind::Internal) + }; + self.persist_wallet().await?; Ok(address_info.address) } - pub(crate) fn cancel_tx(&self, tx: &Transaction) -> Result<(), Error> { - let mut locked_wallet = self.inner.lock().unwrap(); - let mut locked_persister = self.persister.lock().unwrap(); - - locked_wallet.cancel_tx(tx); - locked_wallet.persist(&mut locked_persister).map_err(|e| { - log_error!(self.logger, "Failed to persist wallet: {}", e); - Error::PersistenceFailed - })?; - - Ok(()) + pub(crate) async fn cancel_tx(&self, tx: &Transaction) -> Result<(), Error> { + { + let mut locked_wallet = self.inner.lock().unwrap(); + locked_wallet.cancel_tx(tx); + } + self.persist_wallet().await } pub(crate) fn get_balances( @@ -467,7 +500,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 { @@ -636,18 +669,14 @@ impl Wallet { }, } - let mut locked_persister = self.persister.lock().unwrap(); - locked_wallet.persist(&mut locked_persister).map_err(|e| { - log_error!(self.logger, "Failed to persist wallet: {}", e); - Error::PersistenceFailed - })?; - psbt.extract_tx().map_err(|e| { log_error!(self.logger, "Failed to extract transaction: {}", e); e })? }; + self.persist_wallet().await?; + self.broadcaster.broadcast_transactions(&[( &tx, lightning::chain::chaininterface::TransactionType::Sweep { channels: vec![] }, @@ -830,14 +859,13 @@ impl Wallet { } #[allow(deprecated)] - fn get_change_script_inner(&self) -> Result { - let mut locked_wallet = self.inner.lock().unwrap(); - let mut locked_persister = self.persister.lock().unwrap(); - - let address_info = locked_wallet.next_unused_address(KeychainKind::Internal); - locked_wallet.persist(&mut locked_persister).map_err(|e| { + async fn get_change_script_inner(&self) -> Result { + let address_info = { + let mut locked_wallet = self.inner.lock().unwrap(); + locked_wallet.next_unused_address(KeychainKind::Internal) + }; + self.persist_wallet().await.map_err(|e| { log_error!(self.logger, "Failed to persist wallet: {}", e); - () })?; Ok(address_info.address.script_pubkey()) } @@ -995,45 +1023,44 @@ impl Listen for Wallet { } fn block_connected(&self, block: &bitcoin::Block, height: u32) { - let mut locked_wallet = self.inner.lock().unwrap(); - - 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 events = { + let mut locked_wallet = self.inner.lock().unwrap(); - 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); - return; - } - }, - Err(e) => { - log_error!( + let pre_checkpoint = locked_wallet.latest_checkpoint(); + if pre_checkpoint.height() != height - 1 + || pre_checkpoint.hash() != block.header.prev_blockhash + { + log_debug!( self.logger, - "Failed to apply connected block to on-chain wallet: {}", - e + "Detected reorg while applying a connected block to on-chain wallet: new block with hash {} at height {}", + block.header.block_hash(), + height ); - return; - }, + } + + 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; + }, + } }; - let mut locked_persister = self.persister.lock().unwrap(); - match locked_wallet.persist(&mut locked_persister) { - Ok(_) => (), - Err(e) => { + let rt_handle = tokio::runtime::Handle::current(); + tokio::task::block_in_place(|| { + if let Err(e) = rt_handle.block_on(self.persist_wallet()) { log_error!(self.logger, "Failed to persist on-chain wallet: {}", e); return; - }, - }; + } + if let Err(e) = rt_handle.block_on(self.update_payment_store(events)) { + log_error!(self.logger, "Failed to update payment store: {}", e); + } + }); } fn blocks_disconnected(&self, _fork_point_block: BestBlock) { @@ -1051,7 +1078,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 sign_psbt<'a>( @@ -1174,16 +1201,22 @@ 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 rt_handle = tokio::runtime::Handle::current(); + let address = + tokio::task::block_in_place(|| rt_handle.block_on(self.wallet.get_new_address())) + .map_err(|e| { + log_error!(self.logger, "Failed to retrieve new address from wallet: {}", e); + })?; 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 rt_handle = tokio::runtime::Handle::current(); + let address = + tokio::task::block_in_place(|| rt_handle.block_on(self.wallet.get_new_address())) + .map_err(|e| { + log_error!(self.logger, "Failed to retrieve new address from wallet: {}", e); + })?; match address.witness_program() { Some(program) => ShutdownScript::new_witness_program(&program).map_err(|e| { @@ -1207,6 +1240,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/src/wallet/persist.rs b/src/wallet/persist.rs index 10be1fac0..ae65c0dac 100644 --- a/src/wallet/persist.rs +++ b/src/wallet/persist.rs @@ -5,10 +5,13 @@ // http://opensource.org/licenses/MIT>, at your option. You may not use this file except in // accordance with one or both of these licenses. +use std::future::Future; +use std::pin::Pin; use std::sync::Arc; use bdk_chain::Merge; -use bdk_wallet::{ChangeSet, WalletPersister}; +use bdk_wallet::AsyncWalletPersister; +use bdk_wallet::ChangeSet; use crate::io::utils::{ read_bdk_wallet_change_set, write_bdk_wallet_change_descriptor, write_bdk_wallet_descriptor, @@ -29,146 +32,172 @@ impl KVStoreWalletPersister { } } -impl WalletPersister for KVStoreWalletPersister { +impl AsyncWalletPersister for KVStoreWalletPersister { type Error = std::io::Error; - fn initialize(persister: &mut Self) -> Result { - // Return immediately if we have already been initialized. - if let Some(latest_change_set) = persister.latest_change_set.as_ref() { - return Ok(latest_change_set.clone()); - } - - let change_set_opt = read_bdk_wallet_change_set(&*persister.kv_store, &*persister.logger)?; - - let change_set = match change_set_opt { - Some(persisted_change_set) => persisted_change_set, - None => { - // BDK docs state: "The implementation must return all data currently stored in the - // persister. If there is no data, return an empty changeset (using - // ChangeSet::default())." - ChangeSet::default() - }, - }; - persister.latest_change_set = Some(change_set.clone()); - Ok(change_set) + fn initialize<'a>( + persister: &'a mut Self, + ) -> Pin> + Send + 'a>> + where + Self: 'a, + { + Box::pin(async move { + // Return immediately if we have already been initialized. + if let Some(latest_change_set) = persister.latest_change_set.as_ref() { + return Ok(latest_change_set.clone()); + } + + let change_set_opt = + read_bdk_wallet_change_set(&*persister.kv_store, &*persister.logger).await?; + + let change_set = match change_set_opt { + Some(persisted_change_set) => persisted_change_set, + None => { + // BDK docs state: "The implementation must return all data currently stored in + // the persister. If there is no data, return an empty changeset (using + // ChangeSet::default())." + ChangeSet::default() + }, + }; + persister.latest_change_set = Some(change_set.clone()); + Ok(change_set) + }) } - fn persist(persister: &mut Self, change_set: &ChangeSet) -> Result<(), Self::Error> { - if change_set.is_empty() { - return Ok(()); - } - - // We're allowed to fail here if we're not initialized, BDK docs state: "This method can fail if the - // persister is not initialized." - let latest_change_set = persister.latest_change_set.as_mut().ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::Other, - "Wallet must be initialized before calling persist", - ) - })?; - - // Check that we'd never accidentally override any persisted data if the change set doesn't - // match our descriptor/change_descriptor/network. - if let Some(descriptor) = change_set.descriptor.as_ref() { - if latest_change_set.descriptor.is_some() - && latest_change_set.descriptor.as_ref() != Some(descriptor) - { - debug_assert!(false, "Wallet descriptor must never change"); - log_error!( - persister.logger, - "Wallet change set doesn't match persisted descriptor. This should never happen." - ); - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidData, - "Wallet change set doesn't match persisted descriptor. This should never happen." - )); - } else { - latest_change_set.descriptor = Some(descriptor.clone()); - write_bdk_wallet_descriptor(&descriptor, &*persister.kv_store, &*persister.logger)?; + fn persist<'a>( + persister: &'a mut Self, change_set: &'a ChangeSet, + ) -> Pin> + Send + 'a>> + where + Self: 'a, + { + Box::pin(async move { + if change_set.is_empty() { + return Ok(()); + } + + // We're allowed to fail here if we're not initialized, BDK docs state: "This method + // can fail if the persister is not initialized." + let latest_change_set = persister.latest_change_set.as_mut().ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::Other, + "Wallet must be initialized before calling persist", + ) + })?; + + // Check that we'd never accidentally override any persisted data if the change set + // doesn't match our descriptor/change_descriptor/network. + if let Some(descriptor) = change_set.descriptor.as_ref() { + if latest_change_set.descriptor.is_some() + && latest_change_set.descriptor.as_ref() != Some(descriptor) + { + debug_assert!(false, "Wallet descriptor must never change"); + log_error!( + persister.logger, + "Wallet change set doesn't match persisted descriptor. This should never happen." + ); + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Wallet change set doesn't match persisted descriptor. This should never happen." + )); + } else { + latest_change_set.descriptor = Some(descriptor.clone()); + write_bdk_wallet_descriptor( + &descriptor, + &*persister.kv_store, + &*persister.logger, + ) + .await?; + } + } + + if let Some(change_descriptor) = change_set.change_descriptor.as_ref() { + if latest_change_set.change_descriptor.is_some() + && latest_change_set.change_descriptor.as_ref() != Some(change_descriptor) + { + debug_assert!(false, "Wallet change_descriptor must never change"); + log_error!( + persister.logger, + "Wallet change set doesn't match persisted change_descriptor. This should never happen." + ); + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Wallet change set doesn't match persisted change_descriptor. This should never happen." + )); + } else { + latest_change_set.change_descriptor = Some(change_descriptor.clone()); + write_bdk_wallet_change_descriptor( + &change_descriptor, + &*persister.kv_store, + &*persister.logger, + ) + .await?; + } + } + + if let Some(network) = change_set.network { + if latest_change_set.network.is_some() && latest_change_set.network != Some(network) + { + debug_assert!(false, "Wallet network must never change"); + log_error!( + persister.logger, + "Wallet change set doesn't match persisted network. This should never happen." + ); + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Wallet change set doesn't match persisted network. This should never happen.", + )); + } else { + latest_change_set.network = Some(network); + write_bdk_wallet_network(&network, &*persister.kv_store, &*persister.logger) + .await?; + } } - } - - if let Some(change_descriptor) = change_set.change_descriptor.as_ref() { - if latest_change_set.change_descriptor.is_some() - && latest_change_set.change_descriptor.as_ref() != Some(change_descriptor) - { - debug_assert!(false, "Wallet change_descriptor must never change"); - log_error!( - persister.logger, - "Wallet change set doesn't match persisted change_descriptor. This should never happen." - ); - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidData, - "Wallet change set doesn't match persisted change_descriptor. This should never happen." - )); - } else { - latest_change_set.change_descriptor = Some(change_descriptor.clone()); - write_bdk_wallet_change_descriptor( - &change_descriptor, + + debug_assert!( + latest_change_set.descriptor.is_some() + && latest_change_set.change_descriptor.is_some() + && latest_change_set.network.is_some(), + "descriptor, change_descriptor, and network are mandatory ChangeSet fields" + ); + + // Merge and persist the sub-changesets individually if necessary. + // + // According to the BDK team the individual sub-changesets can be persisted + // individually/non-atomically, "(h)owever, the localchain tip is used by block-by-block + // chain sources as a reference as to where to sync from, so I would persist that last", + // "I would write in this order: indexer, tx_graph, local_chain", which is why we follow + // this particular order. + if !change_set.indexer.is_empty() { + latest_change_set.indexer.merge(change_set.indexer.clone()); + write_bdk_wallet_indexer( + &latest_change_set.indexer, &*persister.kv_store, - &*persister.logger, - )?; + Arc::clone(&persister.logger), + ) + .await?; } - } - - if let Some(network) = change_set.network { - if latest_change_set.network.is_some() && latest_change_set.network != Some(network) { - debug_assert!(false, "Wallet network must never change"); - log_error!( - persister.logger, - "Wallet change set doesn't match persisted network. This should never happen." - ); - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidData, - "Wallet change set doesn't match persisted network. This should never happen.", - )); - } else { - latest_change_set.network = Some(network); - write_bdk_wallet_network(&network, &*persister.kv_store, &*persister.logger)?; + + if !change_set.tx_graph.is_empty() { + latest_change_set.tx_graph.merge(change_set.tx_graph.clone()); + write_bdk_wallet_tx_graph( + &latest_change_set.tx_graph, + &*persister.kv_store, + Arc::clone(&persister.logger), + ) + .await?; } - } - - debug_assert!( - latest_change_set.descriptor.is_some() - && latest_change_set.change_descriptor.is_some() - && latest_change_set.network.is_some(), - "descriptor, change_descriptor, and network are mandatory ChangeSet fields" - ); - - // Merge and persist the sub-changesets individually if necessary. - // - // According to the BDK team the individual sub-changesets can be persisted - // individually/non-atomically, "(h)owever, the localchain tip is used by block-by-block - // chain sources as a reference as to where to sync from, so I would persist that last", "I - // would write in this order: indexer, tx_graph, local_chain", which is why we follow this - // particular order. - if !change_set.indexer.is_empty() { - latest_change_set.indexer.merge(change_set.indexer.clone()); - write_bdk_wallet_indexer( - &latest_change_set.indexer, - &*persister.kv_store, - Arc::clone(&persister.logger), - )?; - } - - if !change_set.tx_graph.is_empty() { - latest_change_set.tx_graph.merge(change_set.tx_graph.clone()); - write_bdk_wallet_tx_graph( - &latest_change_set.tx_graph, - &*persister.kv_store, - Arc::clone(&persister.logger), - )?; - } - - if !change_set.local_chain.is_empty() { - latest_change_set.local_chain.merge(change_set.local_chain.clone()); - write_bdk_wallet_local_chain( - &latest_change_set.local_chain, - &*persister.kv_store, - Arc::clone(&persister.logger), - )?; - } - - Ok(()) + + if !change_set.local_chain.is_empty() { + latest_change_set.local_chain.merge(change_set.local_chain.clone()); + write_bdk_wallet_local_chain( + &latest_change_set.local_chain, + &*persister.kv_store, + Arc::clone(&persister.logger), + ) + .await?; + } + + Ok(()) + }) } } diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 5f6657260..cecf68d5a 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -45,6 +45,7 @@ use logging::TestLogWriter; use rand::distr::Alphanumeric; use rand::{rng, Rng}; use serde_json::{json, Value}; +use tokio::runtime::Runtime; macro_rules! expect_event { ($node:expr, $event_type:ident) => {{ @@ -660,6 +661,7 @@ pub async fn open_channel_push_amt( push_amount_msat, None, ) + .await .unwrap(); } else { node_a @@ -670,6 +672,7 @@ 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()); @@ -686,8 +689,8 @@ pub(crate) async fn do_channel_full_cycle( node_a: TestNode, node_b: TestNode, bitcoind: &BitcoindClient, electrsd: &E, allow_0conf: 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 }; @@ -748,6 +751,7 @@ 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()); @@ -817,11 +821,15 @@ 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()); @@ -857,7 +865,10 @@ pub(crate) async fn do_channel_full_cycle( assert!(matches!(node_b.payment(&payment_id).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!( + Err(NodeError::DuplicatePayment), + node_a.bolt11_payment().send(&invoice, None).await + ); 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)); @@ -870,24 +881,29 @@ pub(crate) async fn do_channel_full_cycle( 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, .. } => { @@ -914,16 +930,18 @@ pub(crate) async fn do_channel_full_cycle( 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); @@ -959,8 +977,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, @@ -1002,8 +1021,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, @@ -1011,7 +1032,7 @@ 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!( @@ -1047,6 +1068,7 @@ 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; @@ -1101,9 +1123,9 @@ 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(); + node_b.splice_out(&user_channel_id_b, node_a.node_id(), &addr_a, splice_out_sat).await.unwrap(); expect_splice_pending_event!(node_a, node_b.node_id()); expect_splice_pending_event!(node_b, node_a.node_id()); @@ -1125,7 +1147,7 @@ pub(crate) async fn do_channel_full_cycle( 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_pending_event!(node_a, node_b.node_id()); expect_splice_pending_event!(node_b, node_a.node_id()); @@ -1148,9 +1170,9 @@ pub(crate) async fn do_channel_full_cycle( println!("\nB close_channel (force: {})", force_close); if force_close { tokio::time::sleep(Duration::from_secs(1)).await; - 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(); } expect_event!(node_a, ChannelClosed); @@ -1404,6 +1426,7 @@ struct TestSyncStoreInner { test_store: TestStore, fs_store: FilesystemStore, sqlite_store: SqliteStore, + rt: Runtime, } impl TestSyncStoreInner { @@ -1421,15 +1444,19 @@ impl TestSyncStoreInner { ) .unwrap(); let test_store = TestStore::new(false); - Self { serializer, fs_store, sqlite_store, test_store } + let rt = Runtime::new().unwrap(); + Self { serializer, fs_store, sqlite_store, test_store, rt } } fn do_list( &self, primary_namespace: &str, secondary_namespace: &str, ) -> lightning::io::Result> { let fs_res = KVStoreSync::list(&self.fs_store, primary_namespace, secondary_namespace); - let sqlite_res = - KVStoreSync::list(&self.sqlite_store, primary_namespace, secondary_namespace); + let sqlite_res = self.rt.block_on(KVStore::list( + &self.sqlite_store, + primary_namespace, + secondary_namespace, + )); let test_res = KVStoreSync::list(&self.test_store, primary_namespace, secondary_namespace); match fs_res { @@ -1460,8 +1487,12 @@ impl TestSyncStoreInner { let _guard = self.serializer.read().unwrap(); let fs_res = KVStoreSync::read(&self.fs_store, primary_namespace, secondary_namespace, key); - let sqlite_res = - KVStoreSync::read(&self.sqlite_store, primary_namespace, secondary_namespace, key); + let sqlite_res = self.rt.block_on(KVStore::read( + &self.sqlite_store, + primary_namespace, + secondary_namespace, + key, + )); let test_res = KVStoreSync::read(&self.test_store, primary_namespace, secondary_namespace, key); @@ -1492,13 +1523,13 @@ impl TestSyncStoreInner { key, buf.clone(), ); - let sqlite_res = KVStoreSync::write( + let sqlite_res = self.rt.block_on(KVStore::write( &self.sqlite_store, primary_namespace, secondary_namespace, key, buf.clone(), - ); + )); let test_res = KVStoreSync::write( &self.test_store, primary_namespace, @@ -1532,13 +1563,13 @@ impl TestSyncStoreInner { let _guard = self.serializer.write().unwrap(); let fs_res = KVStoreSync::remove(&self.fs_store, primary_namespace, secondary_namespace, key, lazy); - let sqlite_res = KVStoreSync::remove( + let sqlite_res = self.rt.block_on(KVStore::remove( &self.sqlite_store, primary_namespace, secondary_namespace, key, lazy, - ); + )); let test_res = KVStoreSync::remove( &self.test_store, primary_namespace, diff --git a/tests/integration_tests_cln.rs b/tests/integration_tests_cln.rs index 0245f1fdf..8717c5ccb 100644 --- a/tests/integration_tests_cln.rs +++ b/tests/integration_tests_cln.rs @@ -90,6 +90,7 @@ async fn test_cln() { let funding_amount_sat = 1_000_000; node.open_channel(cln_node_id, cln_address, funding_amount_sat, Some(500_000_000), None) + .await .unwrap(); let funding_txo = common::expect_channel_pending_event!(node, cln_node_id); @@ -121,7 +122,7 @@ async fn test_cln() { cln_client.pay(&ldk_invoice.to_string(), Default::default()).unwrap(); common::expect_event!(node, PaymentReceived); - node.close_channel(&user_channel_id, cln_node_id).unwrap(); + node.close_channel(&user_channel_id, cln_node_id).await.unwrap(); common::expect_event!(node, ChannelClosed); node.stop().unwrap(); } diff --git a/tests/integration_tests_lnd.rs b/tests/integration_tests_lnd.rs index 8f1d4c868..670956a94 100755 --- a/tests/integration_tests_lnd.rs +++ b/tests/integration_tests_lnd.rs @@ -71,6 +71,7 @@ async fn test_lnd() { let funding_amount_sat = 1_000_000; node.open_channel(lnd_node_id, lnd_address, funding_amount_sat, Some(500_000_000), None) + .await .unwrap(); let funding_txo = common::expect_channel_pending_event!(node, lnd_node_id); @@ -117,7 +118,7 @@ async fn test_lnd() { lnd.pay_invoice(&ldk_invoice.to_string()).await; common::expect_event!(node, PaymentReceived); - node.close_channel(&user_channel_id, lnd_node_id).unwrap(); + node.close_channel(&user_channel_id, lnd_node_id).await.unwrap(); common::expect_event!(node, ChannelClosed); node.stop().unwrap(); } diff --git a/tests/integration_tests_rust.rs b/tests/integration_tests_rust.rs index 605dd0613..0c7bead5f 100644 --- a/tests/integration_tests_rust.rs +++ b/tests/integration_tests_rust.rs @@ -119,8 +119,8 @@ async fn channel_open_fails_when_funds_insufficient() { let chain_source = TestChainSource::Esplora(&electrsd); let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); - 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; @@ -139,13 +139,15 @@ async fn channel_open_fails_when_funds_insufficient() { 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 ); } @@ -167,7 +169,10 @@ async fn multi_hop_sending() { nodes.push(node); } - let addresses = nodes.iter().map(|n| n.onchain_payment().new_address().unwrap()).collect(); + let mut addresses = Vec::new(); + for n in &nodes { + addresses.push(n.onchain_payment().new_address().await.unwrap()); + } let premine_amount_sat = 5_000_000; premine_and_distribute_funds( &bitcoind.client, @@ -233,8 +238,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); @@ -269,7 +275,7 @@ async fn start_stop_reinit() { let expected_node_id = node.node_id(); assert_eq!(node.start(), 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); @@ -326,8 +332,8 @@ async fn onchain_send_receive() { let chain_source = TestChainSource::Esplora(&electrsd); let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); - 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(); @@ -394,22 +400,22 @@ 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(); @@ -473,8 +479,8 @@ 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; @@ -496,8 +502,8 @@ async fn onchain_send_receive() { node_b.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Onchain { .. })); 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; @@ -528,8 +534,8 @@ async fn onchain_send_all_retains_reserve() { let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); // 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; @@ -548,7 +554,7 @@ async fn onchain_send_all_retains_reserve() { 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; @@ -589,7 +595,7 @@ 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; @@ -617,7 +623,7 @@ async fn onchain_wallet_recovery() { 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, @@ -629,7 +635,7 @@ async fn onchain_wallet_recovery() { original_node.sync_wallets().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 @@ -663,10 +669,10 @@ async fn onchain_wallet_recovery() { ); // 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 @@ -723,8 +729,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::new(); + 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 { @@ -811,10 +819,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; } @@ -846,8 +854,8 @@ async fn connection_multi_listen() { 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(); } } @@ -866,7 +874,7 @@ async fn do_connection_restart_behavior(persist: bool) { 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(); assert_eq!(peer_details_a.node_id, node_id_b); @@ -919,14 +927,14 @@ async fn concurrent_connections_succeed() { 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 handle = tokio::spawn(async move { + thread_node.connect(node_id_b, thread_addr, false).await.unwrap(); }); handles.push(handle); } for h in handles { - h.join().unwrap(); + h.await.unwrap(); } } @@ -939,8 +947,8 @@ async fn run_splice_channel_test(bitcoind_chain_source: bool) { }; let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); - 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, @@ -982,24 +990,24 @@ async fn run_splice_channel_test(bitcoind_chain_source: bool) { 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!( - node_b.splice_out(&user_channel_id_b, node_b.node_id(), &address, amount_msat / 1000), + node_b.splice_out(&user_channel_id_b, node_b.node_id(), &address, amount_msat / 1000).await, 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_pending_event!(node_a, node_b.node_id()); expect_splice_pending_event!(node_b, node_a.node_id()); @@ -1026,7 +1034,7 @@ async fn run_splice_channel_test(bitcoind_chain_source: bool) { assert_eq!(node_b.list_balances().total_lightning_balance_sats, 4_000_000); 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); @@ -1041,8 +1049,11 @@ async fn run_splice_channel_test(bitcoind_chain_source: bool) { assert_eq!(node_b.list_balances().total_lightning_balance_sats, 4_000_000 - amount_msat / 1000); // Splice-out funds for Node A from the payment sent by Node B - let address = node_a.onchain_payment().new_address().unwrap(); - node_a.splice_out(&user_channel_id_a, node_b.node_id(), &address, amount_msat / 1000).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) + .await + .unwrap(); let txo = expect_splice_pending_event!(node_a, node_b.node_id()); expect_splice_pending_event!(node_b, node_a.node_id()); @@ -1084,7 +1095,7 @@ async fn simple_bolt12_send_receive() { let chain_source = TestChainSource::Esplora(&electrsd); let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); - 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, @@ -1121,6 +1132,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(); expect_payment_successful_event!(node_a, Some(payment_id), None); @@ -1177,6 +1189,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() @@ -1187,6 +1200,7 @@ async fn simple_bolt12_send_receive() { expected_payer_note.clone(), None, ) + .await .unwrap(); expect_payment_successful_event!(node_a, Some(payment_id), None); @@ -1250,8 +1264,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 @@ -1349,10 +1364,10 @@ async fn async_payment() { TestLogWriter::Custom(Arc::new(MultiNodeLogger::new("receiver ".to_string()))); let node_receiver = setup_node(&chain_source, config_receiver); - 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, @@ -1431,8 +1446,11 @@ async fn async_payment() { node_receiver.stop().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; @@ -1474,7 +1492,7 @@ async fn test_node_announcement_propagation() { let node_a = setup_node(&chain_source, config_a); let node_b = setup_node(&chain_source, config_b); - 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, @@ -1541,7 +1559,7 @@ async fn generate_bip21_uri() { let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); - 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; @@ -1552,6 +1570,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); @@ -1581,6 +1600,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); @@ -1596,7 +1616,7 @@ async fn unified_send_receive_bip21_uri() { let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); - 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( @@ -1628,7 +1648,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 { @@ -1671,7 +1692,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(); @@ -1753,9 +1774,9 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) { let payer_node = payer_builder.build(payer_config.node_entropy.into()).unwrap(); payer_node.start().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; @@ -1788,11 +1809,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); @@ -1823,13 +1845,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); @@ -1851,11 +1876,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()); @@ -1904,11 +1930,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()); @@ -1923,7 +1950,7 @@ 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); @@ -1953,7 +1980,7 @@ async fn spontaneous_send_with_custom_preimage() { let chain_source = TestChainSource::Esplora(&electrsd); let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); - 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, @@ -1980,6 +2007,7 @@ 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 @@ -2071,9 +2099,9 @@ async fn lsps2_client_trusts_lsp() { let payer_node = payer_builder.build(payer_config.node_entropy.into()).unwrap(); payer_node.start().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; @@ -2113,11 +2141,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()); @@ -2247,9 +2276,9 @@ async fn lsps2_lsp_trusts_client_but_client_does_not_claim() { let payer_node = payer_builder.build(payer_config.node_entropy.into()).unwrap(); payer_node.start().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; @@ -2289,11 +2318,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()); @@ -2345,8 +2375,8 @@ async fn payment_persistence_after_restart() { let config_b = random_config(true); let node_b = setup_node(&chain_source, config_b); - 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; @@ -2380,8 +2410,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); diff --git a/tests/reorg_test.rs b/tests/reorg_test.rs index 89660a407..7168c5c7b 100644 --- a/tests/reorg_test.rs +++ b/tests/reorg_test.rs @@ -51,8 +51,10 @@ proptest! { } let amount_sat = 2_100_000; - let addr_nodes = - nodes.iter().map(|node| node.onchain_payment().new_address().unwrap()).collect::>(); + let mut addr_nodes = Vec::new(); + 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 { @@ -126,9 +128,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);