-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Description
What happened?
WalletNode.add_states_from_peer() stores untrusted CoinStateUpdate data in a per-peer race_cache when fork_height is present, but sync_from_untrusted_close_to_peak() only drains the race_cache of the peer whose new_peak is currently being processed.
Because of this, one synced peer can advance wallet processing while valid wallet updates from another synced peer remain stranded in that other peer’s cache. If the late peer’s new_peak is then processed after the wallet has already moved forward on the close-to-peak sync path, the normal path can return without ever applying the valid cached update from that other peer.
This is a correctness / robustness issue, not a fund-loss report. Recovery exists via wallet restart, peer reconnection, or connection to a new peer. However, in the normal two-synced-peer close-to-peak path there is no automatic recovery, and the effect is broader than stale visibility alone: newly received confirmed coins can remain temporarily unspendable through both internal spend flow and user-facing RPC spend flow until a separate recovery/apply path occurs.
Reproduction
I reproduced this with deterministic tests added to chia/_tests/wallet/test_wallet_node.py:
test_cross_peer_race_cache_misses_wallet_updatetest_cross_peer_race_cache_breaks_spend_pathtest_cross_peer_race_cache_breaks_rpc_spend_path
From the repository root:
cd /home/areksaxyz/chia/chia-blockchain
timeout 120s ./activated.py pytest -n 0 \
chia/_tests/wallet/test_wallet_node.py::test_cross_peer_race_cache_misses_wallet_update \
chia/_tests/wallet/test_wallet_node.py::test_cross_peer_race_cache_breaks_spend_path \
chia/_tests/wallet/test_wallet_node.py::test_cross_peer_race_cache_breaks_rpc_spend_path -q
Observed result:
3 passedWhat the tests show
- test_cross_peer_race_cache_misses_wallet_update
- a valid CoinStateUpdate is cached under peer_a
- later peak processing proceeds via peer_b
- peer_a’s valid state remains stranded in peer_a’s race_cache
- the wallet coin record is still missing
test_cross_peer_race_cache_breaks_spend_path
- before recovery, wallet.select_coins(uint64(1), ...) fails
- before recovery, wallet.generate_signed_transaction([uint64(1)], ...) fails
- after applying the same valid CoinState, spendable balance becomes available and the same spend flow succeeds
test_cross_peer_race_cache_breaks_rpc_spend_path
- before recovery, WalletRpcApi.select_coins() fails
- before recovery, WalletRpcApi.create_signed_transaction() fails
- after applying the same valid CoinState, the same RPC flows succeed
Expected behavior
- valid wallet updates relevant to the processed peak should be applied exactly once regardless of which synced peer supplied the corresponding new_peak
- newly received confirmed funds should become usable through normal spend flows once the wallet has processed the relevant peak
Actual behavior
- valid wallet updates can remain stranded in another synced peer’s race_cache
- the wallet can advance through the relevant peak while the valid state remains unapplied
- internal spend-path and RPC spend-path operations fail until a later recovery or explicit apply path occurs
Relevant references
- helper creating the stranded-update condition: chia/_tests/wallet/test_wallet_node.py:55
- stale-state PoC: chia/_tests/wallet/test_wallet_node.py:734
- internal spend-path PoC: chia/_tests/wallet/test_wallet_node.py:841
- RPC spend-path PoC: chia/_tests/wallet/test_wallet_node.py:882
- per-peer cache creation: chia/wallet/wallet_node.py:206
- untrusted updates stored in peer-local race_cache: chia/wallet/wallet_node.py:877
- CoinStateUpdate processing entrypoint: chia/wallet/wallet_node.py:1011
- only the current peer’s race_cache is drained during close-to-peak sync: chia/wallet/wallet_node.py:1239
This issue is distinct from the public fork-boundary / off-by-one race-cache bug. This reproduction does not rely on fork-boundary behavior. The root cause here is cross-peer race_cache isolation / peak-source mismatch.
Version
What platform are you using?
Linux
What ui mode are you using?
CLI
Relevant log output
timeout 120s ./activated.py pytest -n 0 \
chia/_tests/wallet/test_wallet_node.py::test_cross_peer_race_cache_misses_wallet_update \
chia/_tests/wallet/test_wallet_node.py::test_cross_peer_race_cache_breaks_spend_path \
chia/_tests/wallet/test_wallet_node.py::test_cross_peer_race_cache_breaks_rpc_spend_path -q
==================================================================== test session starts ====================================================================
platform linux -- Python 3.13.11, pytest-8.4.2, pluggy-1.5.0
rootdir: /home/areksaxyz/chia/chia-blockchain
configfile: pytest.ini
plugins: mock-3.15.1, rerunfailures-16.1, anyio-4.12.1, xdist-3.8.0, tach-0.33.4, cov-7.0.0
collected 3 items
chia/_tests/wallet/test_wallet_node.py ... [3/3]
===================================================================== 3 passed in 2.05s =====================================================================