Skip to content

fix(syncing): only gate sends during initial restore - #998

Merged
QuantumExplorer merged 1 commit into
developfrom
codex/initial-restore-send-gate
Aug 12, 2026
Merged

fix(syncing): only gate sends during initial restore#998
QuantumExplorer merged 1 commit into
developfrom
codex/initial-restore-send-gate

Conversation

@llbartekll

@llbartekll llbartekll commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

  • allow normal Core payments during ordinary foreground sync/catch-up
  • block Core-funded routes only while the existing restored-wallet resync marker is active and chain sync is incomplete
  • keep the execution backstop before authentication/input selection and update the UI note
  • add focused policy coverage for restored-wallet sync and normal catch-up

Validation

  • git diff --check passed
  • focused tests and app builds were not run; local SDK rebuild was stopped at the requester’s direction so they can build locally
  • local platform v4.2-dev contains merged platform #4381 and rust-dashcore revision 36b49cb7f9c07ce8f2ed63f965489c50968d19f7

Out of scope

Summary by CodeRabbit

  • Improvements

    • Sending funds is now blocked only during a wallet’s initial restore synchronization.
    • Payments remain available during normal foreground catch-up after the chain has synced.
    • This behavior applies to standard sends, selected-input sends, swap deposits, contact payments, and internal transfers.
    • Updated messaging clarifies when sending is temporarily unavailable.
  • Tests

    • Added coverage for initial restore blocking and normal catch-up spending availability.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR replaces the general chain-sync send block with an initial-restore synchronization gate. Core-funded sends and payment routes use the shared policy. Tests cover initial restore, completed sync, and ordinary catch-up states.

Changes

Initial restore send gating

Layer / File(s) Summary
Shared send gating policy
DashWallet/Sources/Models/Transactions/WalletSendService.swift
WalletSendService blocks sends during incomplete initial restore synchronization. Standard sends, selected-input sends, swap deposits, and DashPay payments use .initialRestoreSync.
Payment route integration
DashWallet/Sources/UI/Payments/Pay/SendViewModel.swift, DashWallet/Sources/UI/Payments/InternalTransfer/InternalTransferViewModel.swift
Core-funded routes use WalletSendService.isBlockedByInitialRestoreSync. Non-Core routes remain unblocked by this check.
Restore gate messaging and tests
DashWallet/Sources/UI/Payments/Pay/SendScreen.swift, DashWalletTests/PassiveWalletStateUITailTests.swift
The sync-gate text describes restored-wallet initial synchronization. Tests cover blocked initial restore, completed synchronization, and non-initial catch-up.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PaymentRoute
  participant WalletSendService
  participant WalletState
  PaymentRoute->>WalletSendService: check initial restore sync
  WalletSendService->>WalletState: read resync and chain sync state
  WalletState-->>WalletSendService: return current state
  WalletSendService-->>PaymentRoute: block or allow Core-funded spending
Loading

Possibly related PRs

  • dashpay/dashwallet-ios#995: Refines the same initial-restore Core-spending gates in the wallet send service and payment view models.

