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

feat(vm): Add boojum integration folder #805

Merged
merged 1 commit into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
44 changes: 44 additions & 0 deletions core/lib/multivm/src/versions/vm_boojum_integration/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_latest::{
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(super) 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