Repository navigation
feat!: replace RISC Zero proving with OpenVM [skip-line-limit] - #2191
hmzakhalid wants to merge 20 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedToo many files! This PR contains 216 files, which is 116 over the limit of 100. To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch. Upgrade to a paid plan to raise the limit. This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry. ⚙️ Run configuration
⛔ Files ignored due to path filters (9)
📒 Files selected for processing (216)
You can disable this status message by setting the
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Make OpenVM the default compute backend for CRISP. The support service runs the Secure Process in an OpenVM guest, and a separate worker generates the application proof, the recursive aggregate and the Halo2 EVM proof. Remove the legacy prover configuration and dependencies, and require explicit checked verifier bindings (OpenVmReceiptVerifier, OpenVmBfvCiphertextVerifier). Keep deployment-local artifacts and test data outside Git. This squashes feat/openvm-integration onto current main. Conflicts with main since 2026-09-19 are resolved to keep both sides: compute batching from main and phase observation from this branch in ComputeInput, main's CI change filters, main's scheduler defaults in the configs, and main's restructured docs and harness docs with the OpenVM wording applied.
gnosisguild/fhe.rs#210 merged the two FHE changes that the OpenVM guest applied as a local patch: lazy BFV multiplication tables and canonical power-basis decoding. Pin every fhe.rs dependency to that merge (873dc69, v0.4.1 plus #210) in the root, support, CRISP and template workspaces, and remove the patch, its setup script and the guest's [patch] section. This moves the FHE library for every ciphernode from v0.4.1 to 873dc69. The new code is additive or lazy: parameter construction no longer builds multiplication tables until a multiplication needs them, so a construction error appears at first use. Move the pin to v0.4.2 once it is tagged.
- Remove the `ctl` support links in the template and CRISP. They pointed to the deleted RISC Zero launch scripts, so `interfold init` failed to copy the template. - CRISP local development and the end-to-end test run the unproved development runner against a verifier that accepts every receipt (`CRISP_UNPROVED_TEST`, chain 31337 only). `CRISP_REAL_PROOFS=1` selects the configured OpenVM worker and the real verifier. - A config that still has `program.risc0` loads again, so ciphernodes that share the file keep starting; `interfold program` refuses it with a migration message. - The live OpenVM service test registers the secure parameter set at index 2. - The CRISP governance builder no longer sends mainnet operators to the RISC Zero activation. - The manifest and address checks know the OpenVM verifiers. - `update_revs.sh` rewrites only Interfold revisions, not the fhe.rs pin. - Correct stale RISC Zero comments, ignore entries and receipt-identity NatSpec.
The OpenVM path proved only CRISP's policy and capped a round at 1,024 inputs. - e3-compute-provider: `SecureProcess` reads a round in two passes, one ciphertext at a time: every input for its leaf, commitment and Keccak hash, then the selected inputs again, each refused unless it matches its first-pass hash. `ComputeInput::run` runs the same code over a round held in memory. The processor takes the selected ciphertexts as an iterator, and selection sees an `InputRecord` without ciphertext bytes. - Each project has its own guest (`guest/`) and proving service (`.interfold/support/openvm`), both linked to the project's `program/`. The guest's 512 MiB no longer bounds a round. - e3-openvm-host runs the program natively, writes the guest's input stream and runs the worker. At startup it uses the CUDA worker when one is configured and its `probe` opens a GPU, and otherwise the CPU worker. Every worker run has a deadline and is stopped at it. - interfold-openvm-prover (moved to crates/openvm-prover) reads framed input items, loads the Halo2 key and KZG parameters in `check`, and gains `probe` and `write-config`. - e3-program-server admits a request before reading its body, takes a configurable body limit, and retries callbacks. - `program.openvm` takes `prover_bin`, `prover_bin_cuda`, `backend`, `prover_config` and `setup_dir`. `interfold program compile` builds the guest, keys, receipt identity, worker configuration and service; the template and CRISP deploys read that identity by default. - `interfold init` copies the OpenVM service folder and pins the guest's Interfold crates to the template commit.
- CRISP leaf: pass the ciphertext hash as a slice. The OpenVM guest's SHA-256 takes `&[u8]`, so the guest did not build. - Template: the local scripts run the development runner and the mock receipt verifier unless TEMPLATE_REAL_PROOFS=1, as CRISP's do with CRISP_REAL_PROOFS. Before, `pnpm dev:setup` and `pnpm dev:all` needed OpenVM artifacts. - compile: rely on cargo-openvm's own OPENVM_BUILD_LOCKED handling, which passed --locked twice, and default the segment memory to 8 GiB, which fits a 16 GB GPU. - Worker: `execute` runs the guest without proving and checks the revealed digest against the host's journal. The CRISP fixture can write an insecure round and the guest input for it. - Docs: compute provider, verification, configuration, CLI, template, CRISP setup, tutorials and learn pages describe OpenVM, per-project guests, streamed inputs and GPU or CPU workers. Live network facts keep their RISC Zero verifiers.
…tlement check - CI builds the template's OpenVM proving service, and a new openvm_worker job runs the CPU worker's tests when crates/openvm-prover changes. The guest and proofs need a GPU host. - The ciphernode Dockerfile copies the two new workspace manifests, as check-invariants requires. - Program server: tests show that a full server refuses before reading the body and that an oversized body frees its slot. - The live CRISP service test credited the whole reward to the owner. Rewards are held per committee operator, so it now checks the RewardsDistributed amounts, each node's pending reward, and one operator's claim. - Worker lock: the CUDA build's direct openvm-cuda-common dependency.
… fixes - Program server: an admitted request must deliver its body within a deadline (default 120 s, `with_body_timeout`, OPENVM_BODY_TIMEOUT_SECS in the OpenVM service), or it gets 408 and its compute slot is released. Before, a stalled upload could hold the only slot. - Worker configuration: `compile`, `start`, and the template and CRISP deploys all use OPENVM_PROVER_CONFIG or .interfold/caches/openvm/prover.json. The `program.openvm.prover_config` key, added earlier in this branch, is removed, so the deploy can no longer read a different identity from the one the service proves. - The OpenVM `compile` and `start` build without --locked, because after `interfold init` the project's lockfile still names the template's path sources. - init pins only inline path dependencies on known Interfold crates, keeping their features. - CI: an openvm_guest job builds both guests and runs CRISP's on a fresh fixture with `cargo openvm run`, checking the revealed digest against the native journal (`pnpm openvm guest-parity`). - Provenance: --project selects the project whose lockfiles and guest configuration are recorded. - CRISP: setup.sh compiles the program when CRISP_REAL_PROOFS=1; README and docs give the real steps. The custom-circuit tutorial uses IOpenVmReceiptVerifier. The flow trace and invariants describe the streamed Secure Process and the per-project guest. The processor docs say the on-chain index is not bound by the proof. compile explains a missing cargo-openvm or Halo2 setup.
…uest CI filter - The program-server test sends a stalled upload through `handle_compute` and checks the 408 and that the compute slot is free again, instead of calling the body reader alone. - The openvm_guest job also runs for changes to crates/parity-matrix, which both guests compile, and to the project manifests and CRISP's lockfile, which the programs and the native fixture build from.
fhe.rs v0.4.2 is 873dc69, the merge of gnosisguild/fhe.rs#210 that the previous commits pinned by revision. Pin every fhe.rs dependency to the tag in the root, CRISP and template workspaces. The lock files record the tag for the same commit, so no dependency code changes. The docs name the tag again. The circuit SOURCE_HASH hashes the external pins in Cargo.lock, so it moves to a2d8be7b5816e40c. The circuits do not change.
5e0f979 to
3a6ee88
Compare
The support image now builds from the repository root, and the root .dockerignore lets in examples/CRISP/server, where setup.sh creates a .env that holds the server's keys on Sepolia. A maintainer who pushed the image from such a checkout would publish those keys. Exclude every .env and .env.* file except .env.example, after all allow rules so none can bring one back. crates/support/.dockerignore no longer applies to any build, because Docker reads the ignore file at the context root, and crates/support/tests is empty. Remove both.
interfold init points a new project's Interfold crates at git, so the Cargo.lock it copies is out of date and cargo build --locked refuses it. predev:all used to ignore that failure; it now runs compile_program.sh, which stops on it, so pnpm dev:all failed in every new project. The OpenVM compile and start scripts already build unlocked for the same reason.
The worker's check loads the application, aggregation and Halo2 proving keys and both KZG parameter files: 27.7 s and 17.9 GB on the GPU host. The shared 60-second command limit therefore left the manifest incomplete on a slower machine, and the reason it gave, "supply a working --prover", hid the timeout. The check now gets OPENVM_CHECK_TIMEOUT_SECS, the service's limit (default 30 minutes), and prints why it failed.
kill_on_drop and the deadline's kill reached only the worker process. A wrapper script that starts the prover as a child, for example to set LD_LIBRARY_PATH for CUDA, left the prover running and holding the GPU after the deadline. The worker now leads its own process group, and the deadline, or a cancelled request, kills the whole group. The new test starts a wrapper whose prover is a background child and checks that the deadline stops it. libc is a 0.2 range, not the workspace pin, so the CRISP and template workspaces keep the release they lock.
The seal carried the nine journal words, and OpenVmReceiptVerifier checked that their SHA-256 equals the caller's journal digest before it passed that digest to the Halo2 verifier as the proof's public value. The Halo2 verifier already binds the digest, so the words added 288 bytes of calldata to every verification and no security. The seal is now abi.encode(uint8 version, bytes proofData), 1,856 bytes, and the envelope 1,984 bytes. JournalDigestMismatch is gone: a proof checked against another journal fails in the Halo2 verifier. The version stays 1: no OpenVM deployment exists, and a seal in the earlier layout fails the canonical-encoding check, which a new test pins. The worker still verifies the proof against the journal before it writes a seal. The real-proof and live-service tests verify against the digest of a journal they rebuild, so they still show that a proof fails for any other journal.
OpenVmBfvCiphertextVerifier, CRISPProgram and the template's MyProgram stored the receipt identity they were given without comparing it with the identity their receipt verifier accepts. A manual or governance deployment with another value, such as the coming OpenVM cutover, would reject every proof, and each round would fail as a compute timeout billed to the requester. IOpenVmReceiptVerifier now exposes imageId(), and every constructor refuses an identity that differs from it. CRISPProgram could also change its identity and its verifier separately. setImageId is removed: setOpenVmVerifier takes the new verifier's identity with it. The CRISP test helper now deploys a mock receipt verifier that reports an identity, instead of an address without code.
The docs said the host and the guest run the same code, so the host's predicted journal is the guest's. They run the same SecureProcess, but the guest build swaps in OpenVM's Poseidon2, Keccak and SHA-256 and the fast power-basis commitment and RNS packing, which the host does not run. Agreement rests on the unit tests that compare each fast path with its reference and on running the real guest against the host's journal, which CI did only on an insecure fixture. CI now also runs a secure-8192 fixture, and the docs say what the agreement rests on: a mismatch stops proving, but cannot make a false proof, because contracts rebuild the journal from chain state.
- deployVerifier ignored its mock argument: USE_MOCKS no longer selects a compute mock, only CRISP_UNPROVED_TEST=1 does, on the local chain. Remove the parameter, and say in the deploy and the setup docs what USE_MOCKS still mocks. - crates/support/contracts/ImageID.sol can no longer be regenerated, but the secure CRISP upgrade scripts still read it. The build invariants now mark it frozen. - The server's .env.example set CRISP_BIND_ADDR=127.0.0.1:4000, which the docker-compose port mapping cannot reach. It is now a commented example; the default, 0.0.0.0:4000, serves Docker. - packages/interfold-sdk/tests/utils.test.ts only checked a constant that no code acts on.
openvm-proof.test.ts needs the journal of a real proof to check that the Halo2 verifier rejects every other digest. The live round now writes journal.bin next to seal.bin, so its proof can feed that test.
The tests write shell scripts and execute them while other tests in the same process fork. A forked child keeps a copy of a script this process still had open for writing until it executes, and executing the script then fails with ETXTBSY: 2 of 25 cargo test runs on the test host failed so, and 40 of 40 pass now. A child process writes the stand-in workers, and the process-group test runs its wrapper through /bin/sh and checks that the deadline, not the start, failed the run. CI's nextest runs each test in its own process and did not see the race.
What
OpenVM replaces RISC Zero and Boundless as the proving backend. The operator's own machine proves
each round, on a GPU when it has one and on the CPU otherwise. There is no proving market, program
upload or payment wallet. Each project proves its own E3 program, and CRISP ships its OpenVM
service, guest and contract verifiers.
guest/, its own Cargoworkspace) and a proving service (
.interfold/support/openvm), and both link the project'sprogram/. The guest, the native host and the contract therefore use one processor and oneinput policy.
interfold program compilebuilds the guest, its application key, theaggregation key and receipt identity (
prepare), the worker configuration and the service.interfold program startruns the service.interfold initcopies the service folder and pinsthe guest's Interfold crates to the template's commit. The template and CRISP both work this
way; the old CRISP-bound
crates/supportservice is gone.SecureProcess(e3-compute-provider) reads a round in two passes, oneciphertext at a time. The first pass builds every leaf and keeps each ciphertext's Keccak hash.
Selection runs over records without bytes. The second pass reads the selected ciphertexts again
and refuses any that differ from the first read. The guest therefore holds one ciphertext at a
time and its 512 MiB no longer bounds a round. The host runs the same code, so its predicted
journal is the guest's. The processor takes the selected ciphertexts as an iterator.
program.openvmnames a CPU worker (prover_bin) andoptionally a CUDA worker (
prover_bin_cuda), withbackend: auto|cpu|cuda. Withauto, theservice runs the CUDA worker's
probe, which opens a device; when that fails, or no CUDA workeris set, the CPU worker proves and the service logs why.
crates/openvm-prover,interfold-openvm-prover). Proves the application, aggregatesit, makes the Halo2 EVM proof, verifies it against the expected journal and both application
commitments, and only then writes a seal.
checkloads every key and both KZG parameter files atstartup.
executeruns the guest without proving and checks the revealed digest against thehost's journal. Every worker run has a deadline and is stopped at it.
within a deadline and under a configurable limit; callbacks are retried.
OpenVmReceiptVerifierbinds the Halo2 verifier, the executable commitment andthe VM commitment.
OpenVmBfvCiphertextVerifier(protocol) andCRISPProgram/ the templateprogram (application) rebuild the nine-word journal from chain state and verify the same
receipt. Deploys read the identity that
compilewrote unlessOPENVM_*settings name another.The legacy RISC Zero contracts and deployment records stay for the existing deployments.
a mock receipt verifier (chain 31337 only) unless
TEMPLATE_REAL_PROOFS=1/CRISP_REAL_PROOFS=1.v0.4.2. Every fhe.rs pin moves tov0.4.2, and the local patch is removed.pages describe OpenVM. The pages keep the deployed networks' RISC Zero verifiers as facts.
Run on the GPU host (RTX 5070 Ti, 32 cores)
compile: guest, keys, identity, worker configuration and service.execute): 300 insecure and 4 secure CRISP ballots; the guestrevealed the digest of the host's journal. A 734 MB stream (more than the guest's memory) ran
without the guest running out of memory.
pnpm openvm service-e2e), 4 secure ballots, GPU: the real proof verified onchain through both verifiers, a changed proof was rejected, CRISP published the output, the
settlement checked out and the indexed tally matched (about 9.5 minutes from compute to
publication).
logged the failed probe and proved the template's own program on the CPU (2 ballots, about 20
minutes). The proof verifies on chain against the real Halo2 and receipt verifiers bound to the
template's identity, and a changed journal digest is rejected.
pnpm openvm guest-parity(the new CI check) passes on a fresh fixture and fails when one journalbyte changes.
CI
All checks pass on 3a6ee88 (rebased onto main, fhe.rs
v0.4.2), includingbuild_circuits(pull, release provenance and every generated BFV verifier against this head's published
circuits),
zk_prover_e2e, the integration tests,openvm_guest,openvm_worker,template_integration,crisp_e2eandbuild_ciphernode_image.rust_unit_tests (doc)neededtwo reruns because the runner's
apt-get updatehung while installing solc.Circuit artifacts
The Noir circuits are unchanged, but this PR changes inputs of the circuit
SOURCE_HASH:crates/safe/src,crates/zk-helpers/srcand the external pins inCargo.lock(fhe.rsv0.4.2;the OpenVM crates that
e3-safeande3-compute-provideruse). The six preset/committee pairs forthis head (
SOURCE_HASHa2d8be7b5816e40c) are built with the CI toolchain (nargo 1.0.0-beta.26,bb 5.1.0) and pushed to
circuit-artifacts. They match main's published set except for the buildmachine's source paths in the compiled JSON; the bytecode and every verification key are identical.
pnpm store:circuits push.circuit-artifacts. Until this PR merges, that isthis PR's set, so a release cut from main fails its circuit check until main's set
(
f58562a8eff3c827) is pushed on top.Before merge
cutover is a separate PR.
Checklist
cargo testfore3-compute-provider (with and without
parallel), e3-openvm-types, e3-openvm-host,e3-program-server, e3-config, e3-support-scripts, e3-init; the CRISP program tests and
server check; the template program test; the worker's tests (CPU build); clippy and
cargo fmt --checkfor these crates and both projects; locked builds of both projects'services;
check-invariants,check-doc-sync, license headers. On the GPU host: the runsabove.
agent/CRATES_ARCHITECTURE.md,agent/flow-trace/04and08,agent/invariants/02describe the per-project guest, streamed inputs and worker selection.still derived from the consumed ciphertexts, every input still has a leaf, and programs still
compare the input root with their own.
program.openvmreplacesprogram.risc0, and the processor and selection APIs change.rebinds programs. The RISC Zero activation scripts must not be used for it.
Sol review of this revision.