feat(nitro): automated enclave key self-registration (register CLI + --auto-register) - #938
Conversation
| // Optionally self-register the enclave's generated signing key on-chain before leasing | ||
| // any jobs. Without a registered key the proofs this worker submits would not verify. | ||
| if args.auto_register { | ||
| use world_chain_proof_nitro::register::{ |
There was a problem hiding this comment.
Why it is conditionally included here? What about moving it to top?
There was a problem hiding this comment.
Agreed — moved to the module-level use at the top of run.rs (with the other world_chain_* imports) in 7d10cda. It was a leftover local import; there's no reason to gate it since the whole file is already #![cfg(target_os = "linux")].
- register.rs: declare KeyAlreadyRegistered/KeyRevokedPermanently/InvalidPublicKey on the sol! interface so alloy can decode the reverts; make the idempotency race-check decode-independent by re-querying isKeyRegistered instead of matching the error string. Validate non-empty l1_rpc/registry/key up front. - worker run.rs: move the register import to module top (no conditional import). - Justfile proof-register-key: pass secrets (incl. funding key) over stdin instead of argv, with proper single-quote escaping, so keys don't leak into container argv / kubectl audit logs and metacharacters can't break out. Co-authored-by: Otto <otto@toolsforhumanity.com>
|
Addressed all review comments in 7d10cda (replied inline on each thread):
Verified: |
Resolves E0283/E0284 (ambiguous PartialEq for .parse()) in the new enclave_signer_address unit test; fmt. Co-authored-by: Otto <otto@toolsforhumanity.com>
- register.rs: wrap registerKey submit in a bounded retry loop (5 attempts, backoff) so multi-replica --auto-register survives funding-account nonce races and transient RPC errors; each attempt re-checks isSignerRegistered. - worker run.rs: drop --register-l1-rpc / REGISTER_L1_RPC_URL; registration reuses --l1-rpc / L1_RPC_URL. - prover-nitro: hex_to_pcr now strips an optional 0x prefix (parity with the worker's build_expected_pcrs). - Justfile: proof-register-key gains a Running-container guard before exec'ing and piping the funding key; clarify that proof-setup runs phases 0a-3b only and Phase 4 (register) is separate / handled by --auto-register. Co-authored-by: Otto <otto@toolsforhumanity.com>
…egistry - register.rs: only retry when the tx never mined (send/receipt error, e.g. a funding-account nonce race). A mined revert (unapproved PCR set, revoked signer, cold CertManager, bad hints) is deterministic, so bail immediately instead of resubmitting and burning gas. - Justfile: resolve the registry address with jq '// empty' so a missing key yields "" (caught by the required-var check) instead of the literal "null". Co-authored-by: Otto <otto@toolsforhumanity.com>
A transient RPC failure on the post-receipt registration check was being unwrap_or(false)'d and turned into a permanent bail, aborting --auto-register even when the key was already on-chain. Now the check's Result is matched explicitly: Ok(true) => success, Ok(false) => deterministic fail-fast, Err => retry. The pre-check is likewise tolerant of RPC errors (proceed instead of aborting). Co-authored-by: Otto <otto@toolsforhumanity.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit a3125d8. Configure here.
Classify registerKey send errors: a revert surfaced at estimation carries revert data (as_revert_data) and is deterministic (unapproved PCRs, revoked signer, cold CertManager, bad hints) -> fail fast; a transport/nonce error has no revert data -> retry. Makes the send-error path consistent with the already fail-fast mined-revert path. Co-authored-by: Otto <otto@toolsforhumanity.com>
The 'known gap' section is now implemented (PR #938); replace it with an 'Automated self-registration' section (register CLI / --auto-register / just proof-register-key) and a 'Kubernetes deployment (alphanet auto-register)' section that captures the rationale trimmed out of the crypto-apps values: production-mode enclave, probe-based keep-alive (jq + trap + sleep infinity), the funding-key provisioning chain (infra Terraform -> kube-ops -> application secret -> /etc/secrets), and PCR verification. Co-authored-by: Otto <otto@toolsforhumanity.com>

What
Automates on-chain registration of the nitro enclave's generated secp256k1 signing key, so operators no longer have to run the manual
castsequence described in the deploy runbook. Follow-up to the self-registration gap noted in #937.Three entry points, one shared implementation:
world-chain-prover-nitro register— one-shot CLI (dev/local).nitro-worker register— same flow on the long-running worker binary (this is what runs in the alphanet pod, so it's whatjustexecs).nitro-worker run --auto-register(AUTO_REGISTER=true) — startup hook: the worker registers its key before it starts leasing jobs.Plus a
just proof-register-key <env>recipe (Phase 4) that execsnitro-worker registerin the running worker pod, resolving the registry address from the deployment file.How the flow works
register::register_enclave_key(Linux +enclavefeature):public_key-embedding attestation from the enclave over vsock (NitroProver::get_public_key_async— thePublicKeyrequest, not the bareget-attestation).registerKey(attestationTbs, signature, attestationSigHints)calldata via the new pureregister::build_registration_calldata, which reuses:cose::decode_attestation_tbs— reconstructs the exact COSE_Sign1 TBS bytes + P-384 signature (matches on-chainNitroValidator.decodeAttestationTbs),attestation::leaf_cert_pubkey_xy— extracts the leaf cert P-384 key,p384_hints::collect_hints— the existing hint generator (called directly, no shelling out).registerKeytoNitroEnclaveKeyRegistrywith an alloy provider +PrivateKeySigner, waits for the receipt, and confirmsisKeyRegistered.isKeyRegisteredand also treats aKeyAlreadyRegisteredrevert as success.registerKeyis not owner-gated, so any funded key works (authorization is the attestation + the owner-approved PCR allowlist) — the CLIs takeREGISTER_PRIVATE_KEYand fall back toPRIVATE_KEY.Code changes
proofs/nitro/src/cose.rs(new) — pure COSE_Sign1 TBS/signature decoder + unit tests.proofs/nitro/src/register.rs(new) — pure calldata builder + gated async self-registration flow (alloy#[sol(rpc)]binding for the registry) + unit test.proofs/nitro/src/attestation.rs— addleaf_cert_pubkey_xy.proofs/nitro/src/bin/p384_hints.rs— reuse the sharedcosedecoder (removes a duplicate CBOR parser).proofs/nitro/Cargo.toml— alloy provider/contract/signer/url as optional deps tied to theenclavefeature.proofs/prover-nitro—registersubcommand.proofs/nitro/worker—registersubcommand +run --auto-registerstartup hook.Justfile—proof-register-keyrecipe (Phase 4).docs/proof/proof-cli.md— document the new subcommand + wrappers.Testing
Validated locally (Linux,
enclavefeature):cargo check -p world-chain-proof-nitro --features enclave— clean.cargo test -p world-chain-proof-nitro --features enclave --lib— 24 passed, including the newcose::*andregister::*tests.cargo check -p world-chain-prover-nitro -p world-chain-nitro-worker— clean (both binary crates).No live enclave / L1 was available in the dev environment, so the end-to-end vsock→
registerKey→confirm path (register_enclave_key) is exercised only via the pure calldata unit test, not against real hardware/chain. The alloy provider/contract/signer usage mirrors the existingdefender/proposerbinaries.Follow-ups
docs/proof/deploy-proof-system.mdPhase 4 to point atjust proof-register-key/--auto-registerand mark the manualcaststeps as fallback.Co-authored-by: Otto otto@toolsforhumanity.com
Note
Medium Risk
Submits real L1
registerKeytransactions and handles funding keys in deployment scripts; mistakes could leave workers unable to prove or leak keys if misconfigured, though authorization remains on-chain via attestation and PCR allowlist.Overview
Adds automated on-chain registration of the Nitro enclave’s generated secp256k1 signing key, replacing manual
caststeps soNitroProofVerifierwill accept that enclave’s proofs.A shared
register_enclave_keyflow fetches a public-key attestation over vsock, buildsregisterKey(attestationTbs, signature, attestationSigHints)via newcose::decode_attestation_tbs,leaf_cert_pubkey_xy, andp384_hints, then submits toNitroEnclaveKeyRegistrywith retries and idempotentisSignerRegisteredchecks. Entry points:world-chain-prover-nitro register,nitro-worker register, andnitro-worker run --auto-register.just proof-register-key(Phase 4) execs registration in the worker pod (secrets via stdin);proof-setupnow stops after phases 0a–3b and points operators at Phase 4 or auto-register. Docs updated for the new CLI.Reviewed by Cursor Bugbot for commit 85bf6ef. Bugbot is set up for automated code reviews on this repo. Configure here.