Skip to content

rs-sdk address-sync silently discards platform-returned balance updates for addresses not in the pre-RPC pending_addresses snapshot #3649

Description

@lklimek

Component

packages/rs-sdkdash_sdk::platform::address_sync

Mechanism

sync_address_balances (packages/rs-sdk/src/platform/address_sync/mod.rs) builds a key_to_tag lookup once from a single provider.pending_addresses() snapshot and passes it by immutable reference into the private incremental_catch_up. In both the compacted apply loop and the recent apply loop the predicate if let Some(&(tag, address)) = address_lookup.get(&addr_bytes) had no else branch: any balance change the platform returns for an address absent from the frozen snapshot was dropped with no log, metric, or error. Unlike the tree-scan path, incremental_catch_up never re-polled pending_addresses() (contrast after_branch_iteration, which does refresh during the branch loop).

Repro

Within one sync pass, derive a fresh receive address A after the pass's snapshot is taken (routine under concurrent multi-identity funding), fund A, let it chain-confirm. The recent/compacted balance-changes RPC returns A's balance but the SDK discards it: result.found never gets A, on_address_found is never called, and the wallet's local sync map shows A empty. On single-threaded runs the derive usually precedes the snapshot, hiding the bug — hence "flaky", not "always red", in live e2e.

Impact

Root cause of repeated e2e flakes in the rs-platform-wallet suite: TK-001 / TK-007 / TK-013 / TK-014 / id_005, all funding-gate Found-025 under parallel multi-identity churn.

Fix (implemented)

PR #PLACEHOLDER on v3.1-dev:

  • Extract the two inline apply loops into a pure pub(crate) apply_address_changes seam (no Sdk/network/async) that returns applied updates plus the addresses absent from the snapshot.
  • apply_block_changes re-polls pending_addresses() when an unknown address appears (mirroring the tree-scan refresh) and replays only the previously-unknown subset, so a freshly-derived address is recovered and known-address AddToCredits deltas are never double-counted.
  • Addresses still unknown after the refresh are logged at warn (observable, never silently dropped).
  • Known-address behavior is byte-for-byte identical.
  • Three deterministic #[cfg(test)] regression guards on the pure seam (red on pre-fix logic, green post-fix).

Deterministic-test enablement

The extracted pub(crate) pure seam makes the discard a pure-data assertion (no Sdk/proof) that is pinned in rs-sdk's own test module.

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions