Overview
ZKsync is rebuilding the core of the protocol around two complementary pillars: ZKsync Airbender, a next-generation proof system, and ZKsync OS, a modular execution layer capable of hosting multiple virtual machines.
In this section, we will deep dive into ZKsync OS.
ZKsync OS: The Execution Layer
ZKsync OS is a system-level implementation for ZKsync's state transition function. Under ZKsync's new architecture, execution is decoupled from proving. ZKsync OS acts as the operation layer in this new architecture. It takes block data and an initial state as input and computes the new state after the application of the block.
ZKsync OS is implemented as a Rust program that will be compiled to two targets. This first one, x86, is used for running in the sequencer. The second, RISC-V, is fed as an input to the ZKsync Airbender prover to produce the validity proof of the state transition.
Components of ZKsync OS
ZKsync OS is designed to support multiple VMs. This is needed to seamlessly migrate old Era chains while adding full native EVM equivalence. In addition, this will allow us to support alt-VMs.
The main components of ZKsync OS are:
- Bootloader: The entry point program. It initializes the system and then runs transactions using two components: the system and the execution environment interpreters.
- Execution Environments: Regular interpreters that take bytecode, calldata, resources (similar to gas) and some other call context values as its input. Interpreters are instantiated with some local state to execute a frame. When an interpreter sees a call to another contract, return/revert from current frame, or contract creation it triggers special functionality to process it, as a potentially different interpreter should be run.
- System: Common for all environments and the bootloader. Provides an abstract interface for low-level handling of IO (storage, events, L1 messages, oracles) and memory management. The system communicates with the external oracle (non-determinism source), which is needed to read block data , and also for some IO operations, e.g. to perform the initial read for a storage slot.
This modular design enables us to isolate a minimal interface required to implement an Execution Environment. In addition, the system abstraction makes the storage model customizable and allows for different instances of the entire system.
Running Environments
As mentioned before, we have two targets for ZKsync OS. However, this is not just a compilation target, but also how some system primitives are handled.
The two running environments are:
- Forward Running Mode: To be used in the sequencer. In such mode we expect code to be run on the usual platform with OS, so default memory allocator can be used (as it’s part of the OS). For non-determinism source, we can just pass Oracle's Rust implementation as a bootloader input. Some code can be skipped in this mode as well (e.g. merkle proof verification for the storage reads).
- Proving Running Mode: To be used during proof generation. The code runs on a pure RISC-V platform without an OS, so memory management must be handled manually. Additionally, special care is needed to pass external data into the RISC-V machine due to the absence of standard non-determinism sources. All behavior must be fully deterministic and provable.
System Resources
In ZKsync OS, the concept of "resources" is required to limit and charge for both computation (primarily proving) and data usage. This is more complex than it may initially appear: ZKsync OS is designed to be EVM gas-equivalent, meaning that EVM code execution should follow the same gas schedule as on Ethereum.
However, the EVM gas schedule does not accurately reflect the cost of ZK proof generation. To address this mismatch, ZKsync OS introduces double accounting: tracking both Execution Environment (EE) gas-equivalent to EVM gas, and a "native" computational resource that models the cost of proving.
L1 Integration
ZKsync OS is designed for use in ZK rollups and validiums, where state transition correctness must be verified on the settlement layer (referred to here as L1 for simplicity).
Specifically, a state commitment is stored on L1. For each block or batch, a proof is generated to verify that a valid state transition has occurred from a known L1 state commitment to a new one based on some set of inputs. This means the state pre and post transition must be included in the public inputs (or preimage) of the ZK proof.
In addition, the public input will include other components required for:
- Messaging
- Data availability (DA) validation
- Input validation
ZKsync OS also includes a messaging mechanism that enables trustless communication between L1 and L2, fully compatible with EraVM. This includes L1 to L2 transactions and L2 to L1 messages.