diff --git a/Cargo.lock b/Cargo.lock index 69b2d79..8a0b771 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1952,8 +1952,9 @@ dependencies = [ [[package]] name = "dig-clvm" -version = "0.1.1" -source = "git+https://github.com/DIG-Network/dig-clvm.git?rev=356771034afd54a61d6199bc5960786139e39988#356771034afd54a61d6199bc5960786139e39988" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd0d335284c54d0edbb92e4de204bfc1e2fce15914e68b969a8746d9531bee4" dependencies = [ "chia-bls 0.26.0", "chia-consensus", @@ -1966,18 +1967,17 @@ dependencies = [ "clvm-traits", "clvm-utils", "clvmr", - "dig-constants 0.1.0", + "dig-constants 0.9.0", "hex", "thiserror 2.0.19", ] [[package]] name = "dig-constants" -version = "0.1.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a16228a534843f72d269f6e6cb3b6202495195c128f1bad4c7b04b8bf70690" +checksum = "46a31979a82805496a6142427edc1eeeabf6b35e05169626529010cc41e071f7" dependencies = [ - "chia-bls 0.26.0", "chia-consensus", "chia-protocol", "hex-literal", @@ -1985,9 +1985,9 @@ dependencies = [ [[package]] name = "dig-constants" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a31979a82805496a6142427edc1eeeabf6b35e05169626529010cc41e071f7" +checksum = "10260fd1f143402e5c87d36671932c3f3fc7640da00a94fa7ffffd80a9b9a1b2" dependencies = [ "chia-consensus", "chia-protocol", @@ -1996,9 +1996,9 @@ dependencies = [ [[package]] name = "dig-constants" -version = "0.5.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10260fd1f143402e5c87d36671932c3f3fc7640da00a94fa7ffffd80a9b9a1b2" +checksum = "a6c8b18e4c82af7bd809037195f3c8af5c58fe6cbe4387ab4d2defbc9790c062" dependencies = [ "chia-consensus", "chia-protocol", @@ -2007,9 +2007,9 @@ dependencies = [ [[package]] name = "dig-constants" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6c8b18e4c82af7bd809037195f3c8af5c58fe6cbe4387ab4d2defbc9790c062" +checksum = "194aefd3f9ee813b10b43322c0381dbab55ac6d8cb99bc6c3782e53d60405dd5" dependencies = [ "chia-consensus", "chia-protocol", @@ -2250,7 +2250,7 @@ dependencies = [ "chia-traits 0.26.0", "dig-chainsource-interface 0.1.0", "dig-chat-protocol", - "dig-constants 0.4.0", + "dig-constants 0.9.0", "dig-dht", "dig-download", "dig-gossip", @@ -2295,7 +2295,7 @@ dependencies = [ [[package]] name = "dig-node-service" -version = "0.101.0" +version = "0.102.0" dependencies = [ "async-trait", "axum", @@ -2303,7 +2303,7 @@ dependencies = [ "base64", "clap", "dig-cert", - "dig-constants 0.4.0", + "dig-constants 0.9.0", "dig-logging", "dig-node-core", "dig-node-service", diff --git a/Cargo.toml b/Cargo.toml index 66c54dd..6123e64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ edition = "2021" # the ROOT manifest (`[workspace.package].version`), so it MUST be set here for a # release to fire (§3.6). The library crates (dig-node-core/dig-runtime/dig-wallet) # keep their own independent versions — only the released binary tracks the workspace version. -version = "0.101.0" +version = "0.102.0" # Release hardening, matching digstore: keep integer-overflow checks ON in release. # The node parses untrusted serialized input and does offset/length arithmetic over diff --git a/DEVELOPMENT_LOG.md b/DEVELOPMENT_LOG.md index eca8781..eafd15e 100644 --- a/DEVELOPMENT_LOG.md +++ b/DEVELOPMENT_LOG.md @@ -1245,3 +1245,50 @@ Sharp edge — advancing the wire cursor without re-implementing it: the shape s (discarding the values) so the wire format lives in one place and cannot drift; only the `custom` block is read by hand, and each value's JSON text is read as RAW BYTES — never `serde_json`-parsed — so nothing the hostile value describes is ever materialized. + +## dig-constants drift is a chia-line boundary, not a version-string gap (#2072) + +dig-node's lock carried FOUR `dig-constants` copies at once (0.1.0, 0.4.0, 0.5.1, 0.8.0) against a +published tip of 0.10.0. The instinct is to read that as four stale pins. It is not: only two of the +holders are dig-node's own crates. The rest are held down by upstream crates whose PUBLISHED metadata +names an old range, and a published range cannot be edited from a consumer — `dig-gossip` (`>=0.2, <0.5`), +`dig-nat` (`>=0.4, <0.6`), `dig-download` (`^0.8`), `digstore-chain` (`^0.5`), `dig-clvm` (`^0.9`). Each +needs its own release before dig-node can unify. Derive that set from `cargo tree -i dig-constants@`; +a manifest read shows only the two pins dig-node owns and hides the other five entirely. + +**The 0.10.0 tip is not reachable from this workspace at all, and the reason is not dig-constants.** +0.9.0 → 0.10.0 moved the crate from `chia-protocol` 0.26 / `chia-wallet-sdk` 0.30 to 0.36.1 / 0.34. +dig-node builds against the 0.26 line, including a VENDORED `chia-protocol` fork that `dig-gossip` +supplies through `[patch.crates-io]`. Depending on dig-constants 0.10 therefore links a SECOND +`chia_protocol` into the graph, and `DIG_MAINNET.genesis_challenge()` returns a `Bytes32` that no +function in the workspace accepts — eleven type errors of the form "expected `BytesImpl<32>`, found +`chia_protocol::bytes::BytesImpl<32>`". Being current on dig-constants is downstream of migrating the +whole node to chia 0.36; it is a platform migration wearing a dependency bump's clothes. 0.9 is the tip +of dig-node's chia line and is the correct target until that migration lands. + +**The one value that actually moved: the DIG L2 genesis challenge, in 0.1.0 → 0.4.0.** 0.1.0 shipped an +all-zeros PLACEHOLDER `DIG_MAINNET_GENESIS_CHALLENGE`, with all six AGG_SIG additional-data domains +correctly derived from that placeholder — self-consistent, so no derivation test could see it. 0.4.0 +finalized the real challenge (`0af98186…`) and recomputed all six. Every value is stable from 0.4.0 +through 0.10.0; 0.5.1/0.8.0/0.9.0 are purely additive (DIG_ASSET_ID, treasury hash/address, DEK labels, +`dig.local`, `rpc.dig.net`), and 0.9.0 → 0.10.0 changes only upstream chia plot-consensus FIELD NAMES, +no DIG value. So a bump anywhere at or above 0.4.0 is value-neutral, and the ONE copy that mattered was +0.1.0 — reached through `dig-clvm` 0.1.1, pinned by git rev in dig-wallet, whose spend-validation +`ValidationContext` therefore described a different chain identity than the rest of the node. Nothing +was mis-signed (that call site sets `DONT_VALIDATE_SIGNATURE`, and the signing domain is injected by the +caller, not read from `DIG_MAINNET`), but the divergence was one refactor away from mattering. Moving +dig-clvm to crates.io `0.2` removes it. + +The general lesson: when a shared-constants crate shows several versions in one lock, diff the VALUES +across them before treating the collapse as a chore, and diff the crate's own dependency line before +treating the tip as reachable. Here the version count was the least informative number in the problem. + +The placeholder was invisible to the test suite in both directions, and the reason generalizes: no +source line pins the genesis literal (`grep 0af98186 --include=*.rs` finds nothing), and every runtime +check reads `dig_constants::DIG_MAINNET.genesis_challenge()` on BOTH sides of its comparison. That is +circular — it passes identically under the real value and under an all-zeros placeholder, so the suite +stayed green while the defect shipped AND stayed green after it was fixed. A constant that is only ever +compared against itself is unguarded no matter how many assertions mention it. The guard therefore lives +where the defect is decided, in `dig-node-core/tests/dependency_tree.rs` against the workspace lock, as +a FLOOR (no copy below 0.4.0) rather than an inequality against the one known-bad release — 0.2.x and +0.3.x carry the same placeholder, so `!= "0.1.0"` would be bypassed by the next one. diff --git a/crates/dig-node-core/Cargo.toml b/crates/dig-node-core/Cargo.toml index dc419ad..98b05fc 100644 --- a/crates/dig-node-core/Cargo.toml +++ b/crates/dig-node-core/Cargo.toml @@ -215,10 +215,25 @@ dig-nat = "0.18" # are re-declared at this SAME rev in the workspace `[patch.crates-io]` (a git dep's own patches do not # apply transitively — the #1529 3-rev lockstep: this dep rev + the two patch revs move together). dig-gossip = { git = "https://github.com/DIG-Network/dig-gossip", rev = "06bd79fb359834b416c466eca770d4a3c3f71aa1", default-features = false, features = ["rustls", "relay"] } -# The canonical DIG network id (genesis challenge) for the gossip handshake / introducer scope. From -# crates.io: dig-nat 0.7 requires `>=0.4, <0.6` and dig-gossip 0.7.1 requires `>=0.2, <0.5`, so `0.4` -# sits in both windows and the whole graph resolves to ONE crates.io dig-constants 0.4.x. -dig-constants = "0.4" +# The canonical DIG network id (genesis challenge) for the gossip handshake / introducer scope. +# +# 0.9 is the TIP OF DIG-NODE'S CHIA LINE, and that — not 0.10 — is the correct target here (#2072). +# dig-constants 0.10.0 moved to chia-protocol 0.36.1 / chia-wallet-sdk 0.34, while this workspace +# builds against 0.26 / 0.30 including the chia-protocol fork dig-gossip vendors via +# `[patch.crates-io]` above. Depending on 0.10 links a SECOND chia_protocol and every `Bytes32` this +# crate reads off `DIG_MAINNET` stops unifying with the rest of the workspace. Being current on +# dig-constants is therefore downstream of the chia 0.36 migration, not a version-string edit. +# +# Values are identical from 0.4.0 through 0.10.0 — the L2 genesis challenge and its six AGG_SIG +# domains were finalized in 0.4.0 and have not moved since — so this bump is value-neutral and buys +# the additive surface (DIG_ASSET_ID, the treasury hash/address, `dig.local`, `rpc.dig.net`). +# +# The graph still carries 0.4.0/0.5.1/0.8.0 because upstream crates hold them down through their +# PUBLISHED ranges (dig-gossip `>=0.2, <0.5`, dig-nat `>=0.4, <0.6`, dig-download `^0.8`), which a +# consumer cannot edit. Collapsing to a single copy needs each of those to release against 0.9 — +# the release-first cascade on #2072. Do NOT pin this BACK to widen a window: the fix is upstream +# moving forward, never dig-node drifting backward. +dig-constants = "0.9" # -- L7 content-location DHT (PHASE-B, #163) --------------------------------------------------------- # The Kademlia DHT that maps DIG content (store / capsule / root / resource) to the peer_ids holding # it. The node LOCATES content it lacks via `find_providers`, and keeps its OWN held-inventory provider diff --git a/crates/dig-node-core/tests/dependency_tree.rs b/crates/dig-node-core/tests/dependency_tree.rs index 7539218..0221419 100644 --- a/crates/dig-node-core/tests/dependency_tree.rs +++ b/crates/dig-node-core/tests/dependency_tree.rs @@ -135,3 +135,70 @@ fn the_peer_client_and_pull_engine_are_not_duplicated() { ); } } + +/// A `major.minor.patch` triple, ordered, so a version can be compared against a FLOOR rather than +/// checked for equality against one known-bad release. +fn semver_triple(version: &str) -> (u64, u64, u64) { + let mut parts = version + .split(['-', '+']) + .next() + .unwrap_or(version) + .split('.') + .map(|p| p.parse::().unwrap_or(0)); + ( + parts.next().unwrap_or(0), + parts.next().unwrap_or(0), + parts.next().unwrap_or(0), + ) +} + +/// The release in which `dig-constants` replaced its PLACEHOLDER DIG L2 genesis challenge with the +/// real one. Every copy at or above this floor agrees on the chain identity; every copy below it does +/// not. See the assertion below for why this is a floor and not an equality check. +const REAL_GENESIS_FLOOR: (u64, u64, u64) = (0, 4, 0); + +/// **Proves:** no `dig-constants` copy in the resolved workspace predates the real DIG L2 genesis +/// challenge — that is, every copy is at or above 0.4.0. +/// +/// **Catches:** a dependency edit that pulls a pre-0.4.0 `dig-constants` back into the tree. That is +/// not hypothetical — it is the state this test was written to close (#2072). `dig-constants` 0.1.0 +/// shipped an all-zeros PLACEHOLDER `DIG_MAINNET_GENESIS_CHALLENGE`, and it reached production through +/// `dig-clvm` 0.1.1, whose requirement `>=0.1.0, <0.2.0` could never resolve forward off it. So +/// `dig-wallet`'s spend-validation `ValidationContext` described a different chain identity than the +/// rest of the node. 0.4.0 finalized the real value (`0af98186…`, the header hash of DIG L2 block +/// 9021277) and recomputed all six AGG_SIG additional-data domains from it. +/// +/// **Why the lock and not a source assertion.** No line of Rust pins the genesis literal, and none +/// should: every runtime check reads `dig_constants::DIG_MAINNET.genesis_challenge()` on BOTH sides of +/// its comparison, which is circular — it passes identically under the real value and under the +/// placeholder. The defect was therefore invisible to the entire suite, before the fix and after it. +/// It is decided by the BUILD, so it is asserted where it is decided. +/// +/// **Why a FLOOR and not `!= "0.1.0"`.** The property is "no copy predating the real genesis", not +/// "not that one bad release" — an equality check is bypassed by the next pre-0.4.0 version to appear +/// (0.2.x and 0.3.x carry the same placeholder). This is the same rule the release gate enforces as +/// its 0.4.0 floor; one property, asserted at two levels. +#[test] +fn no_dig_constants_copy_predates_the_real_genesis_challenge() { + let versions = locked_versions("dig-constants"); + + // Without this the assertion below is vacuous: a lock with no dig-constants at all — a rename, a + // vendoring, a botched merge — would satisfy an "every copy is current" claim while proving nothing. + assert!( + !versions.is_empty(), + "the workspace must resolve dig-constants; finding none means this assertion has stopped \ + guarding anything" + ); + + let below_floor: Vec<&&str> = versions + .iter() + .filter(|v| semver_triple(v) < REAL_GENESIS_FLOOR) + .collect(); + assert!( + below_floor.is_empty(), + "dig-constants {below_floor:?} predates 0.4.0, the release that replaced the PLACEHOLDER DIG \ + L2 genesis challenge with the real one. A copy below that floor puts a different chain \ + identity — and six differently-derived AGG_SIG domains — inside this binary. Find the holder \ + with `cargo tree -i dig-constants@` and bump it; the resolved set was {versions:?}" + ); +} diff --git a/crates/dig-node-service/Cargo.toml b/crates/dig-node-service/Cargo.toml index b754932..55e7fb3 100644 --- a/crates/dig-node-service/Cargo.toml +++ b/crates/dig-node-service/Cargo.toml @@ -157,9 +157,10 @@ dig-urn-resolver = "0.4.0" # `dig_constants::DIG_NODE_PORT` (§5.3) — the single-sourced canonical default localhost port # for client→node connections; `config.rs`'s DEFAULT_PORT reads it rather than re-declaring the -# literal. From crates.io "0.4" (#1280 cascade), matching dig-node-core / dig-nat 0.7 / dig-gossip -# 0.7.1, so the whole graph resolves to ONE crates.io dig-constants 0.4.x. -dig-constants = "0.4" +# literal. Held in lockstep with dig-node-core's pin (#2072) — 0.9, the tip of dig-node's chia 0.26 +# line (see that manifest for why 0.10 is a chia-migration question) — so the two node crates can +# never disagree about a canonical value. +dig-constants = "0.9" # Windows Service Control Protocol. service-manager only REGISTERS the service in the # SCM; the binary the SCM launches must itself speak the service protocol diff --git a/crates/dig-wallet/Cargo.toml b/crates/dig-wallet/Cargo.toml index e602237..73bbc45 100644 --- a/crates/dig-wallet/Cargo.toml +++ b/crates/dig-wallet/Cargo.toml @@ -86,7 +86,14 @@ async-trait = "0.1" # Pre-broadcast spend-bundle validation (C.6 fail-closed): validate_spend_bundle runs the # bundle through chia-consensus before any peer broadcast. Same chia-* 0.26 / SDK 0.30 line # as dig-wallet, so Coin/CoinSpend/SpendBundle unify across the crate boundary. -dig-clvm = { git = "https://github.com/DIG-Network/dig-clvm.git", rev = "356771034afd54a61d6199bc5960786139e39988" } +# +# crates.io, NOT the old git rev (#2072). That rev resolved to dig-clvm 0.1.1, which re-exported +# `dig_constants::DIG_MAINNET` from dig-constants **0.1.0** — the version whose DIG L2 genesis +# challenge was still the all-zeros PLACEHOLDER, with every AGG_SIG domain derived from it. Those +# values were finalized in 0.4.0, so the git rev pinned a spend-validation context describing a +# different chain identity than the rest of the node. Signature checking is disabled at that call +# site, so nothing was mis-signed, but the divergence must not be allowed to persist. +dig-clvm = "0.2" # Spend building (XCH/CAT), coin selection, signing, and NFT/DID/CAT singleton # reconstruction all reuse the canonical `chia-wallet-sdk` driver constructors # (SpendContext + StandardLayer + Cat::spend_all + the Nft/Did parsers) already depended on