Files
zeekayandHanzo Dev ba5b688839 Read the validator set from a file, the way the other two do
noded derived its validator set from --index I --n N --base-port P. That set
was a private convention: it could only agree with copies of itself that had
been told the same three numbers, and an operator had no way to hand one
network description to more than one implementation. The flags are gone.

What replaces them is the file the Rust node already reads: one line per
validator, `<identity> <key> <proof>`, `#` starts a comment, weight 1 each,
file order kept because --peers is positional against it. A node finds ITSELF
by name — its seat is where its own ML-DSA identity sits — so no process can be
told it is a validator it holds no key for. --publish prints this node's line.

Committee::root() is Go's encoding over the names and the uncompressed keys,
computed by the one implementation of it this repo has. For the fixture
committee it is cd75055e…f40ba23f in C++, in Rust, and in Go — the Go value
from a program importing luxfi/validators unmodified, the Rust one from a path
dependency on lux-rs/node, so the encoder is never compared to itself.

Possession is checked at the door: Committee::validators() goes through
consensus::admit, so a member whose proof does not bind its name to its key
never reaches the gate. Half the fixtures were published by the Rust node and
half by this one, interleaved; the refusal fixtures are files either daemon
refuses with the same words.

Both scripts are updated to publish and pass a committee. chain.sh's per-node
state-root check was passing vacuously — it compared five refusals of a
historical block to each other, and these nodes are frontier-resident — so it
now samples the tip and requires a real root.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-07 23:51:50 -07:00
..