Imported from zeko-labs/zeko (
AGENTS.md). Install upstream withnpx skills add zeko-labs/zeko. Copyright stays with the author.
AGENTS.md
Repo overview
- This is a fork of the Mina blockchain repository for the L2 project Zeko.
- Zeko-specific code lives under
src/app/zeko. - Changes to upstream Mina code are either prefixed with
zeko_or marked by aZEKO NOTEcomment.
Key directories
src/app/zeko/circuits: zero-knowledge circuits for the rollup smart contract.src/app/zeko/circuits/design: rollup and bridge design docs/specs (see notes below).src/app/zeko/da_layer: multisig DA layer code.src/app/zeko/sequencer: off-chain service that accepts transactions, proves them, sends to DA nodes, collects signatures, then submits to L1.src/app/zeko/sequencer/prover: proving service used by the sequencer.
Build
- Build the Zeko project with:
dune build ./src/app/zeko/sequencer ./src/app/zeko/da_layer ./src/app/zeko/signer
- The build can take longer than 10 seconds; prefer running with a longer timeout (e.g., 120s) in CI or local scripts.
Submodules
- Initialize submodules recursively after cloning.
- When enabling recursive Git commands, also make pushes check that referenced
submodule commits are published instead of trying to push the superproject
branch name into third-party submodule remotes:
git config --local submodule.recurse truegit config --local push.recurseSubmodules check
Testing
- Build the Zeko project first
- run ./src/app/zeko/tests/run-sequencer-test.sh fake 3 true false
Ethereum settlement export
src/app/zeko/sequencer/lib/ethereum_settlement_export.mlexports the real Pickles proof bundle consumed by the siblingethereum-settlementrepo.- Proof and verification-key JSON use a versioned, Zeko-owned OCaml wire. The proof wire contains the wrap proof plus padded recursion accumulators; the VK wire contains the wrap domain metadata and commitments required to reconstruct Kimchi's verifier index.
- Keep settlement serialization out of the
proof-systemssubmodule. Its recorded revision must be fetchable from the configured upstream remote so fresh clones and CI do not depend on an unpublished fork commit. - The Rust compatibility decoder is
ethereum-settlement/crates/pickles-verifier/src/wire.rs; change both sides together and preserve the legacy serde reader while retained fixtures use it.
Project language
- OCaml (Dune build system).
Design docs (circuits)
- Primary, current spec:
src/app/zeko/circuits/design/rollup-centralized-spec.md. src/app/zeko/circuits/design/README.mdnotes that other files may be out of date; treatsrc/app/zeko/circuits/design/oldas historical context only.- Core rollup model: outer (L1) and inner (L2) accounts with action states; commits synchronize a prefix of the outer action state into the inner account; inner steps append to outer action state and advance its length.
- Commit semantics: sequencer supplies a transaction SNARK and a
valid_whilerange; commit action records ledger hash, inner action state, synchronized outer action state, and length;valid_whilerange must be bounded. - Rollup state includes
sequencer,pause_key,da_key, andacc_set; pause is an emergency switch and can be updated via governance. - Transaction logic differences: failed transactions are not processed; time preconditions exist (currently unimplemented) and commits must use compatible slot ranges.
- Bridge/token transfer (see
bridge-explanation.md,bridge-spec.md): deposits are witnessed on the rollup outer account and finalized on L2 if followed by a commit before timeout; withdrawals are witnessed on L2 and finalized on L1 after a delay; helper accounts track last processed indices to prevent double spends; cancellations are handled via timeout and helper indices. - Bridge governance: may be separate from rollup governance; circuits can enforce vk hash matching across inner/outer bridge accounts; a failsafe design rotates multiple outer bank accounts to limit impact of a circuit bug.
Circuits (code concepts)
- Action states are typed fields with optional length tracking (
rollup_state.ml); inner app state stores the outer action state+length, outer app state stores the ledger hash, inner action state+length, sequencer key, pause key, DA multisig commitment, and account-set root. - Outer actions are either
Commit(rollup step summary) orWitness(arbitrary action payload); inner actions areWitnessonly (rollup_state.ml). rule_commit.mlverifies the transaction SNARK, DA multisig signatures over the target ledger hash, slot-range bounds, and action-state-extension proofs; it updates outer app state, emits aCommitaction, and requires a sequencer signature as a child update.rule_commit.mlhas an emergency branch that verifiesVerify_emergency_folders(Verify_both_ases + Count_commits), enforces amax_sequencer_inactivitygap, and allows commit without the sequencer precondition if no commits occurred since the last one.rule_action_witness.mlposts aWitnessaction on L1 and forbids actions while paused;rule_pause.mllets the pause key sign an update that setspaused = true.rule_inner_sync.mladvances the inner account’s stored outer action state using an A.S.E. proof;rule_inner_action_witness.mlposts innerWitnessactions.txn_rules.mlbuilds the rollup transaction SNARK with branches for signed commands, zkapp commands (proved/unproved), and merge; the statement type isTxn_state.Zeko_stmt.txn_state.mldefines the rollup SNARK statement, including source/target ledger hashes, account-set roots, slot ranges, and local zkapp state (must be empty at commit).ase.mlimplements action-state-extension folding circuits (with/without length) and is used by commit and inner sync to prove action-state progression.account_set.ml/indexed_merkle_tree.mlimplement the indexed merkle tree used for the account-set root recorded in the outer state.multisig.mldefines DA-layer multisig checking; only the commitment hash is stored in the outer state (da_key).- Bridge circuits use
bridge_state.mlfor enable/disable window parameters and helper-user indices;bridge_rules.mlwires deposit/withdrawal/cancel/enable/disable rules for Mina and custom tokens. folder.ml/folder.mliimplement a generic recursive “state machine” folding circuit (leaf/extend/merge rules) used by action-state extension proofs; it folds arrays of elements with optional mid-proof selection and configurable iteration counts.
DA layer (code concepts)
- Each DA node stores and signs diffs that deterministically transform a source ledger hash into a target ledger hash; signatures attest the node can reconstruct the target ledger.
diff.mldefines the DA diff format: source ledger hash, changed accounts (index + account), optional command with action-step flags, and a timestamp (V2 adds time; V1 is time-less).core.mlvalidates a posted diff by checking source hash, DB presence/genesis, unique indices, and recomputed target hash; it also validates receipt-chain updates for included commands before signing the target hash and persisting the diff.rpc.mlexposes Async.Rpc endpoints for posting diffs, fetching diffs, checking presence, listing keys, and fetching signatures/ledger-hash chains.node.mlruns the server with a local KV DB, signs ledger hashes, and can return the signature for a target hash. DA nodes receive ordered diffs from the sequencer; they do not restore or synchronize their databases from peer DA nodes.client.mlprovides RPC helpers with retry logic and (for sequencer usage) enforces ordered posting of diffs; it also includes optional relational DB tables for cached diffs/signatures.db.mlis the KV storage for diffs plus an index and migration marker;migrations.mlhandles version upgrades (e.g., adding the top-level version tag).
Sequencer (core flow)
zeko_sequencer.mlis the main orchestrator: it validates incoming user commands, applies them to the local ledger/IMT, queues proofs, posts DA diffs, and periodically commits to L1.- Transaction application uses
zeko_transaction_logic.ml, which applies Mina signed payments and zkapp commands to the local ledger, updates the indexed merkle tree, and returnsTxn_snark_witnesssegments plus a sparse source ledger for proof inputs. - Command validation checks pool capacity, minimum fee (dynamic based on prover queue size), slot-range bounds, and signature/proof validity via
verifier.mlbefore applying to state. - Applied zkapp commands have events/actions persisted to the archive; fee accumulation is tracked in local KV state and periodically converted into a fee-transfer command.
- DA integration: after applying a command, the sequencer builds a
Da_layer.Difffrom changed accounts and enqueues it to all DA nodes in order; commits use DA multisig signatures over the target ledger hash. - Proof generation uses a parallel merger (
Parallel_mergerviazeko_sequencer.ml) with three stages:Basecreates per-transaction snarks,Mergecombines two snarks into a larger proof, andCommitfinalizes a batch into an L1 zkapp command. committer.mlassembles the outer commit proof: it proves the txn snark, validates A.S.E. proofs, includes DA multisig witness, and constructs the rollup commit account update; it also persists commit witnesses for manual resend.update_inner_account(inzeko_sequencer.ml) syncs L1 actions into the L2 inner account using an inner-sync proof, then applies a dummy-fee zkapp command locally to advance action state before committing.prover/lib/prover.mlis the prover worker: it handles txn-snark branches (signed/zkapp/merge), folder/A.S.E. proofs, inner sync, and outer commit proofs;prover/lib/client.mlsends jobs via a message queue and caches A.S.E. proofs in Postgres.executor.mlis the L1 sender: it signs zkapp commands, infers/refreshes nonce, retries on transient failures, and serializes submissions to avoid nonce races.
Circuits (engineering knowhow)
- Zkapp action payloads are limited to ~100 field elements; large data should be compressed into hashes or split across transactions (emergency DA uses a single account per tx).
- Action payload encoding is done via
zeko_util.var_to_actions, which pushes a struct’s field elements into actions as data-as-hash. - Action-state extension proofs (A.S.E.) are built with
ase.ml+folder.ml; these are the canonical way to prove action-state progression with bounded iterations. - Emergency commits rely on a counted-commit fold:
Count_commitsproduces{source_action_state; target_action_state; n_commits}and the emergency rule requiresn_commits = 0and that counting starts right after the last commit action. - Valid-while ranges are inclusive in Mina; commit rules enforce max window size and subset checks against transaction snark slot ranges.
Mina zkApp model (transaction logic)
- zkApp transactions are a call forest of account updates; execution walks the forest with a call stack, deriving
caller_idbased onmay_use_tokenand parent relationships. - Each account update checks: account/ledger inclusion, account preconditions, protocol-state preconditions, valid-while range, and authorization (proof or signature) against the transaction commitment.
- Transaction commitments are computed from the account-update call forest;
use_full_commitmentswitches between transaction and full commitments for authorization and replay protection. - Permissions gate every field update (balance, app state, action state, permissions, delegate, nonce, voting-for, zkapp URI, verification key); updates are applied only when the corresponding permission controller authorizes.
- Action state update logic pushes events into action slots and may shift slots; in Zeko this shift can be forced by the sequencer (no time-based shifting).
- Receipt chain hashes are updated for account updates authorized by proof/signature, using the full transaction commitment and the account-update index.
- Account creation fees are handled inside zkapp logic; they may be deducted from balance or from fee excess depending on flags.
- Failed zkapp updates are disallowed on Zeko: local_state.success must hold for the last account update; failed transactions are rejected rather than partially applied.
Transaction SNARK structure
- Transaction snarks support base proofs for signed commands and zkapp command segments, plus merge proofs that combine two proofs into one.
- Zkapp command segments are categorized by authorization pattern:
Opt_signed,Opt_signed_opt_signed, orProved; proved segments require a side-loaded verification key. - The zkapp snark uses implied merkle roots from account+path to validate ledger inclusion and ensures consistent commitments across segments.
