OpenZeppelin / OpenZeppelin/compact-contracts

dev: add a proof-erased sim test backend (real ledger, no proving)

Open
#690 0 comments 0 reactions 1 assignee View on GitHub

@andrew-fleming is already working on this.

Since Jul 16, 2026.

2-low effort: 3-M enhancement
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:

  1. dry — circuit logic floor: guards, reverts, arithmetic. Inner dev loop.
  2. sim (new) — real ledger semantics, proofs erased, instant blocks. PR-gate default for value / coin / serialization paths.
  3. live — real proving + node. Nightly smoke for what sim cannot 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 enforceBalancing ON, 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). Pin wallet-sdk-capabilities and 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-alias ZswapSecretKeys. 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.