Skip to content

fix: prevent Firo sync stall at ~65% by adding timeouts and retry logic#1296

Draft
reubenyap wants to merge 1 commit intocypherstack:stagingfrom
reubenyap:claude/fix-firo-sync-stall-dP7vn
Draft

fix: prevent Firo sync stall at ~65% by adding timeouts and retry logic#1296
reubenyap wants to merge 1 commit intocypherstack:stagingfrom
reubenyap:claude/fix-firo-sync-stall-dP7vn

Conversation

@reubenyap
Copy link
Copy Markdown

@reubenyap reubenyap commented Apr 7, 2026

Summary

Firo wallet sync frequently stalls at 65% and requires force-closing the app to recover. This PR fixes both symptoms with two targeted changes to _refresh() in wallet.dart.

Root cause

When any sub-operation during sync hangs (network issue, unresponsive server, OS suspending sockets), refreshMutex is never released because the catch/finally blocks only run when the future completes or throws — a permanently pending future does neither. All future sync attempts bail out immediately at the if (refreshMutex.isLocked) return check (line 620), so the wallet can never sync again until force-closed.

The 65% number specifically comes from progress being fired to 0.65 before updateUTXOs() is awaited, making it appear stuck at that number while the real work is still happening.

Changes

1. Master timeout on refresh (prevents permanent mutex lock)

Wrap the _refresh() body in a 5-minute timeout. If any sub-operation hangs, TimeoutException is caught by the existing catch block → completer.completeError()finally releases refreshMutex. The next periodic sync (every 150s) gets a fresh attempt.

This is an intentionally blunt safety net — not a per-call timeout. Connection-level stall detection is handled by the electrum adapter's existing connectionTimeout and aliveTimerDuration (both 60s). The master timeout only fires when something slips through those layers.

2. Fix misleading progress reporting

Before: 0.6 → 0.65 → [wait UTXOs] → 0.70 → [wait txns]
After: 0.6 → [wait UTXOs] → 0.65 → [wait txns] → 0.70

Progress now reflects actual completion rather than jumping ahead.

Test plan

  • Sync a Firo wallet and verify it completes past 65%
  • Verify progress bar advances smoothly without premature jumps
  • Kill the ElectrumX server mid-sync and verify the wallet recovers on next periodic refresh (mutex released)
  • Verify non-Firo wallets (BTC, LTC, etc.) are unaffected — only wallet.dart is changed

@reubenyap reubenyap force-pushed the claude/fix-firo-sync-stall-dP7vn branch 4 times, most recently from d66d2c2 to 3d943be Compare April 7, 2026 21:05
Two root issues fixed:

1. Sync stall at ~65% requiring force-close:
   When any sub-operation during _refresh() hangs, refreshMutex is held
   forever. Added a 5-minute master timeout that guarantees mutex
   release. Fixed progress to fire 0.65 after updateUTXOs() completes.

2. Anonymity set downloads restart from scratch on interruption:
   All sectors were accumulated in memory and written to SQLite only
   after the entire download completed. Now each sector is persisted
   immediately. On resume, only remaining sectors are fetched.

   Key design decisions verified against firod source (firoorg/firo@ccaf130):
   - API uses absolute indices (0 = newest coin, counting backwards)
   - blockHash pins the iteration start point (stable indices)
   - Same-block resume: offset indices by prevSize to skip saved coins
   - Cross-block resume: use `complete` flag on SparkSet to detect
     whether the previous download finished. Complete → use delta.
     Partial → full re-download (indices shifted, gap is unavoidable).
   - INSERT OR IGNORE handles crash-recovery and cross-block overlap
   - Progress uses consistent (indexOffset + fetched, meta.size)
   - All groupIds processed every sync (removed skip optimization)
   - Removed old all-or-nothing writer (dead code)
   - Schema: added `complete` column + UNIQUE index on SparkSetCoins

https://claude.ai/code/session_01GF78pBWxrpN9rfsLEEwbMR
@reubenyap reubenyap force-pushed the claude/fix-firo-sync-stall-dP7vn branch from 3d943be to 9248798 Compare April 7, 2026 21:21
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