Skip to content

SPV broadcast_transaction does not notify local wallet manager — balance stale until block #487

@lklimek

Description

@lklimek

Summary

When DashSpvClient broadcasts a transaction via PeerNetworkManager::broadcast(), it does not call WalletManager::process_mempool_transaction() on its own wallet. As a result, the local wallet is unaware of the outgoing transaction until the transaction is confirmed in a mined block.

Steps to Reproduce

  1. Use dash-spv in SPV mode with a WalletManager attached.
  2. Broadcast a core-to-core transaction via DashSpvClient.
  3. Observe wallet balance and UTXO set immediately after the broadcast.

Expected: Wallet balance reflects the sent amount (with spent UTXOs marked as pending) immediately after broadcast.

Actual: Wallet balance is unchanged. UTXOs are not marked as spent. No WalletEvent::TransactionReceived or WalletEvent::BalanceUpdated is emitted. The wallet only updates after the transaction is included in a block (~2.5 min on mainnet).

Root Cause

In dash-spv/src/client/lifecycle.rs (and the broadcast handler in the sync coordinator), after a successful nm.broadcast(NetworkMessage::Tx(tx)) call, there is no follow-up call to notify the local wallet about the outgoing transaction.

The peer that received the broadcast will not relay the transaction back to the originating node (self-relay prevention in Bitcoin/Dash P2P), so the wallet cannot discover the transaction through normal mempool tracking. Other peers may eventually relay it back, but with unpredictable and often significant delay.

All Required Infrastructure Already Exists

All the necessary pieces are already implemented — the only missing step is wiring them together after a successful broadcast:

  • WalletInterface::process_mempool_transaction() — exists and is implemented in WalletManager
  • TransactionContext::Mempool variant — exists
  • WalletEvent::TransactionReceived and WalletEvent::BalanceUpdated — exist
  • Mempool tracking infrastructure (MempoolStrategy::FetchAll, store_mempool_transaction) — fully built
  • The broadcast handler has access to the wallet

Proposed Fix

After a successful nm.broadcast(NetworkMessage::Tx(tx)) in the broadcast handler, add:

// Notify the local wallet about the outgoing transaction
wallet.process_mempool_transaction(&tx);

This will:

  1. Mark the spent UTXOs as pending.
  2. Update the wallet balance immediately.
  3. Emit WalletEvent::TransactionReceived and WalletEvent::BalanceUpdated so consumers can react immediately.

Impact

Any application using dash-spv for SPV wallet functionality combined with transaction broadcasting is affected. After sending a transaction, users see a stale balance until the next block is mined. On mainnet this is approximately 2.5 minutes; on testnet it can be significantly longer.

This issue was observed in Dash Evo Tool where core-to-core SPV transactions do not reflect in the wallet UI until block confirmation.

Severity

Medium — the funds are not lost and the transaction is broadcast correctly, but the UX regression is significant: users cannot confirm their send completed from the wallet UI alone, and may attempt to re-send.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions