OpenZeppelin / OpenZeppelin/compact-contracts
dev: add a proof-erased sim test backend (real ledger, no proving)
@andrew-fleming is already working on this.
Since Jul 16, 2026.
- Dominant language
- TypeScript
- Stars
- 55
- Forks
- 29
- Avg merge
- 5d 7h
- Merged PRs (30d)
- 25
Description
Context
The test suite has two backends today: dry (pure in-memory circuit logic, fast, but mocks ledger semantics — e.g. placeholder mt_index: 0n) and live by https://github.com/OpenZeppelin/compact-contracts/pull/673 (full node + proving, real fidelity, hours per category). Bugs in the coin-tree / dust / serialization / ledger-apply layer are invisible on dry and expensive to catch on live (#656 is the canonical example: dry missed it, live caught it).
A spike by @andrew-fleming confirmed a middle tier is possible with the official @midnight-ntwrk/wallet-sdk-capabilities Simulator: deploy + state-mutating circuit call ran proof-erased, fully in-process — real ledger-v8 LedgerState (coin tree, nullifiers, dust, serialization), no proof-server, node, indexer, or docker.
Proposal
Add a third backend, MIDNIGHT_BACKEND=sim, behind the existing backend seam:
dry— circuit logic floor: guards, reverts, arithmetic. Inner dev loop.sim(new) — real ledger semantics, proofs erased, instant blocks. PR-gate default for value / coin / serialization paths.live— real proving + node. Nightly smoke for whatsimcannot cover (circuit satisfiability, prover behavior, node acceptance policy).
Key design rule: no `` boolean. sim breaks the two-axis coincidence that `isLiveBackend()` relies on, so conditional sites re-key to capability predicates:
backend.hasRealLedger // dry:false sim:true live:true
backend.usesRealNode // dry:false sim:false live:true
backend.directLedgerAccess // dry:false sim:true live:false
Value branches (getQualifiedShieldedCoinInfo placeholder-vs-real mt_index) key on hasRealLedger; node-dependent skips key on usesRealNode. Some live skips flip to runs on sim (e.g. burn-success: the coin tree is in-process and directly queryable, no wallet ciphertext discovery needed).
StrictnessConfig is per-axis, so balancing / signatures / conservation can stay ON with only proof verification OFF.
Open questions (must be answered by the prototype)
- Value flows. The spike proved deploy + a no-coin circuit call. With
enforceBalancingON, a deposit needs a funded input coin in the zswap state, and there is no wallet or faucet in-process. Fabricate genesis-like coin state, or relax balancing (losing fidelity)? This is the make-or-break question. - Benchmark vs dry. Sim per-circuit-call cost is unmeasured (tx build + transcript + ledger apply + WASM serialize-bridge). Measure it; if sim lands within a small factor of dry, reconsider whether dry remains the PR floor.
- Serialize-bridge fragility.
RtContractState.deserialize(simContractState.serialize())couples two WASM instances (ledger-v8 vs compact-runtime). Pinwallet-sdk-capabilitiesand cover the bridge with a test so SDK bumps fail loudly. - Multi-party flows.
.as(alias)has no pooled wallets on sim; signer identity can map to witness-set keys as on dry, but coin ownership needs per-aliasZswapSecretKeys. Needs a design note before multisig specs pass.
Suggested first step
A one-file prototype: a SimSimulatorBackend registering as sim that runs one existing multisig spec green end-to-end, with the branches that spec hits re-keyed to capability predicates. The spike only proved the raw deploy+call round-trip; this proves the spec/DSL integration and answers the value-flow question.
Non-goals
- Replacing
live: proving bugs (e.g. the unprovable guarded-branch class) and node acceptance policy remain live-only. - Landing in #673: the harness PR stays as-is; this builds on its backend seam afterwards.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.