Commit Graph
17 Commits
Author SHA1 Message Date
minibits-cash 45cef1c60a New welcome screen 2026-07-08 15:44:11 +02:00
minibits-cash 81699251aa Fix fee estimation 2026-06-27 23:55:40 +02:00
minibits-cashandClaude Opus 4.8 3caf54fa35 fix(melt): recover NUT-08 change when mint reorders signatures
Some mints (nutshell < 0.20.1) return paid-melt-quote change[]
signatures in an order that does not match the blank outputs the
wallet sent. cashu-ts OutputData.toProof assumes positional pairing
and throws on its DLEQ check, so mapping over the array aborted on the
first mismatch and the catch discarded ALL change — recording it as
fee. A user lost ~100k sats of change on a single transfer this way.

Add CashuUtils.recoverMeltChange, a resilient helper that:
  - matches each signature to the blank whose blinded message makes the
    mint's DLEQ proof verify (verifyDLEQProof as alignment oracle),
    correctly re-pairing reordered change while keeping DLEQ as a hard
    guarantee;
  - on a genuine DLEQ failure (no blank verifies) falls back to
    unblinding the positional blank WITHOUT DLEQ so funds are recovered
    rather than dropped, logged at error level;
  - returns stats so callers can surface anomalies.

Both melt finalize paths (transferOperationApi._finalizePaid and
meltOperations.handlePendingMeltTask) use the helper and append a
COMPLETED-status tx.data entry with the recovery stats when a genuine
error occurred (no-DLEQ fallback / unmatched) — benign reordering is
not flagged.

Covered by __tests__/recoverMeltChange.test.ts (in-order, shuffled,
fewer-sigs-than-blanks, duplicate denominations, no-DLEQ fallback,
unmatched).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 21:55:04 +02:00
minibits-cashandClaude Opus 4.8 14f8d1de8c feat(inflight): move inFlightRequests to SQLite (off-MST) [M2]
Relocate per-transaction in-flight request data (params for NUT-19
idempotent retry) from the MST MintProofsCounter to a dedicated SQLite
table, so retries work with no MST loaded — completing the off-MST set
needed for background NWC.

- schema/migration v29: inflight_requests (txId PK, mintUrl, keysetId,
  request JSON); added to cleanAll.
- inFlightRepo: add (INSERT OR REPLACE = set semantics) / get /
  getInFlightRequestsByMint / remove / seed (ON CONFLICT DO NOTHING).
- WalletStore: write/remove via Database (receive/send/mint paths).
- inFlightOperations: enumerate via a flat Database.getInFlightRequestsByMint
  query instead of the mint.proofsCountersWithInFlightRequests nested loop;
  removeInFlightRequest via Database; queue guard uses the DB count.
- Mint model: remove inFlightRequests map, InFlightRequestModel, all the
  in-flight actions/views (counter + mint level). The InFlightRequest TYPE
  is kept (WalletStore option signatures). MintProofsCounter is now just
  {keyset, unit, counter}. migrateSnapshot strips inFlightRequests AND
  meltCounterValues from old snapshots.
- one-time seed (rootStoreModelVersion 34->35): _runMigrations reads the
  raw pre-upgrade snapshot for any in-flight requests; idempotent.
- tests: __tests__/inFlightRequests.test.ts.

Full suite green (15 suites / 163 tests). With M1+M2, the MintProofsCounter
sub-model now carries only the counter (itself SQLite-authoritative) — a
candidate to collapse later.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 09:50:56 +02:00
minibits-cashandClaude Opus 4.8 8d91fad254 feat(melt): move meltCounterValues to SQLite (off-MST, durable) [M1]
Relocate per-transaction melt recovery data (the serialized meltPreview)
from the MST MintProofsCounter (debounced MMKV) to a dedicated SQLite
table, so it can be written synchronously and read with no MST loaded.

Why: meltPreview is recovery-critical — it unblinds the change of a paid-
but-unconfirmed melt. It was persisted only via the batched whole-tree
MMKV snapshot, so a crash right after the payment was submitted could lose
it and the change ecash. It's also a prerequisite for off-MST background
melt (NWC pay_invoice).

- schema/migration v28: new melt_recovery table (txId PK, mintUrl,
  keysetId, meltPreview JSON); added to cleanAll.
- meltRecoveryRepo: add (ON CONFLICT DO NOTHING — first preview wins,
  matching the old "already tracked" guard) / get / remove / seed.
- WalletStore: write the preview synchronously via Database.addMeltRecovery
  BEFORE completeMelt; remove on terminal success/failure.
- meltOperations / transferOperationApi: read/remove via Database instead
  of the counter model; drop the now-needless counter fetch in those blocks.
- Mint model: remove meltCounterValues map, MeltCounterValueModel, the melt
  actions/views, the dead counterAtMelt field, and serializeMeltPreview
  (moved to cashuUtils). migrateSnapshot now STRIPS meltCounterValues from
  old snapshots so applySnapshot tolerates the removed field.
