Skip to content

feat(cli): add digest + set-validators subcommands to genesis command - #449

Merged
samlaf merged 3 commits into
mainfrom
genesis-digest-subcommand
Aug 5, 2026
Merged

feat(cli): add digest + set-validators subcommands to genesis command#449
samlaf merged 3 commits into
mainfrom
genesis-digest-subcommand

Conversation

@samlaf

@samlaf samlaf commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Human Summary

These are purely changes to the summit CLI that processes genesis files. It doesn't touch any of the main binary logic. I need this for TEE network bootstrap phase where I need to build the summit genesis.

2 separate PRs, which should probably be merged as a rebase to keep their history separate:

  1. add a genesis digest command to compute the SSZ digest of the genesis file, so that it can be pinned as part of the TEE's network-manifest.
  2. move the separate genesis binary that we had as a subcommand genesis set-validators since that's all that it does: injects (or overwrites) validators into a genesis template file.

LLM Summary

Two related gaps around genesis files, closed together.

Nothing printed a genesis file's config digest. That digest identifies the
chain a genesis founds: it derives the P2P and consensus domains, so two nodes
that disagree on it cannot authenticate each other as peers or verify each
other's certificates. It existed only inside a running node — no way to obtain
it offline, and no way to learn a genesis file was malformed short of having a
validator exit on it at startup.

Genesis emission lived in a separate genesis binary that no image ships
and that duplicated knowledge of the Genesis schema. Since what it writes is
the byte source of the digest — field set, hex spelling, validator order are all
chain identity — that logic belongs next to the type it emits, not in a binary
whose only caller is external tooling. Otherwise the next tool that needs to
write a genesis reimplements summit's canonical form and has to keep
reproducing it; dae625e already fixed that class of bug once, when this binary
carried its own copy of the Genesis struct and silently dropped fields the
runtime requires.

summit genesis set-validators -i <template> -v <validators.json> [-o <file>]
summit genesis digest <genesis.toml>

Commits

feat: add a genesis digest subcommand — prints the digest as a single
0x-prefixed hex line so tooling can shell out for it, the same way it already
does for seismic-reth genesis-hash. The file goes through
Genesis::load_from_file, exactly as a starting validator loads it, so a
successful digest doubles as a verdict that the genesis is well formed. Failures
exit non-zero with the parse or validation error on stderr rather than a panic
backtrace.

feat: move genesis emission into summit genesis set-validators — the
genesis binary's logic moves into the CLI and the binary is deleted. The
validators read from JSON become the whole set (replaced, never appended to),
emitted sorted by node public key because config_digest hashes them in file
order; everything else comes from the input untouched.

Behaviour changes from the deleted binary

  • -o names a file, not a directory, and is optional. With no -o the
    genesis goes to stdout, so a caller re-emitting it with current validator IPs
    can pipe instead of staging a temp file. Diagnostics go to stderr, so stdout
    carries genesis bytes and nothing else.
  • -i is now required. It defaulted to ./example_genesis.toml, so a
    forgotten flag silently founded a chain on the example's namespace, EL genesis
    hash, and stake bounds.
  • -g (eth_genesis_hash override) is gone. Nothing passed it, and it could
    emit a hash disagreeing with the template a network manifest commits to,
    quietly defeating that check.
  • Validators sort by decoded node key, so a 0x-prefixed or upper-case key
    in the JSON sorts correctly instead of panicking in from_hex().unwrap().
  • The emitted genesis is verified before it leaves the process. The rendered
    TOML is parsed back through the validator's own path for both destinations —
    writing a genesis no node can load is the failure this command exists to
    prevent, so it fails here rather than at someone's boot.

Worth a reviewer's eye

  • Genesis::from_toml_str is new in summit-types: parse + validate from a
    string, with load_from_file now delegating to it. It is what lets the
    emitter check its own output on the stdout path, where there is no file to
    reload.
  • example_genesis.toml gains a header stating what the digest does and does
    not cover (validators[].ip_address is the one excluded field), a separator
    marking which parameters are governed after launch via ProtocolParams.sol,
    and explicit values for the two governed fields that were relying on serde
    defaults.

Breaking

Tooling that resolved the genesis binary on PATH must point at
summit genesis set-validators in lockstep — the argv is otherwise unchanged
apart from -o now taking a file path. Nothing inside this repo referenced the
binary.

samlaf added 3 commits August 3, 2026 11:18
The genesis config digest identifies the chain a genesis file founds: it
derives the P2P and consensus domains, so two nodes that disagree on it
cannot authenticate each other as peers or verify each other's
certificates. Nothing printed it — the value existed only inside a
running node.

`genesis digest` prints it as a single 0x-prefixed hex line, so tooling
that needs to commit to a chain's identity can shell out for it the same
way it already does for `seismic-reth genesis-hash`.

The file goes through `Genesis::load_from_file`, exactly as a starting
validator loads it, so a successful digest doubles as a verdict that the
genesis is well formed. Failures exit non-zero with the parse or
validation error on stderr rather than a panic backtrace, since this is
meant to be shelled out to.
The `genesis` binary built a complete genesis file from a template plus a
validator list. Its logic moves into the CLI and the binary is deleted.

Emission belongs next to the type it emits. What this command writes is
the byte source of `config_digest`, so the field set, hex spelling, and
validator order are all chain identity, and a second implementation of
that serialization — in another language, in the tooling that founds
networks — would have to reproduce summit's canonical form and keep
reproducing it. dae625e already fixed this class of bug once, when the
binary carried its own copy of the `Genesis` struct and silently dropped
fields the runtime requires.

Beyond the move:

- `-o` names a file rather than a directory, and is optional: with no
  `-o` the genesis goes to stdout, so a caller re-emitting it with
  current validator IPs can pipe instead of staging a temp file.
  Diagnostics go to stderr, so stdout carries genesis and nothing else.
- `-i` is now required. It defaulted to `./example_genesis.toml`, so a
  forgotten flag silently founded a chain on the example's namespace,
  hash, and stake bounds.
- The `-g` eth_genesis_hash override is gone. Nothing passed it, and it
  could emit a hash disagreeing with the template a network manifest
  commits to, quietly defeating that check.
- Validators sort by decoded node key, so a `0x`-prefixed or upper-case
  key in the JSON sorts correctly instead of panicking in
  `from_hex().unwrap()`.
- The rendered genesis is parsed back through `Genesis::from_toml_str`
  (new; `load_from_file` now delegates to it) before being emitted, for
  both destinations. Emitting a genesis no node can load is the failure
  this command exists to prevent, so it fails here and not at a boot.

Tests cover what a binary couldn't: a complete, sorted genesis built
from reverse-ordered input, an emitted genesis that reloads with an
unchanged config digest, and a non-hex node key rejected.

example_genesis.toml gains a header stating what the digest does and does
not cover, a separator marking which parameters are governed after
launch, and explicit values for the two governed fields that were
relying on serde defaults.

Note for anyone bisecting: the tooling that shelled out to the deleted
binary needs its call site pointed at the subcommand in lockstep.
@samlaf
samlaf merged commit 633fc2e into main Aug 5, 2026
4 checks passed
@samlaf
samlaf deleted the genesis-digest-subcommand branch August 5, 2026 17:00
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.

1 participant