feat(wallet): sync node-custodied coin DB from chain so live $DIG spends select coins - #27
Merged
Merged
Conversation
…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
force-pushed
the
feat/wallet-coin-sync
branch
from
July 12, 2026 13:52
f1bd714 to
17e32fd
Compare
MichaelTaylor3d
marked this pull request as ready for review
July 12, 2026 13:52
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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_tipruns 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, whilechia-query/coinset.org require0x-prefixed hex. The tolerant peer tier accepted bare hex (it strips an optional0x), so the bug only surfaced when a bring-up fell through to coinset — reading back zero coins → every node-custodied$DIGspend skippedinsufficient CAT balance: have 0, need N.Wired existing sync (not rebuilt). One localized fix + simulator TDD proving the full path.
Root cause
refresh_tracked_coinsbuilds its tracked puzzle hashes with barehex::encode(no0x).CoinsetFallbackforwarded them verbatim tochia_query; the coinset tier passes them straight to coinset.org, whose full-node RPC matches only0x-prefixed hex → empty result.0x-prefixed everywhere ("0xph","0xhint"), confirming the crate convention.Fix
CoinsetFallbacknormalizes puzzle-hash / hint / coin-id query inputs to lowercased0x-prefixed hex at thechia_queryboundary (all threeChainFallbackreads). The DB,is_candidate, attribution and config stay bare-hex — only the outbound coinset query is normalized. Peer tier still correct (strips0x), coinset tier now correct.How verified (all against the
chia-sdk-testsimulator / mocks — NO live broadcast)refresh_tracked_coins_feeds_cat_selection_and_build_sign: issues a real CAT hinted to the wallet, exposes it through aChainFallback(coin_records_by_hints) + a lineage source, then asserts:select_catsreturns the exact live failure (insufficient CAT balance: have 0);refresh_tracked_coinsupserts the coin and attributes it to its TAIL;unspent_coins/select_catsresolve it;dig-clvm) + signs + reaches a recordingMockBroadcaster— the identical build/sign/broadcast mechanism the tip runs (build_and_broadcast_dig_tipjust fixes the asset id toDIG_ASSET_ID, which the simulator can't mint, so an arbitrary sim CAT stands in for$DIG).query_hash_prefixes_0x_and_lowercasespins the normalization.cargo test -p dig-wallet --lib→ 255 passed; 0 failed.cargo fmt --all -- --checkclean;cargo clippy -p dig-wallet --all-targets --all-features -- -D warningsclean.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 reportsNotExecutable/insufficient, retryable — never a blind spend). No live broadcast in any test.SPEC
SPEC.md §18.12now 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 canonical0xquery-hex requirement.gitnexus blast radius
select_cats(rpc) feedssend_cat/bulk_send_cat/ offersresolve_offer_catsand 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, sorefresh_tracked_coinsis 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-
$DIGlive-funds e2e (#428live_funds_tip_e2e.rs) once the funded seed is provided.