Benefits of EOF (EVM Object Format) for Zero Knowledge Proofs
By Cairo / Succinct Residency 2024
The Ethereum Virtual Machine (EVM) is the computational heart of Ethereum. It enables developers to build decentralized applications accessible to anyone. One of the upcoming EVM updates is transformational: the EVM Object Format (EOF) upgrade introduces a series of improvements to the stack, control flow, and observability.
Not only are EOF contract transactions cheaper i.e., they consume less gas, but they're also incredibly ZK-friendly. Compared to the current EVM version, ZK proving of EOF contracts is 3x more efficient in terms of cycles and runs 2.69x faster, according to our benchmarks. This means using libraries like RSP or OP-Succinct will become even more affordable.
Best of all, in most cases, developers can enjoy these performance boosts without modifying their codebase—simply by upgrading to a version of the compiler with EOF enabled (solc in this case).1
Background
Currently, the EVM operates using dynamic jumps, where the destination of the jump is an argument on the stack. Although determining the jump destination is often trivial (e.g., when it is pushed onto the stack immediately prior to the jump), it can also be computationally infeasible (e.g., when the destination address is retrieved from storage). Furthermore, the number of possible execution paths increases combinatorially with the number of jumps and potential destinations. Consequently, dynamic jumps obfuscate the program's control flow and significantly impede static analysis techniques such as control-flow and data-flow analysis.
To address this, EOF enables instructions with immediate arguments (EIP-3670), replaces dynamic branching instructions with their static equivalent (EIP-4200), and introduces instructions for calling subroutines.
Another limitation of the current EVM is that instructions to manipulate the stack (such as SWAP and DUP) can only reach a depth of 16 items, while the maximum stack depth of the EVM is 1024. As EOF enables immediate arguments, the long-awaited EIP-663 can ease this issue by introducing SWAPN and DUPN, where N is an immediate argument with a value between 1 and 256.
The introduction of new opcodes in EOF enables leaner, more efficient contracts, reducing the gas costs of transactions for users. Additionally, validation can be performed once at deployment rather than during every runtime execution, decreasing the overall overhead of contract transactions. Alongside these improvements, linear-time static analysis accelerates every part of the toolchain and unlocks new tools. For example, faster control-flow analysis results in quicker compilers (including JIT) and clients, improved data-flow analysis, and more powerful security tools.
Proving with EOF
To concretely benchmark EOF, we implemented a Solidity smart contract that exposes a function which takes an integer input n and computes the n-th Fibonacci number mod 7919. We then compiled the smart contract using two versions of Solidity’s solc compiler: the latest stable version and the EOF experimental version. From each compilation, we obtained the corresponding runtime bytecode.
Since Revm’s interpreter supports EOF, we implemented two similar Rust programs that set up and use this interpreter to deploy each respective runtime bytecode and call the Fibonacci function with a fixed input. The programs are then compiled to RISC-V ISA and their RISC-V ELF files are generated.
To generate the proofs, we implemented an SP1 program that consumes the ELF files from each Rust program and proves their correct execution, generating a separate STARK proof for each. The deserialized proofs and public values are then verified to ensure correctness.
Performance Benefits
Reduced Proving Costs
To ensure accurate benchmarking, we leveraged SP1’s cycle tracking to measure the number of cycles spent in each phase of the program. Additionally, for both versions, we measured the end-to-end (E2E) execution time, kHz achieved, and the STARK proof size.
As shown above, the EOF program performs significantly better across all metrics. Specifically, the EOF program is roughly 3x more efficient in terms of cycles for the interpreter and total phases, while running 2.69 times faster. Furthermore, the generated STARK proof size is half the size of its counterpart, which allows for faster compression times.
When looking at the opcode usage from SP1’s zkVM, we notice that EOF generally requires half as many opcodes—there are significant savings for the BEQ, OR, BLTU, and SRL opcodes. The last opcode in particular, SRL, was used 1570 times by EOF and 46,680 times by the current EVM program, a decrease of 96.64%.
Reduced Transaction Costs
When looking at the benefits for the EVM, the EOF version demonstrates several key improvements. First, the EOF version requires 16.08% less gas when calling the Fibonacci function compared to the current EVM version. Additionally, the EOF bytecode is significantly smaller at 327 bytes, whereas the current EVM bytecode is 595 bytes, a 45.04% reduction in size. In terms of performance testing, the individually cached Foundry tests highlight substantial gains in efficiency. The EOF version completed testing in 0.602 seconds, while the current EVM version took significantly longer at 1.857 seconds.
How EOF Drives Performance Improvements
These impressive results can be attributed to several key improvements introduced by the EOF upgrade, which collectively enhance the efficiency of smart contract execution and proof generation in SP1.
From the interpreter perspective, the current EVM adds significant overhead, both in terms of opcode execution and cycle consumption, due to the dependence on dynamic jumps and the complexity it brings. On the other hand, EOF’s static jumps eliminate the need for runtime computation of jump destinations. This not only reduces the number of opcodes executed but also simplifies the control flow, enabling more efficient interpretation and execution. In the context of the Fibonacci contract—which relies heavily on loops and conditional branches—the use of static jumps significantly reduces the costs associated with branching. This efficiency is demonstrated by reduced opcode counts for BEQ (branch if equal), OR, and BLTU (branch if less than unsigned).
Furthermore, Solidity’s workarounds to avoid the stack limitations involve additional opcodes and complex stack shuffling. For instance, accessing deeper stack elements might require multiple SWAP and DUP operations or even bitwise shifts (SRL), inflating the opcode usage. In the Fibonacci contract, which performs iterative calculations and requires frequent access to various stack depths, this enhancement reduces the need for additional stack manipulation logic, resulting in the dramatic decrease in the usage of opcodes (like SRL) as the compiler no longer needs to emulate deeper stack access through inefficient means.
The cumulative effect of the above improvements translates to faster proof generation times and smaller STARK proofs.
New Unlocks
Aside from the enhanced performance, we are particularly excited about EOF as it unlocks new capabilities both within SP1 and beyond.
EOF's structured bytecode and static control flow make execution paths predictable and linearly analyzable, enabling the parallelization of SP1 proof generation across multiple transactions, significantly reducing overall proving time. This also means that transactions can be easily partitioned into independent segments without the risk of state conflicts or dependencies, which is key for efficient access lists. Moreover, since EOF separates data and code into distinct sections, it can play a pivotal role in making stateless clients more practical and efficient. Existing products that already leverage SP1, such as SP1 Reth and OP Succinct, can take advantage of these improvements by integrating EOF. Additionally, with EOF's stack improvements, the on-chain SP1 verifier can be optimized to manage the stack more efficiently, leading to cheaper on-chain proof verification for users.
The repository with the methodology and results can be found here. If you are interested in EOF and its intersection with SP1, we encourage you to reach out. You can find us at @cairoeth and @SuccinctLabs.
Footnotes
1. If a codebase uses opcodes that are deprecated in EOF, these must be manually updated to their equivalent EOF opcodes. Additionally, there are limitations in determining whether another address is a contract as EXTCODESIZE is not supported in EOF (EIP-7761 aims to address this).