Proofessoor
A clientless requestor that asks zkboost to prove Ethereum execution blocks without a specialized EL/CL fork, then tracks how every request goes.
Overview
Proofessoor
is a small, clientless requestor for zkboost. It watches Ethereum blocks, drives a proof request to zkboost for each one, and records how every request turns out without running an execution or consensus client of its own.
Clientless means it speaks only HTTP: to a Beacon API to read blocks, and to zkboost to request proofs. It runs no validator, signs nothing, and executes no blocks. That keeps it small and easy to run next to existing infrastructure.
- Client-agnostic: works with any consensus client that serves the standard Beacon API. No specialized fork required.
- Any block: head, genesis, finalized, justified, a slot number, or a
0xblock root; one-shot or a continuous stream of new blocks. - Observability: records when each block was observed, how long the request took to prepare, and how long proving took, with a built-in dashboard, Prometheus metrics, and a health endpoint.
Why Proofessoor?
The zk-proving stack is powerful but dense and tightly coupled. A typical setup runs proving backends (GPU machines running a zkVM), zkboost (the coordinator between backends and requestors), a specialized CL fork that has the proof lifecycle built in, and optionally a VC to sign proofs.
That coupling is a burden if all you want is to check whether your infrastructure can produce zk proofs:
- The specialized CL forks need active maintenance to track upstream.
- Standard consensus clients don't carry the proof lifecycle.
- Even fully set up, you still have to run and manage the specialized nodes. Your existing full node, validator, or beacon client does not help with that.
Proofessoor is built on a simple observation: the request format zkboost expects is independent of where the request came from. You don't need a specialized client just to test proving-infra capabilities. You need something that can turn a beacon block into the request zkboost wants.
How it works
Proofessoor reads a beacon block from any Beacon API, reconstructs the NewPayloadRequest zkboost expects, submits it, and tracks the outcome. zkboost coordinates the rest: it fetches the execution witness from the EL and drives whatever prover backend it is configured with.
Proofessoor is deliberately narrow: it requests proofs and tracks their status. It doesn't care how zkboost proves a block, whether through a single ZisK server, a cluster, or a mock backend. It only speaks zkboost's API.
Where it fits
Proofessoor is useful when you want to exercise proving infrastructure without turning your beacon node into part of the proof lifecycle. It gives operators and protocol developers a lightweight way to answer practical questions:
- Can this zkboost deployment accept and route proof requests?
- How long does request preparation and proof completion take end to end?
- Which stage failed when a block could not be proved?
- Can my proving infrastructure keep up with the blocks being requested?
In practice, Proofessoor sits next to an existing Beacon API and a zkboost instance. zkboost still owns the execution-layer RPC connection, witness retrieval, backend selection, proof generation, and verification. Proofessoor only turns blocks into requests, submits them, and records the outcome.
Request reconstruction
Skipping the specialized client is only possible because the proving request can be rebuilt from the beacon block alone. This is the protocol-critical part of Proofessoor.
- Fork-aware decode: Beacon blocks are SSZ-encoded, and SSZ carries no schema in the bytes; since each fork introduces different block structures, Proofessoor reads the
Eth-Consensus-Versionresponse header to decode every block against the right one. Only blocks that carry an execution payload can be proved, and a fork it doesn't support yet fails loudly rather than producing a wrong request. - Rebuilding the request: From the block alone it assembles the
NewPayloadRequesta specialized CL would normally produce. Some fields are lifted straight from the block; others have to be recomputed, and the exact set depends on the fork. - Matching the root: The
tree_hash_rootof the assembled request is thenew_payload_request_rootzkboost keys the request on, so it must match to the byte.
Proofessoor decodes blocks with lighthouse's types crate and builds the request from zkboost-types, which is itself built on lighthouse. Both are pinned to the same source so the request format stays identical to what the prover computes.
Operational view
For each block, Proofessoor records when it was observed, how long the payload request took to prepare, and how long proving took. This gives a holistic view of end-to-end latency and where the bottleneck is. Records are kept per request and exposed through its dashboard, status output, and Prometheus metrics.
- Persistent state: Status is stored locally as JSON behind a swappable interface (easy to back with a database). A separate Proofessoor instance can read a state directory to analyze results across combinations of EL, CL, guest program, and zkVM.
- Restart-safe: On restart, Proofessoor de-duplicates by request root, so it never re-requests a block it has already handled.
- Failure boundary: Failures are split by stage: a submit-stage failure is on Proofessoor's side of the wire, a prove-stage failure is the prover's (e.g. a witness timeout). Proofessoor records both and reconnects the beacon event stream when it drops, but never auto-resubmits a proof. zkboost owns proof coordination.
For exact command-line flags, environment variables, and Docker Compose stacks, see the Proofessoor repository.