Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ ignore:
- "packages/rs-dpp/src/data_contract/extra/drive_api_tests.rs"
# Versioned dispatch methods — pure version routing with no logic
- "packages/rs-dpp/src/data_contract/document_type/methods/versioned_methods.rs"

coverage:
status:
project:
Expand Down
2 changes: 1 addition & 1 deletion .serena/project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ read_only: false
# This extends the existing exclusions (e.g. from the global configuration)
#
# Below is the complete list of tools for convenience.
# To make sure you have the latest list of tools, and to view their descriptions,
# To make sure you have the latest list of tools, and to view their descriptions,
# execute `uv run scripts/print_tool_overview.py`.
#
# * `activate_project`: Activates a project by name.
Expand Down
50 changes: 28 additions & 22 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@ members = [
]

[workspace.dependencies]
dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "cfb01fa751951b628a7272558e93278348b2cd1f" }
dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "cfb01fa751951b628a7272558e93278348b2cd1f" }
dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "cfb01fa751951b628a7272558e93278348b2cd1f" }
key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "cfb01fa751951b628a7272558e93278348b2cd1f" }
key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "cfb01fa751951b628a7272558e93278348b2cd1f" }
key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "cfb01fa751951b628a7272558e93278348b2cd1f" }
dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "cfb01fa751951b628a7272558e93278348b2cd1f" }
dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "cfb01fa751951b628a7272558e93278348b2cd1f" }
dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "f569e7b7b99dfe589c41f9ba7d36fbbe6805acdc" }
dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "f569e7b7b99dfe589c41f9ba7d36fbbe6805acdc" }
dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "f569e7b7b99dfe589c41f9ba7d36fbbe6805acdc" }
key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "f569e7b7b99dfe589c41f9ba7d36fbbe6805acdc" }
key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "f569e7b7b99dfe589c41f9ba7d36fbbe6805acdc" }
key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "f569e7b7b99dfe589c41f9ba7d36fbbe6805acdc" }
dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "f569e7b7b99dfe589c41f9ba7d36fbbe6805acdc" }
dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "f569e7b7b99dfe589c41f9ba7d36fbbe6805acdc" }

# Optimize heavy crypto crates even in dev/test builds so that
# Halo 2 proof generation and verification run at near-release speed.
Expand Down
86 changes: 17 additions & 69 deletions packages/rs-platform-wallet/src/spv/runtime.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
//! SPV client runtime — manages the DashSpvClient lifecycle.

use std::sync::{Arc, Mutex as StdMutex};
use std::sync::Arc;

use tokio::sync::RwLock;

use dashcore::sml::llmq_type::LLMQType;
use dashcore::{QuorumHash, Transaction};
use tokio_util::sync::CancellationToken;

use dash_spv::network::PeerNetworkManager;
use dash_spv::storage::DiskStorageManager;
Expand All @@ -30,10 +29,6 @@ pub struct SpvRuntime {
event_manager: Arc<PlatformEventManager>,
wallet_manager: Arc<RwLock<WalletManager<PlatformWalletInfo>>>,
client: RwLock<Option<SpvClient>>,
/// Cancel token for the `run()` task when it was spawned via
/// [`spawn_in_background`]. [`stop`] fires this token and joins
/// on the client shutdown.
background_cancel: StdMutex<Option<CancellationToken>>,
}
// TODO: We want it better
impl SpvRuntime {
Expand All @@ -46,7 +41,6 @@ impl SpvRuntime {
event_manager,
wallet_manager,
client: RwLock::new(None),
background_cancel: StdMutex::new(None),
}
}

Expand Down Expand Up @@ -109,6 +103,9 @@ impl SpvRuntime {
.await
.map_err(|e| PlatformWalletError::SpvError(e.to_string()))?;

let mut client = self.client.write().await;
let _ = client.take();

Ok(())
}

Expand All @@ -135,63 +132,25 @@ impl SpvRuntime {
Ok(*quorum.quorum_entry.quorum_public_key.as_ref())
}

