mirror of
https://github.com/minibits-cash/minibits_wallet.git
synced 2026-07-30 19:46:17 +00:00
A test wallet lost every mint on restart. Confirmed on device. The mints were in neither place. postProcessSnapshot strips them from every save, and the one-time seed that was supposed to copy them into SQLite never ran — so the snapshot stopped carrying them while the table stayed empty, and the next launch had nothing to load. The seed was gated on `currentVersion < 39`, and the version lied. rootStore.version is `types.optional(types.number, rootStoreModelVersion)`, so a factory reset stamps a wallet as fully migrated on the spot. Restore an older snapshot over that — install the v26 native bundle, whose rootStoreModelVersion is 32 — and the version stays 39: `39 < 39` skips the seed forever, on a wallet that has never seeded anything. The circumstances were unusual; the design was wrong. Gating a DATA migration on a version number assumes the version and the data agree, and SQLite and the MMKV snapshot are separate engines that fail independently — the database migration can roll back while the rootStore migration commits, or vice versa. When they disagree, the version is bookkeeping and the data is the truth. hydrateMintsFromDatabase now converges on the actual state, every launch: - table has mints -> SQLite wins; the snapshot no longer carries them - table empty, store has -> they exist only in the snapshot; write them through - both empty -> a fresh wallet; nothing to do No version consulted, so it cannot be skipped, and it is idempotent (an upsert by mint id). The <39 block is gone, with a note on why it must not come back. The mintId backfills in <38 stay version-gated, and correctly so: those migrate pre-existing rows once. This one is not that — it is a reconciliation between two stores of the same fact, and reconciliations must look. Tests: 510 pass. Two reproduce the device's exact state (mints only in the snapshot, no migration running) and fail against the version-gated design. Recovery for an already-emptied wallet needs no code: re-adding the mint at the same url reattaches its proofs (proofs.mintUrl never moved) and addMint restores the derivation index from mint_counters, which is keyed by keysetId and so was never touched. No blinded-secret reuse — which is what keying counters by keyset, and giving Mint.id referential authority only, were for. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>