Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
47 changes: 47 additions & 0 deletions DEVELOPMENT_LOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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@<ver>`;
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.
23 changes: 19 additions & 4 deletions crates/dig-node-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Comment thread
MichaelTaylor3d marked this conversation as resolved.
# -- 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
Expand Down
67 changes: 67 additions & 0 deletions crates/dig-node-core/tests/dependency_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<u64>().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@<version>` and bump it; the resolved set was {versions:?}"
);
}
7 changes: 4 additions & 3 deletions crates/dig-node-service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 8 additions & 1 deletion crates/dig-wallet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Comment thread
MichaelTaylor3d marked this conversation as resolved.
# 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
Expand Down
Loading