From e06f7f8c9da34ffec4eb8c2a663f0e0421ff76f0 Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Wed, 18 Feb 2026 03:33:00 -0600 Subject: [PATCH 1/4] refactor(context): replace RwLock with ArcSwap Sdk is internally thread-safe (Arc, ArcSwapOption, atomics) and all methods take &self. The RwLock was adding unnecessary contention across backend tasks. Using ArcSwap instead of plain Sdk because reinit_core_client_and_sdk() needs to atomically swap the entire Sdk instance when config changes. ArcSwap provides lock-free reads with atomic swap for the rare write. Suggested-by: lklimek --- Cargo.lock | 1 + Cargo.toml | 1 + .../identity/discover_identities.rs | 2 +- src/backend_task/identity/load_identity.rs | 5 +-- .../identity/load_identity_from_wallet.rs | 5 +-- .../refresh_loaded_identities_dpns_names.rs | 5 +-- .../identity/register_dpns_name.rs | 5 +-- .../identity/register_identity.rs | 10 +---- src/backend_task/identity/top_up_identity.rs | 5 +-- src/backend_task/identity/transfer.rs | 5 +-- .../identity/withdraw_from_identity.rs | 5 +-- src/backend_task/mod.rs | 5 +-- src/backend_task/platform_info.rs | 5 +-- .../wallet/fetch_platform_address_balances.rs | 5 +-- .../fund_platform_address_from_asset_lock.rs | 2 +- ...fund_platform_address_from_wallet_utxos.rs | 2 +- .../wallet/transfer_platform_credits.rs | 2 +- .../wallet/withdraw_from_platform_address.rs | 2 +- src/context/connection_status.rs | 3 +- src/context/mod.rs | 37 ++++--------------- src/context/wallet_lifecycle.rs | 5 +-- 21 files changed, 29 insertions(+), 88 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d1a8f4e41..1f4117fd2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1800,6 +1800,7 @@ version = "1.0.0-dev" dependencies = [ "aes-gcm", "arboard", + "arc-swap", "argon2", "base64 0.22.1", "bincode 2.0.1", diff --git a/Cargo.toml b/Cargo.toml index 8f2313ae1..dfffb323d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,7 @@ humantime = "2.2.0" which = { version = "8.0.0" } tz-rs = { version = "0.7.0" } tempfile = "3.20.0" +arc-swap = "1" [target.'cfg(not(target_os = "windows"))'.dependencies] zmq = "0.10.0" diff --git a/src/backend_task/identity/discover_identities.rs b/src/backend_task/identity/discover_identities.rs index 9d77af0eb..c16b61e09 100644 --- a/src/backend_task/identity/discover_identities.rs +++ b/src/backend_task/identity/discover_identities.rs @@ -18,7 +18,7 @@ impl AppContext { const AUTH_KEY_LOOKUP_WINDOW: u32 = 12; - let sdk = self.sdk.read().map_err(|e| e.to_string())?.clone(); + let sdk = self.sdk.load().as_ref().clone(); let seed_hash = wallet.read().map_err(|e| e.to_string())?.seed_hash(); tracing::info!( diff --git a/src/backend_task/identity/load_identity.rs b/src/backend_task/identity/load_identity.rs index c5b332f37..2007d0cbf 100644 --- a/src/backend_task/identity/load_identity.rs +++ b/src/backend_task/identity/load_identity.rs @@ -285,10 +285,7 @@ impl AppContext { start: None, }; - let sdk_guard = { - let guard = self.sdk.read().unwrap(); - guard.clone() - }; + let sdk_guard = self.sdk.load().as_ref().clone(); let maybe_owned_dpns_names = Document::fetch_many(&sdk_guard, dpns_names_document_query) .await diff --git a/src/backend_task/identity/load_identity_from_wallet.rs b/src/backend_task/identity/load_identity_from_wallet.rs index 8ccc3ba4c..a628b9924 100644 --- a/src/backend_task/identity/load_identity_from_wallet.rs +++ b/src/backend_task/identity/load_identity_from_wallet.rs @@ -115,10 +115,7 @@ impl AppContext { start: None, }; - let sdk_guard = { - let guard = self.sdk.read().unwrap(); - guard.clone() - }; + let sdk_guard = self.sdk.load().as_ref().clone(); let maybe_owned_dpns_names = Document::fetch_many(&sdk_guard, dpns_names_document_query) .await diff --git a/src/backend_task/identity/refresh_loaded_identities_dpns_names.rs b/src/backend_task/identity/refresh_loaded_identities_dpns_names.rs index 2f480d085..1b528381f 100644 --- a/src/backend_task/identity/refresh_loaded_identities_dpns_names.rs +++ b/src/backend_task/identity/refresh_loaded_identities_dpns_names.rs @@ -34,10 +34,7 @@ impl AppContext { start: None, }; - let sdk_guard = { - let guard = self.sdk.read().unwrap(); - guard.clone() - }; + let sdk_guard = self.sdk.load().as_ref().clone(); let owned_dpns_names = Document::fetch_many(&sdk_guard, dpns_names_document_query) .await diff --git a/src/backend_task/identity/register_dpns_name.rs b/src/backend_task/identity/register_dpns_name.rs index 1520aef09..2ac42b19a 100644 --- a/src/backend_task/identity/register_dpns_name.rs +++ b/src/backend_task/identity/register_dpns_name.rs @@ -178,10 +178,7 @@ impl AppContext { start: None, }; - let sdk_guard = { - let guard = self.sdk.read().unwrap(); - guard.clone() - }; + let sdk_guard = self.sdk.load().as_ref().clone(); let owned_dpns_names = Document::fetch_many(&sdk_guard, dpns_names_document_query) .await diff --git a/src/backend_task/identity/register_identity.rs b/src/backend_task/identity/register_identity.rs index 47ae6fa5d..8b48db79a 100644 --- a/src/backend_task/identity/register_identity.rs +++ b/src/backend_task/identity/register_identity.rs @@ -36,10 +36,7 @@ impl AppContext { identity_funding_method, } = input; - let sdk = { - let guard = self.sdk.read().unwrap(); - guard.clone() - }; + let sdk = self.sdk.load().as_ref().clone(); let (_, metadata) = ExtendedEpochInfo::fetch_with_metadata(&sdk, 0, None) .await @@ -646,10 +643,7 @@ impl AppContext { ) -> Result { use dash_sdk::platform::transition::put_identity::PutIdentity; - let sdk = { - let guard = self.sdk.read().unwrap(); - guard.clone() - }; + let sdk = self.sdk.load().as_ref().clone(); let public_keys = keys.to_public_keys_map(); diff --git a/src/backend_task/identity/top_up_identity.rs b/src/backend_task/identity/top_up_identity.rs index 76d115a26..93c89c461 100644 --- a/src/backend_task/identity/top_up_identity.rs +++ b/src/backend_task/identity/top_up_identity.rs @@ -28,10 +28,7 @@ impl AppContext { identity_funding_method, } = input; - let sdk = { - let guard = self.sdk.read().unwrap(); - guard.clone() - }; + let sdk = self.sdk.load().as_ref().clone(); let (_, metadata) = ExtendedEpochInfo::fetch_with_metadata(&sdk, 0, None) .await diff --git a/src/backend_task/identity/transfer.rs b/src/backend_task/identity/transfer.rs index 84c58af0b..be1c93302 100644 --- a/src/backend_task/identity/transfer.rs +++ b/src/backend_task/identity/transfer.rs @@ -18,10 +18,7 @@ impl AppContext { credits: Credits, id: Option, ) -> Result { - let sdk_guard = { - let guard = self.sdk.read().unwrap(); - guard.clone() - }; + let sdk_guard = self.sdk.load().as_ref().clone(); // Track balance before transfer for fee calculation let balance_before = qualified_identity.identity.balance(); diff --git a/src/backend_task/identity/withdraw_from_identity.rs b/src/backend_task/identity/withdraw_from_identity.rs index 1370e3ae5..2fca07bf5 100644 --- a/src/backend_task/identity/withdraw_from_identity.rs +++ b/src/backend_task/identity/withdraw_from_identity.rs @@ -21,10 +21,7 @@ impl AppContext { credits: Credits, id: Option, ) -> Result { - let sdk_guard = { - let guard = self.sdk.read().unwrap(); - guard.clone() - }; + let sdk_guard = self.sdk.load().as_ref().clone(); // First, refresh the identity from Platform to get the latest revision and balance tracing::info!( diff --git a/src/backend_task/mod.rs b/src/backend_task/mod.rs index 7d8413fad..2890e6cc7 100644 --- a/src/backend_task/mod.rs +++ b/src/backend_task/mod.rs @@ -311,10 +311,7 @@ impl AppContext { task: BackendTask, sender: SenderAsync, ) -> Result { - let sdk = { - let guard = self.sdk.read().unwrap(); - guard.clone() - }; + let sdk = self.sdk.load().as_ref().clone(); match task { BackendTask::ContractTask(contract_task) => { self.run_contract_task(*contract_task, &sdk, sender).await diff --git a/src/backend_task/platform_info.rs b/src/backend_task/platform_info.rs index 6cbf246f0..85980b249 100644 --- a/src/backend_task/platform_info.rs +++ b/src/backend_task/platform_info.rs @@ -333,10 +333,7 @@ impl AppContext { &self, request: PlatformInfoTaskRequestType, ) -> Result { - let sdk = { - let sdk_guard = self.sdk.read().unwrap(); - sdk_guard.clone() - }; + let sdk = self.sdk.load().as_ref().clone(); match request { PlatformInfoTaskRequestType::BasicPlatformInfo => { diff --git a/src/backend_task/wallet/fetch_platform_address_balances.rs b/src/backend_task/wallet/fetch_platform_address_balances.rs index d07f7e030..737129fd3 100644 --- a/src/backend_task/wallet/fetch_platform_address_balances.rs +++ b/src/backend_task/wallet/fetch_platform_address_balances.rs @@ -76,10 +76,7 @@ impl AppContext { }; // Sync using SDK's privacy-preserving method - let sdk = { - let guard = self.sdk.read().map_err(|e| e.to_string())?; - guard.clone() - }; + let sdk = self.sdk.load().as_ref().clone(); let (_checkpoint_height, highest_block_processed) = if needs_full_sync { tracing::info!( diff --git a/src/backend_task/wallet/fund_platform_address_from_asset_lock.rs b/src/backend_task/wallet/fund_platform_address_from_asset_lock.rs index 02983677c..f189b7fc2 100644 --- a/src/backend_task/wallet/fund_platform_address_from_asset_lock.rs +++ b/src/backend_task/wallet/fund_platform_address_from_asset_lock.rs @@ -34,7 +34,7 @@ impl AppContext { .ok_or_else(|| "Wallet not found".to_string())? }; let wallet = wallet_arc.read().map_err(|e| e.to_string())?.clone(); - let sdk = self.sdk.read().map_err(|e| e.to_string())?.clone(); + let sdk = self.sdk.load().as_ref().clone(); // Get the private key for the asset lock address let private_key = wallet diff --git a/src/backend_task/wallet/fund_platform_address_from_wallet_utxos.rs b/src/backend_task/wallet/fund_platform_address_from_wallet_utxos.rs index aa67973ab..ee95378e9 100644 --- a/src/backend_task/wallet/fund_platform_address_from_wallet_utxos.rs +++ b/src/backend_task/wallet/fund_platform_address_from_wallet_utxos.rs @@ -203,7 +203,7 @@ impl AppContext { }; let wallet = wallet_arc.read().map_err(|e| e.to_string())?.clone(); - let sdk = self.sdk.read().map_err(|e| e.to_string())?.clone(); + let sdk = self.sdk.load().as_ref().clone(); (wallet, sdk, change_platform_address) }; diff --git a/src/backend_task/wallet/transfer_platform_credits.rs b/src/backend_task/wallet/transfer_platform_credits.rs index 3549af4be..0d580a0dd 100644 --- a/src/backend_task/wallet/transfer_platform_credits.rs +++ b/src/backend_task/wallet/transfer_platform_credits.rs @@ -28,7 +28,7 @@ impl AppContext { .ok_or_else(|| "Wallet not found".to_string())? }; let wallet = wallet_arc.read().map_err(|e| e.to_string())?.clone(); - let sdk = self.sdk.read().map_err(|e| e.to_string())?.clone(); + let sdk = self.sdk.load().as_ref().clone(); (wallet, sdk) }; diff --git a/src/backend_task/wallet/withdraw_from_platform_address.rs b/src/backend_task/wallet/withdraw_from_platform_address.rs index a12b8948f..f717943e4 100644 --- a/src/backend_task/wallet/withdraw_from_platform_address.rs +++ b/src/backend_task/wallet/withdraw_from_platform_address.rs @@ -32,7 +32,7 @@ impl AppContext { .ok_or_else(|| "Wallet not found".to_string())? }; let wallet = wallet_arc.read().map_err(|e| e.to_string())?.clone(); - let sdk = self.sdk.read().map_err(|e| e.to_string())?.clone(); + let sdk = self.sdk.load().as_ref().clone(); (wallet, sdk) }; diff --git a/src/context/connection_status.rs b/src/context/connection_status.rs index 9672804cf..882b631b7 100644 --- a/src/context/connection_status.rs +++ b/src/context/connection_status.rs @@ -330,7 +330,8 @@ impl ConnectionStatus { } // Update DAPI endpoint status - if let Ok(sdk) = app_context.sdk.read() { + { + let sdk = app_context.sdk.load(); let address_list = sdk.address_list(); let total = address_list.len() as u16; // get_live_address() returns Option<&Uri>, so count it as 1 if available, 0 if not diff --git a/src/context/mod.rs b/src/context/mod.rs index 3ccaf835d..a709f6488 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -22,6 +22,7 @@ use crate::spv::{CoreBackendMode, SpvManager}; use crate::utils::tasks::TaskManager; use connection_status::ConnectionStatus; use crossbeam_channel::{Receiver, Sender}; +use arc_swap::ArcSwap; use dash_sdk::Sdk; use dash_sdk::dashcore_rpc::{Auth, Client}; use dash_sdk::dpp::dashcore::{Network, Txid}; @@ -54,7 +55,7 @@ pub struct AppContext { #[allow(dead_code)] // May be used for devnet identification pub(crate) devnet_name: Option, pub(crate) db: Arc, - pub(crate) sdk: RwLock, + pub(crate) sdk: ArcSwap, // Context providers for SDK, so we can switch when backend mode changes spv_context_provider: RwLock, rpc_context_provider: RwLock, @@ -241,7 +242,7 @@ impl AppContext { developer_mode: AtomicBool::new(developer_mode_enabled), devnet_name: None, db, - sdk: sdk.into(), + sdk: ArcSwap::from_pointee(sdk), spv_context_provider: spv_provider.into(), rpc_context_provider: rpc_provider.into(), config: config_lock, @@ -298,13 +299,6 @@ impl AppContext { } } else { // Ensure SDK uses the SPV provider - let sdk_lock = match app_context.sdk.write() { - Ok(lock) => lock, - Err(_) => { - tracing::error!("SDK lock poisoned"); - return None; - } - }; let provider = match app_context.spv_context_provider.read() { Ok(p) => p.clone(), Err(_) => { @@ -312,7 +306,7 @@ impl AppContext { return None; } }; - sdk_lock.set_context_provider(provider); + app_context.sdk.load().set_context_provider(provider); } app_context.bootstrap_loaded_wallets(); @@ -364,13 +358,6 @@ impl AppContext { tracing::error!("Failed to bind SPV provider: {}", e); return; } - let sdk = match self.sdk.write() { - Ok(lock) => lock, - Err(_) => { - tracing::error!("SDK lock poisoned in set_core_backend_mode"); - return; - } - }; let provider = match self.spv_context_provider.read() { Ok(p) => p.clone(), Err(_) => { @@ -378,7 +365,7 @@ impl AppContext { return; } }; - sdk.set_context_provider(provider); + self.sdk.load().set_context_provider(provider); } CoreBackendMode::Rpc => { // RPC provider binding also sets itself on the SDK @@ -503,13 +490,7 @@ impl AppContext { .map_err(|_| "Core client lock poisoned".to_string())?; *client_lock = new_client; } - { - let mut sdk_lock = self - .sdk - .write() - .map_err(|_| "SDK lock poisoned".to_string())?; - *sdk_lock = new_sdk; - } + self.sdk.store(Arc::new(new_sdk)); // Rebind providers to ensure they hold the new AppContext reference self.spv_context_provider @@ -522,16 +503,12 @@ impl AppContext { .map_err(|_| "RPC provider lock poisoned".to_string())? .bind_app_context(self.clone())?; } else { - let sdk_lock = self - .sdk - .write() - .map_err(|_| "SDK lock poisoned".to_string())?; let provider = self .spv_context_provider .read() .map_err(|_| "SPV provider lock poisoned".to_string())? .clone(); - sdk_lock.set_context_provider(provider); + self.sdk.load().set_context_provider(provider); } Ok(()) diff --git a/src/context/wallet_lifecycle.rs b/src/context/wallet_lifecycle.rs index 8d1117bdd..537cb5689 100644 --- a/src/context/wallet_lifecycle.rs +++ b/src/context/wallet_lifecycle.rs @@ -478,10 +478,7 @@ impl AppContext { return Ok(()); } - let sdk = { - let guard = self.sdk.read().map_err(|_| "SDK lock poisoned")?; - guard.clone() - }; + let sdk = self.sdk.load().as_ref().clone(); for txid in pending_txids { match get_transaction_info(&sdk, &txid).await { From 863310d91169656c41abc4aa83f25fd3fa828215 Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Wed, 18 Feb 2026 03:50:08 -0600 Subject: [PATCH 2/4] fix: address CodeRabbit review findings for ArcSwap migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix import ordering: move arc_swap::ArcSwap before crossbeam_channel - Remove redundant SDK loads in load_identity_from_wallet, register_dpns_name, and load_identity — use the sdk parameter already passed to these functions - Fix stale TODO referencing removed sdk.read().unwrap() API - Rename sdk_guard → sdk in transfer, withdraw_from_identity, and refresh_loaded_identities_dpns_names (no longer lock guards) - Pass &sdk to run_platform_info_task from dispatch site instead of reloading internally - Fix leftover sdk.write() call in context_provider.rs (RwLock remnant) - Add missing Color32 import in wallets dialogs Co-Authored-By: Claude Opus 4.6 --- src/backend_task/identity/load_identity.rs | 4 +--- .../identity/load_identity_from_wallet.rs | 4 +--- .../refresh_loaded_identities_dpns_names.rs | 4 ++-- .../identity/register_dpns_name.rs | 6 ++---- src/backend_task/identity/transfer.rs | 4 ++-- .../identity/withdraw_from_identity.rs | 6 +++--- src/backend_task/mod.rs | 2 +- src/backend_task/platform_info.rs | 20 +++++++++---------- src/context/mod.rs | 4 ++-- src/context_provider.rs | 6 +----- src/ui/wallets/wallets_screen/dialogs.rs | 2 +- 11 files changed, 26 insertions(+), 36 deletions(-) diff --git a/src/backend_task/identity/load_identity.rs b/src/backend_task/identity/load_identity.rs index 2007d0cbf..08038cf80 100644 --- a/src/backend_task/identity/load_identity.rs +++ b/src/backend_task/identity/load_identity.rs @@ -285,9 +285,7 @@ impl AppContext { start: None, }; - let sdk_guard = self.sdk.load().as_ref().clone(); - - let maybe_owned_dpns_names = Document::fetch_many(&sdk_guard, dpns_names_document_query) + let maybe_owned_dpns_names = Document::fetch_many(sdk, dpns_names_document_query) .await .map(|document_map| { document_map diff --git a/src/backend_task/identity/load_identity_from_wallet.rs b/src/backend_task/identity/load_identity_from_wallet.rs index a628b9924..8f51fb0a3 100644 --- a/src/backend_task/identity/load_identity_from_wallet.rs +++ b/src/backend_task/identity/load_identity_from_wallet.rs @@ -115,9 +115,7 @@ impl AppContext { start: None, }; - let sdk_guard = self.sdk.load().as_ref().clone(); - - let maybe_owned_dpns_names = Document::fetch_many(&sdk_guard, dpns_names_document_query) + let maybe_owned_dpns_names = Document::fetch_many(sdk, dpns_names_document_query) .await .map(|document_map| { document_map diff --git a/src/backend_task/identity/refresh_loaded_identities_dpns_names.rs b/src/backend_task/identity/refresh_loaded_identities_dpns_names.rs index 1b528381f..61e6ed50b 100644 --- a/src/backend_task/identity/refresh_loaded_identities_dpns_names.rs +++ b/src/backend_task/identity/refresh_loaded_identities_dpns_names.rs @@ -34,9 +34,9 @@ impl AppContext { start: None, }; - let sdk_guard = self.sdk.load().as_ref().clone(); + let sdk = self.sdk.load().as_ref().clone(); - let owned_dpns_names = Document::fetch_many(&sdk_guard, dpns_names_document_query) + let owned_dpns_names = Document::fetch_many(&sdk, dpns_names_document_query) .await .map(|document_map| { document_map diff --git a/src/backend_task/identity/register_dpns_name.rs b/src/backend_task/identity/register_dpns_name.rs index 2ac42b19a..baee1f9c1 100644 --- a/src/backend_task/identity/register_dpns_name.rs +++ b/src/backend_task/identity/register_dpns_name.rs @@ -178,9 +178,7 @@ impl AppContext { start: None, }; - let sdk_guard = self.sdk.load().as_ref().clone(); - - let owned_dpns_names = Document::fetch_many(&sdk_guard, dpns_names_document_query) + let owned_dpns_names = Document::fetch_many(sdk, dpns_names_document_query) .await .map(|document_map| { document_map @@ -219,7 +217,7 @@ impl AppContext { // Calculate actual fee paid // Note: We need to re-fetch the identity to get the updated balance let refreshed_identity = dash_sdk::platform::Identity::fetch_by_identifier( - &sdk_guard, + sdk, qualified_identity.identity.id(), ) .await diff --git a/src/backend_task/identity/transfer.rs b/src/backend_task/identity/transfer.rs index be1c93302..78e39b9d4 100644 --- a/src/backend_task/identity/transfer.rs +++ b/src/backend_task/identity/transfer.rs @@ -18,7 +18,7 @@ impl AppContext { credits: Credits, id: Option, ) -> Result { - let sdk_guard = self.sdk.load().as_ref().clone(); + let sdk = self.sdk.load().as_ref().clone(); // Track balance before transfer for fee calculation let balance_before = qualified_identity.identity.balance(); @@ -28,7 +28,7 @@ impl AppContext { .identity .clone() .transfer_credits( - &sdk_guard, + &sdk, to_identifier, credits, id.and_then(|key_id| qualified_identity.identity.get_public_key_by_id(key_id)), diff --git a/src/backend_task/identity/withdraw_from_identity.rs b/src/backend_task/identity/withdraw_from_identity.rs index 2fca07bf5..871f4fe77 100644 --- a/src/backend_task/identity/withdraw_from_identity.rs +++ b/src/backend_task/identity/withdraw_from_identity.rs @@ -21,7 +21,7 @@ impl AppContext { credits: Credits, id: Option, ) -> Result { - let sdk_guard = self.sdk.load().as_ref().clone(); + let sdk = self.sdk.load().as_ref().clone(); // First, refresh the identity from Platform to get the latest revision and balance tracing::info!( @@ -31,7 +31,7 @@ impl AppContext { ); let refreshed_identity = - Identity::fetch_by_identifier(&sdk_guard, qualified_identity.identity.id()) + Identity::fetch_by_identifier(&sdk, qualified_identity.identity.id()) .await .map_err(|e| format!("Failed to fetch identity from Platform: {}", e))? .ok_or_else(|| "Identity not found on Platform".to_string())?; @@ -86,7 +86,7 @@ impl AppContext { .identity .clone() .withdraw( - &sdk_guard, + &sdk, to_address, credits, Some(1), diff --git a/src/backend_task/mod.rs b/src/backend_task/mod.rs index 2890e6cc7..656c0d2a9 100644 --- a/src/backend_task/mod.rs +++ b/src/backend_task/mod.rs @@ -342,7 +342,7 @@ impl AppContext { mnlist::run_mnlist_task(self, mnlist_task).await } BackendTask::PlatformInfo(platform_info_task) => { - self.run_platform_info_task(platform_info_task).await + self.run_platform_info_task(platform_info_task, &sdk).await } BackendTask::GroveSTARKTask(grovestark_task) => { grovestark::run_grovestark_task(grovestark_task, &sdk).await diff --git a/src/backend_task/platform_info.rs b/src/backend_task/platform_info.rs index 85980b249..eed11ee2e 100644 --- a/src/backend_task/platform_info.rs +++ b/src/backend_task/platform_info.rs @@ -1,5 +1,6 @@ use crate::backend_task::BackendTaskSuccessResult; use crate::context::AppContext; +use dash_sdk::Sdk; use dash_sdk::dashcore_rpc::RpcApi; use dash_sdk::dpp::block::extended_epoch_info::{v0::ExtendedEpochInfoV0Getters, ExtendedEpochInfo}; use dash_sdk::dpp::core_types::validator_set::v0::ValidatorSetV0Getters; @@ -332,9 +333,8 @@ impl AppContext { pub async fn run_platform_info_task( &self, request: PlatformInfoTaskRequestType, + sdk: &Sdk, ) -> Result { - let sdk = self.sdk.load().as_ref().clone(); - match request { PlatformInfoTaskRequestType::BasicPlatformInfo => { // Get platform version from SDK @@ -362,7 +362,7 @@ impl AppContext { )) } PlatformInfoTaskRequestType::CurrentEpochInfo => { - match ExtendedEpochInfo::fetch_current(&sdk).await { + match ExtendedEpochInfo::fetch_current(sdk).await { Ok(epoch_info) => { // Cache the fee multiplier for UI fee estimation let fee_multiplier = epoch_info.fee_multiplier_permille(); @@ -382,7 +382,7 @@ impl AppContext { } } PlatformInfoTaskRequestType::TotalCreditsOnPlatform => { - match TotalCreditsInPlatform::fetch_current(&sdk).await { + match TotalCreditsInPlatform::fetch_current(sdk).await { Ok(total_credits) => { let dash_amount = total_credits.0 as f64 * 10f64.powf(-11.0); let formatted = format!( @@ -399,7 +399,7 @@ impl AppContext { } } PlatformInfoTaskRequestType::CurrentVersionVotingState => { - match ProtocolVersionVoteCount::fetch_many(&sdk, ()).await { + match ProtocolVersionVoteCount::fetch_many(sdk, ()).await { Ok(votes) => { let votes: ProtocolVersionUpgrades = votes; let votes_info = votes @@ -425,7 +425,7 @@ impl AppContext { } } PlatformInfoTaskRequestType::CurrentValidatorSetInfo => { - match CurrentQuorumsInfo::fetch_unproved(&sdk, NoParamQuery {}).await { + match CurrentQuorumsInfo::fetch_unproved(sdk, NoParamQuery {}).await { Ok(Some(current_quorums_info)) => { let formatted = format_current_quorums_info(¤t_quorums_info); Ok(BackendTaskSuccessResult::PlatformInfo( @@ -458,13 +458,13 @@ impl AppContext { start: None, }; - match Document::fetch_many(&sdk, queued_document_query.clone()).await { + match Document::fetch_many(sdk, queued_document_query.clone()).await { Ok(documents) => { let withdrawal_docs: Vec = documents.values().filter_map(|a| a.clone()).collect(); // Try to get total credits for daily limit calculation - match TotalCreditsInPlatform::fetch_current(&sdk).await { + match TotalCreditsInPlatform::fetch_current(sdk).await { Ok(total_credits) => { let formatted = format_withdrawal_documents_with_daily_limit( &withdrawal_docs, @@ -523,7 +523,7 @@ impl AppContext { start: None, }; - match Document::fetch_many(&sdk, completed_document_query).await { + match Document::fetch_many(sdk, completed_document_query).await { Ok(documents) => { let mut withdrawal_docs: Vec = documents.values().filter_map(|a| a.clone()).collect(); @@ -630,7 +630,7 @@ impl AppContext { // Fetch the address info using FetchMany with BTreeSet let mut addresses = std::collections::BTreeSet::new(); addresses.insert(platform_address); - match AddressInfo::fetch_many(&sdk, addresses).await { + match AddressInfo::fetch_many(sdk, addresses).await { Ok(address_infos) => { // The result is a map of PlatformAddress -> Option let result: Option<&Option> = diff --git a/src/context/mod.rs b/src/context/mod.rs index a709f6488..ffe17476a 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -20,9 +20,9 @@ use crate::model::wallet::{Wallet, WalletSeedHash}; use crate::sdk_wrapper::initialize_sdk; use crate::spv::{CoreBackendMode, SpvManager}; use crate::utils::tasks::TaskManager; +use arc_swap::ArcSwap; use connection_status::ConnectionStatus; use crossbeam_channel::{Receiver, Sender}; -use arc_swap::ArcSwap; use dash_sdk::Sdk; use dash_sdk::dashcore_rpc::{Auth, Client}; use dash_sdk::dpp::dashcore::{Network, Txid}; @@ -517,7 +517,7 @@ impl AppContext { /// Returns the default platform version for the given network. pub(crate) const fn default_platform_version(network: &Network) -> &'static PlatformVersion { - // TODO: Use self.sdk.read().unwrap().version() instead of hardcoding + // TODO: Use self.sdk.load().version() instead of hardcoding match network { Network::Dash => &PLATFORM_V11, Network::Testnet => &PLATFORM_V11, diff --git a/src/context_provider.rs b/src/context_provider.rs index 65e931466..76df9c10b 100644 --- a/src/context_provider.rs +++ b/src/context_provider.rs @@ -70,11 +70,7 @@ impl Provider { ac.replace(cloned); drop(ac); - let sdk = app_context - .sdk - .write() - .map_err(|_| "SDK lock poisoned".to_string())?; - sdk.set_context_provider(self.clone()); + app_context.sdk.load().set_context_provider(self.clone()); Ok(()) } } diff --git a/src/ui/wallets/wallets_screen/dialogs.rs b/src/ui/wallets/wallets_screen/dialogs.rs index ccda62dd7..ac3359a12 100644 --- a/src/ui/wallets/wallets_screen/dialogs.rs +++ b/src/ui/wallets/wallets_screen/dialogs.rs @@ -16,7 +16,7 @@ use dash_sdk::dpp::key_wallet::bip32::DerivationPath; use eframe::egui::{self, ComboBox, Context}; use eframe::epaint::TextureHandle; use egui::load::SizedTexture; -use egui::{Frame, Margin, RichText, TextureOptions}; +use egui::{Color32, Frame, Margin, RichText, TextureOptions}; use std::sync::{Arc, RwLock}; use super::WalletsBalancesScreen; From c792ba47267fa93c2ba5221358d8e202cf345274 Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Wed, 18 Feb 2026 04:09:57 -0600 Subject: [PATCH 3/4] refactor: address remaining CodeRabbit review feedback on ArcSwap migration - Move SDK load outside for loop in refresh_loaded_identities_dpns_names.rs so it's loaded once for the batch instead of on each iteration - Update stale TODO comment in default_platform_version() to reflect that this is a free function with no sdk access Co-Authored-By: Claude Opus 4.6 --- .../identity/refresh_loaded_identities_dpns_names.rs | 4 ++-- src/context/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/backend_task/identity/refresh_loaded_identities_dpns_names.rs b/src/backend_task/identity/refresh_loaded_identities_dpns_names.rs index 61e6ed50b..a017e2541 100644 --- a/src/backend_task/identity/refresh_loaded_identities_dpns_names.rs +++ b/src/backend_task/identity/refresh_loaded_identities_dpns_names.rs @@ -17,6 +17,8 @@ impl AppContext { .load_local_qualified_identities() .map_err(|e| format!("Error refreshing owned DPNS names: Database error: {}", e))?; + let sdk = self.sdk.load().as_ref().clone(); + for mut qualified_identity in qualified_identities { let identity_id = qualified_identity.identity.id(); @@ -34,8 +36,6 @@ impl AppContext { start: None, }; - let sdk = self.sdk.load().as_ref().clone(); - let owned_dpns_names = Document::fetch_many(&sdk, dpns_names_document_query) .await .map(|document_map| { diff --git a/src/context/mod.rs b/src/context/mod.rs index ffe17476a..bb091bb94 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -517,7 +517,7 @@ impl AppContext { /// Returns the default platform version for the given network. pub(crate) const fn default_platform_version(network: &Network) -> &'static PlatformVersion { - // TODO: Use self.sdk.load().version() instead of hardcoding + // TODO: Ideally use sdk.load().version() but this is a free function with no sdk access match network { Network::Dash => &PLATFORM_V11, Network::Testnet => &PLATFORM_V11, From cb1dfcd3cf2058c3a2d6f6ae70f0cf8477e3d3f3 Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Wed, 18 Feb 2026 04:21:01 -0600 Subject: [PATCH 4/4] refactor: consolidate double read-lock on spv_context_provider Clone the SPV provider in a single lock acquisition, then bind app context on the clone instead of locking twice. Co-Authored-By: Claude Opus 4.6 --- src/context/mod.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/context/mod.rs b/src/context/mod.rs index bb091bb94..a10533275 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -348,16 +348,7 @@ impl AppContext { // Switch SDK context provider to match the selected backend match mode { CoreBackendMode::Spv => { - // Make sure SPV provider knows about the app context - if let Err(e) = self - .spv_context_provider - .read() - .map_err(|_| "SPV provider lock poisoned".to_string()) - .and_then(|provider| provider.bind_app_context(Arc::clone(self))) - { - tracing::error!("Failed to bind SPV provider: {}", e); - return; - } + // Clone the SPV provider and bind app context on the clone let provider = match self.spv_context_provider.read() { Ok(p) => p.clone(), Err(_) => { @@ -365,6 +356,10 @@ impl AppContext { return; } }; + if let Err(e) = provider.bind_app_context(Arc::clone(self)) { + tracing::error!("Failed to bind SPV provider: {}", e); + return; + } self.sdk.load().set_context_provider(provider); } CoreBackendMode::Rpc => {