- one-time seed (rootStoreModelVersion 33->34): _runMigrations reads the
  RAW pre-upgrade snapshot (the model no longer holds it) and copies any
  in-flight meltPreview into SQLite. Idempotent.
- tests: __tests__/meltRecovery.test.ts (JSON round-trip, first-wins,
  remove, isolation, idempotent seed).

Full suite green (14 suites / 157 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 09:36:53 +02:00
minibits-cashandClaude Opus 4.8 8ffce9e46a test(counters): cover mint_counters invariants
Add unit coverage for the derivation-counter migration (mirrors the
production SQL against node:sqlite, like proofReservation.test.ts):

- setCounter is monotonic — raises, ignores a lower value, no-ops on equal
- bumpCounter advances relatively; non-positive delta is a no-op
- (mintUrl, keysetId) primary key isolates keysets and mints
- seedCounters is idempotent and never regresses an advanced counter;
  a too-high seed is kept (conservative-safe)
- the folded counterUpdate persists the counter atomically with new proofs,
  rolls back with them on a failed batch, and stays monotonic in-batch

Locks in the no-secret-reuse safety property of the migration.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 15:23:23 +02:00
minibits-cashandClaude Opus 4.8 7bf7685ec6 Fix jest test suite: scope testMatch, mock quick-crypto, drop dead tests
- testMatch now only picks up *.test/*.spec files so the i18n scripts
  (run via `yarn test:i18n`) are no longer globbed as empty suites
- Allowlist react-native-flash-message for transform and map
  react-native-quick-crypto to a Node crypto shim, unblocking the 11
  cashuDleq tests that load it transitively via @scure/bip32
- Remove orphaned storage.test.ts (module + async-storage dep gone) and
  the boilerplate App.test.tsx smoke test that mounted the full native tree

Suite now: 12 suites / 136 tests passing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 22:31:36 +02:00
minibits-cashandClaude Opus 4.8 63d89bb6f8 Split SQLite monolith into focused db/ modules
Pure reorganization behind the existing Database facade — no behavior change.
The 1646-line services/sqlite.ts is broken into services/db/:

  errors.ts          dbError() helper
  connection.ts      op-sqlite adapter (from previous commit)
  schema.ts          table column definitions (single source of truth)
  migrations.ts      _dbVersion, getDatabaseVersion, ordered migration registry
  mappers.ts         transaction row normalizers
  instance.ts        _db singleton, getInstance, schema bootstrap, cleanAll
  transactionsRepo.ts / proofsRepo.ts / reservationsRepo.ts
  index.ts           assembles the Database facade, re-exports public types

services/sqlite.ts is now a thin `export * from './db'` barrel so every
existing import path (services, services/sqlite, ../../sqlite) resolves
unchanged. The Database.* contract and the exported types
(TransactionSearchFilters, LockedProofSnapshot, ReservationRow,
ReservationTransactionUpdate) are preserved.

Notable refactors folded in:
  - schema single source of truth: proofs/reservations columns are defined
    once; first-run CREATE and the v25 rebuild / v26 add are generated from
    them so they cannot drift. PROOFS_COLUMN_NAMES (drives the v25 copy) is
    tested to stay in sync with the table.
  - migration if-chain replaced by an ordered MIGRATIONS registry; adding a
    migration is now a one-line append.
  - updateStatusesAsync and expireAllAfterRecovery now use getInstance()
    instead of reaching for the module-global _db directly (the only two
    functions that did; behavior-equivalent, more robust before init).

New: __tests__/dbSchema.test.ts validates the generated DDL against node:sqlite.
Tests: 125/125 pass (was 121 + 4 new), no regressions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 21:46:15 +02:00
minibits-cash 3b91194025 Transaction lifecycle methods 2026-05-26 00:08:47 +02:00
minibits-cash 586f5c098d Fix wrong assignement of tId 2026-05-22 22:38:18 +02:00
minibits-cash 25330d3b73 Proofs reservation and rollback 2026-05-22 22:03:18 +02:00
KeshavandGitHub a20044e4f7 NUT-12: validate offline ecash DLEQ proofs (#198) 2026-05-20 16:42:43 +02:00
KraXen72andGitHub b466331588 spanish translation (#160) 2025-07-16 22:11:54 +02:00
KraXen72andGitHub 847e36a66c Update translations (#159) 2025-07-15 22:37:06 +02:00
minibits-cash 9e057ba698 React native 0.77 upgrade 2025-02-03 22:09:46 +01:00
minibits-cash bb8995c717 RN 0.75 and libs upgrade 2024-09-17 00:52:26 +02:00
minibits-cash f7a170af58 Initial commit
Generated by react-native@0.75.3
2024-09-14 22:55:53 +02:00