Commit Graph
9 Commits
Author SHA1 Message Date
jwinterm 7854da0b23 scanner: operational logging (tick / forward-scan / backfill progress)
Add debug logs for each tick + forward-scan summary (accounts, caught-up,
index range, scanned/matched counts) and info logs when a new account's
backfill starts and completes. No per-output or commitment logging (keeps
amounts/commits out of logs). Added while validating against a live grin
node + a funded wallet: grin-lws recovered the wallet's exact balance via
both the forward-scan and backfill paths.
2026-07-09 15:24:37 -04:00
jwinterm f2d59af0ec docs: mark grin-lws FUNCTIONAL (Phases 1-5 done); refresh build order
The rewind crypto, PMMR-index node reads, and the scanner are
implemented + tested (unit + end-to-end). Update README status, the
build-order checklist (rewind/reads/scanner/reorg done; live-node smoke
+ backend proxy remain), the main.rs module status, and the startup log.
2026-07-09 13:35:50 -04:00
jwinterm d839b75a76 Phase 4+5: genuine dual Postgres/SQLite + end-to-end scanner proof
Building an end-to-end integration test (mock grin node → real tick() →
SQLite store) surfaced the real dual-DB blockers in sqlx::Any — NOT the
placeholder issue the review flagged:
- Any truncates i64 -> i32 for SQLite (12345678900 -> -539222988),
  which would corrupt grin amounts. Store outputs.value as a decimal
  string, sum in Rust. (Probe-documented; other int columns are < 2^31
  for decades.)
- Any can't decode SQLite BOOLEAN; is_coinbase/spent are now INTEGER 0/1.
- DEFAULT now() -> DEFAULT CURRENT_TIMESTAMP (portable).

The integration test now proves the whole pipeline end-to-end on SQLite:
node UTXO listing → view-only rewind (golden output) → store with
value + key_id → correct balance → spend-by-absence reconcile → zero.
One schema + one query set now serve both backends.
2026-07-09 13:24:01 -04:00
jwinterm 0dc1804e67 Phase 4: verify + fix genuine dual Postgres/SQLite support
The review's premise (sqlx::Any breaks on SQLite $N placeholders) is
wrong for sqlx 0.7: SQLite accepts $N numbered params and binds them
positionally, including reused placeholders. A probe test applies the
real migration + exercises set_cursor's reused-$N upsert, insert_output
(quoted "commit", bool), coinbase-maturity reads, and mark_spent against
SQLite — all pass. The only true incompatibility was DEFAULT now(),
changed to the portable DEFAULT CURRENT_TIMESTAMP. Schema + queries now
run unchanged on both backends. Updated the stale dialect notes.
2026-07-09 13:11:49 -04:00
jwinterm 75542f6358 Phase 3: PMMR-index background scanner (fills the store, serves balances)
Replace the stub scanner with the real incremental algorithm:
- discover: page get_unspent_outputs (with proofs) from the cursor to
  the tip, rewind each new output against caught-up accounts, store
  matches with their derivation path + a derived lock_height (coinbase
  maturity = 1440)
- spend reconcile: mark any stored-unspent commit the node no longer
  lists (grin has no per-block inputs; spends = absence from the set)
- reorg: detect via header-hash mismatch at the checkpoint height,
  roll back a bounded depth, reseek from the fork
- backfill: bring a newly-registered account up from its birthday index
- cursor only advances after discover AND reconcile succeed
DB: chain_cursor gains output_mmr_index (tip height/hash become the
reorg checkpoint); new helpers (list_accounts, all_unspent_commits,
mark_unspent, account_unlocked_totals, scan-height advance/clamp).
Read path: get_balance adds `unlocked` (spendable-now) and
get_unspent_outs flags per-output `spendable`, so immature coinbase /
locked outputs are never presented as spendable.
2026-07-09 13:06:17 -04:00
jwinterm 577eaef01f Phase 2: grin-node PMMR-index RPC layer (the real scan data source)
Grin scans by PMMR output index, not block-by-block, so replace the
block-based stubs with the actual Foreign-API-v2 surface:
- get_unspent_outputs(start,end,max, include_proof=true): page the UTXO
  set WITH rangeproofs (the discovery call the scanner rewinds)
- get_outputs(commits): spend detection by absence from the unspent set
- get_pmmr_indices(height): birthday height -> start index for backfill
- get_header(height) + get_tip(): reorg detection + tip hash
- push_transaction: fix the {"Ok":null} success case (the generic rpc
  helper mis-read null as an error) via a null-tolerant rpc_ok_unit
- delete get_block/BlockView (grin's get_block returns no proofs)
- reshape ChainOutput to the UTXO-listing fields; permissive,
  version-tolerant deserialization; per-output decode failures skip.
Tests for the node JSON parsing + spent/proofless filtering.
2026-07-09 12:55:52 -04:00
jwinterm d9edee0743 Phase 1: view-only rangeproof rewind (recognize outputs from rewind_hash)
Replace the rewind_output stub with a real, view-only implementation:
- src/rewind.rs: derive the v3 rewind nonce (keyed BLAKE2b over the
  commitment) and rewind the Bulletproof via upstream grin_secp256k1zkp,
  then parse the 20-byte proof message into the authoritative key_id +
  value. Faithful minimal port of grin-ext's cross-validated crypto; no
  private key is ever involved. v3-only (legacy needs the master secret,
  same limit as grin-wallet's view-only scan); paths are advisory.
- src/grin.rs: wire rewind_output with hostile-input-safe hex decoding.
- Cargo.toml: depend on upstream grin_secp256k1zkp + blake2 (not the
  vendored fork — the server target needs no wasm patch).
- Golden-vector tests generated by grin's OWN reference ProofBuilder
  (depth-3 Grim + depth-4 Smirk [0,0,n,0]); proves byte-compatibility
  with the published secp crate. Passed an adversarial money-safety
  review (no false-positive/negative, panic-safe).
2026-07-09 12:25:16 -04:00
jwinterm 255c13e27e Prepare for public release: generic wording, MIT LICENSE
Make the scaffold standalone and publish-ready:
- Rewrite code + README references to the originating backend as a generic
  "wallet backend" so the service reads as a reusable, run-anywhere LWS.
- Add the MIT LICENSE file (matches Cargo.toml) and a short provenance note.
- Add repository/readme/keywords/categories metadata to Cargo.toml.
2026-07-09 07:11:10 -04:00
jwinterm 1dabd1513c scaffold: grin-lws — Grin light-wallet-server (view-key scan + DB store)
A standalone, self-hostable Grin light-wallet-server mirroring the Monero LWS:
register a view/rewind credential, run a background chain scanner, rewind outputs
against registered keys, store per-account matches in a DB, and serve balance /
outputs / broadcast / height fast (vs the current on-demand stateless scan proxy).
This is the scaffold — README (architecture + HTTP surface + DB schema + how a
wallet backend proxies to it), Cargo skeleton, migrations sketch, and route/scanner
stubs. Public-clean: no operator-specific infra, hosts, or secrets.
2026-07-09 04:47:13 -04:00