Skip to content

fix(crowdnode): single history scan per restore pass instead of two - #973

Merged
QuantumExplorer merged 1 commit into
developfrom
claude/quirky-jackson-0133b7
Aug 10, 2026
Merged

fix(crowdnode): single history scan per restore pass instead of two#973
QuantumExplorer merged 1 commit into
developfrom
claude/quirky-jackson-0133b7

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Aug 10, 2026

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

App startup on a large wallet logged "restoring CrowdNode state" followed by two identical full-history scans (CrowdNode scan: fetch 5940 rows in 6496ms / 6474ms on a 7k-tx mainnet wallet — up to 38s per scan under concurrent sync load). One restoreState() pass ran the same SwiftData fetch twice: once in tryRestoreSignUp(), and — when no signup was found, i.e. on every wallet that never used CrowdNode — again in getApiAddressConfirmationTx() via getOnlineAccountAddress(). Each fetch materializes every post-2022 row (~1.1ms/row), so the common case paid the multi-second cost twice at every launch sync-done.

What was done?

  • restoreState() now fetches one shared snapshot (fetchObserved(firstSeenAtOrAfter: januaryFirst2022Epoch)) and passes it to both consumers: tryRestoreSignUp(_:) and getOnlineAccountAddress(state:observed:)getApiAddressConfirmationTx(in:).
  • Every restore path now performs exactly one scan (previously one or two); no consumer sees fewer rows than it needs. The existing in-session fruitless-scan memo is unchanged and still suppresses repeat scans.
  • Deliberately no fetchLimit: the fetch is newest-first, so a cap would silently drop old signup transactions and fail to restore real accounts — now documented at the fetch site.
  • Doc-comment fix in FullCrowdNodeSignUpTxSet (the floor constant is applied by restoreState, not tryRestoreSignUp).

Reviewer note: skipping the restore scan outright under the CrowdNode-suspension release posture was considered and rejected for now — the suspension is not implemented on this branch (Explore menu, shortcuts, balance reminder, and interrupted-signup resume all still consume restored CrowdNode state). If/when the hide lands, gating checkCrowdNodeState() behind the same flag can zero this cost entirely.

How Has This Been Tested?

  • Clean dashpay scheme build (arm64 simulator).
  • Smoke on the QA-iPhone16 simulator against a synthetic 7,007-tx mainnet store: pre-fix log shows restoring CrowdNode state followed by two CrowdNode scan lines (12,005ms + 38,585ms in the worst observed launch); with this fix the same launch logs exactly one scan (fetch 5944 rows in 11035ms) followed by CrowdNode: account not found, and the in-session memo still prevents any further scans.
  • Unit-test target is currently broken repo-wide (pre-existing), so no new tests were added.

Breaking Changes

None.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved CrowdNode account and linked-account restoration by using a consistent transaction history snapshot.
    • Reduced redundant transaction-history retrieval during state restoration.
  • Documentation
    • Updated the signup transaction timestamp guidance to reference the current restoration flow.

restoreState() ran the identical full post-2022 history fetch twice
back-to-back on wallets with no CrowdNode account: once in
tryRestoreSignUp(), then again in getApiAddressConfirmationTx() via
getOnlineAccountAddress(). On a 7k-tx wallet each fetch materializes
~5,900 SwiftData rows (~6.5s, observed up to 38s under sync load), so
the restore paid that cost twice at every launch sync-done.

Hoist one shared snapshot into restoreState() and pass it to both
consumers. Every path now performs exactly one scan (previously one or
two); no consumer sees fewer rows than before. No fetchLimit on
purpose: the fetch is newest-first, so a cap would silently drop old
signup transactions and fail to restore real accounts.

Verified on the QA-iPhone16 simulator against a synthetic 7,007-tx
mainnet store: "restoring CrowdNode state" is now followed by exactly
one "CrowdNode scan" line (fetch 5944 rows) and the account-not-found
memo still suppresses repeat scans in-session.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c53cb12c-df32-47d9-b688-15198c1e177c

📥 Commits

Reviewing files that changed from the base of the PR and between 3888fc2 and 1f594cf.

📒 Files selected for processing (2)
  • DashWallet/Sources/Models/CrowdNode/CrowdNode.swift
  • DashWallet/Sources/Models/CrowdNode/TxFilters/FullCrowdNodeSignUpTxSet.swift

📝 Walkthrough

Walkthrough

CrowdNode.restoreState() now fetches post-2022 observed transactions once. It passes the snapshot through signup and linked-account restoration. Internal duplicate history scans were removed, and the related comment now references restoreState.

Changes

CrowdNode restoration

Layer / File(s) Summary
Shared restoration transaction snapshot
DashWallet/Sources/Models/CrowdNode/CrowdNode.swift, DashWallet/Sources/Models/CrowdNode/TxFilters/FullCrowdNodeSignUpTxSet.swift
restoreState() creates one post-2022 transaction snapshot and passes it to signup and linked-account recovery. The recovery methods use the supplied collection instead of fetching history again. The timestamp-boundary comment now identifies restoreState() as the fetch consumer.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers: llbartekll, jeanpierreroma

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/quirky-jackson-0133b7

Comment @coderabbitai help to get the list of available commands.

@QuantumExplorer
QuantumExplorer merged commit 19c48d5 into develop Aug 10, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant