Skip to content

feat(wallet): sync node-custodied coin DB from chain so live $DIG spends select coins - #27

Merged
MichaelTaylor3d merged 1 commit into
mainfrom
feat/wallet-coin-sync
Jul 12, 2026
Merged

feat(wallet): sync node-custodied coin DB from chain so live $DIG spends select coins#27
MichaelTaylor3d merged 1 commit into
mainfrom
feat/wallet-coin-sync

Conversation

@MichaelTaylor3d

@MichaelTaylor3d MichaelTaylor3d commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

TLDR

The node-custodied wallet coin-DB sync (WalletBackend::refresh_tracked_coins) already exists and is already wired into the tip spend path (NodeTipSpender::send_dig_tip runs it before selecting). The live "have 0 $DIG" failure was a hex-format bug in the existing sync, not a missing sync loop: the coinset fallback tier was queried with bare hex, while chia-query/coinset.org require 0x-prefixed hex. The tolerant peer tier accepted bare hex (it strips an optional 0x), so the bug only surfaced when a bring-up fell through to coinset — reading back zero coins → every node-custodied $DIG spend skipped insufficient CAT balance: have 0, need N.

Wired existing sync (not rebuilt). One localized fix + simulator TDD proving the full path.

Root cause

  • refresh_tracked_coins builds its tracked puzzle hashes with bare hex::encode (no 0x).
  • CoinsetFallback forwarded them verbatim to chia_query; the coinset tier passes them straight to coinset.org, whose full-node RPC matches only 0x-prefixed hex → empty result.
  • Every mock/unit test used bare hex on both sides, so it was invisible until live coinset reads. The chia-query test suite itself uses 0x-prefixed everywhere ("0xph", "0xhint"), confirming the crate convention.

Fix

CoinsetFallback normalizes puzzle-hash / hint / coin-id query inputs to lowercased 0x-prefixed hex at the chia_query boundary (all three ChainFallback reads). The DB, is_candidate, attribution and config stay bare-hex — only the outbound coinset query is normalized. Peer tier still correct (strips 0x), coinset tier now correct.

How verified (all against the chia-sdk-test simulator / mocks — NO live broadcast)

  • New simulator full-path test refresh_tracked_coins_feeds_cat_selection_and_build_sign: issues a real CAT hinted to the wallet, exposes it through a ChainFallback (coin_records_by_hints) + a lineage source, then asserts:
    1. BEFORE sync → select_cats returns the exact live failure (insufficient CAT balance: have 0);
    2. refresh_tracked_coins upserts the coin and attributes it to its TAIL;
    3. AFTER sync → unspent_coins/select_cats resolve it;
    4. the CAT send builds + validates (dig-clvm) + signs + reaches a recording MockBroadcaster — the identical build/sign/broadcast mechanism the tip runs (build_and_broadcast_dig_tip just fixes the asset id to DIG_ASSET_ID, which the simulator can't mint, so an arbitrary sim CAT stands in for $DIG).
  • New red-first unit test query_hash_prefixes_0x_and_lowercases pins the normalization.
  • Full crate suite green: cargo test -p dig-wallet --lib255 passed; 0 failed.
  • cargo fmt --all -- --check clean; cargo clippy -p dig-wallet --all-targets --all-features -- -D warnings clean.

Money-safety

Flag-OFF path unchanged (no broadcaster attached ⇒ tip cleanly NotExecutable). Sync is best-effort + fail-closed: a sync failure is never a spend failure (selection reports NotExecutable/insufficient, retryable — never a blind spend). No live broadcast in any test.

SPEC

SPEC.md §18.12 now documents the wallet coin-DB sync contract: what feeds it (fallback point-reads by puzzle hash + hint), when it runs (on the spend path, before selection), idempotency, and the canonical 0x query-hex requirement.

gitnexus blast radius

select_cats (rpc) feeds send_cat / bulk_send_cat / offers resolve_offer_cats and the tip path (build_and_broadcast_dig_tip). This change only hardens the sync→select path and adds tests; behavior of those consumers is unchanged except that selection now sees real synced coins. (Note: the dig-node gitnexus index is stale — it predates #428, so refresh_tracked_coins is absent and line numbers differ; blast radius read against the v0.22.0 source.)

Version

Minor bump (feat): 0.22.0 → 0.23.0 ([workspace.package].version).

Closes #430. Refs #428 #377 #374.

Unblocks the real-$DIG live-funds e2e (#428 live_funds_tip_e2e.rs) once the funded seed is provided.

…nds select coins

The wallet coin-DB sync (WalletBackend::refresh_tracked_coins) already exists and is
wired into the tip spend path, but the coinset fallback tier was queried with bare hex
while chia-query/coinset require 0x-prefixed hex. The tolerant peer tier accepted bare
hex, so a live bring-up that fell through to coinset read back zero coins and every
node-custodied $DIG spend skipped with "insufficient CAT balance: have 0" (#430).

- Fix: CoinsetFallback normalizes puzzle-hash / hint / coin-id query inputs to lowercased
  0x-prefixed hex at the chia-query boundary (all three ChainFallback reads). The DB and
  internal comparisons stay bare-hex; only the outbound coinset query is normalized.
- TDD (simulator, no live broadcast): a chia-sdk-test full-path test issues a real CAT
  hinted to the wallet, proves select_cats reports the exact "have 0" failure BEFORE the
  sync, then after refresh_tracked_coins the coin lands in the DB attributed to its TAIL,
  select_cats resolves it, and a CAT send builds + validates (dig-clvm) + signs + reaches
  a recording MockBroadcaster — the identical mechanism the tip uses. Plus a red-first
  unit test pinning the 0x query-hex normalization.
- SPEC §18.12: documents the wallet coin-DB sync contract (what feeds it, when it runs,
  the canonical 0x query-hex requirement).

Version bump minor (feat): 0.22.0 -> 0.23.0.

Closes #430
Refs #428 #377 #374

Co-Authored-By: Claude <noreply@anthropic.com>
@MichaelTaylor3d
MichaelTaylor3d force-pushed the feat/wallet-coin-sync branch from f1bd714 to 17e32fd Compare July 12, 2026 13:52
@MichaelTaylor3d MichaelTaylor3d changed the title feat(wallet): sync node-custodied coin DB from chain so live $DIG spends can select coins feat(wallet): sync node-custodied coin DB from chain so live $DIG spends select coins Jul 12, 2026
@MichaelTaylor3d
MichaelTaylor3d marked this pull request as ready for review July 12, 2026 13:52
@MichaelTaylor3d
MichaelTaylor3d merged commit e1a284e into main Jul 12, 2026
10 checks passed
@MichaelTaylor3d
MichaelTaylor3d deleted the feat/wallet-coin-sync branch July 12, 2026 13:59
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.

1 participant