Task
Give the node a way to drop the cached coin database and re-sync it from chain, exposed as a
control method and a dign verb. The DIG App button (dig-app#TBD) drives this; it must not reach into
the database itself.
User directive, 2026-08-27 (verbatim): "we will need to reset the database when this is fixed.
There should be a button in dig-app to reset the coin datbase and resync"
Parent epic: https://github.com/DIG-Network/dig_ecosystem/issues/3170
Related: #382 (the CAT attribution defect that prompted it)
Why this is worth building even though #382's fix backfills
reconstruct_all walks every coin in the DB and attributes any that is unspent, unattributed and
a candidate — so once #382 wires the attributor, existing asset_id = NULL rows are repaired on the
next update tick without a reset. A reset is not the remedy for #382.
The case that genuinely needs it is narrower and permanent:
let Some(parent) = lineage.parent_spend(&c.parent_coin_info, created as u32).await? else {
continue;
};
A coin whose parent spend could not be fetched — source unreachable, a transient failure, a pruned
response — is skipped, silently, and never retried. Nothing re-queues it. That coin stays
unattributed for the life of the database, so its asset never resolves and its value never appears in
an asset-scoped balance. There is currently no way for a user to recover from that except deleting
the file by hand.
That is the honest justification: not "the DB is stale" but "a one-off fetch failure is permanent
and otherwise unrecoverable."
The hazard this MUST get right — the repo already documents it
Between emptying the table and finishing the re-sync, the database is empty. The routing gate
(WalletDb::is_synced → routing::route) is what makes the local replica authoritative. So an
emptied-but-still-synced database answers balance 0, synced true on a funded wallet.
sync.rs already states this for the reorg-rollback path, in almost these words:
"The routing gate … is what makes an emptied DB authoritative, so a destructive push must clear it:
the wallet then reads from the fallback tier until a genuine catch-up re-establishes the flag.
Without this, one hostile frame makes a funded wallet answer balance 0, synced true."
The reset must clear is_synced in the same transaction that empties the coins, so a crash
between the two cannot leave an empty authoritative replica. Reads then fall back until a real
catch-up re-establishes the flag. This is the money-lie class (§2.6's narrow stop) and it is the whole
risk of the feature.
Other constraints
- Never touch key material. Coins are chain-derived and safe to discard; a seed is not. Scope the
reset to chain-cached tables and say explicitly which ones.
- Refuse while a spend is in flight.
coin_reservations, client_coin_reservations and
pending_transactions reference coins by id. Wiping the coins beneath a reserved, unconfirmed spend
loses the record of what is committed and invites a double-spend attempt. Refuse with a clear
machine-branchable error rather than resetting anyway.
- Re-entrant. Pressing it twice, or while a resync is running, must not corrupt or start two
catch-ups.
- Report progress. A resync from genesis is slow. The caller needs to distinguish "running" from
"stalled" — dign wallet sync-status already reports phase and replica height; make the reset
observable through the same read rather than inventing a second status surface.
Deliverables
- A control method (declare it in
dig-node-control-interface, release-first per §4.1)
- A
dign wallet verb, --json supported (§6.2)
SPEC.md updated (§4.2); the #426 CLI/RPC parity test kept green
Acceptance
On a real funded wallet: reset, watch the balance become unknown (never a confident zero), watch the
resync complete, and see the same balance and coins as before. §2.6 — evidence is that sequence on a
real machine.
Task
Give the node a way to drop the cached coin database and re-sync it from chain, exposed as a
control method and a
dignverb. The DIG App button (dig-app#TBD) drives this; it must not reach intothe database itself.
User directive, 2026-08-27 (verbatim): "we will need to reset the database when this is fixed.
There should be a button in dig-app to reset the coin datbase and resync"
Parent epic: https://github.com/DIG-Network/dig_ecosystem/issues/3170
Related: #382 (the CAT attribution defect that prompted it)
Why this is worth building even though #382's fix backfills
reconstruct_allwalks every coin in the DB and attributes any that is unspent, unattributed anda candidate — so once #382 wires the attributor, existing
asset_id = NULLrows are repaired on thenext update tick without a reset. A reset is not the remedy for #382.
The case that genuinely needs it is narrower and permanent:
A coin whose parent spend could not be fetched — source unreachable, a transient failure, a pruned
response — is skipped, silently, and never retried. Nothing re-queues it. That coin stays
unattributed for the life of the database, so its asset never resolves and its value never appears in
an asset-scoped balance. There is currently no way for a user to recover from that except deleting
the file by hand.
That is the honest justification: not "the DB is stale" but "a one-off fetch failure is permanent
and otherwise unrecoverable."
The hazard this MUST get right — the repo already documents it
Between emptying the table and finishing the re-sync, the database is empty. The routing gate
(
WalletDb::is_synced→routing::route) is what makes the local replica authoritative. So anemptied-but-still-
synceddatabase answersbalance 0, synced trueon a funded wallet.sync.rsalready states this for the reorg-rollback path, in almost these words:The reset must clear
is_syncedin the same transaction that empties the coins, so a crashbetween the two cannot leave an empty authoritative replica. Reads then fall back until a real
catch-up re-establishes the flag. This is the money-lie class (§2.6's narrow stop) and it is the whole
risk of the feature.
Other constraints
reset to chain-cached tables and say explicitly which ones.
coin_reservations,client_coin_reservationsandpending_transactionsreference coins by id. Wiping the coins beneath a reserved, unconfirmed spendloses the record of what is committed and invites a double-spend attempt. Refuse with a clear
machine-branchable error rather than resetting anyway.
catch-ups.
"stalled" —
dign wallet sync-statusalready reports phase and replica height; make the resetobservable through the same read rather than inventing a second status surface.
Deliverables
dig-node-control-interface, release-first per §4.1)dign walletverb,--jsonsupported (§6.2)SPEC.mdupdated (§4.2); the #426 CLI/RPC parity test kept greenAcceptance
On a real funded wallet: reset, watch the balance become unknown (never a confident zero), watch the
resync complete, and see the same balance and coins as before. §2.6 — evidence is that sequence on a
real machine.