Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Brush up repo for publishing (pt. 3) #60

Merged
merged 32 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
d754ca4
Place snapshot inside VM
slowli Sep 13, 2024
f011adc
Place `W: World<_>` bounds everywhere
slowli Sep 13, 2024
13689bb
Make `Snapshot` private
slowli Sep 13, 2024
fceafc1
Brush up `Program` construction
slowli Sep 13, 2024
690c72e
Reduce visibility of `Program` methods
slowli Sep 13, 2024
9f72c5f
Remove UB in invalid instruction handling
slowli Sep 13, 2024
ff157bc
Move integration tests to library
slowli Sep 13, 2024
a3cf1d3
Document VM interface
slowli Sep 13, 2024
22fbfd6
Document VM implementation
slowli Sep 13, 2024
d04126e
Document instruction; make `PtrOp` private
slowli Sep 13, 2024
779e5c8
Export `WorldDiff`-related types
slowli Sep 13, 2024
1c996c0
Enable more clippy lints
slowli Sep 13, 2024
6139f00
Enable even more clippy lints
slowli Sep 13, 2024
71f4e38
Simplify immediate getters
slowli Sep 13, 2024
5057923
Simplify binary ops
slowli Sep 13, 2024
f2450ae
Revert "Remove UB in invalid instruction handling"
slowli Sep 13, 2024
210fd04
Remove UB in `offset_from`
slowli Sep 13, 2024
753af1b
Fix clippy lints in test code
slowli Sep 13, 2024
edc2960
Fix clippy lints config
slowli Sep 13, 2024
ab46e97
Add doc generation to CI and readme badges
slowli Sep 13, 2024
b692d88
Remove unnecessary tracer / world bounds
slowli Sep 17, 2024
ccc0bd0
Use `match *self {}`
slowli Sep 17, 2024
7086e4a
Use "heap" instead of "heap page"
slowli Sep 17, 2024
13da271
Brush up interface terminology
slowli Sep 17, 2024
9cf7142
Fix misc review suggestions
slowli Sep 17, 2024
0f2f2e9
Remove `inline(always)` from `Instruction` constructors
slowli Sep 17, 2024
6664f26
Enable index page for docs
slowli Sep 17, 2024
fbabb85
Revert `invalid_instruction()` changes
slowli Sep 17, 2024
2426c14
Use `BTreeSet` in `RollbackableSet`
slowli Sep 17, 2024
f0d8f29
Downgrade FIXME to TODO, provide more details
slowli Sep 17, 2024
1ae1ba8
Revert unnecessary changes
slowli Sep 17, 2024
e523bce
Move doc comment
slowli Sep 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .clippy.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Configures additional identifiers excempt from `clippy::doc_markdown` lint
doc-valid-idents = ["EraVM", "ZKsync", ".."]
35 changes: 34 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ on:

env:
CARGO_TERM_COLOR: always
# Nightly Rust necessary for building docs.
RUST_NIGHTLY_VERSION: nightly-2024-08-01

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
Expand All @@ -32,7 +34,7 @@ jobs:
uses: Swatinem/rust-cache@v2

- name: Install cargo-afl
run: cargo install cargo-afl --version=^0.15 --force
run: cargo install cargo-afl --version=^0.15 --locked --force

- name: Build project
run: |
Expand Down Expand Up @@ -65,3 +67,34 @@ jobs:
run: |
AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 \
cargo afl fuzz -i tests/afl-fuzz/in -o tests/afl-fuzz/out -V 60 target/debug/zksync_vm2_afl_fuzz

document:
needs:
- build_and_test
if: github.event_name == 'push' && github.ref_type == 'branch'
permissions:
contents: write
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4

- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.RUST_NIGHTLY_VERSION }}
- name: Rust Cache
uses: Swatinem/rust-cache@v2

- name: Build docs
run: |
cargo clean --doc && \
cargo rustdoc -p zksync_vm2_interface -- -Z unstable-options --enable-index-page && \
cargo rustdoc -p zksync_vm2 -- -Z unstable-options --enable-index-page

- name: Deploy
uses: JamesIves/github-pages-deploy-action@v4
with:
branch: gh-pages
folder: target/doc
single-commit: true
13 changes: 13 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,16 @@ zk_evm = { git = "https://github.com/matter-labs/era-zk_evm.git", rev = "b7caa02
# Dependencies within the workspace
zksync_vm2_interface = { version = "0.1.0", path = "crates/vm2-interface" }
zksync_vm2 = { version = "0.1.0", path = "crates/vm2" }

[workspace.lints.rust]
missing_docs = "warn"
missing_debug_implementations = "warn"
unreachable_pub = "warn"

[workspace.lints.clippy]
all = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }
must_use_candidate = "allow"
module_name_repetitions = "allow"
inline_always = "allow"
struct_field_names = "allow"
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# High-Performance ZKsync Era VM (EraVM)

