Task
Nothing anywhere verifies a mirror-coin claim against chain. Build the consumer.
Measured 2026-08-31 across all of modules/: MirrorCoin::advertises has eight call sites, and every one is inside dig-mirror-coin itself — two crate-internal (census.rs:723, query.rs:373) and six in its own tests. Zero consumers. No node, no peer, no DHT reader, no download path calls it.
So the economic guarantee of the whole mirror-coin system is unenforced end to end. Coins are created, listed, and reclaimed correctly; a peer's claim that it holds content backed by collateral is never checked by anybody.
Why this is the load-bearing gap, not one of several
Everything else in the family works or is close. The flywheel's premise is that a holder's advertisement is backed — that is what makes the collateral mean anything and what a bad actor would attack. Without a verifier, unverified_mirror_coin_id is a field that travels the wire and is read by nothing, and a peer can claim any bond it likes at zero cost.
The name of the wire field says this outright: unverified_. It is an attacker-supplied shortcut, correctly named, and the verification it is a shortcut to does not exist.
The contract already exists — implement against it, do not invent one
SYSTEM.md's mirror-coin row is normative and states the whole algorithm. Consult it first; the summary:
- Fetch the coin and verify it sits at
dig_mirror_coin::mirror_coin_puzzle_hash().
- Verify it is $DIG, with the asset id re-derived from the creating spend.
- Verify it carries the full collateral.
- Call
MirrorCoin::advertises(store_launcher_id, root_hash, epoch) — exact equality on the coin's memo-declared triple, plus the hint recomputed with the owner taken from the coin's own lineage proof, never from a memo.
Steps 1-3 alone prove only that a valid mirror coin exists somewhere. Step 4 is what binds it to the claim.
Verification MUST NOT be an arithmetic recompute of the morph alone. mirror_hint sums four terms, one an unbounded freely-chosen epoch, so a coin's author can solve for a value placing their advertisement on any other's hint — the crate asserts this itself in namespace.rs (a_freely_chosen_epoch_solves_onto_any_other_advertisements_hint).
The three failure directions that must be right
- Absence of
unverified_mirror_coin_id is NORMAL and must never read as "uncollateralised". A publisher that has not created its coin, an older publisher, and one mid-epoch-rollover all legitimately omit it. Fall back to the hint scan (discover/list) — slower, not weaker.
- A wrong pointer costs the publisher, not the verifier. One chain read, no retry loop; a miss falls straight back to the scan. It MUST NOT blocklist or penalise the publisher — a stale pointer is indistinguishable from an epoch rollover.
- A chain that cannot answer is not a failed verification. Keep "unverified" and "verified absent" distinct, the same three-state discipline
absence_established uses on the wire. Collapsing them is how a partitioned node starts rejecting honest peers.
Settle the shape first (§1.10)
Who verifies, and when? Candidates, each with a different cost profile: the download path before accepting a holder; the DHT reader on ingesting a provider record; a background sweep over known holders. Verifying on every provider record is a chain read per record and will not scale; verifying never is where we are now.
And what does a failed verification DO? Deprioritise the holder, refuse it, or merely record it? A verifier whose result nothing acts on reproduces today's situation with more code. State the answer before building.
Scope
The deliverable is a verifier that is actually called on a production path, plus what acts on its result. A crate-internal helper with no consumer is exactly the state this ticket exists to end — the acceptance is a call site, not a function.
Out of scope: changing dig-mirror-coin's verification primitive, which is sound and exercised by its own tests.
Evidence
Sweep of all modules/ on 2026-08-31, both origin/main and origin/loop/batch-mirror. Also note dig-node's only use of the crate's verifying surface is collateral_census.rs:42, which prices this node's own requirement network-wide — not a check of any peer's claim.
Acceptance
A peer advertising a bond it does not hold is detected on a production path, proven by a test with a coin that does not advertise the claimed triple — plus the three controls: a valid bond verifies, an absent pointer falls back to the scan rather than failing, and an unreachable chain yields "unverified" rather than "absent".
Parent
Epic https://github.com/DIG-Network/dig_ecosystem/issues/3166 · lifecycle #412 · pointer supply #435
Task
Nothing anywhere verifies a mirror-coin claim against chain. Build the consumer.
Measured 2026-08-31 across all of
modules/:MirrorCoin::advertiseshas eight call sites, and every one is insidedig-mirror-coinitself — two crate-internal (census.rs:723,query.rs:373) and six in its own tests. Zero consumers. No node, no peer, no DHT reader, no download path calls it.So the economic guarantee of the whole mirror-coin system is unenforced end to end. Coins are created, listed, and reclaimed correctly; a peer's claim that it holds content backed by collateral is never checked by anybody.
Why this is the load-bearing gap, not one of several
Everything else in the family works or is close. The flywheel's premise is that a holder's advertisement is backed — that is what makes the collateral mean anything and what a bad actor would attack. Without a verifier,
unverified_mirror_coin_idis a field that travels the wire and is read by nothing, and a peer can claim any bond it likes at zero cost.The name of the wire field says this outright:
unverified_. It is an attacker-supplied shortcut, correctly named, and the verification it is a shortcut to does not exist.The contract already exists — implement against it, do not invent one
SYSTEM.md's mirror-coin row is normative and states the whole algorithm. Consult it first; the summary:dig_mirror_coin::mirror_coin_puzzle_hash().MirrorCoin::advertises(store_launcher_id, root_hash, epoch)— exact equality on the coin's memo-declared triple, plus the hint recomputed with the owner taken from the coin's own lineage proof, never from a memo.Steps 1-3 alone prove only that a valid mirror coin exists somewhere. Step 4 is what binds it to the claim.
Verification MUST NOT be an arithmetic recompute of the morph alone.
mirror_hintsums four terms, one an unbounded freely-chosenepoch, so a coin's author can solve for a value placing their advertisement on any other's hint — the crate asserts this itself innamespace.rs(a_freely_chosen_epoch_solves_onto_any_other_advertisements_hint).The three failure directions that must be right
unverified_mirror_coin_idis NORMAL and must never read as "uncollateralised". A publisher that has not created its coin, an older publisher, and one mid-epoch-rollover all legitimately omit it. Fall back to the hint scan (discover/list) — slower, not weaker.absence_establisheduses on the wire. Collapsing them is how a partitioned node starts rejecting honest peers.Settle the shape first (§1.10)
Who verifies, and when? Candidates, each with a different cost profile: the download path before accepting a holder; the DHT reader on ingesting a provider record; a background sweep over known holders. Verifying on every provider record is a chain read per record and will not scale; verifying never is where we are now.
And what does a failed verification DO? Deprioritise the holder, refuse it, or merely record it? A verifier whose result nothing acts on reproduces today's situation with more code. State the answer before building.
Scope
The deliverable is a verifier that is actually called on a production path, plus what acts on its result. A crate-internal helper with no consumer is exactly the state this ticket exists to end — the acceptance is a call site, not a function.
Out of scope: changing
dig-mirror-coin's verification primitive, which is sound and exercised by its own tests.Evidence
Sweep of all
modules/on 2026-08-31, bothorigin/mainandorigin/loop/batch-mirror. Also notedig-node's only use of the crate's verifying surface iscollateral_census.rs:42, which prices this node's own requirement network-wide — not a check of any peer's claim.Acceptance
A peer advertising a bond it does not hold is detected on a production path, proven by a test with a coin that does not advertise the claimed triple — plus the three controls: a valid bond verifies, an absent pointer falls back to the scan rather than failing, and an unreachable chain yields "unverified" rather than "absent".
Parent
Epic https://github.com/DIG-Network/dig_ecosystem/issues/3166 · lifecycle #412 · pointer supply #435