/// Run the SPV sync loop until the cancellation token fires.
pub async fn run(
&self,
config: ClientConfig,
cancel_token: CancellationToken,
) -> Result<(), PlatformWalletError> {
/// Run the SPV sync loop until calling [`stop`]. This blocks the current thread.
pub async fn run(&self, config: ClientConfig) -> Result<(), PlatformWalletError> {
tracing::info!("SpvRuntime::run() starting client...");
self.start(config).await?;
tracing::info!("SpvRuntime::run() client started, entering sync loop");

let result = {
let client_guard = self.client.read().await;
let client = client_guard
.as_ref()
.ok_or(PlatformWalletError::SpvNotRunning)?;

let run_cancel = CancellationToken::new();
let run_future = client.run(run_cancel.clone());
tokio::pin!(run_future);

tokio::select! {
res = &mut run_future => {
tracing::info!("SpvRuntime::run() client.run() completed: {:?}", res.is_ok());
res.map_err(|e| PlatformWalletError::SpvError(e.to_string()))
}
_ = cancel_token.cancelled() => {
tracing::info!("SpvRuntime::run() cancel_token fired, cancelling client");
run_cancel.cancel();
Ok(())
}
}
};

tracing::info!(
"SpvRuntime::run() exiting sync loop, result ok={}",
result.is_ok()
);
if let Err(e) = self.stop().await {
tracing::warn!("SPV stop error during cleanup: {}", e);
}
tracing::info!("SpvRuntime::run() done");
result
let client_guard = self.client.read().await;
let client = client_guard
.as_ref()
.ok_or(PlatformWalletError::SpvNotRunning)?;

client
.run()
.await
.map_err(|e| PlatformWalletError::SpvError(e.to_string()))
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

/// Stop SPV sync gracefully.
///
/// If a `run()` task was spawned via [`spawn_in_background`], its
/// cancel token is fired here too so the background task exits.
pub async fn stop(&self) -> Result<(), PlatformWalletError> {
if let Some(token) = self
.background_cancel
.lock()
.expect("background_cancel poisoned")
.take()
{
token.cancel();
}
let mut client = self.client.write().await;
if let Some(c) = client.take() {
c.stop()
Expand All @@ -203,22 +162,11 @@ impl SpvRuntime {

/// Spawn `run()` on the current tokio runtime and return immediately.
///
/// The returned cancel token is stashed internally; calling [`stop`]
/// fires it and awaits client shutdown. Replacing an already-running
/// background task cancels the previous one first.
/// Call [`stop`] to stop it
pub fn spawn_in_background(self: &Arc<Self>, config: ClientConfig) {
// Cancel any previous run.
let mut guard = self.background_cancel.lock().expect("bg_cancel poisoned");
if let Some(prev) = guard.take() {
prev.cancel();
}
let cancel = CancellationToken::new();
*guard = Some(cancel.clone());
drop(guard);

let this = Arc::clone(self);
tokio::spawn(async move {
if let Err(e) = this.run(config, cancel).await {
if let Err(e) = this.run(config).await {
tracing::warn!("SpvRuntime background run exited with error: {}", e);
}
});
Expand Down
9 changes: 3 additions & 6 deletions packages/rs-platform-wallet/tests/spv_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ use platform_wallet::changeset::{
use platform_wallet::events::{EventHandler, PlatformEventHandler};
use platform_wallet::wallet::platform_wallet::WalletId;
use platform_wallet::PlatformWalletManager;
use tokio_util::sync::CancellationToken;

/// Recording persister that captures all store calls for test verification.
struct RecordingPersister {
Expand Down Expand Up @@ -220,11 +219,9 @@ async fn test_spv_sync_and_balance() {
}

// --- Start SPV in background ---
let cancel = CancellationToken::new();
let manager_for_spv = Arc::clone(&manager);
let cancel_for_spv = cancel.clone();
let spv_handle = tokio::spawn(async move {
if let Err(e) = manager_for_spv.spv().run(config, cancel_for_spv).await {
if let Err(e) = manager_for_spv.spv().run(config).await {
eprintln!("SPV runtime error: {}", e);
}
});
Expand All @@ -243,7 +240,7 @@ async fn test_spv_sync_and_balance() {

loop {
if start.elapsed() > timeout {
cancel.cancel();
let _ = manager.spv().stop().await;
let _ = spv_handle.await;
panic!("Timeout waiting for wallet balance after {:?}", timeout);
}
Expand All @@ -268,7 +265,7 @@ async fn test_spv_sync_and_balance() {

if confirmed > 0 {
println!("SUCCESS: Wallet has confirmed balance: {} duffs", confirmed);
cancel.cancel();
let _ = manager.spv().stop().await;
let _ = spv_handle.await;

// --- Verify persistence ---
Expand Down
Loading