From 4c107a00da23ff4ca38538109a015a466c642453 Mon Sep 17 00:00:00 2001 From: a-shannon Date: Thu, 30 Apr 2026 00:28:53 +0200 Subject: [PATCH] =?UTF-8?q?EIP-0045:=20Native=20STARK=20Proof=20Verificati?= =?UTF-8?q?on=20=E2=80=94=20Draft=20specification?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eip-0045.md | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 eip-0045.md diff --git a/eip-0045.md b/eip-0045.md new file mode 100644 index 0000000..b2f3f60 --- /dev/null +++ b/eip-0045.md @@ -0,0 +1,183 @@ +Native STARK Proof Verification +=============================== + +* Author: a-shannon +* Status: Draft +* Created: 30-Apr-2026 +* License: CC0 +* Forking: soft-fork needed +* Reference Implementation: [sigmastate-interpreter PR #1116](https://github.com/ergoplatform/sigmastate-interpreter/pull/1116) + +Description +----------- + +This EIP proposes adding a native `VerifyStark` opcode to ErgoScript that enables on-chain verification of STARK (Scalable Transparent Arguments of Knowledge) proofs. This allows zero-knowledge virtual machine execution proofs — from systems like RISC Zero, SP1, and Valida — to be verified as a first-class L1 consensus operation. + + +Background And Motivation +------------------------- + +STARK proof verification involves finite field arithmetic over extension fields (BabyBear to Ext16), Poseidon hash evaluations, FRI (Fast Reed-Solomon IOP) folding with iFFT, Merkle tree path verification, and DEEP-ALI quotient polynomial checks. + +These operations **cannot** be expressed in ErgoScript within a single block's JIT budget using existing opcodes. Even with the `UnsignedBigInt` operations from EIP-0050, the overhead of interpreting field arithmetic through generic opcodes would exceed the block cost limit by orders of magnitude. + +A native opcode encapsulates the entire STARK verification pipeline in optimized Scala, provides preemptive AOT cost estimation from proof metadata to prevent DoS attacks, returns a simple Boolean, and is extensible via a `vmType` registry to support multiple zkVM architectures. + +### Use Cases + +* **Privacy pools**: Verify that a withdrawal proof was computed correctly without revealing the depositor's identity +* **Rollups**: Verify L2 state transitions on Ergo L1 +* **Verifiable computation**: Prove arbitrary program execution (RISC-V programs) on-chain +* **Cross-chain bridges**: Verify proofs of state from other chains + + +Native VerifyStark Opcode +------------------------- + +### Signature + +``` +VerifyStark( + proofChunks: Coll[Coll[Byte]], // Segmented proof data + publicInputs: Coll[Byte], // Public inputs to the computation + imageId: Coll[Byte], // Hash of the zkVM program image + vmType: Int, // AIR registry identifier + costParams: Coll[Int] // [Q, D, ...] cost derivation parameters +) => Boolean +``` + +### Parameters + +* `proofChunks` (`Coll[Coll[Byte]]`) — STARK proof data, split into chunks to avoid SigmaSerializer OOM on large proofs. Chunks are concatenated by the verifier. +* `publicInputs` (`Coll[Byte]`) — Public inputs to the verified computation (e.g., transaction hash, nullifier). +* `imageId` (`Coll[Byte]`) — 32-byte Blake2b hash identifying the zkVM program whose execution is being verified. +* `vmType` (`Int`) — Selects the AIR (Algebraic Intermediate Representation) constraint system. `0` = Fibonacci test AIR; future values map to RISC-V, Cairo, etc. +* `costParams` (`Coll[Int]`) — Parameters for AOT cost derivation: `[Q, D]` where Q = number of FRI queries and D = Merkle tree depth. + +### Return Value + +* `true` — the STARK proof is valid for the given public inputs and program image +* `false` — the proof is invalid, malformed, or the vmType is unrecognized + +The opcode never throws exceptions to user-level ErgoScript. Invalid proofs return `false`. Only `CostLimitException` (budget overflow) propagates upward to the block evaluator. + + +OpCode Assignment +----------------- + +``` +VerifyStarkCode = newOpCode(73) // byte value: 112 + 73 = 185 (0xB9) +``` + +This claims one slot from the reserved `73-80` gap in the opcode space. + +**Important**: The opcode space is fully saturated. `XorOfCode = newOpCode(143)` maps to byte `0xFF`. Any index >= 144 would overflow the signed-byte range and collide with existing opcodes. New opcodes MUST use reserved slots. + + +AOT Cost Model +-------------- + +The evaluation follows a three-phase strategy designed to prevent DoS attacks: + +### Phase 1: Preemptive Cost (O(1) scalars only) + +``` +aotCost = BASE_COST + (Q * PER_QUERY_COST) + (Q * D * PER_MERKLE_LAYER_COST) +``` + +This cost is charged before any byte arrays are evaluated. If it exceeds the block budget, `CostLimitException` is thrown immediately. + +### Phase 2: Data Loading (after cost secured) + +Only after Phase 1 succeeds, the heavy `proofChunks`, `publicInputs`, and `imageId` byte arrays are evaluated. + +### Phase 3: Byte Ingestion + Verification + +``` +ingestionCost = PerItemCost(base=10, perChunk=1, chunkSize=1024) * totalBytes +``` + +### Preliminary Cost Constants + +| Constant | Value (JIT units) | Rationale | +|---|---|---| +| BASE_COST | 5,000 | Transcript setup, OOD derivation, DEEP-ALI | +| PER_QUERY_COST | 50 | iFFT-8 folding + Horner evaluation per query | +| PER_MERKLE_LAYER_COST | 10 | Hash verification per query per Merkle layer | + +These constants are preliminary and require JMH benchmark calibration on the Foundation reference machine. + +### Example: Standard Proof (Q=35, D=16) + +``` +AOT cost = 5000 + 35*50 + 35*16*10 = 12,350 JIT units = 1,235 block cost +Block budget = 1,000,000 block cost +Utilization = 0.12% +``` + +### Example: DoS Attack (Q=1000, D=1000) + +``` +AOT cost = 5000 + 1000*50 + 1000*1000*10 = 10,055,000 JIT units +Block budget = 1,000,000 block cost (= 10,000,000 JIT) +Result: CostLimitException thrown BEFORE byte arrays loaded +``` + + +STARK Verifier Architecture +--------------------------- + +The verifier is implemented in pure Scala with zero external dependencies, located in `org.ergo.stark`: + +* `BabyBearField` — Prime field p = 2^31 - 2^27 + 1 +* `QuadraticTower` — Ext4 to Ext16 tower via x^2 - 11 (quadratic non-residue), Karatsuba multiplication +* `Poseidon1BabyBear` — Sponge hash with BabyBear-optimized S-box (x^7), width=16, rate=8, zero-allocation +* `MerkleVerifier` — Batch Merkle path verification with zero-sibling deduplication +* `FiatShamirTranscript` — QROM-safe Fiat-Shamir with rejection sampling and anti-grinding +* `FriVerifier` — FRI with folding factor 8 (Radix-8), unrolled iFFT butterflies, zero Ext16 inversions +* `DeepAliVerifier` — DEEP-ALI quotient verification with Montgomery batch inverse +* `StarkVerifier` — Top-level orchestrator +* `AirRegistry` — Pluggable AIR constraint registry for vmType dispatch + +### Security Level + +With Q=35 FRI queries and D=16 Merkle depth: 132-bit FRI soundness + 155-bit DEEP-ALI soundness, exceeding the 128-bit minimum requirement for institutional custody applications. + + +Serialization Format +-------------------- + +`VerifyStarkSerializer` serializes the 5 children in strict consensus order: + +``` +[proofChunks][publicInputs][imageId][vmType][costParams] +``` + +Each child is serialized using the standard `ValueSerializer.serialize/deserialize` protocol. + + +Security Considerations +----------------------- + +### DoS Resistance +The AOT preemptive costing model (Phase 1) ensures that an attacker cannot force expensive verification without paying the corresponding JIT cost upfront. The cost is computed from O(1) integer parameters, not from proof data. + +### Proof Inflation +Chunked `proofChunks` prevents memory exhaustion. The byte ingestion cost (Phase 3) ensures that padding and spam attacks are economically infeasible. + +### CostLimitException Propagation +The catch block in `eval()` explicitly re-throws `CostLimitException`. Without this, a generic catch-all would silently defeat the budget enforcement. + +### Soft-Fork Compatibility +Invalid proofs, unrecognized `vmType` values, and malformed data all return `false` without throwing user-visible exceptions. This ensures forward compatibility: older nodes treat unknown STARK proofs as invalid (conservative rejection), while upgraded nodes verify them. + + +Backward Compatibility +---------------------- + +This EIP requires a soft fork (new opcode activation via protocol version increment): + +* **Pre-activation nodes**: Will not recognize `VerifyStarkCode (0xB9)` and will reject transactions containing it (standard unknown-opcode behavior). +* **Post-activation nodes**: Will evaluate `VerifyStark` with the full STARK verifier engine. + +The opcode uses reserved slot 73 from the `73-80` gap, which was explicitly set aside for future consensus extensions.