Skip to content

Commit

Permalink
feat(vm): Separate boojum integration vm (matter-labs#806)
Browse files Browse the repository at this point in the history
## What ❔

Changes needed to make the boojum integration VM work as a separate VM
version from "latest"

## 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 `cargo spellcheck
--cfg=./spellcheck/era.cfg --code 1`.
  • Loading branch information
StanislavBreadless authored Jan 4, 2024
1 parent d77e26f commit 61712a6
Show file tree
Hide file tree
Showing 76 changed files with 613 additions and 103 deletions.
22 changes: 19 additions & 3 deletions core/lib/multivm/src/glue/history_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ pub trait HistoryMode:
+ GlueInto<Self::VmVirtualBlocksMode>
+ GlueInto<Self::VmVirtualBlocksRefundsEnhancement>
+ GlueInto<Self::VmBoojumIntegration>
+ GlueInto<Self::VmLatest>
{
type VmM6Mode: crate::vm_m6::HistoryMode;
type Vm1_3_2Mode: crate::vm_1_3_2::HistoryMode;
type VmVirtualBlocksMode: crate::vm_virtual_blocks::HistoryMode;
type VmVirtualBlocksRefundsEnhancement: crate::vm_refunds_enhancement::HistoryMode;
type VmBoojumIntegration: crate::vm_latest::HistoryMode;
type VmBoojumIntegration: crate::vm_boojum_integration::HistoryMode;
type VmLatest: crate::vm_latest::HistoryMode;
}

impl GlueFrom<crate::vm_latest::HistoryEnabled> for crate::vm_m6::HistoryEnabled {
Expand All @@ -39,6 +41,12 @@ impl GlueFrom<crate::vm_latest::HistoryEnabled> for crate::vm_refunds_enhancemen
}
}

impl GlueFrom<crate::vm_latest::HistoryEnabled> for crate::vm_boojum_integration::HistoryEnabled {
fn glue_from(_: crate::vm_latest::HistoryEnabled) -> Self {
Self
}
}

impl GlueFrom<crate::vm_latest::HistoryDisabled> for crate::vm_m6::HistoryDisabled {
fn glue_from(_: crate::vm_latest::HistoryDisabled) -> Self {
Self
Expand All @@ -65,18 +73,26 @@ impl GlueFrom<crate::vm_latest::HistoryDisabled>
}
}

impl GlueFrom<crate::vm_latest::HistoryDisabled> for crate::vm_boojum_integration::HistoryDisabled {
fn glue_from(_: crate::vm_latest::HistoryDisabled) -> Self {
Self
}
}

impl HistoryMode for crate::vm_latest::HistoryEnabled {
type VmM6Mode = crate::vm_m6::HistoryEnabled;
type Vm1_3_2Mode = crate::vm_1_3_2::HistoryEnabled;
type VmVirtualBlocksMode = crate::vm_virtual_blocks::HistoryEnabled;
type VmVirtualBlocksRefundsEnhancement = crate::vm_refunds_enhancement::HistoryEnabled;
type VmBoojumIntegration = crate::vm_latest::HistoryEnabled;
type VmBoojumIntegration = crate::vm_boojum_integration::HistoryEnabled;
type VmLatest = crate::vm_latest::HistoryEnabled;
}

impl HistoryMode for crate::vm_latest::HistoryDisabled {
type VmM6Mode = crate::vm_m6::HistoryDisabled;
type Vm1_3_2Mode = crate::vm_1_3_2::HistoryDisabled;
type VmVirtualBlocksMode = crate::vm_virtual_blocks::HistoryDisabled;
type VmVirtualBlocksRefundsEnhancement = crate::vm_refunds_enhancement::HistoryDisabled;
type VmBoojumIntegration = crate::vm_latest::HistoryDisabled;
type VmBoojumIntegration = crate::vm_boojum_integration::HistoryDisabled;
type VmLatest = crate::vm_latest::HistoryDisabled;
}
33 changes: 28 additions & 5 deletions core/lib/multivm/src/glue/tracers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ use crate::HistoryMode;
pub type MultiVmTracerPointer<S, H> = Box<dyn MultiVMTracer<S, H>>;

pub trait MultiVMTracer<S: WriteStorage, H: HistoryMode>:
IntoLatestTracer<S, H> + IntoVmVirtualBlocksTracer<S, H> + IntoVmRefundsEnhancementTracer<S, H>
IntoLatestTracer<S, H>
+ IntoVmVirtualBlocksTracer<S, H>
+ IntoVmRefundsEnhancementTracer<S, H>
+ IntoVmBoojumIntegrationTracer<S, H>
{
fn into_tracer_pointer(self) -> MultiVmTracerPointer<S, H>
where
Expand All @@ -48,7 +51,7 @@ pub trait MultiVMTracer<S: WriteStorage, H: HistoryMode>:
}

pub trait IntoLatestTracer<S: WriteStorage, H: HistoryMode> {
fn latest(&self) -> crate::vm_latest::TracerPointer<S, H::VmBoojumIntegration>;
fn latest(&self) -> crate::vm_latest::TracerPointer<S, H::VmLatest>;
}

pub trait IntoVmVirtualBlocksTracer<S: WriteStorage, H: HistoryMode> {
Expand All @@ -63,13 +66,19 @@ pub trait IntoVmRefundsEnhancementTracer<S: WriteStorage, H: HistoryMode> {
) -> Box<dyn crate::vm_refunds_enhancement::VmTracer<S, H::VmVirtualBlocksRefundsEnhancement>>;
}

pub trait IntoVmBoojumIntegrationTracer<S: WriteStorage, H: HistoryMode> {
fn vm_boojum_integration(
&self,
) -> Box<dyn crate::vm_boojum_integration::VmTracer<S, H::VmBoojumIntegration>>;
}

impl<S, T, H> IntoLatestTracer<S, H> for T
where
S: WriteStorage,
H: HistoryMode,
T: crate::vm_latest::VmTracer<S, H::VmBoojumIntegration> + Clone + 'static,
T: crate::vm_latest::VmTracer<S, H::VmLatest> + Clone + 'static,
{
fn latest(&self) -> crate::vm_latest::TracerPointer<S, H::VmBoojumIntegration> {
fn latest(&self) -> crate::vm_latest::TracerPointer<S, H::VmLatest> {
Box::new(self.clone())
}
}
Expand Down Expand Up @@ -103,12 +112,26 @@ where
}
}

impl<S, T, H> IntoVmBoojumIntegrationTracer<S, H> for T
where
S: WriteStorage,
H: HistoryMode,
T: crate::vm_boojum_integration::VmTracer<S, H::VmBoojumIntegration> + Clone + 'static,
{
fn vm_boojum_integration(
&self,
) -> Box<dyn crate::vm_boojum_integration::VmTracer<S, H::VmBoojumIntegration>> {
Box::new(self.clone())
}
}

impl<S, H, T> MultiVMTracer<S, H> for T
where
S: WriteStorage,
H: HistoryMode,
T: IntoLatestTracer<S, H>
+ IntoVmVirtualBlocksTracer<S, H>
+ IntoVmRefundsEnhancementTracer<S, H>,
+ IntoVmRefundsEnhancementTracer<S, H>
+ IntoVmBoojumIntegrationTracer<S, H>,
{
}
3 changes: 2 additions & 1 deletion core/lib/multivm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ pub use zk_evm_1_4_0;
pub use zksync_types::vm_version::VmVersion;

pub use self::versions::{
vm_1_3_2, vm_latest, vm_m5, vm_m6, vm_refunds_enhancement, vm_virtual_blocks,
vm_1_3_2, vm_boojum_integration, vm_latest, vm_m5, vm_m6, vm_refunds_enhancement,
vm_virtual_blocks,
};
pub use crate::{
glue::{
Expand Down
1 change: 1 addition & 0 deletions core/lib/multivm/src/tracers/call_tracer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use zksync_types::vm_trace::Call;
use crate::tracers::call_tracer::metrics::CALL_METRICS;

mod metrics;
pub mod vm_boojum_integration;
pub mod vm_latest;
pub mod vm_refunds_enhancement;
pub mod vm_virtual_blocks;
Expand Down
216 changes: 216 additions & 0 deletions core/lib/multivm/src/tracers/call_tracer/vm_boojum_integration/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
use zk_evm_1_4_0::{
tracing::{AfterExecutionData, VmLocalStateData},
zkevm_opcode_defs::{
FarCallABI, FatPointer, Opcode, RetOpcode, CALL_IMPLICIT_CALLDATA_FAT_PTR_REGISTER,
RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER,
},
};
use zksync_state::{StoragePtr, WriteStorage};
use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS;
use zksync_types::{
vm_trace::{Call, CallType},
FarCallOpcode, U256,
};

use crate::{
interface::{
tracer::VmExecutionStopReason, traits::tracers::dyn_tracers::vm_1_4_0::DynTracer,
VmRevertReason,
},
tracers::call_tracer::CallTracer,
vm_boojum_integration::{BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState},
};

impl<S, H: HistoryMode> DynTracer<S, SimpleMemory<H>> for CallTracer {
fn after_execution(
&mut self,
state: VmLocalStateData<'_>,
data: AfterExecutionData,
memory: &SimpleMemory<H>,
_storage: StoragePtr<S>,
) {
match data.opcode.variant.opcode {
Opcode::NearCall(_) => {
self.increase_near_call_count();
}
Opcode::FarCall(far_call) => {
// We use parent gas for properly calculating gas used in the trace.
let current_ergs = state.vm_local_state.callstack.current.ergs_remaining;
let parent_gas = state
.vm_local_state
.callstack
.inner
.last()
.map(|call| call.ergs_remaining + current_ergs)
.unwrap_or(current_ergs);

let mut current_call = Call {
r#type: CallType::Call(far_call),
gas: 0,
parent_gas,
..Default::default()
};

self.handle_far_call_op_code_vm_boojum_integration(
state,
memory,
&mut current_call,
);
self.push_call_and_update_stats(current_call, 0);
}
Opcode::Ret(ret_code) => {
self.handle_ret_op_code_vm_boojum_integration(state, memory, ret_code);
}
_ => {}
};
}
}

impl<S: WriteStorage, H: HistoryMode> VmTracer<S, H> for CallTracer {
fn after_vm_execution(
&mut self,
_state: &mut ZkSyncVmState<S, H>,
_bootloader_state: &BootloaderState,
_stop_reason: VmExecutionStopReason,
) {
self.store_result()
}
}

impl CallTracer {
fn handle_far_call_op_code_vm_boojum_integration<H: HistoryMode>(
&mut self,
state: VmLocalStateData<'_>,
memory: &SimpleMemory<H>,
current_call: &mut Call,
) {
let current = state.vm_local_state.callstack.current;
// All calls from the actual users are mimic calls,
// so we need to check that the previous call was to the deployer.
// Actually it's a call of the constructor.
// And at this stage caller is user and callee is deployed contract.
let call_type = if let CallType::Call(far_call) = current_call.r#type {
if matches!(far_call, FarCallOpcode::Mimic) {
let previous_caller = state
.vm_local_state
.callstack
.inner
.last()
.map(|call| call.this_address)
// Actually it's safe to just unwrap here, because we have at least one call in the stack
// But i want to be sure that we will not have any problems in the future
.unwrap_or(current.this_address);
if previous_caller == CONTRACT_DEPLOYER_ADDRESS {
CallType::Create
} else {
CallType::Call(far_call)
}
} else {
CallType::Call(far_call)
}
} else {
unreachable!()
};
let calldata = if current.code_page.0 == 0 || current.ergs_remaining == 0 {
vec![]
} else {
let packed_abi =
state.vm_local_state.registers[CALL_IMPLICIT_CALLDATA_FAT_PTR_REGISTER as usize];
assert!(packed_abi.is_pointer);
let far_call_abi = FarCallABI::from_u256(packed_abi.value);
memory.read_unaligned_bytes(
far_call_abi.memory_quasi_fat_pointer.memory_page as usize,
far_call_abi.memory_quasi_fat_pointer.start as usize,
far_call_abi.memory_quasi_fat_pointer.length as usize,
)
};

current_call.input = calldata;
current_call.r#type = call_type;
current_call.from = current.msg_sender;
current_call.to = current.this_address;
current_call.value = U256::from(current.context_u128_value);
current_call.gas = current.ergs_remaining;
}

fn save_output_vm_boojum_integration<H: HistoryMode>(
&mut self,
state: VmLocalStateData<'_>,
memory: &SimpleMemory<H>,
ret_opcode: RetOpcode,
current_call: &mut Call,
) {
let fat_data_pointer =
state.vm_local_state.registers[RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER as usize];

// if fat_data_pointer is not a pointer then there is no output
let output = if fat_data_pointer.is_pointer {
let fat_data_pointer = FatPointer::from_u256(fat_data_pointer.value);
if !fat_data_pointer.is_trivial() {
Some(memory.read_unaligned_bytes(
fat_data_pointer.memory_page as usize,
fat_data_pointer.start as usize,
fat_data_pointer.length as usize,
))
} else {
None
}
} else {
None
};

match ret_opcode {
RetOpcode::Ok => {
current_call.output = output.unwrap_or_default();
}
RetOpcode::Revert => {
if let Some(output) = output {
current_call.revert_reason =
Some(VmRevertReason::from(output.as_slice()).to_string());
} else {
current_call.revert_reason = Some("Unknown revert reason".to_string());
}
}
RetOpcode::Panic => {
current_call.error = Some("Panic".to_string());
}
}
}

fn handle_ret_op_code_vm_boojum_integration<H: HistoryMode>(
&mut self,
state: VmLocalStateData<'_>,
memory: &SimpleMemory<H>,
ret_opcode: RetOpcode,
) {
let Some(mut current_call) = self.stack.pop() else {
return;
};

if current_call.near_calls_after > 0 {
current_call.near_calls_after -= 1;
self.push_call_and_update_stats(current_call.farcall, current_call.near_calls_after);
return;
}

current_call.farcall.gas_used = current_call
.farcall
.parent_gas
.saturating_sub(state.vm_local_state.callstack.current.ergs_remaining);

self.save_output_vm_boojum_integration(
state,
memory,
ret_opcode,
&mut current_call.farcall,
);

// If there is a parent call, push the current call to it
// Otherwise, push the current call to the stack, because it's the top level call
if let Some(parent_call) = self.stack.last_mut() {
parent_call.farcall.calls.push(current_call.farcall);
} else {
self.push_call_and_update_stats(current_call.farcall, current_call.near_calls_after);
}
}
}
16 changes: 15 additions & 1 deletion core/lib/multivm/src/tracers/multivm_dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,27 @@ impl<S: WriteStorage, H: HistoryMode> Default for TracerDispatcher<S, H> {
}

impl<S: WriteStorage, H: HistoryMode> From<TracerDispatcher<S, H>>
for crate::vm_latest::TracerDispatcher<S, H::VmBoojumIntegration>
for crate::vm_latest::TracerDispatcher<S, H::VmLatest>
{
fn from(value: TracerDispatcher<S, H>) -> Self {
Self::new(value.tracers.into_iter().map(|x| x.latest()).collect())
}
}

impl<S: WriteStorage, H: HistoryMode> From<TracerDispatcher<S, H>>
for crate::vm_boojum_integration::TracerDispatcher<S, H::VmBoojumIntegration>
{
fn from(value: TracerDispatcher<S, H>) -> Self {
Self::new(
value
.tracers
.into_iter()
.map(|x| x.vm_boojum_integration())
.collect(),
)
}
}

impl<S: WriteStorage, H: HistoryMode> From<TracerDispatcher<S, H>>
for crate::vm_refunds_enhancement::TracerDispatcher<S, H::VmVirtualBlocksRefundsEnhancement>
{
Expand Down
1 change: 1 addition & 0 deletions core/lib/multivm/src/tracers/storage_invocation/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod vm_boojum_integration;
pub mod vm_latest;
pub mod vm_refunds_enhancement;
pub mod vm_virtual_blocks;
Expand Down
Loading

0 comments on commit 61712a6

Please sign in to comment.