Persist the mirror-bond COIN IDs — bond identity is currently re-derived from chain on every read
User request, verbatim: "the bonds coinids need to be remembered in the database."
The evidence that prompted it
On a live host, immediately after upgrading 0.254.72 -> 0.254.85 (which restarts the service):
before restart: epoch 105: 3 bond(s) — 0 bonded, 0 unfunded, 3 unadvertised
after restart: epoch 105: 0 bond(s) — 0 bonded, 0 unfunded, 0 other · complete
The capsules did not go anywhere — dign stores reports 5 hosted stores, dign cache reports 407 MB on disk. Only the bond list emptied.
Why this happens — measured, not assumed
mirror/observe.rs documents itself plainly:
"it turns four already-gathered readings — the capsules on disk, the mirror coins on chain, this node's own open creates, and the $DIG it can spend — into the BondObservation the surface pages."
observe is a pure function over values with no persistence of its own. Bond identity is reconstructed each pass from a chain query, so the answer is only as good as that query at that instant. A restart, a cold replica, a chain source that has not caught up, or a transient RPC failure all render the same as "you have no bonds."
Why that is worse than a stale number
A bond is money that is currently locked. The failure direction is exactly wrong:
- It fails OPEN into "nothing exists." An empty list is indistinguishable from "this node never bonded anything," so a reader — human or code — concludes there is nothing to protect.
- The reconcile pass reads the same observation. If a transient chain gap makes existing bonds invisible, the pass sees capsules with no coins and its correct response is to create coins that already exist — spending collateral twice for the same
(store, root).
- Reclaim depends on knowing what to reclaim. Collateral the node cannot enumerate is collateral it cannot return.
I have not confirmed the double-create actually occurs — the in-flight suppression in runner.rs may cover the within-pass case. But that suppression is derived from the audit record, not from persisted coin ids, so it is the same class of dependency. Establish whether a cold start with a lagging chain source can double-create; that answer sets this ticket's severity.
What to persist
The (store, root, epoch) -> coin_id mapping the node created, written when the create is submitted, not when it confirms — an unconfirmed spend is precisely the state most in need of a durable record.
Alongside each: the epoch it covers, the amount locked, and the advertised URL it carries (which the reset work in dig_ecosystem#3203 will need to compare against the current one).
The constraint that decides the design
A local record must never be believed OVER chain — it must be believed IN ADDITION to it.
- Chain remains authoritative for "is this coin still unspent."
bond_verify.rs::chain_bond_verdict already answers that and must keep doing so.
- The local record answers a different question: "what did this node create, so it knows what to go look for."
- Where they disagree, that is a finding to surface, not to silently resolve. A locally-recorded coin that chain says is spent may mean someone else reclaimed it; a chain coin with no local record may mean a create landed after a crash. Both are worth telling an operator about.
BondVerdict::Unverified must stay reachable. A persisted id makes it possible to verify; it is not itself verification, and a badge or a spend decision must never treat "we wrote it down" as "chain agrees."
Related
Persist the mirror-bond COIN IDs — bond identity is currently re-derived from chain on every read
User request, verbatim: "the bonds coinids need to be remembered in the database."
The evidence that prompted it
On a live host, immediately after upgrading
0.254.72 -> 0.254.85(which restarts the service):The capsules did not go anywhere —
dign storesreports 5 hosted stores,dign cachereports 407 MB on disk. Only the bond list emptied.Why this happens — measured, not assumed
mirror/observe.rsdocuments itself plainly:observeis a pure function over values with no persistence of its own. Bond identity is reconstructed each pass from a chain query, so the answer is only as good as that query at that instant. A restart, a cold replica, a chain source that has not caught up, or a transient RPC failure all render the same as "you have no bonds."Why that is worse than a stale number
A bond is money that is currently locked. The failure direction is exactly wrong:
(store, root).I have not confirmed the double-create actually occurs — the in-flight suppression in
runner.rsmay cover the within-pass case. But that suppression is derived from the audit record, not from persisted coin ids, so it is the same class of dependency. Establish whether a cold start with a lagging chain source can double-create; that answer sets this ticket's severity.What to persist
The
(store, root, epoch) -> coin_idmapping the node created, written when the create is submitted, not when it confirms — an unconfirmed spend is precisely the state most in need of a durable record.Alongside each: the epoch it covers, the amount locked, and the advertised URL it carries (which the reset work in
dig_ecosystem#3203will need to compare against the current one).The constraint that decides the design
A local record must never be believed OVER chain — it must be believed IN ADDITION to it.
bond_verify.rs::chain_bond_verdictalready answers that and must keep doing so.BondVerdict::Unverifiedmust stay reachable. A persisted id makes it possible to verify; it is not itself verification, and a badge or a spend decision must never treat "we wrote it down" as "chain agrees."Related