Skip to content

feat(cli,sdk): add l2 register-vk to register a prover verification key - #249

Merged
ilitteri merged 2 commits into
mainfrom
feat/register-verification-key
Aug 26, 2026
Merged

feat(cli,sdk): add l2 register-vk to register a prover verification key#249
ilitteri merged 2 commits into
mainfrom
feat/register-verification-key

Conversation

@ilitteri

Copy link
Copy Markdown
Collaborator

Motivation

Every ethrex upgrade of an L2 that verifies real proofs needs the new build's verification key registered against the commit hash it commits batches under. This has been true since ethrex v9.0.0, when verification keys became per-commit-hash and the Timelock became the OnChainProposer's owner.

It is not optional. commitBatch rejects a commit hash the deployment holds no key for, so an upgraded sequencer commits nothing until the key is registered — every commit reverts with MissingVerificationKeyForCommit() (0xf6b9798e) while the L2 keeps producing blocks. The symptom looks like a stuck committer, not a missing key.

Doing it by hand is easy to get wrong in two ways, and both failures look the same from the outside:

  • The hashed value is the full git sha the binary reports, as ASCII — the segment after HEAD- in ethrex --version. Hashing an abbreviated sha silently produces a different key, and the only symptom is that commits keep reverting. The ethrex docs carried a worked example using a shortened sha that does not reproduce; that is fixed in docs(l2): run the release upgrade test with a real prover, and fix the verification-key instructions ethrex#7203.
  • upgradeSP1VerificationKey is onlyOwner, and that owner is the Timelock, so the call has to be routed through emergencyExecute (Security Council) or Governance schedule + execute. Sending it straight to the OnChainProposer from an EOA reverts with OwnableUnauthorizedAccount (0x118cdaa7), which surfaces as an opaque abi-decode failure.

Description

Adds rex l2 register-vk (alias vk) and the SDK functions behind it.

rex l2 register-vk --commit <GIT_SHA> --vk <VERIFICATION_KEY> \
  --on-chain-proposer <OCP> --timelock <TIMELOCK> --private-key <SECURITY_COUNCIL_PK>
  • --commit takes the git sha and hashes it, or accepts an already-hashed 32-byte value, so the caller never has to know which form the contract wants.
  • --timelock routes through emergencyExecute. Omit it only when an EOA still owns the contract. If the direct call is rejected as not-owner, the revert is translated into a message saying to pass the flag rather than an abi-decode error.
  • --prover risc0 targets upgradeRISC0VerificationKey; the default is SP1.
  • --dry-run prints the derived commit hash and the key currently on chain without sending.
  • The key is read back after the transaction is mined, because a call routed to the wrong owner can be mined without taking effect. A re-run when the key already matches is a no-op.

New: sdk/src/l2/verification_key.rs (commit_hash_from_git_sha, get_verification_key, register_verification_key, Prover), plus the four contract signatures in sdk/src/l2/constants.rs.

How to test

Against a dev L2 deployed with --sp1 true (this is the flow lambdaclass/ethrex#7203 documents as Step 3.6 of the release upgrade test):

# what would happen, nothing sent
rex l2 register-vk --commit "$(./ethrex --version | sed 's|.*HEAD-||; s|/.*||')" \
  --vk 0x<key from the release's ethrex-contracts.tar.gz> \
  --on-chain-proposer "$OCP" --timelock "$TIMELOCK" --private-key "$PK" --dry-run

# register, then re-run: the second call reports nothing to do
rex l2 register-vk --commit ... --vk ... --on-chain-proposer "$OCP" --timelock "$TIMELOCK" --private-key "$PK"

Verified end to end against a live v24.0.0 → v25.0.0 upgrade: the dry run reproduced a hand-computed commit hash, the registration went through the Timelock and read back correctly, a repeat run reported nothing to do, and omitting --timelock produced the translated error. cargo check --all-targets, clippy and fmt are clean.

… key

Every ethrex upgrade of an L2 that verifies real proofs needs the new build's
verification key registered against the commit hash it commits batches under.
`commitBatch` rejects a commit hash it holds no key for, so an upgraded
sequencer commits nothing at all until this is done — it looks like a stuck
committer rather than a key problem.

Doing it by hand is easy to get wrong in two ways, both of which this command
removes:

- The hashed value is the full git sha the binary reports, as ASCII. Hashing an
  abbreviated sha silently yields a different key. `--commit` takes the sha and
  hashes it, or accepts an already-hashed 32-byte value.
- `upgradeSP1VerificationKey` is `onlyOwner`, and that owner is the Timelock in
  any deployment made since ethrex v9.0.0, so the call has to be routed through
  `emergencyExecute`. Passing `--timelock` does that; without it the direct call
  reverts with `OwnableUnauthorizedAccount`, which surfaces as an opaque
  abi-decode failure, so that revert is translated into a message saying to pass
  the flag.

The key is read back after the transaction is mined, because a call routed to
the wrong owner can be mined without taking effect. `--dry-run` prints the
derived commit hash and the key currently on chain without sending, and a
re-run when the key already matches is a no-op.

Verified against a live v24 -> v25 upgrade: dry run reproduced the hand-computed
commit hash, the registration went through the Timelock and read back correctly,
a repeat run reported nothing to do, and omitting --timelock produced the
translated error.
@ilitteri
ilitteri enabled auto-merge (squash) August 26, 2026 17:21
@ilitteri
ilitteri disabled auto-merge August 26, 2026 17:21
@ilitteri
ilitteri merged commit 6f17cc8 into main Aug 26, 2026
10 checks passed
@ilitteri
ilitteri deleted the feat/register-verification-key branch August 26, 2026 17:21
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