Skip to content

Commit

Permalink
Merge pull request #254 from lambdaclass/era_vm_zk_server_v2
Browse files Browse the repository at this point in the history
ZK Server Integration
  • Loading branch information
gianbelinche authored Sep 4, 2024
2 parents 96cb8dc + f6be6d9 commit 81f61c8
Show file tree
Hide file tree
Showing 33 changed files with 2,116 additions and 325 deletions.
21 changes: 19 additions & 2 deletions core/lib/multivm/src/glue/tracers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
//! - Add this trait as a trait bound for `T` in `MultiVMTracer` implementation.
//! - Implement the trait for `T` with a bound to `VmTracer` for a specific version.
use zksync_state::WriteStorage;
use zksync_state::{ImmutableStorageView, WriteStorage};

use crate::{tracers::old_tracers::OldTracers, HistoryMode};

Expand All @@ -44,6 +44,7 @@ pub trait MultiVMTracer<S: WriteStorage, H: HistoryMode>:
+ IntoVm1_4_1IntegrationTracer<S, H>
+ IntoVm1_4_2IntegrationTracer<S, H>
+ IntoOldVmTracer
+ IntoEraVmTracer<S, H>
{
fn into_tracer_pointer(self) -> MultiVmTracerPointer<S, H>
where
Expand All @@ -57,6 +58,10 @@ pub trait IntoLatestTracer<S: WriteStorage, H: HistoryMode> {
fn latest(&self) -> crate::vm_latest::TracerPointer<S, H::Vm1_5_0>;
}

pub trait IntoEraVmTracer<S: WriteStorage, H: HistoryMode> {
fn into_era_vm(&self) -> Box<dyn crate::era_vm::tracers::traits::VmTracer<S>>;
}

pub trait IntoVmVirtualBlocksTracer<S: WriteStorage, H: HistoryMode> {
fn vm_virtual_blocks(
&self,
Expand Down Expand Up @@ -106,6 +111,17 @@ where
}
}

impl<S, T, H> IntoEraVmTracer<S, H> for T
where
S: WriteStorage,
H: HistoryMode,
T: crate::era_vm::tracers::traits::VmTracer<S> + Clone + 'static,
{
fn into_era_vm(&self) -> Box<dyn crate::era_vm::tracers::traits::VmTracer<S>> {
Box::new(self.clone())
}
}

impl<S, T, H> IntoVmVirtualBlocksTracer<S, H> for T
where
S: WriteStorage,
Expand Down Expand Up @@ -180,6 +196,7 @@ where
+ IntoVmBoojumIntegrationTracer<S, H>
+ IntoVm1_4_1IntegrationTracer<S, H>
+ IntoVm1_4_2IntegrationTracer<S, H>
+ IntoOldVmTracer,
+ IntoOldVmTracer
+ IntoEraVmTracer<S, H>,
{
}
188 changes: 188 additions & 0 deletions core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
use era_vm::{
opcode::{RetOpcode, Variant},
value::FatPointer,
Execution, Opcode,
};
use zksync_state::ReadStorage;
use zksync_types::{
vm_trace::{Call, CallType},
zk_evm_types::FarCallOpcode,
CONTRACT_DEPLOYER_ADDRESS, U256,
};

use super::CallTracer;
use crate::{
era_vm::tracers::traits::{Tracer, VmTracer},
interface::VmRevertReason,
};

impl Tracer for CallTracer {
fn after_execution(
&mut self,
opcode: &Opcode,
execution: &mut era_vm::Execution,
_state: &mut era_vm::state::VMState,
) {
match opcode.variant {
Variant::NearCall(_) => {
self.increase_near_call_count();
}
Variant::FarCall(far_call) => {
// We use parent gas for properly calculating gas used in the trace.
let current_ergs = execution.gas_left().unwrap();
let parent_gas = execution
.running_contexts
.last()
.map(|call| call.frame.gas_left.0.saturating_add(current_ergs))
.unwrap_or(current_ergs) as u64;

// we need to do this cast because `Call` uses another library
let far_call_variant = match far_call as u8 {
0 => FarCallOpcode::Normal,
1 => FarCallOpcode::Delegate,
2 => FarCallOpcode::Mimic,
_ => FarCallOpcode::Normal, // Should never happen
};

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

self.handle_far_call_op_code_era(execution, &mut current_call);
self.push_call_and_update_stats(current_call, 0);
}
Variant::Ret(ret_code) => {
self.handle_ret_op_code_era(execution, ret_code);
}
_ => {}
};
}
}

impl<S: ReadStorage> VmTracer<S> for CallTracer {
fn after_bootloader_execution(&mut self, _state: &mut crate::era_vm::vm::Vm<S>) {
self.store_result();
}
}

impl CallTracer {
fn handle_far_call_op_code_era(&mut self, execution: &Execution, current_call: &mut Call) {
// since this is a far_call, the current_context represents the current frame
let current = execution.current_context().unwrap();
// 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 = execution
.running_contexts
.first()
.map(|call| call.caller)
// 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.caller);
if previous_caller == CONTRACT_DEPLOYER_ADDRESS {
CallType::Create
} else {
CallType::Call(far_call)
}
} else {
CallType::Call(far_call)
}
} else {
return;
};
let calldata = if current.heap_id == 0 || current.frame.gas_left.0 == 0 {
vec![]
} else {
let packed_abi = execution.get_register(1);
assert!(packed_abi.is_pointer);
let pointer = FatPointer::decode(packed_abi.value);
execution
.heaps
.get(pointer.page)
.unwrap()
.read_unaligned_from_pointer(&pointer)
.unwrap_or_default()
};

current_call.input = calldata;
current_call.r#type = call_type;
current_call.from = current.caller;
current_call.to = current.contract_address;
current_call.value = U256::from(current.context_u128);
current_call.gas = current.frame.gas_left.0 as u64;
}

