From 8e984498e4a106a47946d2b41285091a81a36f25 Mon Sep 17 00:00:00 2001 From: Mariusz Klochowicz Date: Wed, 7 Jun 2023 13:09:36 +0200 Subject: [PATCH 1/2] chore: Remove unneeded OnChainWallet abstraction A factory function is good enough, we were not using the struct for anything other than calling new() --- crates/ln-dlc-node/src/node/mod.rs | 7 +-- crates/ln-dlc-node/src/on_chain_wallet.rs | 68 +++++++++++------------ 2 files changed, 34 insertions(+), 41 deletions(-) diff --git a/crates/ln-dlc-node/src/node/mod.rs b/crates/ln-dlc-node/src/node/mod.rs index 6cb52484d..301bac83e 100644 --- a/crates/ln-dlc-node/src/node/mod.rs +++ b/crates/ln-dlc-node/src/node/mod.rs @@ -8,7 +8,7 @@ use crate::ln::TracingLogger; use crate::ln_dlc_wallet::LnDlcWallet; use crate::node::dlc_channel::process_pending_dlc_actions; use crate::node::peer_manager::broadcast_node_announcement; -use crate::on_chain_wallet::OnChainWallet; +use crate::on_chain_wallet::new_bdk_wallet; use crate::seed::Bip39Seed; use crate::util; use crate::ChainMonitor; @@ -273,8 +273,7 @@ where )?); let on_chain_dir = data_dir.join("on_chain"); - let on_chain_wallet = - OnChainWallet::new(on_chain_dir.as_path(), network, seed.wallet_seed())?; + let on_chain_wallet = new_bdk_wallet(on_chain_dir.as_path(), network, seed.wallet_seed())?; let esplora_client = Arc::new(EsploraSyncClient::new( esplora_server_url.clone(), @@ -285,7 +284,7 @@ where let ln_dlc_wallet = { Arc::new(LnDlcWallet::new( esplora_client.clone(), - on_chain_wallet.inner, + on_chain_wallet, fee_rate_estimator.clone(), storage.clone(), seed.clone(), diff --git a/crates/ln-dlc-node/src/on_chain_wallet.rs b/crates/ln-dlc-node/src/on_chain_wallet.rs index 6e79deea3..3a8a57f92 100644 --- a/crates/ln-dlc-node/src/on_chain_wallet.rs +++ b/crates/ln-dlc-node/src/on_chain_wallet.rs @@ -1,50 +1,44 @@ use crate::seed::WalletSeed; use anyhow::Context; +use anyhow::Result; use bdk::bitcoin::secp256k1::Secp256k1; -use bdk::sled; use bdk::wallet::wallet_name_from_descriptor; use bdk::KeychainKind; use std::path::Path; -pub struct OnChainWallet { - pub inner: bdk::Wallet, -} - -impl OnChainWallet { - pub fn new( - data_dir: &Path, - network: bitcoin::Network, - seed: WalletSeed, - ) -> Result { - tracing::info!(?network, "Creating the wallet"); - - let data_dir = data_dir.join(network.to_string()); - if !data_dir.exists() { - std::fs::create_dir_all(&data_dir).context(format!( - "Could not create data dir ({data_dir:?}) for {network}" - ))?; - } +pub fn new_bdk_wallet( + data_dir: &Path, + network: bitcoin::Network, + seed: WalletSeed, +) -> Result> { + tracing::info!(?network, "Creating the wallet"); + + let data_dir = data_dir.join(network.to_string()); + if !data_dir.exists() { + std::fs::create_dir_all(&data_dir).context(format!( + "Could not create data dir ({data_dir:?}) for {network}" + ))?; + } - let ext_priv_key = seed.derive_extended_priv_key(network)?; + let ext_priv_key = seed.derive_extended_priv_key(network)?; - let wallet_name = wallet_name_from_descriptor( - bdk::template::Bip84(ext_priv_key, KeychainKind::External), - Some(bdk::template::Bip84(ext_priv_key, KeychainKind::Internal)), - ext_priv_key.network, - &Secp256k1::new(), - )?; + let wallet_name = wallet_name_from_descriptor( + bdk::template::Bip84(ext_priv_key, KeychainKind::External), + Some(bdk::template::Bip84(ext_priv_key, KeychainKind::Internal)), + ext_priv_key.network, + &Secp256k1::new(), + )?; - // Create a database (using default sled type) to store wallet data - let db = bdk::sled::open(data_dir.join("wallet"))?; - let db = db.open_tree(wallet_name)?; + // Create a database (using default sled type) to store wallet data + let db = bdk::sled::open(data_dir.join("wallet"))?; + let db = db.open_tree(wallet_name)?; - let bdk_wallet = bdk::Wallet::new( - bdk::template::Bip84(ext_priv_key, KeychainKind::External), - Some(bdk::template::Bip84(ext_priv_key, KeychainKind::Internal)), - ext_priv_key.network, - db, - )?; + let bdk_wallet = bdk::Wallet::new( + bdk::template::Bip84(ext_priv_key, KeychainKind::External), + Some(bdk::template::Bip84(ext_priv_key, KeychainKind::Internal)), + ext_priv_key.network, + db, + )?; - Ok(OnChainWallet { inner: bdk_wallet }) - } + Ok(bdk_wallet) } From df57f34ae66eabe5fa403fa821a0902a3094404d Mon Sep 17 00:00:00 2001 From: Lucas Soriano del Pino Date: Thu, 8 Jun 2023 18:42:40 +1000 Subject: [PATCH 2/2] feat: Add wallet actor An actor that manages the `bdk::Wallet` resource. It allows us to use the wallet whilst the inevitably expensive on-chain sync is happening in the background. We would like to use the async version of `EsploraBlockchain`, but https://github.com/bitcoindevkit/bdk/issues/165 is still an issue. The only hacky bit stems from the fact that we still have to implement certain non-async foreign traits and to access any `bdk::Wallet` resource we now have to go via async methods, since the actor is async. To do so, at some point we have to call an async function "from a sync context". The natural way to do so (for me) would be to use `runtime.block_on`, which schedules an async task and waits for it to resolve, blocking the thread. *But*, these non-async foreign trait methods are actually called elswhere in _async_ contexts. This leads to `runtime.block_on` panicking because `tokio` is trying to prevent us from blocking a thread in an async context. This is analogous to us misusing async and doing an expensive CPU-bound computation in an async context, but here `tokio` is able to "aid" us since `block_on` is provided by `tokio`. The solution is to use the following pattern: ```rust tokio::task::block_in_place(|| { tokio::runtime::Handle::current().block_on(async { // async code }) }); ``` From the documentation of `block_in_place`, we are able to run "the provided blocking function on the current thread without blocking the executor". We therefore avoid the panic, as we no longer block the executor when calling `block_on`. This has one final side-effect, which is that all `ln-dlc-node` async tests now need to go back to using the `multi_thread` flavour. `block_in_place` works by moving all scheduled tasks to a different worker thread and without `multi_thread` there is only one worker thread, so it just panics. Co-authored-by: Mariusz Klochowicz --- Cargo.lock | 41 ++- Cargo.toml | 1 + coordinator/src/admin.rs | 29 +- coordinator/src/routes.rs | 5 +- crates/ln-dlc-node/Cargo.toml | 2 + crates/ln-dlc-node/src/bdk_actor.rs | 254 ++++++++++++++++++ crates/ln-dlc-node/src/dlc_custom_signer.rs | 26 +- crates/ln-dlc-node/src/ldk_node_wallet.rs | 189 +++---------- crates/ln-dlc-node/src/lib.rs | 1 + crates/ln-dlc-node/src/ln/event_handler.rs | 2 +- crates/ln-dlc-node/src/ln_dlc_wallet.rs | 30 ++- crates/ln-dlc-node/src/node/mod.rs | 58 ++-- crates/ln-dlc-node/src/node/wallet.rs | 10 +- .../src/tests/dlc/collaborative_settlement.rs | 4 +- crates/ln-dlc-node/src/tests/dlc/create.rs | 2 +- .../tests/dlc/dlc_setup_with_reconnects.rs | 2 +- .../tests/dlc/non_collaborative_settlement.rs | 4 +- .../just_in_time_channel/channel_close.rs | 14 +- .../src/tests/just_in_time_channel/create.rs | 6 +- .../just_in_time_channel/multiple_payments.rs | 2 +- crates/ln-dlc-node/src/tests/mod.rs | 4 +- .../src/tests/multi_hop_payment.rs | 2 +- .../ln-dlc-node/src/tests/onboard_from_lnd.rs | 2 +- .../src/tests/single_hop_payment.rs | 2 +- maker/src/routes.rs | 18 +- mobile/native/src/api.rs | 5 +- mobile/native/src/ln_dlc/mod.rs | 11 +- mobile/native/src/ln_dlc/node.rs | 4 +- 28 files changed, 461 insertions(+), 269 deletions(-) create mode 100644 crates/ln-dlc-node/src/bdk_actor.rs diff --git a/Cargo.lock b/Cargo.lock index b4c670b59..9381048e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -466,6 +466,15 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +[[package]] +name = "catty" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf0adb3cc1c06945672f8dcc827e42497ac6d0aff49f459ec918132b82a5cbc" +dependencies = [ + "spin 0.9.8", +] + [[package]] name = "cc" version = "1.0.79" @@ -1008,6 +1017,12 @@ dependencies = [ "ureq", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "fastrand" version = "1.9.0" @@ -1671,6 +1686,7 @@ name = "ln-dlc-node" version = "0.1.0" dependencies = [ "anyhow", + "async-trait", "autometrics", "bdk", "bip39", @@ -1709,6 +1725,7 @@ dependencies = [ "tracing-log", "tracing-subscriber", "ureq", + "xtra", ] [[package]] @@ -2703,7 +2720,7 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", + "spin 0.5.2", "untrusted", "web-sys", "winapi", @@ -3143,6 +3160,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "state" version = "0.5.3" @@ -4097,3 +4120,19 @@ checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" dependencies = [ "winapi", ] + +[[package]] +name = "xtra" +version = "0.6.0" +source = "git+https://github.com/Restioson/xtra?rev=d98393a#d98393a115ea52656585c43df327d3e392833810" +dependencies = [ + "async-trait", + "catty", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "pin-project-lite", + "spin 0.9.8", + "tracing", +] diff --git a/Cargo.toml b/Cargo.toml index c36b42d89..96eb0fe32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,4 @@ lightning-transaction-sync = { git = "https://github.com/get10101/rust-lightning lightning-net-tokio = { git = "https://github.com/get10101/rust-lightning/", rev = "5aa1449c" } lightning-persister = { git = "https://github.com/get10101/rust-lightning/", rev = "5aa1449c" } rust-bitcoin-coin-selection = { git = "https://github.com/p2pderivatives/rust-bitcoin-coin-selection" } +xtra = { git = "https://github.com/Restioson/xtra", rev = "d98393a" } diff --git a/coordinator/src/admin.rs b/coordinator/src/admin.rs index 9388a8270..85bdf7117 100644 --- a/coordinator/src/admin.rs +++ b/coordinator/src/admin.rs @@ -31,20 +31,21 @@ pub struct Balance { #[autometrics] pub async fn get_balance(State(state): State>) -> Result, AppError> { - spawn_blocking(move || { - let offchain = state.node.inner.get_ldk_balance(); - let onchain = - state.node.inner.get_on_chain_balance().map_err(|e| { - AppError::InternalServerError(format!("Failed to get balance: {e:#}")) - })?; - - Ok(Json(Balance { - offchain: offchain.available, - onchain: onchain.confirmed, - })) - }) - .await - .map_err(|e| AppError::InternalServerError(format!("Failed to get balance: {e:#}")))? + let onchain = state + .node + .inner + .get_on_chain_balance() + .await + .map_err(|e| AppError::InternalServerError(format!("Failed to get balance: {e:#}")))?; + + let offchain = spawn_blocking(move || state.node.inner.get_ldk_balance()) + .await + .map_err(|e| AppError::InternalServerError(format!("Failed to get balance: {e:#}")))?; + + Ok(Json(Balance { + offchain: offchain.available, + onchain: onchain.confirmed, + })) } #[autometrics] diff --git a/coordinator/src/routes.rs b/coordinator/src/routes.rs index b21c3eb49..de41fd3ea 100644 --- a/coordinator/src/routes.rs +++ b/coordinator/src/routes.rs @@ -182,7 +182,7 @@ pub async fn get_new_address( State(app_state): State>, ) -> Result, AppError> { let address = - app_state.node.inner.get_new_address().map_err(|e| { + app_state.node.inner.get_new_address().await.map_err(|e| { AppError::InternalServerError(format!("Failed to get new address: {e:#}")) })?; Ok(Json(address.to_string())) @@ -359,7 +359,8 @@ async fn update_settings( .node .inner .update_settings(updated_settings.ln_dlc) - .await; + .await + .map_err(|e| AppError::InternalServerError(format!("Could not write settings: {e:#}")))?; Ok(()) } diff --git a/crates/ln-dlc-node/Cargo.toml b/crates/ln-dlc-node/Cargo.toml index 8d56d99eb..42ae63890 100644 --- a/crates/ln-dlc-node/Cargo.toml +++ b/crates/ln-dlc-node/Cargo.toml @@ -8,6 +8,7 @@ description = "A common interface for using Lightning and DLC channels side-by-s [dependencies] anyhow = { version = "1", features = ["backtrace"] } +async-trait = "0.1.68" autometrics = "0.5" bdk = { version = "0.27.0", default-features = false, features = ["key-value-db", "use-esplora-blocking"] } bip39 = { version = "2", features = ["rand_core"] } @@ -42,6 +43,7 @@ tokio = { version = "1", default-features = false, features = ["io-util", "macro tracing = "0.1.37" tracing-log = "0.1.3" ureq = "2.5.0" +xtra = { version = "0.6", features = ["instrumentation", "sink"] } [dev-dependencies] clap = { version = "4", features = ["derive"] } diff --git a/crates/ln-dlc-node/src/bdk_actor.rs b/crates/ln-dlc-node/src/bdk_actor.rs new file mode 100644 index 000000000..2d68233f0 --- /dev/null +++ b/crates/ln-dlc-node/src/bdk_actor.rs @@ -0,0 +1,254 @@ +use anyhow::bail; +use anyhow::Context as _; +use anyhow::Result; +use async_trait::async_trait; +use bdk::blockchain::EsploraBlockchain; +use bdk::wallet::AddressIndex; +use bdk::Balance; +use bdk::FeeRate; +use bdk::SignOptions; +use bdk::SyncOptions; +use bdk::TransactionDetails; +use bitcoin::Address; +use bitcoin::Script; +use bitcoin::Transaction; +use std::sync::Arc; +use std::sync::RwLock; +use std::time::Duration; +use std::time::Instant; +use time::OffsetDateTime; +use xtra::Mailbox; + +/// An actor that manages the [`bdk::Wallet`] resource. +/// +/// It allows us to use the wallet whilst the inevitably expensive on-chain sync is happening in the +/// background. +/// +/// We would like to use the async version of [`EsploraBlockchain`], but +/// https://github.com/bitcoindevkit/bdk/issues/165 is still an issue. +pub struct BdkActor { + wallet: bdk::Wallet, + blockchain_client: EsploraBlockchain, + sync_interval: Arc>, +} + +#[derive(Debug, Clone)] +pub struct WalletInfo { + pub balance: Balance, + pub transactions: Vec, + pub last_updated_at: OffsetDateTime, +} + +/// Message to trigger an on-chain sync. +#[derive(Clone, Copy)] +pub struct Sync; + +/// Message to get new on-chain address. +#[derive(Clone, Copy)] +pub struct GetNewAddress; + +/// Message to get last unused on-chain address. +#[derive(Clone, Copy)] +pub struct GetLastUnusedAddress; + +/// Message to get current on-chain balance. +#[derive(Clone, Copy)] +pub struct GetBalance; + +/// Message to get current on-chain balance. +#[derive(Clone, Copy)] +pub struct GetHistory; + +/// Message to get current on-chain balance. +#[derive(Clone)] +pub struct BuildAndSignTx { + pub script_pubkey: Script, + pub amount_sats_or_drain: Option, + pub fee_rate: FeeRate, +} + +/// Message to set the on-chain sync interval. +pub struct UpdateSyncInterval(pub Duration); + +impl BdkActor { + pub fn new( + wallet: bdk::Wallet, + blockchain_client: EsploraBlockchain, + sync_interval: Duration, + ) -> Self { + Self { + wallet, + blockchain_client, + sync_interval: Arc::new(RwLock::new(sync_interval)), + } + } +} + +impl BdkActor { + #[tracing::instrument(name = "On-chain sync", skip_all, err)] + async fn sync(&mut self) -> Result { + let now = Instant::now(); + tracing::debug!("On-chain sync started"); + + self.wallet + .sync(&self.blockchain_client, SyncOptions::default()) + .context("Failed to sync on-chain wallet")?; + + let balance = self.wallet.get_balance()?; + let transactions = self.wallet.list_transactions(false)?; + + let wallet_info = WalletInfo { + balance, + last_updated_at: OffsetDateTime::now_utc(), + transactions, + }; + + tracing::trace!(sync_time_ms = %now.elapsed().as_millis(), "On-chain sync done"); + + Ok(wallet_info) + } +} + +#[async_trait] +impl xtra::Actor for BdkActor { + type Stop = (); + + async fn started(&mut self, mailbox: &mut Mailbox) -> Result<(), Self::Stop> { + tokio::spawn({ + let this = mailbox.address(); + let sync_interval = self.sync_interval.clone(); + async move { + let sync_interval = *sync_interval.read().expect("RwLock to not be poisoned"); + while this.send(Sync).await.is_ok() { + tokio::time::sleep(sync_interval).await; + } + + tracing::warn!("On-chain sync stopped because actor shut down"); + } + }); + + Ok(()) + } + + async fn stopped(self) -> Self::Stop {} +} + +#[async_trait] +impl xtra::Handler for BdkActor { + type Return = Result; + + async fn handle(&mut self, _: Sync, _: &mut xtra::Context) -> Self::Return { + self.sync().await + } +} + +#[async_trait] +impl xtra::Handler for BdkActor { + type Return = Result
; + + async fn handle(&mut self, _: GetNewAddress, _: &mut xtra::Context) -> Self::Return { + Ok(self.wallet.get_address(AddressIndex::New)?.address) + } +} + +#[async_trait] +impl xtra::Handler for BdkActor { + type Return = Result
; + + async fn handle( + &mut self, + _: GetLastUnusedAddress, + _: &mut xtra::Context, + ) -> Self::Return { + Ok(self.wallet.get_address(AddressIndex::LastUnused)?.address) + } +} + +#[async_trait] +impl xtra::Handler for BdkActor { + type Return = Result; + + async fn handle(&mut self, _: GetBalance, _: &mut xtra::Context) -> Self::Return { + self.wallet.get_balance().context("Failed to get balance") + } +} + +#[async_trait] +impl xtra::Handler for BdkActor { + type Return = Result>; + + async fn handle(&mut self, _: GetHistory, _: &mut xtra::Context) -> Self::Return { + self.wallet + .list_transactions(false) + .context("Failed to get transactions") + } +} + +#[async_trait] +impl xtra::Handler for BdkActor { + type Return = Result; + + async fn handle(&mut self, msg: BuildAndSignTx, _: &mut xtra::Context) -> Self::Return { + let BuildAndSignTx { + script_pubkey, + amount_sats_or_drain, + fee_rate, + } = msg; + + let tx = { + let mut tx_builder = self.wallet.build_tx(); + if let Some(amount_sats) = amount_sats_or_drain { + tx_builder + .add_recipient(script_pubkey, amount_sats) + .fee_rate(fee_rate) + .enable_rbf(); + } else { + tx_builder + .drain_wallet() + .drain_to(script_pubkey) + .fee_rate(fee_rate) + .enable_rbf(); + } + + let (mut psbt, _) = tx_builder.finish()?; + + if !self.wallet.sign(&mut psbt, SignOptions::default())? { + bail!("Failed to finalize PSBT"); + } + + psbt.extract_tx() + }; + + let txid = tx.txid(); + if let Some(amount_sats) = amount_sats_or_drain { + tracing::info!( + %txid, + %amount_sats, + "Built new transaction", + ); + } else { + tracing::info!( + %txid, + "Built new transaction draining on-chain funds", + ); + } + + Ok(tx) + } +} + +#[async_trait] +impl xtra::Handler for BdkActor { + type Return = (); + + async fn handle( + &mut self, + msg: UpdateSyncInterval, + _: &mut xtra::Context, + ) -> Self::Return { + *self + .sync_interval + .write() + .expect("RwLock to not be poisoned") = msg.0; + } +} diff --git a/crates/ln-dlc-node/src/dlc_custom_signer.rs b/crates/ln-dlc-node/src/dlc_custom_signer.rs index 32481280a..18fbef1e5 100644 --- a/crates/ln-dlc-node/src/dlc_custom_signer.rs +++ b/crates/ln-dlc-node/src/dlc_custom_signer.rs @@ -288,18 +288,28 @@ impl SignerProvider for CustomKeysManager { type Signer = CustomSigner; fn get_destination_script(&self) -> Script { - let address = self - .wallet - .get_last_unused_address() - .expect("Failed to retrieve new address from wallet."); + let address = tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + self.wallet + .get_last_unused_address() + .await + .expect("Failed to retrieve address from wallet.") + }) + }); + address.script_pubkey() } fn get_shutdown_scriptpubkey(&self) -> ShutdownScript { - let address = self - .wallet - .get_last_unused_address() - .expect("Failed to retrieve new address from wallet."); + let address = tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + self.wallet + .get_last_unused_address() + .await + .expect("Failed to retrieve address from wallet.") + }) + }); + match address.payload { bitcoin::util::address::Payload::WitnessProgram { version, program } => { ShutdownScript::new_witness_program(version, &program) diff --git a/crates/ln-dlc-node/src/ldk_node_wallet.rs b/crates/ln-dlc-node/src/ldk_node_wallet.rs index 0b3496c24..52d9724cb 100644 --- a/crates/ln-dlc-node/src/ldk_node_wallet.rs +++ b/crates/ln-dlc-node/src/ldk_node_wallet.rs @@ -1,45 +1,38 @@ +use crate::bdk_actor::BdkActor; +use crate::bdk_actor::BuildAndSignTx; +use crate::bdk_actor::GetBalance; +use crate::bdk_actor::GetHistory; +use crate::bdk_actor::GetLastUnusedAddress; +use crate::bdk_actor::GetNewAddress; use crate::fee_rate_estimator::FeeRateEstimator; use crate::ln::TracingLogger; -use anyhow::bail; -use anyhow::Context; use anyhow::Error; use anyhow::Result; use autometrics::autometrics; use bdk::blockchain::Blockchain; use bdk::blockchain::EsploraBlockchain; use bdk::blockchain::GetHeight; -use bdk::database::BatchDatabase; -use bdk::wallet::AddressIndex; -use bdk::SignOptions; -use bdk::SyncOptions; use bdk::TransactionDetails; use bitcoin::consensus::encode::serialize_hex; use bitcoin::BlockHash; use bitcoin::Script; use bitcoin::Transaction; -use bitcoin::Txid; use lightning::chain::chaininterface::BroadcasterInterface; use lightning::chain::chaininterface::ConfirmationTarget; use lightning::chain::transaction::OutPoint; use lightning::chain::Filter; use lightning::chain::WatchedOutput; use lightning_transaction_sync::EsploraSyncClient; -use parking_lot::Mutex; -use parking_lot::MutexGuard; use std::sync::Arc; use tokio::sync::RwLock; -pub struct Wallet -where - D: BatchDatabase, -{ +pub struct Wallet { // A BDK blockchain used for wallet sync. pub(crate) blockchain: Arc, - // A BDK on-chain wallet. - inner: Mutex>, settings: RwLock, esplora_sync_client: Arc>>, fee_rate_estimator: Arc, + on_chain_actor: xtra::Address, } #[derive(Clone, Debug, Default)] @@ -47,32 +40,24 @@ pub struct WalletSettings { pub max_allowed_tx_fee_rate_when_opening_channel: Option, } -impl Wallet -where - D: BatchDatabase, -{ +impl Wallet { pub(crate) fn new( blockchain: EsploraBlockchain, - wallet: bdk::Wallet, esplora_sync_client: Arc>>, fee_rate_estimator: Arc, + on_chain_actor: xtra::Address, ) -> Self { - let inner = Mutex::new(wallet); let settings = RwLock::new(WalletSettings::default()); Self { blockchain: Arc::new(blockchain), - inner, settings, esplora_sync_client, fee_rate_estimator, + on_chain_actor, } } - fn bdk_lock(&self) -> MutexGuard> { - self.inner.lock() - } - pub async fn update_settings(&self, settings: WalletSettings) { tracing::info!(?settings, "Updating wallet settings"); *self.settings.write().await = settings; @@ -84,9 +69,7 @@ where /// Update the internal BDK wallet database with the blockchain. pub async fn sync(&self) -> Result<()> { - let wallet_lock = self.bdk_lock(); - - wallet_lock.sync(&self.blockchain, SyncOptions::default())?; + self.on_chain_actor.send(crate::bdk_actor::Sync).await??; Ok(()) } @@ -94,136 +77,42 @@ where #[autometrics] pub(crate) async fn create_funding_transaction( &self, - output_script: Script, + script_pubkey: Script, value_sats: u64, confirmation_target: ConfirmationTarget, ) -> Result { - let locked_wallet = self.bdk_lock(); - let mut tx_builder = locked_wallet.build_tx(); - let fee_rate = self.fee_rate_estimator.get(confirmation_target); - tx_builder - .add_recipient(output_script, value_sats) - .fee_rate(fee_rate) - .enable_rbf(); - - let mut psbt = match tx_builder.finish() { - Ok((psbt, _)) => { - tracing::trace!("Created funding PSBT: {:?}", psbt); - psbt - } - Err(err) => { - tracing::error!("Failed to create funding transaction: {}", err); - return Err(err.into()); - } - }; - - match locked_wallet.sign(&mut psbt, SignOptions::default()) { - Ok(finalized) => { - if !finalized { - bail!("Onchain transaction failed"); - } - } - Err(err) => { - tracing::error!("Failed to create funding transaction: {}", err); - return Err(err.into()); - } - } - - Ok(psbt.extract_tx()) + let transaction = self + .on_chain_actor + .send(BuildAndSignTx { + script_pubkey, + amount_sats_or_drain: Some(value_sats), + fee_rate, + }) + .await??; + + Ok(transaction) } #[autometrics] - pub(crate) fn get_new_address(&self) -> Result { - Ok(self.bdk_lock().get_address(AddressIndex::New)?.address) - } + pub(crate) async fn get_new_address(&self) -> Result { + let address = self.on_chain_actor.send(GetNewAddress).await??; - #[autometrics] - pub(crate) fn get_last_unused_address(&self) -> Result { - Ok(self - .bdk_lock() - .get_address(AddressIndex::LastUnused)? - .address) + Ok(address) } #[autometrics] - pub(crate) fn get_balance(&self) -> Result { - Ok(self.bdk_lock().get_balance()?) + pub async fn get_last_unused_address(&self) -> Result { + let address = self.on_chain_actor.send(GetLastUnusedAddress).await??; + + Ok(address) } - /// Send funds to the given address. - /// - /// If `amount_msat_or_drain` is `None` the wallet will be drained, i.e., all available funds - /// will be spent. - #[allow(dead_code)] #[autometrics] - pub(crate) fn send_to_address( - &self, - address: &bitcoin::Address, - amount_msat_or_drain: Option, - ) -> Result { - let fee_rate = self.fee_rate_estimator.get(ConfirmationTarget::Normal); - - let tx = { - let locked_wallet = self.bdk_lock(); - let mut tx_builder = locked_wallet.build_tx(); - - if let Some(amount_sats) = amount_msat_or_drain { - tx_builder - .add_recipient(address.script_pubkey(), amount_sats) - .fee_rate(fee_rate) - .enable_rbf(); - } else { - tx_builder - .drain_wallet() - .drain_to(address.script_pubkey()) - .fee_rate(fee_rate) - .enable_rbf(); - } - - let mut psbt = match tx_builder.finish() { - Ok((psbt, _)) => { - tracing::trace!("Created PSBT: {:?}", psbt); - psbt - } - Err(err) => { - bail!(err) - } - }; - - match locked_wallet.sign(&mut psbt, SignOptions::default()) { - Ok(finalized) => { - if !finalized { - bail!("On chain creation failed"); - } - } - Err(err) => { - bail!(err) - } - } - psbt.extract_tx() - }; - - self.broadcast_transaction(&tx); + pub(crate) async fn get_balance(&self) -> Result { + let balance = self.on_chain_actor.send(GetBalance).await??; - let txid = tx.txid(); - - if let Some(amount_sats) = amount_msat_or_drain { - tracing::info!( - "Created new transaction {} sending {}sats on-chain to address {}", - txid, - amount_sats, - address - ); - } else { - tracing::info!( - "Created new transaction {} sending all available on-chain funds to address {}", - txid, - address - ); - } - - Ok(txid) + Ok(balance) } #[autometrics] @@ -236,17 +125,13 @@ where #[autometrics] pub async fn on_chain_transaction_list(&self) -> Result> { - let wallet_lock = self.bdk_lock(); - wallet_lock - .list_transactions(false) - .context("Failed to list on chain transactions") + let transactions = self.on_chain_actor.send(GetHistory).await??; + + Ok(transactions) } } -impl BroadcasterInterface for Wallet -where - D: BatchDatabase, -{ +impl BroadcasterInterface for Wallet { fn broadcast_transaction(&self, tx: &Transaction) { let txid = tx.txid(); diff --git a/crates/ln-dlc-node/src/lib.rs b/crates/ln-dlc-node/src/lib.rs index 366360426..dbca75aeb 100644 --- a/crates/ln-dlc-node/src/lib.rs +++ b/crates/ln-dlc-node/src/lib.rs @@ -24,6 +24,7 @@ use std::sync::Arc; use std::sync::Mutex; use time::OffsetDateTime; +mod bdk_actor; mod disk; mod dlc_custom_signer; mod fee_rate_estimator; diff --git a/crates/ln-dlc-node/src/ln/event_handler.rs b/crates/ln-dlc-node/src/ln/event_handler.rs index a93624f48..5f1649aba 100644 --- a/crates/ln-dlc-node/src/ln/event_handler.rs +++ b/crates/ln-dlc-node/src/ln/event_handler.rs @@ -396,7 +396,7 @@ where }) .collect::>(); - let destination_script = self.wallet.inner().get_last_unused_address()?; + let destination_script = self.wallet.inner().get_last_unused_address().await?; let tx_feerate = self .fee_rate_estimator .get_est_sat_per_1000_weight(ConfirmationTarget::Normal); diff --git a/crates/ln-dlc-node/src/ln_dlc_wallet.rs b/crates/ln-dlc-node/src/ln_dlc_wallet.rs index e530443b0..3a6e665d7 100644 --- a/crates/ln-dlc-node/src/ln_dlc_wallet.rs +++ b/crates/ln-dlc-node/src/ln_dlc_wallet.rs @@ -1,3 +1,4 @@ +use crate::bdk_actor::BdkActor; use crate::fee_rate_estimator::FeeRateEstimator; use crate::ldk_node_wallet; use crate::seed::Bip39Seed; @@ -6,7 +7,6 @@ use anyhow::Result; use autometrics::autometrics; use bdk::blockchain::EsploraBlockchain; use bdk::esplora_client::TxStatus; -use bdk::sled; use bdk::TransactionDetails; use bitcoin::secp256k1::All; use bitcoin::secp256k1::PublicKey; @@ -35,7 +35,7 @@ use std::sync::Arc; /// This is a wrapper type introduced to be able to implement traits from `rust-dlc` on the /// `ldk_node::LightningWallet`. pub struct LnDlcWallet { - ln_wallet: Arc>, + ln_wallet: Arc, storage: Arc, secp: Secp256k1, seed: Bip39Seed, @@ -46,9 +46,10 @@ impl LnDlcWallet { #[allow(clippy::too_many_arguments)] pub fn new( esplora_client: Arc>>, - on_chain_wallet: bdk::Wallet, + bdk_actor: xtra::Address, fee_rate_estimator: Arc, storage: Arc, + network: Network, seed: Bip39Seed, bdk_client_stop_gap: usize, bdk_client_concurrency: u8, @@ -57,13 +58,11 @@ impl LnDlcWallet { EsploraBlockchain::from_client(esplora_client.client().clone(), bdk_client_stop_gap) .with_concurrency(bdk_client_concurrency); - let network = on_chain_wallet.network(); - let wallet = Arc::new(ldk_node_wallet::Wallet::new( blockchain, - on_chain_wallet, esplora_client, fee_rate_estimator, + bdk_actor, )); Self { @@ -80,7 +79,7 @@ impl LnDlcWallet { } // TODO: Better to keep this private and expose the necessary APIs instead. - pub(crate) fn inner(&self) -> Arc> { + pub(crate) fn inner(&self) -> Arc { self.ln_wallet.clone() } @@ -110,8 +109,8 @@ impl LnDlcWallet { } #[autometrics] - pub fn get_last_unused_address(&self) -> Result
{ - let address = self.inner().get_last_unused_address()?; + pub async fn get_last_unused_address(&self) -> Result
{ + let address = self.inner().get_last_unused_address().await?; Ok(address) } @@ -236,10 +235,15 @@ impl Signer for LnDlcWallet { impl dlc_manager::Wallet for LnDlcWallet { #[autometrics] fn get_new_address(&self) -> Result { - let address = self - .ln_wallet - .get_new_address() - .map_err(|e| Error::BlockchainError(e.to_string()))?; + let address = tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + self.ln_wallet + .get_last_unused_address() + .await + .expect("Failed to retrieve new address from wallet.") + }) + }); + Ok(address) } diff --git a/crates/ln-dlc-node/src/node/mod.rs b/crates/ln-dlc-node/src/node/mod.rs index 301bac83e..3c89e9467 100644 --- a/crates/ln-dlc-node/src/node/mod.rs +++ b/crates/ln-dlc-node/src/node/mod.rs @@ -1,3 +1,5 @@ +use crate::bdk_actor::BdkActor; +use crate::bdk_actor::UpdateSyncInterval; use crate::disk; use crate::dlc_custom_signer::CustomKeysManager; use crate::fee_rate_estimator::FeeRateEstimator; @@ -18,6 +20,7 @@ use crate::PeerManager; use anyhow::ensure; use anyhow::Context; use anyhow::Result; +use bdk::blockchain::EsploraBlockchain; use bitcoin::secp256k1::PublicKey; use bitcoin::Network; use dlc_messages::message_handler::MessageHandler as DlcMessageHandler; @@ -90,6 +93,7 @@ pub struct Node

{ pub network: Network, pub(crate) wallet: Arc, + bdk_actor: xtra::Address, pub peer_manager: Arc, pub channel_manager: Arc, @@ -236,9 +240,17 @@ where ) } - pub async fn update_settings(&self, new_settings: LnDlcNodeSettings) { + pub async fn update_settings(&self, new_settings: LnDlcNodeSettings) -> Result<()> { tracing::info!(?new_settings, "Updating LnDlcNode settings"); + + self.bdk_actor + .send(UpdateSyncInterval(new_settings.on_chain_sync_interval)) + .await + .context("Failed to update on-chain sync interval")?; + *self.settings.write().await = new_settings; + + Ok(()) } #[allow(clippy::too_many_arguments)] @@ -280,13 +292,25 @@ where logger.clone(), )); + let (bdk_actor, mailbox) = xtra::Mailbox::unbounded(); + let blockchain = EsploraBlockchain::from_client( + esplora_client.client().clone(), + settings.bdk_client_stop_gap, + ) + .with_concurrency(settings.bdk_client_concurrency); + tokio::spawn(xtra::run( + mailbox, + BdkActor::new(on_chain_wallet, blockchain, settings.on_chain_sync_interval), + )); + let fee_rate_estimator = Arc::new(FeeRateEstimator::new(esplora_server_url)); let ln_dlc_wallet = { Arc::new(LnDlcWallet::new( esplora_client.clone(), - on_chain_wallet, + bdk_actor.clone(), fee_rate_estimator.clone(), storage.clone(), + network, seed.clone(), settings.bdk_client_stop_gap, settings.bdk_client_concurrency, @@ -294,35 +318,6 @@ where }; let settings = Arc::new(RwLock::new(settings)); - - std::thread::spawn({ - let settings = settings.clone(); - let ln_dlc_wallet = ln_dlc_wallet.clone(); - move || { - tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .expect("to be able to create a runtime") - .block_on(async move { - loop { - let now = Instant::now(); - match ln_dlc_wallet.inner().sync().await { - Ok(()) => tracing::info!( - "Background sync of on-chain wallet finished in {}ms.", - now.elapsed().as_millis() - ), - Err(err) => { - tracing::error!( - "Background sync of on-chain wallet failed: {err:#}", - ) - } - } - tokio::time::sleep(settings.read().await.on_chain_sync_interval).await; - } - }); - } - }); - tokio::spawn({ let settings = settings.clone(); let fee_rate_estimator = fee_rate_estimator.clone(); @@ -635,6 +630,7 @@ where Ok(Self { network, wallet: ln_dlc_wallet, + bdk_actor, peer_manager, keys_manager, chain_monitor, diff --git a/crates/ln-dlc-node/src/node/wallet.rs b/crates/ln-dlc-node/src/node/wallet.rs index 20820d0fc..dbbbd8d8d 100644 --- a/crates/ln-dlc-node/src/node/wallet.rs +++ b/crates/ln-dlc-node/src/node/wallet.rs @@ -5,7 +5,6 @@ use crate::node::PaymentPersister; use crate::PaymentFlow; use anyhow::Context; use anyhow::Result; -use bdk::sled; use bitcoin::secp256k1::SecretKey; use bitcoin::Address; use lightning::ln::PaymentHash; @@ -26,20 +25,21 @@ where self.wallet.get_seed_phrase() } - pub fn wallet(&self) -> Arc> { + pub fn wallet(&self) -> Arc { self.wallet.inner() } - pub fn get_new_address(&self) -> Result

{ - let address = self.wallet.inner().get_new_address()?; + pub async fn get_new_address(&self) -> Result
{ + let address = self.wallet.inner().get_new_address().await?; Ok(address) } - pub fn get_on_chain_balance(&self) -> Result { + pub async fn get_on_chain_balance(&self) -> Result { self.wallet .inner() .get_balance() + .await .context("Failed to get on-chain balance") } diff --git a/crates/ln-dlc-node/src/tests/dlc/collaborative_settlement.rs b/crates/ln-dlc-node/src/tests/dlc/collaborative_settlement.rs index 5c834f8ba..cc830f1a6 100644 --- a/crates/ln-dlc-node/src/tests/dlc/collaborative_settlement.rs +++ b/crates/ln-dlc-node/src/tests/dlc/collaborative_settlement.rs @@ -11,7 +11,7 @@ use dlc_manager::subchannel::SubChannelState; use dlc_manager::Storage; use std::time::Duration; -#[tokio::test] +#[tokio::test(flavor = "multi_thread")] #[ignore] async fn dlc_collaborative_settlement_test() { init_tracing(); @@ -127,7 +127,7 @@ async fn dlc_collaborative_settlement( Ok((app, coordinator)) } -#[tokio::test] +#[tokio::test(flavor = "multi_thread")] #[ignore] async fn open_dlc_channel_after_closing_dlc_channel() { init_tracing(); diff --git a/crates/ln-dlc-node/src/tests/dlc/create.rs b/crates/ln-dlc-node/src/tests/dlc/create.rs index d58e61f2a..7669cb585 100644 --- a/crates/ln-dlc-node/src/tests/dlc/create.rs +++ b/crates/ln-dlc-node/src/tests/dlc/create.rs @@ -11,7 +11,7 @@ use dlc_manager::Storage; use lightning::ln::channelmanager::ChannelDetails; use std::time::Duration; -#[tokio::test] +#[tokio::test(flavor = "multi_thread")] #[ignore] async fn given_lightning_channel_then_can_add_dlc_channel() { init_tracing(); diff --git a/crates/ln-dlc-node/src/tests/dlc/dlc_setup_with_reconnects.rs b/crates/ln-dlc-node/src/tests/dlc/dlc_setup_with_reconnects.rs index 84b05e4b2..ce66c9c6a 100644 --- a/crates/ln-dlc-node/src/tests/dlc/dlc_setup_with_reconnects.rs +++ b/crates/ln-dlc-node/src/tests/dlc/dlc_setup_with_reconnects.rs @@ -9,7 +9,7 @@ use dlc_manager::subchannel::SubChannelState; use dlc_manager::Storage; use std::time::Duration; -#[tokio::test] +#[tokio::test(flavor = "multi_thread")] #[ignore] async fn reconnecting_during_dlc_channel_setup() { init_tracing(); diff --git a/crates/ln-dlc-node/src/tests/dlc/non_collaborative_settlement.rs b/crates/ln-dlc-node/src/tests/dlc/non_collaborative_settlement.rs index d28b90ca9..e0ebf559c 100644 --- a/crates/ln-dlc-node/src/tests/dlc/non_collaborative_settlement.rs +++ b/crates/ln-dlc-node/src/tests/dlc/non_collaborative_settlement.rs @@ -5,7 +5,7 @@ use crate::tests::dlc::create::create_dlc_channel; use crate::tests::dlc::create::DlcChannelCreated; use crate::tests::init_tracing; -#[tokio::test] +#[tokio::test(flavor = "multi_thread")] #[ignore] async fn force_close_ln_dlc_channel() { init_tracing(); @@ -73,7 +73,7 @@ async fn force_close_ln_dlc_channel() { coordinator.wallet().sync().await.unwrap(); let coordinator_on_chain_balance_after_force_close = - coordinator.get_on_chain_balance().unwrap().confirmed; + coordinator.get_on_chain_balance().await.unwrap().confirmed; // Given that we have dynamic transaction fees based on the state of the regtest mempool, it's // less error-prone to choose a conservative lower bound on the funds we expect the coordinator diff --git a/crates/ln-dlc-node/src/tests/just_in_time_channel/channel_close.rs b/crates/ln-dlc-node/src/tests/just_in_time_channel/channel_close.rs index 64745c7da..ac7c7aba8 100644 --- a/crates/ln-dlc-node/src/tests/just_in_time_channel/channel_close.rs +++ b/crates/ln-dlc-node/src/tests/just_in_time_channel/channel_close.rs @@ -7,7 +7,7 @@ use crate::tests::min_outbound_liquidity_channel_creator; use bitcoin::Amount; use std::time::Duration; -#[tokio::test] +#[tokio::test(flavor = "multi_thread")] #[ignore] async fn ln_collab_close() { init_tracing(); @@ -48,7 +48,7 @@ async fn ln_collab_close() { .await .unwrap(); - assert_eq!(payee.get_on_chain_balance().unwrap().confirmed, 0); + assert_eq!(payee.get_on_chain_balance().await.unwrap().confirmed, 0); assert_eq!(payee.get_ldk_balance().available, invoice_amount); assert_eq!(payee.get_ldk_balance().pending_close, 0); @@ -74,7 +74,7 @@ async fn ln_collab_close() { // block tokio::time::sleep(Duration::from_secs(5)).await; - assert_eq!(payee.get_on_chain_balance().unwrap().confirmed, 0); + assert_eq!(payee.get_on_chain_balance().await.unwrap().confirmed, 0); // Mine one block to confirm the close transaction bitcoind::mine(1).await.unwrap(); @@ -87,12 +87,12 @@ async fn ln_collab_close() { assert_eq!(ln_balance.pending_close, 0); assert_eq!( - payee.get_on_chain_balance().unwrap().confirmed, + payee.get_on_chain_balance().await.unwrap().confirmed, invoice_amount ); } -#[tokio::test] +#[tokio::test(flavor = "multi_thread")] #[ignore] async fn ln_force_close() { init_tracing(); @@ -133,7 +133,7 @@ async fn ln_force_close() { .await .unwrap(); - assert_eq!(payee.get_on_chain_balance().unwrap().confirmed, 0); + assert_eq!(payee.get_on_chain_balance().await.unwrap().confirmed, 0); assert_eq!(payee.get_ldk_balance().available, invoice_amount); assert_eq!(payee.get_ldk_balance().pending_close, 0); @@ -152,7 +152,7 @@ async fn ln_force_close() { payee.wallet().sync().await.unwrap(); - assert_eq!(payee.get_on_chain_balance().unwrap().confirmed, 0); + assert_eq!(payee.get_on_chain_balance().await.unwrap().confirmed, 0); assert_eq!(payee.get_ldk_balance().available, 0); assert_eq!(payee.get_ldk_balance().pending_close, invoice_amount); diff --git a/crates/ln-dlc-node/src/tests/just_in_time_channel/create.rs b/crates/ln-dlc-node/src/tests/just_in_time_channel/create.rs index 1f783c1a5..2d893244b 100644 --- a/crates/ln-dlc-node/src/tests/just_in_time_channel/create.rs +++ b/crates/ln-dlc-node/src/tests/just_in_time_channel/create.rs @@ -17,7 +17,7 @@ use std::ops::Div; use std::ops::Mul; use std::time::Duration; -#[tokio::test] +#[tokio::test(flavor = "multi_thread")] #[ignore] async fn open_jit_channel() { init_tracing(); @@ -59,7 +59,7 @@ async fn open_jit_channel() { .unwrap(); } -#[tokio::test] +#[tokio::test(flavor = "multi_thread")] #[ignore] async fn fail_to_open_jit_channel_with_fee_rate_over_max() { init_tracing(); @@ -128,7 +128,7 @@ async fn fail_to_open_jit_channel_with_fee_rate_over_max() { .expect_err("payment should not succeed"); } -#[tokio::test] +#[tokio::test(flavor = "multi_thread")] #[ignore] async fn open_jit_channel_with_disconnected_payee() { init_tracing(); diff --git a/crates/ln-dlc-node/src/tests/just_in_time_channel/multiple_payments.rs b/crates/ln-dlc-node/src/tests/just_in_time_channel/multiple_payments.rs index d7e8e74a5..410dddd08 100644 --- a/crates/ln-dlc-node/src/tests/just_in_time_channel/multiple_payments.rs +++ b/crates/ln-dlc-node/src/tests/just_in_time_channel/multiple_payments.rs @@ -5,7 +5,7 @@ use crate::tests::just_in_time_channel::create::send_interceptable_payment; use crate::tests::min_outbound_liquidity_channel_creator; use bitcoin::Amount; -#[tokio::test] +#[tokio::test(flavor = "multi_thread")] #[ignore] async fn just_in_time_channel_with_multiple_payments() { init_tracing(); diff --git a/crates/ln-dlc-node/src/tests/mod.rs b/crates/ln-dlc-node/src/tests/mod.rs index b23bc7413..23b758e2c 100644 --- a/crates/ln-dlc-node/src/tests/mod.rs +++ b/crates/ln-dlc-node/src/tests/mod.rs @@ -107,7 +107,7 @@ impl Node { let starting_balance = self.get_confirmed_balance().await?; let expected_balance = starting_balance + amount.to_sat(); - let address = self.wallet.get_last_unused_address()?; + let address = self.wallet.get_last_unused_address().await?; fund_and_mine(address, amount).await?; @@ -127,7 +127,7 @@ impl Node { } async fn get_confirmed_balance(&self) -> Result { - let balance = self.wallet.inner().get_balance()?; + let balance = self.wallet.inner().get_balance().await?; Ok(balance.confirmed) } diff --git a/crates/ln-dlc-node/src/tests/multi_hop_payment.rs b/crates/ln-dlc-node/src/tests/multi_hop_payment.rs index 691edb952..5aa82a163 100644 --- a/crates/ln-dlc-node/src/tests/multi_hop_payment.rs +++ b/crates/ln-dlc-node/src/tests/multi_hop_payment.rs @@ -3,7 +3,7 @@ use crate::tests::init_tracing; use crate::tests::min_outbound_liquidity_channel_creator; use bitcoin::Amount; -#[tokio::test] +#[tokio::test(flavor = "multi_thread")] #[ignore] async fn multi_hop_payment() { init_tracing(); diff --git a/crates/ln-dlc-node/src/tests/onboard_from_lnd.rs b/crates/ln-dlc-node/src/tests/onboard_from_lnd.rs index 939b62f5a..6be599c41 100644 --- a/crates/ln-dlc-node/src/tests/onboard_from_lnd.rs +++ b/crates/ln-dlc-node/src/tests/onboard_from_lnd.rs @@ -5,7 +5,7 @@ use crate::tests::log_channel_id; use bitcoin::Amount; use std::time::Duration; -#[tokio::test] +#[tokio::test(flavor = "multi_thread")] #[ignore] async fn onboard_from_lnd() { init_tracing(); diff --git a/crates/ln-dlc-node/src/tests/single_hop_payment.rs b/crates/ln-dlc-node/src/tests/single_hop_payment.rs index 29c3b3b30..5a2a8c4ab 100644 --- a/crates/ln-dlc-node/src/tests/single_hop_payment.rs +++ b/crates/ln-dlc-node/src/tests/single_hop_payment.rs @@ -2,7 +2,7 @@ use crate::node::Node; use crate::tests::init_tracing; use bitcoin::Amount; -#[tokio::test] +#[tokio::test(flavor = "multi_thread")] #[ignore] async fn single_hop_payment() { init_tracing(); diff --git a/maker/src/routes.rs b/maker/src/routes.rs index 0ad80db62..248935c95 100644 --- a/maker/src/routes.rs +++ b/maker/src/routes.rs @@ -65,15 +65,16 @@ pub struct Invoice { } pub async fn index(State(app_state): State>) -> Result, AppError> { - let address = app_state - .node - .get_new_address() - .map_err(|e| AppError::InternalServerError(format!("Failed to get new address: {e:#}")))?; + let address = + app_state.node.get_new_address().await.map_err(|e| { + AppError::InternalServerError(format!("Failed to get new address: {e:#}")) + })?; let offchain = app_state.node.get_ldk_balance(); let onchain = app_state .node .get_on_chain_balance() + .await .map_err(|e| AppError::InternalServerError(format!("Failed to get balance: {e:#}")))?; let amount = 2000; @@ -98,10 +99,10 @@ pub async fn index(State(app_state): State>) -> Result pub async fn get_new_address( State(app_state): State>, ) -> Result, AppError> { - let address = app_state - .node - .get_new_address() - .map_err(|e| AppError::InternalServerError(format!("Failed to get new address: {e:#}")))?; + let address = + app_state.node.get_new_address().await.map_err(|e| { + AppError::InternalServerError(format!("Failed to get new address: {e:#}")) + })?; Ok(Json(address.to_string())) } @@ -116,6 +117,7 @@ pub async fn get_balance(State(state): State>) -> Result Result<()> { orderbook::subscribe(ln_dlc::get_node_key(), runtime) } -pub fn get_new_address() -> Result> { - ln_dlc::get_new_address().map(SyncReturn) +#[tokio::main(flavor = "current_thread")] +pub async fn get_new_address() -> Result> { + ln_dlc::get_new_address().await.map(SyncReturn) } pub fn close_channel() -> Result<()> { diff --git a/mobile/native/src/ln_dlc/mod.rs b/mobile/native/src/ln_dlc/mod.rs index ae14f5ff3..e08d9df00 100644 --- a/mobile/native/src/ln_dlc/mod.rs +++ b/mobile/native/src/ln_dlc/mod.rs @@ -23,7 +23,6 @@ use coordinator_commons::TradeParams; use itertools::chain; use itertools::Itertools; use lightning_invoice::Invoice; -use ln_dlc_node::node::LnDlcNodeSettings; use ln_dlc_node::node::NodeInfo; use ln_dlc_node::seed::Bip39Seed; use orderbook_commons::FakeScidResponse; @@ -70,11 +69,6 @@ pub fn get_node_info() -> NodeInfo { NODE.get().inner.info } -pub async fn update_node_settings(settings: LnDlcNodeSettings) { - let node = NODE.get(); - node.inner.update_settings(settings).await; -} - // TODO: should we also wrap the oracle as `NodeInfo`. It would fit the required attributes pubkey // and address. pub fn get_oracle_pubkey() -> XOnlyPublicKey { @@ -199,6 +193,7 @@ pub fn run(data_dir: String, seed_dir: String, runtime: &Runtime) -> Result<()> async fn keep_wallet_balance_and_history_up_to_date(node: &Node) -> Result<()> { let wallet_balances = node .get_wallet_balances() + .await .context("Failed to get wallet balances")?; let WalletHistories { @@ -355,8 +350,8 @@ async fn keep_wallet_balance_and_history_up_to_date(node: &Node) -> Result<()> { Ok(()) } -pub fn get_new_address() -> Result { - let address = NODE.get().inner.get_new_address()?; +pub async fn get_new_address() -> Result { + let address = NODE.get().inner.get_new_address().await?; Ok(address.to_string()) } diff --git a/mobile/native/src/ln_dlc/node.rs b/mobile/native/src/ln_dlc/node.rs index 9c9c77f6d..02b989805 100644 --- a/mobile/native/src/ln_dlc/node.rs +++ b/mobile/native/src/ln_dlc/node.rs @@ -56,8 +56,8 @@ impl Node { self.inner.get_seed_phrase() } - pub fn get_wallet_balances(&self) -> Result { - let on_chain = self.inner.get_on_chain_balance()?.confirmed; + pub async fn get_wallet_balances(&self) -> Result { + let on_chain = self.inner.get_on_chain_balance().await?.confirmed; let off_chain = self.inner.get_ldk_balance().available; Ok(Balances {