Skip to content

feat: resolve a DID's current XCH payment address from chain (resolve_xch_address) - #3

Merged
MichaelTaylor3d merged 3 commits into
mainfrom
feat/resolve-xch-address
Jul 19, 2026
Merged

feat: resolve a DID's current XCH payment address from chain (resolve_xch_address)#3
MichaelTaylor3d merged 3 commits into
mainfrom
feat/resolve-xch-address

Conversation

@MichaelTaylor3d

@MichaelTaylor3d MichaelTaylor3d commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

What & why (Closes DIG-Network/dig_ecosystem#1239, adopts #1240)

Two coupled changes in one unit of work (§1.3b):

  1. resolve_xch_address — the money-critical DID→XCH payment-address primitive. Walks the DID
    singleton to its current authenticated tip and returns the tip owner's payment Address
    (tip.info.p2_puzzle_hash, bech32m under a caller-supplied prefix — network-agnostic, no
    hardcoded HRP). Chain-READ only: never signs, never broadcasts (INV-1/INV-2).
    • resolve_xch_address(launcher_id, prefix, source) -> DidResult<Option<Address>>
    • resolve_xch_address_from_did_string(did, prefix, source) -> DidResult<Option<Address>>
  2. Migrate the ChainSource seam onto the canonical dig-chainsource-interface v0.1.0 (crates.io):
    dropped dig-did's LOCAL ChainSource trait + SingletonLineage, depend on the published interface,
    re-export both so dig_did::ChainSource / dig_did::SingletonLineage are unchanged (additive).

New public surface: resolve_xch_address, resolve_xch_address_from_did_string, DidError::LauncherMismatch
(additive), and a re-exported chia_sdk_utils::Address.

Money-critical soundness (NC-9) — the parent-walk guard

A wrong resolution silently pays the wrong recipient, so the resolved address MUST be provably bound to
the REQUESTED identity. The cheap tip.info.launcher_id() == launcher_id check is INSUFFICIENT — that
curried launcher id is attacker-chosen (the pay_to_coin_wearing_a_singleton_puzzle_hash class). After
walk_did_lineage_to_tip, resolve runs authenticate_singleton(tip.coin.coin_id(), source) — which
walks the parent-spend chain to the GENUINE launcher — and requires that authenticated launcher to
equal the requested launcher_id, else DidError::LauncherMismatch. NC-9 (on-chain data requires
on-chain proof, fail-closed) is satisfied by this parent-walk authentication
, not by trusting any
attacker-chosen field. Proven load-bearing: neutralizing the guard makes the money test fail (resolves
to the attacker's address).

NC-8 is N/Aresolve_xch_address is a chain READ that writes nothing on-chain (no spend, no
broadcast).

Fail-closed None/error table (implemented exactly)

unlaunched/melted → Ok(None); malformed did:chia → InvalidDidString; tip creating-spend absent →
NoIdentitySingleton; tip not a DID → NotDid; spoofed curry → fail-closed (NotDid/NotASingleton);
genuine launcher ≠ requested → LauncherMismatch; chain read fails → Chain; over-deep → LineageTooDeep;
Address::encode fails → Parse.

Tests (TDD, adversarial)

38 tests pass. The 6 mandated adversarial tests: resolve_rejects_an_echoed_different_dids_tip (THE
money test — dishonest source echoes an attacker DID's tip for the victim launcher → LauncherMismatch,
and asserts the result is NOT the attacker's address), resolve_rejects_a_spoofed_curry_singleton,
resolve_returns_none_for_unlaunched_or_melted, resolve_from_did_string_rejects_malformed,
resolve_happy_path_matches_the_owner_address, resolved_address_roundtrips_through_decode (+ a
did-string happy-path). Uses the interface's MockChainSource (testing feature) + a Simulator-backed
source for genuine singleton spends. No live network.

Blast radius checked

ChainSource trait callers are all in-crate: authenticate_singleton[_bounded],
walk_did_lineage_to_tip, prove_lineage, and the test sources. The migration re-exports the interface
trait/type so the dig_did::* paths are preserved (additive externally — new crate, no external
implementors yet). resolve_xch_address is net-new (no callers). Risk: LOW-MEDIUM (the trait widens
required methods, inherent to adopting the canonical interface per #1240; existing lineage tests updated
to implement the full trait).

Quality gates (local)

  • cargo fmt --all -- --check clean · cargo clippy --all-targets --all-features -- -D warnings clean
  • cargo llvm-cov nextest --all --fail-under-lines 80 → 91.67% lines / 89.01% regions
  • RUSTDOCFLAGS=-D warnings cargo doc --no-deps clean · version bumped 0.3.0 → 0.4.0 (minor: additive)

Do NOT merge — handing back to the orchestrator for the triple gate.

Michael Taylor and others added 3 commits July 19, 2026 10:57
WIP anchor for #1239: resolve a DID's current XCH payment address from
chain via the ChainSource seam (reads only, fail-closed). Implementation
lands in follow-up commits on this branch.

Refs #1239 #1221

Co-Authored-By: Claude <noreply@anthropic.com>
Adopt the canonical dig-chainsource-interface v0.1.0 ChainSource trait (drop
the local trait + SingletonLineage; re-export both to preserve the public
paths). Add the money-critical resolve_xch_address / _from_did_string
primitives: walk the DID to its tip, authenticate the GENUINE launcher via the
parent-spend walk (never the attacker-chosen curried launcher id), then return
the owner's bech32m XCH Address. New additive DidError::LauncherMismatch.

Co-Authored-By: Claude <noreply@anthropic.com>
…tion

SPEC §3 U10 Resolve-XCH-address row, §6 LauncherMismatch row, §10 rewritten for
the canonical dig-chainsource-interface trait. README surface + types + module
map. CHANGELOG Unreleased. Fix a private intra-doc link in the public rustdoc.

Co-Authored-By: Claude <noreply@anthropic.com>
@MichaelTaylor3d
MichaelTaylor3d marked this pull request as ready for review July 19, 2026 21:13

@MichaelTaylor3d MichaelTaylor3d left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PASS — correctness/quality gate (independent fresh-context review)

Money-critical resolve_xch_address primitive reviewed against design #1239 (comment 5016826175). Verdict: PASS.

Money-guard load-bearing: CONFIRMED. Neutralized the launcher check (if false && authenticated.launcher_id != launcher_id) — resolve_rejects_an_echoed_different_dids_tip fails (resolves Ok(Some(...)) for the victim launcher instead of LauncherMismatch). The guard at src/resolve.rs:335-338 runs authenticate_singleton (parent-spend walk to the genuine launcher) AFTER walk_did_lineage_to_tip and BEFORE building/returning the address — correctly ordered, genuinely load-bearing, non-tautological.

Fail-closed: CONFIRMED. No path returns a wrong address or spurious Ok(None). Tip reconstructed from parent's genuine singleton successor (coin-id verified, resolve.rs:268-270); address validated via .encode() before return; every wrong case maps to a typed Err (LauncherMismatch/NotDid/NotASingleton/NoIdentitySingleton/LineageTooDeep/Chain/Parse). Spoofed-curry resolving to NotDid (vs NotASingleton) is fail-closed — design-table doc nit only.

Interface adoption, no regression: CONFIRMED. Local ChainSource/SingletonLineage dropped, re-pointed to dig-chainsource-interface = "0.1" (crates.io, no git dep, no [patch.crates-io]); dig_did::ChainSource/SingletonLineage paths preserved via re-export (resolve.rs:47, lib.rs:78-80). Lineage/auth walk compiles + all lineage tests green.

Gates: cargo test 38/38 green (36 lib + 2 golden); clippy -D warnings clean; fmt clean; coverage 91.67% lines / 89.01% regions (≥80%). Docs: SPEC §3 U10 + §6 LauncherMismatch, README, CHANGELOG, lib.rs re-exports (Address + resolve fns) all present. Version 0.4.0, additive. No cruft.

No blocking findings. No pre-existing review threads. Orchestrator: clear to merge on green checks + zero unresolved threads.

@MichaelTaylor3d
MichaelTaylor3d merged commit 45f31ad into main Jul 19, 2026
8 checks passed
@MichaelTaylor3d
MichaelTaylor3d deleted the feat/resolve-xch-address branch July 19, 2026 21:34
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.

2 participants