Skip to content

Commit

Permalink
feat(vm): Add new VM folder (#1208)
Browse files Browse the repository at this point in the history
## What ❔

Copy `vm_latest`, pasted as `vm_1_4_1`. Replaced all occurrence of
`vm_latest` to `vm_1_4_1` inside `vm_1_4_1` folder

## Why ❔

<!-- Why are these changes done? What goal do they contribute to? What
are the principles behind them? -->
<!-- Example: PR templates ensure PR reviewers, observers, and future
iterators are in context about the evolution of repos. -->

## Checklist

<!-- Check your PR fulfills the following items. -->
<!-- For draft PRs check the boxes as you complete them. -->

- [ ] PR title corresponds to the body of PR (we generate changelog
entries from PRs).
- [ ] Tests for the changes have been added / updated.
- [ ] Documentation comments have been added / updated.
- [ ] Code has been formatted via `zk fmt` and `zk lint`.
- [ ] Spellcheck has been run via `zk spellcheck`.
- [ ] Linkcheck has been run via `zk linkcheck`.
  • Loading branch information
perekopskiy committed Feb 22, 2024
1 parent cef6923 commit 66cdefc
Show file tree
Hide file tree
Showing 78 changed files with 10,888 additions and 0 deletions.
44 changes: 44 additions & 0 deletions core/lib/multivm/src/versions/vm_1_4_1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# VM Crate

This crate contains code that interacts with the VM (Virtual Machine). The VM itself is in a separate repository
[era-zk_evm][zk_evm_repo_ext].

## VM Dependencies

The VM relies on several subcomponents or traits, such as Memory and Storage. These traits are defined in the `zk_evm`
repository, while their implementations can be found in this crate, such as the storage implementation in
`oracles/storage.rs` and the Memory implementation in `memory.rs`.

Many of these implementations also support easy rollbacks and history, which is useful when creating a block with
multiple transactions and needing to return the VM to a previous state if a transaction doesn't fit.

## Running the VM

To interact with the VM, you must initialize it with `L1BatchEnv`, which represents the initial parameters of the batch,
`SystemEnv`, that represents the system parameters, and a reference to the Storage. To execute a transaction, you have
to push the transaction into the bootloader memory and call the `execute_next_transaction` method.

### Tracers

The VM implementation allows for the addition of `Tracers`, which are activated before and after each instruction. This
provides a more in-depth look into the VM, collecting detailed debugging information and logs. More details can be found
in the `tracer/` directory.

This VM also supports custom tracers. You can call the `inspect_next_transaction` method with a custom tracer and
receive the result of the execution.

### Bootloader

In the context of zkEVM, we usually think about transactions. However, from the VM's perspective, it runs a single
program called the bootloader, which internally processes multiple transactions.

### Rollbacks

The `VMInstance` in `vm.rs` allows for easy rollbacks. You can save the current state at any moment by calling
`make_snapshot()` and return to that state using `rollback_to_the_latest_snapshot()`.

This rollback affects all subcomponents, such as memory, storage, and events, and is mainly used if a transaction
doesn't fit in a block.

[zk_evm_repo]: https://github.com/matter-labs/zk_evm 'internal zk EVM repo'
[zk_evm_repo_ext]: https://github.com/matter-labs/era-zk_evm 'external zk EVM repo'
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use std::cmp::Ordering;

use zksync_types::{MiniblockNumber, H256};
use zksync_utils::concat_and_hash;

use crate::{
interface::{L2Block, L2BlockEnv},
vm_1_4_1::{
bootloader_state::{snapshot::L2BlockSnapshot, tx::BootloaderTx},
utils::l2_blocks::l2_block_hash,
},
};

const EMPTY_TXS_ROLLING_HASH: H256 = H256::zero();

#[derive(Debug, Clone)]
pub(crate) struct BootloaderL2Block {
pub(crate) number: u32,
pub(crate) timestamp: u64,
pub(crate) txs_rolling_hash: H256, // The rolling hash of all the transactions in the miniblock
pub(crate) prev_block_hash: H256,
// Number of the first L2 block tx in L1 batch
pub(crate) first_tx_index: usize,
pub(crate) max_virtual_blocks_to_create: u32,
pub(crate) txs: Vec<BootloaderTx>,
}

impl BootloaderL2Block {
pub(crate) fn new(l2_block: L2BlockEnv, first_tx_place: usize) -> Self {
Self {
number: l2_block.number,
timestamp: l2_block.timestamp,
txs_rolling_hash: EMPTY_TXS_ROLLING_HASH,
prev_block_hash: l2_block.prev_block_hash,
first_tx_index: first_tx_place,
max_virtual_blocks_to_create: l2_block.max_virtual_blocks_to_create,
txs: vec![],
}
}

pub(super) fn push_tx(&mut self, tx: BootloaderTx) {
self.update_rolling_hash(tx.hash);
self.txs.push(tx)
}

pub(crate) fn get_hash(&self) -> H256 {
l2_block_hash(
MiniblockNumber(self.number),
self.timestamp,
self.prev_block_hash,
self.txs_rolling_hash,
)
}

fn update_rolling_hash(&mut self, tx_hash: H256) {
self.txs_rolling_hash = concat_and_hash(self.txs_rolling_hash, tx_hash)
}

pub(crate) fn interim_version(&self) -> BootloaderL2Block {
let mut interim = self.clone();
interim.max_virtual_blocks_to_create = 0;
interim
}

pub(crate) fn make_snapshot(&self) -> L2BlockSnapshot {
L2BlockSnapshot {
txs_rolling_hash: self.txs_rolling_hash,
txs_len: self.txs.len(),
}
}

pub(crate) fn apply_snapshot(&mut self, snapshot: L2BlockSnapshot) {
self.txs_rolling_hash = snapshot.txs_rolling_hash;
match self.txs.len().cmp(&snapshot.txs_len) {
Ordering::Greater => self.txs.truncate(snapshot.txs_len),
Ordering::Less => panic!("Applying snapshot from future is not supported"),
Ordering::Equal => {}
}
}
pub(crate) fn l2_block(&self) -> L2Block {
L2Block {
number: self.number,
timestamp: self.timestamp,
hash: self.get_hash(),
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
mod l2_block;
mod snapshot;
mod state;
mod tx;

pub(crate) mod utils;
pub(crate) use snapshot::BootloaderStateSnapshot;
pub use state::BootloaderState;
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use zksync_types::H256;

#[derive(Debug, Clone)]
pub(crate) struct BootloaderStateSnapshot {
/// ID of the next transaction to be executed.
pub(crate) tx_to_execute: usize,
/// Stored L2 blocks in bootloader memory
pub(crate) l2_blocks_len: usize,
/// Snapshot of the last L2 block. Only this block could be changed during the rollback
pub(crate) last_l2_block: L2BlockSnapshot,
/// The number of 32-byte words spent on the already included compressed bytecodes.
pub(crate) compressed_bytecodes_encoding: usize,
/// Current offset of the free space in the bootloader memory.
pub(crate) free_tx_offset: usize,
/// Whether the pubdata information has been provided already
pub(crate) is_pubdata_information_provided: bool,
}

#[derive(Debug, Clone)]
pub(crate) struct L2BlockSnapshot {
/// The rolling hash of all the transactions in the miniblock
pub(crate) txs_rolling_hash: H256,
/// The number of transactions in the last L2 block
pub(crate) txs_len: usize,
}
Loading

0 comments on commit 66cdefc

Please sign in to comment.