Skip to content
Open
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
44 changes: 44 additions & 0 deletions dash-spv/tests/dashd_sync/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,50 @@ pub(super) async fn get_spendable_balance(
wallet_read.get_wallet_balance(wallet_id).expect("Failed to get wallet balance").spendable()
}

/// Wait for a specific wallet's `synced_height` to reach `target`. Used to
/// wait for the per-wallet catch-up rescan rather than the manager-wide
/// progress channel, which only reflects the aggregate.
///
/// Subscribes before the upfront height check so an advance racing the
/// subscription is still observed via the event stream.
pub(super) async fn wait_for_wallet_synced(
wallet: &Arc<RwLock<WalletManager<ManagedWalletInfo>>>,
wallet_id: &WalletId,
target: u32,
) {
let (mut events, mut synced) = {
let reader = wallet.read().await;
let events = reader.subscribe_events();
let synced = reader.get_wallet_info(wallet_id).expect("wallet info").synced_height();
(events, synced)
};
if synced >= target {
return;
}
let deadline = tokio::time::Instant::now() + Duration::from_secs(30);
loop {
let recv = tokio::time::timeout_at(deadline, events.recv()).await;
match recv {
Err(_) => {
panic!("wallet did not reach synced_height {} within 30s, got {}", target, synced)
}
Ok(Err(_)) => {
panic!("wallet event channel error before reaching synced_height {}", target);
}
Ok(Ok(WalletEvent::SyncHeightAdvanced {
wallet_id: id,
height,
})) if id == *wallet_id => {
synced = height;
if synced >= target {
return;
}
}
Ok(Ok(_)) => {}
}
}
}

/// Returns true for sync events that represent meaningful forward progress.
///
/// Used by restart and disconnection tests to decide when to interrupt.
Expand Down
1 change: 1 addition & 0 deletions dash-spv/tests/dashd_sync/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ mod setup;
mod tests_basic;
mod tests_disconnect;
mod tests_mempool;
mod tests_multi_wallet;
mod tests_restart;
mod tests_transaction;
67 changes: 1 addition & 66 deletions dash-spv/tests/dashd_sync/tests_basic.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo;
use key_wallet_manager::WalletManager;
use std::sync::Arc;
use tokio::sync::RwLock;

use dash_spv::sync::ProgressPercentage;
use dash_spv::Network;

use super::helpers::{count_wallet_transactions, get_spendable_balance, wait_for_sync};
use super::setup::{
create_and_start_client, create_test_wallet, test_account_options, TestContext,
};
use super::setup::{create_and_start_client, create_test_wallet, TestContext};
use dash_spv::test_utils::TestChain;

#[tokio::test]
Expand Down Expand Up @@ -67,63 +62,3 @@ async fn test_sync_empty_wallet() {
balance
);
}

/// Verify two wallets in one WalletManager sync independently without cross-contamination.
///
/// Creates a manager with the test mnemonic wallet (has transactions) and the "abandon"
/// wallet (no regtest activity). After sync, the test wallet should have all expected
/// transactions while the abandon wallet remains empty.
#[tokio::test]
async fn test_sync_two_wallets_same_client() {
let Some(ctx) = TestContext::new(TestChain::Full).await else {
return;
};

let empty_mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";

// Create a WalletManager with two wallets
let mut wallet_manager = WalletManager::<ManagedWalletInfo>::new(Network::Regtest);
let test_wallet_id = wallet_manager
.create_wallet_from_mnemonic(&ctx.dashd.wallet.mnemonic, "", 0, test_account_options())
.expect("Failed to create test wallet");

let empty_wallet_id = wallet_manager
.create_wallet_from_mnemonic(empty_mnemonic, "", 0, test_account_options())
.expect("Failed to create empty wallet");

assert_eq!(wallet_manager.wallet_count(), 2, "Should have two wallets");
let multi_wallet = Arc::new(RwLock::new(wallet_manager));

// Sync
tracing::info!("Starting sync with two wallets");
let mut client_handle =
create_and_start_client(&ctx.client_config, Arc::clone(&multi_wallet)).await;
wait_for_sync(&mut client_handle.progress_receiver, ctx.dashd.initial_height).await;

client_handle.stop().await;

// Verify the test wallet has expected transactions and balance
ctx.assert_wallet_synced(
&client_handle.client.progress().await,
&multi_wallet,
&test_wallet_id,
)
.await;

// Verify the empty wallet has zero transactions and zero balance
let empty_tx_count = count_wallet_transactions(&multi_wallet, &empty_wallet_id).await;
let empty_balance = get_spendable_balance(&multi_wallet, &empty_wallet_id).await;

assert_eq!(
empty_tx_count, 0,
"Empty wallet should have 0 transactions, got {}",
empty_tx_count
);
assert_eq!(empty_balance, 0, "Empty wallet should have 0 balance, got {}", empty_balance);

tracing::info!(
"Multi-wallet sync passed: empty_wallet(txs={}, balance={})",
empty_tx_count,
empty_balance
);
}
Loading
Loading