From d39422b4e6ae8601cafeb4946a133b727c9a3c1d Mon Sep 17 00:00:00 2001 From: Michael Taylor Date: Thu, 10 Sep 2026 01:23:06 -0700 Subject: [PATCH 1/3] chore: open lane for #3269 (unit 2 -- rewards chain port + listRewardDistributors) --- DEVELOPMENT_LOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DEVELOPMENT_LOG.md b/DEVELOPMENT_LOG.md index 53cb6ba6..087610ef 100644 --- a/DEVELOPMENT_LOG.md +++ b/DEVELOPMENT_LOG.md @@ -1647,3 +1647,4 @@ Two things worth carrying forward: difference is not scrutiny of the record — it is whether a wrong record can ever, by itself, produce a wrong outcome. Here it cannot: `Unbonded`/`Unverified` from the re-check discards the candidate no matter how confidently the record states it. + From 0d83d92063cb07cdcd9c0a6b8aaa49729874bc06 Mon Sep 17 00:00:00 2001 From: Michael Taylor Date: Thu, 10 Sep 2026 01:45:33 -0700 Subject: [PATCH 2/3] chore(rewards): add dig-rewards-coin 0.2 dep; record blocked-reader finding dig-rewards-coin 0.2.0 is published but ships no chain reader (its own state.rs module doc: SPEC 12.1's read_distributor is withheld pending DIG-Network/dig_ecosystem#3267). Separately, no registry in this codebase records which distributors this node funds. A "real" RewardsChainPort adapter over 0.2.0 therefore has no honest way to answer any of the four trait methods with live data yet -- reimplementing read_distributor or inventing a funded-distributor registry would be exactly the unreviewed money-shape guess kernel invariant 6 says to escalate instead of build. UnavailableChainPort remains the only production adapter; port.rs records the finding for the next unit. Refs #3269 --- Cargo.lock | 18 +++++++++ crates/dig-node-core/Cargo.toml | 5 +++ crates/dig-node-core/src/rewards/port.rs | 47 ++++++++++++++++++++---- 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 058fad93..0f43e859 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3002,6 +3002,7 @@ dependencies = [ "dig-peer", "dig-peer-selector", "dig-pex", + "dig-rewards-coin", "dig-rpc-protocol", "dig-sex", "dig-social-profile", @@ -3208,6 +3209,23 @@ dependencies = [ "tokio", ] +[[package]] +name = "dig-rewards-coin" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d1d998f4ad7966c83ce439a986c07cf0fc7ca7081a22782733145001ad73ded" +dependencies = [ + "chia-bls 0.36.1", + "chia-consensus 0.36.1", + "chia-protocol 0.36.1", + "chia-puzzle-types 0.36.1", + "chia-sdk-driver 0.36.0", + "chia-sdk-types 0.36.0", + "dig-constants 0.13.1", + "hex", + "thiserror 2.0.20", +] + [[package]] name = "dig-rpc-protocol" version = "0.11.0" diff --git a/crates/dig-node-core/Cargo.toml b/crates/dig-node-core/Cargo.toml index e4ad1616..5b1d4747 100644 --- a/crates/dig-node-core/Cargo.toml +++ b/crates/dig-node-core/Cargo.toml @@ -192,6 +192,11 @@ serde_json = "1" # method names + the peer allowlist from HERE (never hand-rolled) so the contract # cannot drift from the other node implementation or the discovery document (#1075). dig-rpc-protocol = "0.11.0" +# The reward-distributor coin driver (dig_ecosystem#3269 unit 2): the DIG-shaped CHIP-0051 constants, +# comment/eligibility/entries/epoch/fund/launch/payout shapes and the `DistributorSnapshot` state +# reader's OWN types. 0.2.0 deliberately ships NO chain reader (`state.rs`'s module doc, +# DIG-Network/dig_ecosystem#3267) — see `rewards/port.rs` for what that means for this unit's adapter. +dig-rewards-coin = "0.2" # The directed-message base protocol (epic #793/#796): the e2e seal/open pipeline + the typed envelope # the chat subsystem seals into. dig-node is the TRANSPORT — it seals an app-supplied opaque DIGCHAT1 # envelope to the recipient's 0x0010 BLS identity key and dig-gossip directed-sends the sealed bytes. diff --git a/crates/dig-node-core/src/rewards/port.rs b/crates/dig-node-core/src/rewards/port.rs index d9844fe9..201aa353 100644 --- a/crates/dig-node-core/src/rewards/port.rs +++ b/crates/dig-node-core/src/rewards/port.rs @@ -1,12 +1,45 @@ //! The chain port — the seam this whole engine is built against instead of `dig-rewards-coin`. //! -//! `dig-rewards-coin` is SPEC-only as of the tag this lane read: `src/lib.rs` is a documented -//! placeholder and `pub mod distributor {}` is empty. Implementing the driver is -//! DIG-Network/dig_ecosystem#3249, a sibling lane. So the prover engine is built COMPLETELY against -//! a narrow trait derived from the SPEC's own described surface (not from the driver's internals, -//! so it is stable across #3249 landing), tested with an in-memory fake, and the production -//! adapter — until #3249 ships — reports [`ChainPortError::Unavailable`] and runs no cycles. See -//! [`unavailable`] for that adapter. +//! `dig-rewards-coin` was SPEC-only as of the tag this lane first read it: `src/lib.rs` was a +//! documented placeholder and `pub mod distributor {}` was empty. So the prover engine is built +//! COMPLETELY against a narrow trait derived from the SPEC's own described surface (not from the +//! driver's internals, so it is stable across the driver landing), tested with an in-memory fake, and +//! the production adapter reports [`ChainPortError::Unavailable`] and runs no cycles. See +//! [`UnavailableChainPort`] for that adapter. +//! +//! # dig_ecosystem#3269 unit 2 — the driver shipped, but still with no reader (blocking finding) +//! +//! `dig-rewards-coin` 0.2.0 is published and adds real types — `DistributorSnapshot` / +//! `DistributorSlots` (its `state` module) plus `clawback`, `comment`, `constants`, `eligibility`, +//! `entries`, `epoch`, `fund`, `launch`, `payout`. **It still ships no chain reader.** 0.2.0's own +//! `state.rs` module doc says so directly: SPEC §12.1's `read_distributor` "does not publish one, +//! deliberately" — the implementation that existed applied +//! `RewardDistributor::from_parent_spend` to the eve coin's spend (the launch inner puzzle) instead +//! of `from_eve_coin_spend`, so every read reported `Malformed`; the correct hop additionally needs +//! `reserve_parent_id`/`reserve_lineage_proof` provenance a reader starting from a launcher id cannot +//! currently discover. That is tracked as real design work at +//! and 0.2.0's own doc states the rule the +//! future reader must honour: "every `ChainSource` error MUST become `RewardsError::ChainUnavailable` +//! … a distributor whose read failed MUST NOT render as 'no entries' or 'nothing accrued'". +//! +//! Separately, and independent of #3267: **nothing in this codebase today records which distributors +//! this node funds.** `funded_distributors` (below) needs that identity set as its starting point — +//! there is no chain-wide "list every distributor and filter to mine" call this crate can make (that +//! is the CLAIM side's `discover_distributors`, a different trait, a different filter, in +//! `dig-node-service`'s `rewards_claim::port::ClaimChainPort`) — and no local registry populates it +//! either (no launch flow, no config, no persisted launcher-id list was found in this crate or in +//! `dig-node-service`). +//! +//! So a "real" `RewardsChainPort` adapter over 0.2.0 cannot honestly answer ANY of the four trait +//! methods with live chain data yet: `funded_distributors` has no identity source, and +//! `distributor_state`/`submit_entry_writes`/`spend_new_epoch` all need the withheld reader (a spend +//! needs the live singleton coin `read_distributor` would supply). Writing one anyway — either by +//! reimplementing `read_distributor` myself or by inventing a funded-distributor registry with no +//! writer — would be exactly the kind of restated, unreviewed money-shape work SPEC §0.1 clause 1 and +//! this crate's own withholding of a broken reader argue against, and is the shape fork this ticket's +//! kernel invariant 6 says to escalate rather than guess. **Escalated to the L1; see this unit's +//! RETURN.** [`UnavailableChainPort`] remains the only production adapter for now — still correct, +//! since every real call would fail for one of the two reasons above regardless. use super::admission::AdmittedPeer; use async_trait::async_trait; From 939a2c44a64a03ce1f5e7172489b697fcd1656b8 Mon Sep 17 00:00:00 2001 From: Michael Taylor Date: Thu, 10 Sep 2026 01:48:50 -0700 Subject: [PATCH 3/3] docs(rewards): revert dep add, name both blockers with evidence in port.rs Per L1 direction: an unused dig-rewards-coin dep with no consumer is inert weight and would want whichever version ships the reader (0.3.0+, PR#6 open against DIG-Network/dig-rewards-coin), not 0.2 -- so it's reverted here and belongs in the unit that actually consumes it. Expanded the port.rs module doc to name both blockers explicitly with what was read (state.rs:1-31, #3267, the open reader PR) and the negative grep that found no funder-ownership registry anywhere in the tree, plus why serving dig.listRewardDistributors through UnavailableChainPort was considered and rejected (false capability signal; the exact "dispatch surface with no function behind it" pattern dig-node#593 was the last PR allowed to land on). No RewardsChainPort adapter, no Node wiring, no dispatch arm -- all three reward methods stay -32601 pending #3267 and a funder-ownership registry (parallel tickets, both required). Refs #3269 --- Cargo.lock | 18 --------- crates/dig-node-core/Cargo.toml | 5 --- crates/dig-node-core/src/rewards/port.rs | 51 ++++++++++++++++-------- 3 files changed, 35 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f43e859..058fad93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3002,7 +3002,6 @@ dependencies = [ "dig-peer", "dig-peer-selector", "dig-pex", - "dig-rewards-coin", "dig-rpc-protocol", "dig-sex", "dig-social-profile", @@ -3209,23 +3208,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "dig-rewards-coin" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d1d998f4ad7966c83ce439a986c07cf0fc7ca7081a22782733145001ad73ded" -dependencies = [ - "chia-bls 0.36.1", - "chia-consensus 0.36.1", - "chia-protocol 0.36.1", - "chia-puzzle-types 0.36.1", - "chia-sdk-driver 0.36.0", - "chia-sdk-types 0.36.0", - "dig-constants 0.13.1", - "hex", - "thiserror 2.0.20", -] - [[package]] name = "dig-rpc-protocol" version = "0.11.0" diff --git a/crates/dig-node-core/Cargo.toml b/crates/dig-node-core/Cargo.toml index 5b1d4747..e4ad1616 100644 --- a/crates/dig-node-core/Cargo.toml +++ b/crates/dig-node-core/Cargo.toml @@ -192,11 +192,6 @@ serde_json = "1" # method names + the peer allowlist from HERE (never hand-rolled) so the contract # cannot drift from the other node implementation or the discovery document (#1075). dig-rpc-protocol = "0.11.0" -# The reward-distributor coin driver (dig_ecosystem#3269 unit 2): the DIG-shaped CHIP-0051 constants, -# comment/eligibility/entries/epoch/fund/launch/payout shapes and the `DistributorSnapshot` state -# reader's OWN types. 0.2.0 deliberately ships NO chain reader (`state.rs`'s module doc, -# DIG-Network/dig_ecosystem#3267) — see `rewards/port.rs` for what that means for this unit's adapter. -dig-rewards-coin = "0.2" # The directed-message base protocol (epic #793/#796): the e2e seal/open pipeline + the typed envelope # the chat subsystem seals into. dig-node is the TRANSPORT — it seals an app-supplied opaque DIGCHAT1 # envelope to the recipient's 0x0010 BLS identity key and dig-gossip directed-sends the sealed bytes. diff --git a/crates/dig-node-core/src/rewards/port.rs b/crates/dig-node-core/src/rewards/port.rs index 201aa353..7eb2d5a0 100644 --- a/crates/dig-node-core/src/rewards/port.rs +++ b/crates/dig-node-core/src/rewards/port.rs @@ -11,24 +11,35 @@ //! //! `dig-rewards-coin` 0.2.0 is published and adds real types — `DistributorSnapshot` / //! `DistributorSlots` (its `state` module) plus `clawback`, `comment`, `constants`, `eligibility`, -//! `entries`, `epoch`, `fund`, `launch`, `payout`. **It still ships no chain reader.** 0.2.0's own -//! `state.rs` module doc says so directly: SPEC §12.1's `read_distributor` "does not publish one, -//! deliberately" — the implementation that existed applied +//! `entries`, `epoch`, `fund`, `launch`, `payout`. **It still ships no chain reader — blocker 1.** +//! 0.2.0's own `state.rs:1-31` module doc says so directly: SPEC §12.1's `read_distributor` "does not +//! publish one, deliberately" — the implementation that existed applied //! `RewardDistributor::from_parent_spend` to the eve coin's spend (the launch inner puzzle) instead //! of `from_eve_coin_spend`, so every read reported `Malformed`; the correct hop additionally needs //! `reserve_parent_id`/`reserve_lineage_proof` provenance a reader starting from a launcher id cannot //! currently discover. That is tracked as real design work at -//! and 0.2.0's own doc states the rule the -//! future reader must honour: "every `ChainSource` error MUST become `RewardsError::ChainUnavailable` -//! … a distributor whose read failed MUST NOT render as 'no entries' or 'nothing accrued'". +//! — as of this unit, open, with a PR up +//! (`DIG-Network/dig-rewards-coin#6`, `feat/3267-chain-reader`, +950/-80, targeting `0.3.0`) — and +//! 0.2.0's own doc states the rule the future reader must honour: "every `ChainSource` error MUST +//! become `RewardsError::ChainUnavailable` … a distributor whose read failed MUST NOT render as 'no +//! entries' or 'nothing accrued'". Read this adapter against `0.3.0`'s actual reader shape when it +//! ships, not against this description. //! -//! Separately, and independent of #3267: **nothing in this codebase today records which distributors -//! this node funds.** `funded_distributors` (below) needs that identity set as its starting point — -//! there is no chain-wide "list every distributor and filter to mine" call this crate can make (that -//! is the CLAIM side's `discover_distributors`, a different trait, a different filter, in -//! `dig-node-service`'s `rewards_claim::port::ClaimChainPort`) — and no local registry populates it -//! either (no launch flow, no config, no persisted launcher-id list was found in this crate or in -//! `dig-node-service`). +//! **Blocker 2, independent of #3267:** nothing in this codebase today records which distributors +//! this node funds. `funded_distributors` (below) needs that identity set as its starting point — +//! there is no chain-wide "list every distributor and filter to mine" call this crate can make. The +//! only adjacent registry is the CLAIM side's `ClaimChainPort::discover_distributors` in +//! `dig-node-service`'s `rewards_claim::port` — a **different trait**, filtering by mirror-admission +//! (which distributors this node might claim FROM), not by funder ownership (which distributors this +//! node funds); it is not a substitute. A repo-wide search for a funder-ownership registry — +//! `grep -rln "funded_launcher_ids\|FundedDistributor\|reward_distributor_registry\|create_distributor\|launch_distributor" crates/ --include=*.rs` +//! — returned **no matches** as of this unit's tip (worth re-running before assuming this is still +//! true; a negative search is a claim about a point in time, not a permanent fact). No launch flow, no +//! config, no persisted launcher-id list exists in this crate or in `dig-node-service` today. Tracked +//! as a separate ticket, parallel to #3267 (not downstream of it): a working reader tells a caller HOW +//! to read one distributor; it does not tell the caller WHICH launcher ids are its own. Both must land +//! before any of `dig.getRewardDistributor` / `dig.listRewardDistributorCommitments` / +//! `dig.listRewardDistributors`'s `funded` half can answer honestly. //! //! So a "real" `RewardsChainPort` adapter over 0.2.0 cannot honestly answer ANY of the four trait //! methods with live chain data yet: `funded_distributors` has no identity source, and @@ -37,9 +48,17 @@ //! reimplementing `read_distributor` myself or by inventing a funded-distributor registry with no //! writer — would be exactly the kind of restated, unreviewed money-shape work SPEC §0.1 clause 1 and //! this crate's own withholding of a broken reader argue against, and is the shape fork this ticket's -//! kernel invariant 6 says to escalate rather than guess. **Escalated to the L1; see this unit's -//! RETURN.** [`UnavailableChainPort`] remains the only production adapter for now — still correct, -//! since every real call would fail for one of the two reasons above regardless. +//! kernel invariant 6 says to escalate rather than guess. Escalated to the L1, and settled: no new +//! adapter and no dispatch arm land until a reader (0.3.0+) and the funder-ownership registry both +//! exist. **`dig.listRewardDistributors` stays `-32601` deliberately** — serving it through +//! `UnavailableChainPort` was considered and rejected: it would be a false capability signal (a +//! feature-probe or `rpc.discover` reading the method as implemented when it always errors) and the +//! exact "dispatch surface with no function behind it" pattern DIG-Network/dig-node#593 was the last +//! PR allowed to land on. `UnavailableChainPort` remains the only production adapter for now — still +//! correct, since every real call would fail for one of the two reasons above regardless. No +//! `dig-rewards-coin` dependency is added by this unit: an unused dependency with no consumer is +//! inert weight and would want whichever version ships the reader (0.3.0+), not 0.2 — add it in the +//! unit that actually consumes it. use super::admission::AdmittedPeer; use async_trait::async_trait;