Skip to content

fix(digstore-chain): anchor verify_pinned_root to the unforgeable launcher coin (#1473) - #33

Merged
MichaelTaylor3d merged 3 commits into
mainfrom
fix/1473-verify-pinned-root-launcher-anchor
Jul 22, 2026
Merged

fix(digstore-chain): anchor verify_pinned_root to the unforgeable launcher coin (#1473)#33
MichaelTaylor3d merged 3 commits into
mainfrom
fix/1473-verify-pinned-root-launcher-anchor

Conversation

@MichaelTaylor3d

Copy link
Copy Markdown
Contributor

Task

Fix the anti-rollback forge in digstore_chain::verify_pinned_root (singleton.rs). Tracks super-repo issue DIG-Network/dig_ecosystem#1473 (custody / anti-rollback, MVP).

The exploit (confirmed)

verify_pinned_root finds a tip by attacker-controllable HINT and gates only on the CURRIED store.info.launcher_id == store_id. Chia's singleton top-layer never binds the curried launcher_id to the real launcher coin_id, so an attacker launches a separate singleton from their OWN launcher coin (coin_id != store_id) currying launcher_id=store_id, root=R_evil, hinted to store_id -> verify returns Ok -> an honest node serves R_evil as chain-verified. sync_datastore is immune because it STARTS from coin_record(store_id) (unforgeable) and walks the real coin graph.

Fix (launcher-anchored backward walk)

Rewrite verify_pinned_root so trust chains to the unforgeable launcher coin:

  1. coin_record(store_id) MUST exist, be spent, puzzle_hash == SINGLETON_LAUNCHER_HASH.
  2. Find candidate tips by hint; require unspent.
  3. Walk BACKWARD via parent_coin_info until coin_id==store_id (SUCCESS) or parent-missing / MAX_HOPS (FAIL). At each hop parse ONLY the SingletonLayer and assert curried launcher_id==store_id (defense-in-depth, #747-immune).
  4. Read the tip's on-chain root; require == pinned_root.
  5. Err on every other path (never a false Ok).

Tests (simulator-based)

  • Impostor REJECTED (PoC; FAILs on old code, PASSes on new).
  • Genuine current root ACCEPTED; stale generation REJECTED; not-minted store_id REJECTED (fail-closed).

WIP — draft. Full triple gate (reviewer + security + adversarial re-forge) before merge.

Co-Authored-By: Claude noreply@anthropic.com

MichaelTaylor3d and others added 3 commits July 21, 2026 16:22
…uncher-anchor fix

WIP anchor per §1.8 push-early. Fix in progress.

Co-Authored-By: Claude <noreply@anthropic.com>
…stor root (#1473)

Co-Authored-By: Claude <noreply@anthropic.com>
…ncher coin (#1473)

verify_pinned_root gated only on the curried SingletonStruct.launcher_id, which
DataStore::from_spend reads verbatim and Chia's singleton top-layer does not bind
to the real launcher coin. An attacker could forge a singleton currying
launcher_id==store_id with an arbitrary root, hint it to store_id, and get an
honest node to serve that root as chain-verified (a break of the #127/NC-9/§5.3
anti-rollback invariant).

Anchor identity on the launcher coin (coin_id==store_id, an unforgeable 256-bit
preimage): each candidate tip must provably descend from it via a bounded backward
walk of coin records, and the launcher coin must exist, be spent, and carry the
singleton launcher puzzle hash. #747-immunity is preserved (only coin records are
required, never intermediate spends); the per-hop SingletonLayer check is
best-effort defense-in-depth.

Bump 0.19.1 -> 0.19.2 (behaviour-tightening security fix). SPEC §9.1 updated.

Co-Authored-By: Claude <noreply@anthropic.com>
@MichaelTaylor3d
MichaelTaylor3d marked this pull request as ready for review July 22, 2026 00:21
@MichaelTaylor3d
MichaelTaylor3d merged commit 4c34f0b into main Jul 22, 2026
10 checks passed
@MichaelTaylor3d
MichaelTaylor3d deleted the fix/1473-verify-pinned-root-launcher-anchor branch July 22, 2026 00:21
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