fn save_output_era(
&mut self,
execution: &Execution,
ret_opcode: RetOpcode,
current_call: &mut Call,
) {
let fat_data_pointer = execution.get_register(1);

// if `fat_data_pointer` is not a pointer then there is no output
let output = match fat_data_pointer.is_pointer {
true => {
let fat_data_pointer = FatPointer::decode(fat_data_pointer.value);
match (fat_data_pointer.len, fat_data_pointer.offset) {
(0, 0) => execution
.heaps
.get(fat_data_pointer.page)
.and_then(|ptr| ptr.read_unaligned_from_pointer(&fat_data_pointer).ok()),
_ => None,
}
}
_ => 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_era(&mut self, execution: &Execution, 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(execution.gas_left().unwrap() as u64);

self.save_output_era(execution, 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);
}
}
}
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 @@ -5,6 +5,7 @@ use zksync_types::vm_trace::Call;

use crate::{glue::tracers::IntoOldVmTracer, tracers::call_tracer::metrics::CALL_METRICS};

pub mod era_vm;
mod metrics;
pub mod vm_1_4_1;
pub mod vm_1_4_2;
Expand Down
12 changes: 10 additions & 2 deletions core/lib/multivm/src/tracers/multivm_dispatcher.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use zksync_state::WriteStorage;
use zksync_state::{ImmutableStorageView, StorageView, WriteStorage};

use crate::{tracers::old_tracers, HistoryMode, MultiVmTracerPointer};
use crate::{tracers::old_tracers, HistoryMode, MultiVMTracer, MultiVmTracerPointer};

/// Tracer dispatcher is a tracer that can dispatch calls to multiple tracers.
pub struct TracerDispatcher<S, H> {
Expand Down Expand Up @@ -37,6 +37,14 @@ impl<S: WriteStorage, H: HistoryMode> From<TracerDispatcher<S, H>>
}
}

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

impl<S: WriteStorage, H: HistoryMode> From<TracerDispatcher<S, H>>
for crate::vm_boojum_integration::TracerDispatcher<S, H::VmBoojumIntegration>
{
Expand Down
22 changes: 22 additions & 0 deletions core/lib/multivm/src/tracers/storage_invocation/era_vm/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use era_vm::{
opcode::{RetOpcode, Variant},
value::FatPointer,
Execution, Opcode,
};
use zksync_state::ReadStorage;
use zksync_types::{
vm_trace::{Call, CallType},
zk_evm_types::FarCallOpcode,
CONTRACT_DEPLOYER_ADDRESS, U256,
};

use super::StorageInvocations;
use crate::{
era_vm::tracers::traits::{Tracer, VmTracer},
interface::VmRevertReason,
};

//TODO: Implement the Tracer trait for StorageInvocations
impl Tracer for StorageInvocations {}

impl<S: ReadStorage> VmTracer<S> for StorageInvocations {}
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,5 +1,6 @@
use crate::{glue::tracers::IntoOldVmTracer, tracers::old_tracers::OldTracers};

pub mod era_vm;
pub mod vm_1_4_1;
pub mod vm_1_4_2;
pub mod vm_boojum_integration;
Expand Down
22 changes: 22 additions & 0 deletions core/lib/multivm/src/tracers/validator/era_vm/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use era_vm::{
opcode::{RetOpcode, Variant},
value::FatPointer,
Execution, Opcode,
};
use zksync_state::ReadStorage;
use zksync_types::{
vm_trace::{Call, CallType},
zk_evm_types::FarCallOpcode,
CONTRACT_DEPLOYER_ADDRESS, U256,
};

use super::ValidationTracer;
use crate::{
era_vm::tracers::traits::{Tracer, VmTracer},
interface::VmRevertReason,
};

//TODO: Implement the Tracer trait for ValidationTracer
impl<H> Tracer for ValidationTracer<H> {}

impl<S: ReadStorage, H> VmTracer<S> for ValidationTracer<H> {}
1 change: 1 addition & 0 deletions core/lib/multivm/src/tracers/validator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::{
tracers::validator::types::{NewTrustedValidationItems, ValidationTracerMode},
};

mod era_vm;
mod types;
mod vm_1_4_1;
mod vm_1_4_2;
Expand Down
4 changes: 2 additions & 2 deletions core/lib/multivm/src/versions/era_vm/hook.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#[derive(Debug)]
#[derive(Debug, Clone)]

pub(crate) enum Hook {
pub enum Hook {
AccountValidationEntered,
PaymasterValidationEntered,
AccountValidationExited,
Expand Down
2 changes: 1 addition & 1 deletion core/lib/multivm/src/versions/era_vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ mod event;
mod hook;
mod initial_bootloader_memory;
mod logs;
mod refunds;
mod snapshot;
#[cfg(test)]
mod tests;
pub mod tracers;
mod transaction_data;
pub mod vm;
Loading

0 comments on commit 81f61c8

Please sign in to comment.