Suggested reviewers: jeanpierreroma, quantumexplorer

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: sends are gated only during the wallet's initial restore synchronization.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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 codex/initial-restore-send-gate

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@DashWallet/Sources/UI/Payments/InternalTransfer/InternalTransferViewModel.swift`:
- Around line 624-630: The restore marker currently bypasses monitor callbacks,
so both view models can become stale. Expose the marker through a typed
publisher, then update InternalTransferViewModel.swift:624-630 and
SendViewModel.swift:464-471 to subscribe to it and recompute their sync-blocking
state when DWRecoverWalletCommand or DWGlobalOptions.restoreToDefaults() changes
the marker; retain existing SyncingActivityMonitor updates.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bc716ac6-485a-4e8f-a5e2-fc733c6261bd

📥 Commits

Reviewing files that changed from the base of the PR and between 129da26 and 2ce1d5f.

📒 Files selected for processing (5)
  • DashWallet/Sources/Models/Transactions/WalletSendService.swift
  • DashWallet/Sources/UI/Payments/InternalTransfer/InternalTransferViewModel.swift
  • DashWallet/Sources/UI/Payments/Pay/SendScreen.swift
  • DashWallet/Sources/UI/Payments/Pay/SendViewModel.swift
  • DashWalletTests/PassiveWalletStateUITailTests.swift

Comment on lines +624 to +630
/// Only Core-funded routes during a restored wallet's first sync block.
var isBlockedBySync: Bool {
switch route {
case .coreToShielded, .coreToPlatform:
return !isChainSynced
return WalletSendService.isBlockedByInitialRestoreSync(
isResyncingWallet: DWGlobalOptions.sharedInstance().isResyncingWallet,
isChainSynced: isChainSynced)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 6 '\bisResyncingWallet\b' \
  --glob '*.swift' --glob '*.m' --glob '*.mm' --glob '*.h'

rg -n -C 5 \
  -e 'objectWillChange' \
  -e 'NotificationCenter.*restore' \
  -e 'NotificationCenter.*resync' \
  -e 'publisher\(for:' \
  --glob '*.swift' --glob '*.m' --glob '*.mm' --glob '*.h'

Repository: dashpay/dashwallet-ios

Length of output: 160


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- target files ---'
git ls-files '*InternalTransferViewModel.swift' '*SendViewModel.swift' '*DWGlobalOptions*'

printf '%s\n' '--- restore-marker references ---'
rg -n -C 8 'isResyncingWallet|DWGlobalOptions' \
  DashWallet/Sources/UI/Payments/InternalTransfer/InternalTransferViewModel.swift \
  DashWallet/Sources/UI/Payments/Pay/SendViewModel.swift \
  --glob '*.swift' --glob '*.m' --glob '*.mm' --glob '*.h' || true

printf '%s\n' '--- publication and observation in target files ---'
rg -n -C 5 'ObservableObject|objectWillChange|`@Published`|publisher\(for:|NotificationCenter|SyncingActivityMonitor|syncDone|init\(|deinit|sink\(' \
  DashWallet/Sources/UI/Payments/InternalTransfer/InternalTransferViewModel.swift \
  DashWallet/Sources/UI/Payments/Pay/SendViewModel.swift \
  --glob '*.swift' || true

printf '%s\n' '--- marker definition and mutation sites ---'
rg -n -C 10 'isResyncingWallet' . \
  --glob '*.swift' --glob '*.m' --glob '*.mm' --glob '*.h' || true

Repository: dashpay/dashwallet-ios

Length of output: 50378


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- direct marker assignments ---'
rg -n -C 4 '(\bisResyncingWallet\b|\bresyncingWallet\b)\s*=' \
  --glob '*.swift' --glob '*.m' --glob '*.mm' --glob '*.h' . || true

printf '%s\n' '--- global-options implementation ---'
cat -n DashWallet/Sources/Models/DWGlobalOptions.h | sed -n '1,90p'
cat -n DashWallet/Sources/Models/DWGlobalOptions.m | sed -n '1,180p'

printf '%s\n' '--- monitor state transitions ---'
cat -n 'DashWallet/Sources/Application/Syncyng Activity Monitor/SyncingActivityMonitor.swift' | sed -n '110,180p'

printf '%s\n' '--- restore/resync lifecycle references ---'
rg -n -C 6 'resync|restore|syncDone|syncStateChangedNotification' \
  --glob '*.swift' --glob '*.m' --glob '*.mm' --glob '*.h' \
  DashWallet/Sources | head -n 500

Repository: dashpay/dashwallet-ios

Length of output: 50378


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- recovery command lifecycle ---'
cat -n DashWallet/Sources/UI/Setup/RecoverWallet/DWRecoverWalletCommand.m | sed -n '1,130p'

printf '%s\n' '--- restoreToDefaults callers ---'
rg -n -C 8 'DWGlobalOptions.*restoreToDefaults|restoreToDefaults\s*\(' \
  --glob '*.swift' --glob '*.m' --glob '*.mm' --glob '*.h' \
  DashWallet/Sources | head -n 300

printf '%s\n' '--- monitor initialization and state updates ---'
cat -n 'DashWallet/Sources/Application/Syncyng Activity Monitor/SyncingActivityMonitor.swift' | sed -n '180,330p'
cat -n 'DashWallet/Sources/Application/Syncyng Activity Monitor/SyncingActivityMonitor.swift' | sed -n '330,460p'

printf '%s\n' '--- payment view-model construction and lifetime anchors ---'
rg -n -C 5 'InternalTransferViewModel\(|SendViewModel\(' \
  --glob '*.swift' DashWallet/Sources/UI | head -n 400

Repository: dashpay/dashwallet-ios

Length of output: 24799


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- recovery command call sites ---'
rg -n -C 10 'DWRecoverWalletCommand|recoverWalletWithPhrase|recoverWallet' \
  --glob '*.swift' --glob '*.m' --glob '*.mm' --glob '*.h' \
  DashWallet/Sources | head -n 500

printf '%s\n' '--- exact view-model initialization paths ---'
cat -n DashWallet/Sources/UI/Payments/InternalTransfer/InternalTransferViewModel.swift | sed -n '540,590p'
cat -n DashWallet/Sources/UI/Payments/Pay/SendViewModel.swift | sed -n '90,155p'

printf '%s\n' '--- all writes to the sync monitor state ---'
rg -n -C 3 '\bstate\s*=' \
  'DashWallet/Sources/Application/Syncyng Activity Monitor/SyncingActivityMonitor.swift' \
  --glob '*.swift'

Repository: dashpay/dashwallet-ios

Length of output: 44018


Publish restore-marker changes to both view models.

DWRecoverWalletCommand changes the marker without changing SyncingActivityMonitor.state. DWGlobalOptions.restoreToDefaults() also changes it without a monitor callback. Expose the marker through a typed publisher and update both InternalTransferViewModel and SendViewModel.

📍 Affects 2 files
  • DashWallet/Sources/UI/Payments/InternalTransfer/InternalTransferViewModel.swift#L624-L630 (this comment)
  • DashWallet/Sources/UI/Payments/Pay/SendViewModel.swift#L464-L471
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@DashWallet/Sources/UI/Payments/InternalTransfer/InternalTransferViewModel.swift`
around lines 624 - 630, The restore marker currently bypasses monitor callbacks,
so both view models can become stale. Expose the marker through a typed
publisher, then update InternalTransferViewModel.swift:624-630 and
SendViewModel.swift:464-471 to subscribe to it and recompute their sync-blocking
state when DWRecoverWalletCommand or DWGlobalOptions.restoreToDefaults() changes
the marker; retain existing SyncingActivityMonitor updates.

@QuantumExplorer
QuantumExplorer merged commit c43bd57 into develop Aug 12, 2026
3 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.

2 participants