[![Build Status](https://github.com/matter-labs/vm2/actions/workflows/ci.yml/badge.svg)](https://github.com/matter-labs/vm2/actions)
[![License: MIT OR Apache-2.0](https://img.shields.io/badge/License-MIT%2FApache--2.0-blue)](https://github.com/matter-labs/vm2#license)

A high-performance rewrite of the out-of-circuit VM for ZKsync Era (aka EraVM).

See [Era docs](https://github.com/matter-labs/zksync-era/tree/main/docs/specs/zk_evm) for the VM overview and formal specification.
Expand Down
3 changes: 3 additions & 0 deletions crates/vm2-interface/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ categories.workspace = true

[dependencies]
primitive-types.workspace = true

[lints]
workspace = true
6 changes: 6 additions & 0 deletions crates/vm2-interface/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Stable Interface for ZKsync Era VM

[![Build Status](https://github.com/matter-labs/vm2/actions/workflows/ci.yml/badge.svg)](https://github.com/matter-labs/vm2/actions)
[![License: MIT OR Apache-2.0](https://img.shields.io/badge/License-MIT%2FApache--2.0-blue)](https://github.com/matter-labs/vm2#license)

**Documentation:**
[![crate docs (main)](https://img.shields.io/badge/main-yellow.svg?label=docs)](https://matter-labs.github.io/vm2/zksync_vm2_interface/)

This library provides a stable interface for EraVM. It defines an interface for tracers that will never change but may be extended.

## License
Expand Down
4 changes: 2 additions & 2 deletions crates/vm2-interface/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
//! With any sane design it would be trivial to take a tracer written for version 1 and
//! update it to work with version 2. However, then it can no longer be used with VM1.
//!
//! This exact thing caused us a lot of trouble when we put many versions of zk_evm in multivm.
//! This exact thing caused us a lot of trouble when we put many versions of `zk_evm` in `multivm`.
//!
//! ## How do I add a new feature to the interface?
//!
//! Do not change the existing traits. In fact, you should delete existing code in the new
//! version that you publish and import it from the previous version instead.
//!
//! This is how you would add a new method to StateInterface and a new opcode.
//! This is how you would add a new method to [`StateInterface`] and a new opcode.
//!
//! ```
//! # use zksync_vm2_interface as zksync_vm2_interface_v1;
Expand Down
116 changes: 95 additions & 21 deletions crates/vm2-interface/src/state_interface.rs
Original file line number Diff line number Diff line change
@@ -1,106 +1,164 @@
use primitive_types::{H160, U256};

/// Public interface of the VM state. Encompasses both read and write methods.
pub trait StateInterface {
/// Reads a register with the specified zero-based index. Returns a value together with a pointer flag.
fn read_register(&self, register: u8) -> (U256, bool);
/// Sets a register with the specified zero-based index
fn set_register(&mut self, register: u8, value: U256, is_pointer: bool);

/// Returns a mutable handle to the current call frame.
fn current_frame(&mut self) -> impl CallframeInterface + '_;
/// Returns the total number of call frames.
fn number_of_callframes(&self) -> usize;
/// Returns a mutable handle to a call frame with the specified index, where
/// zero is the current frame, one is the frame before that etc.
fn callframe(&mut self, n: usize) -> impl CallframeInterface + '_;

fn read_heap_byte(&self, heap: HeapId, index: u32) -> u8;
/// Reads an entire `U256` word in the big-endian order from the specified heap page / `index`
/// Reads a single byte from the specified heap at the specified 0-based offset.
fn read_heap_byte(&self, heap: HeapId, offset: u32) -> u8;
/// Reads an entire `U256` word in the big-endian order from the specified heap / `offset`
/// (which is the index of the most significant byte of the read value).
fn read_heap_u256(&self, heap: HeapId, index: u32) -> U256;
/// Writes an entire `U256` word in the big-endian order to the specified heap page at the specified `index`
fn read_heap_u256(&self, heap: HeapId, offset: u32) -> U256;
/// Writes an entire `U256` word in the big-endian order to the specified heap at the specified `offset`
/// (which is the index of the most significant byte of the written value).
fn write_heap_u256(&mut self, heap: HeapId, index: u32, value: U256);
fn write_heap_u256(&mut self, heap: HeapId, offset: u32, value: U256);

/// Returns current execution flags.
fn flags(&self) -> Flags;
/// Sets current execution flags.
fn set_flags(&mut self, flags: Flags);

/// Returns the currently set 0-based transaction number.
fn transaction_number(&self) -> u16;
/// Sets the current transaction number.
fn set_transaction_number(&mut self, value: u16);

/// Returns the value of the context register.
fn context_u128_register(&self) -> u128;
/// Sets the value of the context register.
joonazan marked this conversation as resolved.
Show resolved Hide resolved
fn set_context_u128_register(&mut self, value: u128);

/// Iterates over storage slots read or written during VM execution.
fn get_storage_state(&self) -> impl Iterator<Item = ((H160, U256), U256)>;

/// Iterates over all transient storage slots set during VM execution.
fn get_transient_storage_state(&self) -> impl Iterator<Item = ((H160, U256), U256)>;
/// Gets value of the specified transient storage slot.
fn get_transient_storage(&self, address: H160, slot: U256) -> U256;
/// Sets value of the specified transient storage slot.
fn write_transient_storage(&mut self, address: H160, slot: U256, value: U256);

/// Iterates over events emitted during VM execution.
fn events(&self) -> impl Iterator<Item = Event>;
/// Iterates over L2-to-L1 logs emitted during VM execution.
fn l2_to_l1_logs(&self) -> impl Iterator<Item = L2ToL1Log>;

/// Gets the current amount of published pubdata.
fn pubdata(&self) -> i32;
/// Sets the current amount of published pubdata.
fn set_pubdata(&mut self, value: i32);
}

/// VM execution flags. See the EraVM reference for more details.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Flags {
/// "Less than" flag.
pub less_than: bool,
/// "Equal" flag.
pub equal: bool,
/// "Greater than" flag.
pub greater: bool,
}

/// Public interface of an EraVM call frame.
pub trait CallframeInterface {
/// Address of the storage context associated with this frame. For delegate calls, this address is inherited from the calling contract;
/// otherwise, it's the same as [`Self::code_address()`].
fn address(&self) -> H160;
/// Sets the address of the executing contract.
fn set_address(&mut self, address: H160);
/// Address of the contract being executed.
fn code_address(&self) -> H160;
/// Sets the address of the contract being executed. Does not cause the contract at the specified address get loaded per se, just updates
/// the value used internally by the VM (e.g., returned by the [`CodeAddress`](crate::opcodes::CodeAddress) opcode).
fn set_code_address(&mut self, address: H160);
/// Address of the calling contract. Respects delegate and mimic calls.
fn caller(&self) -> H160;
/// Sets the address of the calling contract.
fn set_caller(&mut self, address: H160);

/// During panic and arbitrary code execution this returns None.
/// Returns the current program counter (i.e., 0-based index of the instruction being executed).
/// During panic this returns `None`.
fn program_counter(&self) -> Option<u16>;

/// Sets the program counter.
/// The VM will execute an invalid instruction if you jump out of the program.
fn set_program_counter(&mut self, value: u16);

/// Returns the program counter that the parent frame should continue from if this frame fails.
fn exception_handler(&self) -> u16;
/// Sets the exception handler as specified [above](Self::exception_handler()).
fn set_exception_handler(&mut self, value: u16);

/// Checks whether the call is static.
fn is_static(&self) -> bool;
/// Checks whether the call is executed in kernel mode.
fn is_kernel(&self) -> bool;

/// Returns the remaining amount of gas.
fn gas(&self) -> u32;
/// Sets the remaining amount of gas.
fn set_gas(&mut self, new_gas: u32);
/// Additional gas provided for the duration of this callframe.
fn stipend(&self) -> u32;

/// Returns the context value for this call. This context is accessible via [`ContextU128`](crate::opcodes::ContextU128) opcode.
fn context_u128(&self) -> u128;
/// Sets the context value for this call.
fn set_context_u128(&mut self, value: u128);

/// Checks whether this frame corresponds to a near call.
fn is_near_call(&self) -> bool;

/// Reads the specified stack slot. Returns a value together with a pointer flag.
fn read_stack(&self, index: u16) -> (U256, bool);
/// Sets the value and pointer flag for the specified stack slot.
fn write_stack(&mut self, index: u16, value: U256, is_pointer: bool);

/// Returns the stack pointer.
fn stack_pointer(&self) -> u16;
/// Sets the stack pointer.
fn set_stack_pointer(&mut self, value: u16);

/// Returns ID of the main heap used in this call.
fn heap(&self) -> HeapId;
/// Returns the main heap boundary (number of paid bytes).
fn heap_bound(&self) -> u32;
/// Sets the main heap boundary.
fn set_heap_bound(&mut self, value: u32);

/// Returns ID of the auxiliary heap used in this call.
fn aux_heap(&self) -> HeapId;
/// Returns the auxiliary heap boundary (number of paid bytes).
fn aux_heap_bound(&self) -> u32;
/// Sets the auxiliary heap boundary.
fn set_aux_heap_bound(&mut self, value: u32);

fn read_code_page(&self, slot: u16) -> U256;
/// Reads a word from the bytecode of the executing contract.
fn read_contract_code(&self, slot: u16) -> U256;
}

/// Identifier of a VM heap page.
/// Identifier of a VM heap.
///
/// EraVM docs sometimes refer to heaps as *heap pages*; docs in these crate don't to avoid confusion with internal heap structure.
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct HeapId(u32);

impl HeapId {
/// Identifier of the calldata heap page used by the first executed program (i.e., the bootloader).
/// Identifier of the calldata heap used by the first executed program (i.e., the bootloader).
pub const FIRST_CALLDATA: Self = Self(1);
/// Identifier of the heap page used by the first executed program (i.e., the bootloader).
/// Identifier of the heap used by the first executed program (i.e., the bootloader).
pub const FIRST: Self = Self(2);
/// Identifier of the auxiliary heap page used by the first executed program (i.e., the bootloader)
/// Identifier of the auxiliary heap used by the first executed program (i.e., the bootloader)
pub const FIRST_AUX: Self = Self(3);

/// Only for dealing with external data structures, never use internally.
Expand All @@ -109,33 +167,49 @@ impl HeapId {
Self(value)
}

/// Converts this ID to an integer value.
pub const fn as_u32(self) -> u32 {
self.0
}
}

/// Event emitted by EraVM.
///
/// There is no address field because nobody is interested in events that don't come
/// from the event writer, so we simply do not record events coming frome anywhere else.
#[derive(Clone, PartialEq, Debug)]
/// from the event writer, so we simply do not record events coming from anywhere else.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Event {
/// Event key.
pub key: U256,
/// Event value.
pub value: U256,
/// Is this event first in a chain of events?
pub is_first: bool,
/// Shard identifier (currently, always set to 0).
pub shard_id: u8,
/// 0-based index of a transaction that has emitted this event.
pub tx_number: u16,
}

#[derive(Debug)]
/// L2-to-L1 log emitted by EraVM.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct L2ToL1Log {
/// Log key.
pub key: U256,
/// Log value.
pub value: U256,
/// Is this a service log?
slowli marked this conversation as resolved.
Show resolved Hide resolved
pub is_service: bool,
/// Address of the contract that has emitted this log.
pub address: H160,
/// Shard identifier (currently, always set to 0).
pub shard_id: u8,
/// 0-based index of a transaction that has emitted this event.
pub tx_number: u16,
}

#[cfg(test)]
#[derive(Debug)]
pub struct DummyState;

#[cfg(test)]
Expand Down Expand Up @@ -212,11 +286,11 @@ impl StateInterface for DummyState {
unimplemented!()
}

fn events(&self) -> impl Iterator<Item = crate::Event> {
fn events(&self) -> impl Iterator<Item = Event> {
std::iter::empty()
}

fn l2_to_l1_logs(&self) -> impl Iterator<Item = crate::L2ToL1Log> {
fn l2_to_l1_logs(&self) -> impl Iterator<Item = L2ToL1Log> {
std::iter::empty()
}

Expand Down Expand Up @@ -319,7 +393,7 @@ impl CallframeInterface for DummyState {
unimplemented!()
}

fn heap(&self) -> crate::HeapId {
fn heap(&self) -> HeapId {
unimplemented!()
}

Expand All @@ -331,7 +405,7 @@ impl CallframeInterface for DummyState {
unimplemented!()
}

fn aux_heap(&self) -> crate::HeapId {
fn aux_heap(&self) -> HeapId {
unimplemented!()
}

Expand All @@ -343,7 +417,7 @@ impl CallframeInterface for DummyState {
unimplemented!()
}

fn read_code_page(&self, _: u16) -> U256 {
fn read_contract_code(&self, _: u16) -> U256 {
unimplemented!()
}
}
Loading