diff --git a/core/bin/system-constants-generator/src/utils.rs b/core/bin/system-constants-generator/src/utils.rs index 0b1c419f1ae..fc576ff44ee 100644 --- a/core/bin/system-constants-generator/src/utils.rs +++ b/core/bin/system-constants-generator/src/utils.rs @@ -1,24 +1,28 @@ -use multivm::interface::{L1BatchEnv, L2BlockEnv, SystemEnv, TxExecutionMode, VmExecutionMode}; -use multivm::vm_latest::{ - constants::{BLOCK_GAS_LIMIT, BOOTLOADER_HEAP_PAGE}, - BootloaderState, BoxedTracer, DynTracer, HistoryEnabled, HistoryMode, Vm, - VmExecutionStopReason, VmTracer, ZkSyncVmState, -}; use once_cell::sync::Lazy; use std::cell::RefCell; use std::rc::Rc; + +use multivm::interface::{ + dyn_tracers::vm_1_4_0::DynTracer, tracer::VmExecutionStopReason, L1BatchEnv, L2BlockEnv, + SystemEnv, TxExecutionMode, VmExecutionMode, VmInterface, +}; +use multivm::vm_latest::{ + constants::{BLOCK_GAS_LIMIT, BOOTLOADER_HEAP_PAGE}, + BootloaderState, HistoryEnabled, HistoryMode, SimpleMemory, ToTracerPointer, Vm, VmTracer, + ZkSyncVmState, +}; use zksync_contracts::{ load_sys_contract, read_bootloader_code, read_sys_contract_bytecode, read_zbin_bytecode, BaseSystemContracts, ContractLanguage, SystemContractCode, }; use zksync_state::{InMemoryStorage, StorageView, WriteStorage}; -use zksync_types::block::legacy_miniblock_hash; use zksync_types::{ - ethabi::Token, fee::Fee, l1::L1Tx, l2::L2Tx, utils::storage_key_for_eth_balance, AccountTreeId, - Address, Execute, L1BatchNumber, L1TxCommonData, L2ChainId, MiniblockNumber, Nonce, - ProtocolVersionId, StorageKey, Timestamp, Transaction, BOOTLOADER_ADDRESS, H256, - SYSTEM_CONTEXT_ADDRESS, SYSTEM_CONTEXT_GAS_PRICE_POSITION, SYSTEM_CONTEXT_TX_ORIGIN_POSITION, - U256, ZKPORTER_IS_AVAILABLE, + block::legacy_miniblock_hash, ethabi::Token, fee::Fee, l1::L1Tx, l2::L2Tx, + utils::storage_key_for_eth_balance, AccountTreeId, Address, Execute, L1BatchNumber, + L1TxCommonData, L2ChainId, MiniblockNumber, Nonce, ProtocolVersionId, StorageKey, Timestamp, + Transaction, BOOTLOADER_ADDRESS, H256, SYSTEM_CONTEXT_ADDRESS, + SYSTEM_CONTEXT_GAS_PRICE_POSITION, SYSTEM_CONTEXT_TX_ORIGIN_POSITION, U256, + ZKPORTER_IS_AVAILABLE, }; use zksync_utils::{bytecode::hash_bytecode, bytes_to_be_words, u256_to_h256}; @@ -31,7 +35,7 @@ struct SpecialBootloaderTracer { output: Rc>, } -impl DynTracer for SpecialBootloaderTracer {} +impl DynTracer> for SpecialBootloaderTracer {} impl VmTracer for SpecialBootloaderTracer { fn initialize_tracer(&mut self, state: &mut ZkSyncVmState) { @@ -247,14 +251,11 @@ pub(super) fn execute_internal_transfer_test() -> u32 { let tracer = SpecialBootloaderTracer { input, output: tracer_result.clone(), - }; - let mut vm = Vm::new( - l1_batch, - system_env, - Rc::new(RefCell::new(storage_view)), - HistoryEnabled, - ); - let result = vm.inspect(vec![tracer.into_boxed()], VmExecutionMode::Bootloader); + } + .into_tracer_pointer(); + let mut vm: Vm<_, HistoryEnabled> = + Vm::new(l1_batch, system_env, Rc::new(RefCell::new(storage_view))); + let result = vm.inspect(tracer.into(), VmExecutionMode::Bootloader); assert!(!result.result.is_failed(), "The internal call has reverted"); tracer_result.take() @@ -307,12 +308,8 @@ pub(super) fn execute_user_txs_in_test_gas_vm( chain_id: L2ChainId::default(), }; - let mut vm = Vm::new( - l1_batch, - system_env, - Rc::new(RefCell::new(storage_view)), - HistoryEnabled, - ); + let mut vm: Vm<_, HistoryEnabled> = + Vm::new(l1_batch, system_env, Rc::new(RefCell::new(storage_view))); let mut total_gas_refunded = 0; for tx in txs { diff --git a/core/lib/multivm/src/glue/init_vm.rs b/core/lib/multivm/src/glue/init_vm.rs index a587165daa2..cf16b31301e 100644 --- a/core/lib/multivm/src/glue/init_vm.rs +++ b/core/lib/multivm/src/glue/init_vm.rs @@ -1,6 +1,6 @@ use super::GlueInto; use crate::glue::history_mode::HistoryMode; -use crate::interface::{L1BatchEnv, SystemEnv}; +use crate::interface::{L1BatchEnv, SystemEnv, VmInterface}; use crate::vm_instance::VmInstanceVersion; use crate::VmInstance; use zksync_state::{ReadStorage, StoragePtr, StorageView}; @@ -28,7 +28,7 @@ impl VmInstance { VmVersion::M5WithoutRefunds => { let oracle_tools = crate::vm_m5::OracleTools::new( storage_view.clone(), - crate::vm_m5::vm::MultiVMSubversion::V1, + crate::vm_m5::vm_instance::MultiVMSubversion::V1, ); let block_properties = zk_evm_1_3_1::block_properties::BlockProperties { default_aa_code_hash: h256_to_u256( @@ -37,7 +37,7 @@ impl VmInstance { zkporter_is_available: false, }; let inner_vm = crate::vm_m5::vm_with_bootloader::init_vm_with_gas_limit( - crate::vm_m5::vm::MultiVMSubversion::V1, + crate::vm_m5::vm_instance::MultiVMSubversion::V1, oracle_tools, l1_batch_env.glue_into(), block_properties, @@ -54,7 +54,7 @@ impl VmInstance { VmVersion::M5WithRefunds => { let oracle_tools = crate::vm_m5::OracleTools::new( storage_view.clone(), - crate::vm_m5::vm::MultiVMSubversion::V2, + crate::vm_m5::vm_instance::MultiVMSubversion::V2, ); let block_properties = zk_evm_1_3_1::block_properties::BlockProperties { default_aa_code_hash: h256_to_u256( @@ -63,7 +63,7 @@ impl VmInstance { zkporter_is_available: false, }; let inner_vm = crate::vm_m5::vm_with_bootloader::init_vm_with_gas_limit( - crate::vm_m5::vm::MultiVMSubversion::V2, + crate::vm_m5::vm_instance::MultiVMSubversion::V2, oracle_tools, l1_batch_env.glue_into(), block_properties, @@ -88,7 +88,7 @@ impl VmInstance { }; let inner_vm = crate::vm_m6::vm_with_bootloader::init_vm_with_gas_limit( - crate::vm_m6::vm::MultiVMSubversion::V1, + crate::vm_m6::vm_instance::MultiVMSubversion::V1, oracle_tools, l1_batch_env.glue_into(), block_properties, @@ -113,7 +113,7 @@ impl VmInstance { }; let inner_vm = crate::vm_m6::vm_with_bootloader::init_vm_with_gas_limit( - crate::vm_m6::vm::MultiVMSubversion::V2, + crate::vm_m6::vm_instance::MultiVMSubversion::V2, oracle_tools, l1_batch_env.glue_into(), block_properties, @@ -154,7 +154,6 @@ impl VmInstance { l1_batch_env.glue_into(), system_env.clone().glue_into(), storage_view.clone(), - H::VmVirtualBlocksMode::default(), ); let vm = VmInstanceVersion::VmVirtualBlocks(Box::new(vm)); Self { @@ -168,7 +167,6 @@ impl VmInstance { l1_batch_env.glue_into(), system_env.clone(), storage_view.clone(), - H::VmVirtualBlocksRefundsEnhancement::default(), ); let vm = VmInstanceVersion::VmVirtualBlocksRefundsEnhancement(Box::new(vm)); Self { @@ -182,7 +180,6 @@ impl VmInstance { l1_batch_env.glue_into(), system_env.clone(), storage_view.clone(), - H::VmBoojumIntegration::default(), ); let vm = VmInstanceVersion::VmBoojumIntegration(Box::new(vm)); Self { diff --git a/core/lib/multivm/src/glue/mod.rs b/core/lib/multivm/src/glue/mod.rs index 8b27fc39cd5..f351455ac20 100644 --- a/core/lib/multivm/src/glue/mod.rs +++ b/core/lib/multivm/src/glue/mod.rs @@ -11,7 +11,7 @@ pub(crate) mod block_properties; pub(crate) mod history_mode; pub(crate) mod init_vm; pub(crate) mod oracle_tools; -pub(crate) mod tracer; +pub mod tracers; mod types; /// This trait is a workaround on the Rust'c [orphan rule](orphan_rule). diff --git a/core/lib/multivm/src/glue/oracle_tools.rs b/core/lib/multivm/src/glue/oracle_tools.rs index 02ada1b3f60..868f88c015b 100644 --- a/core/lib/multivm/src/glue/oracle_tools.rs +++ b/core/lib/multivm/src/glue/oracle_tools.rs @@ -16,13 +16,17 @@ where pub fn new(version: VmVersion, state: StoragePtr>, history: H) -> Self { match version { VmVersion::M5WithoutRefunds => { - let oracle_tools = - crate::vm_m5::OracleTools::new(state, crate::vm_m5::vm::MultiVMSubversion::V1); + let oracle_tools = crate::vm_m5::OracleTools::new( + state, + crate::vm_m5::vm_instance::MultiVMSubversion::V1, + ); OracleTools::M5(oracle_tools) } VmVersion::M5WithRefunds => { - let oracle_tools = - crate::vm_m5::OracleTools::new(state, crate::vm_m5::vm::MultiVMSubversion::V2); + let oracle_tools = crate::vm_m5::OracleTools::new( + state, + crate::vm_m5::vm_instance::MultiVMSubversion::V2, + ); OracleTools::M5(oracle_tools) } VmVersion::M6Initial | VmVersion::M6BugWithCompressionFixed => { diff --git a/core/lib/multivm/src/glue/tracer/implementations.rs b/core/lib/multivm/src/glue/tracer/implementations.rs deleted file mode 100644 index 7fd7e0b4593..00000000000 --- a/core/lib/multivm/src/glue/tracer/implementations.rs +++ /dev/null @@ -1,112 +0,0 @@ -use crate::glue::tracer::{IntoVmRefundsEnhancementTracer, IntoVmVirtualBlocksTracer}; -use crate::vm_latest::{CallTracer, StorageInvocations, ValidationTracer}; -use zksync_state::WriteStorage; - -impl IntoVmVirtualBlocksTracer for StorageInvocations -where - H: crate::HistoryMode, - S: WriteStorage, -{ - fn vm_virtual_blocks( - &self, - ) -> Box> { - Box::new(crate::vm_virtual_blocks::StorageInvocations::new( - self.limit, - )) - } -} - -impl IntoVmVirtualBlocksTracer for CallTracer -where - H: crate::HistoryMode + 'static, - S: WriteStorage, -{ - fn vm_virtual_blocks( - &self, - ) -> Box> { - Box::new(crate::vm_virtual_blocks::CallTracer::new( - self.result.clone(), - H::VmVirtualBlocksMode::default(), - )) - } -} - -impl IntoVmVirtualBlocksTracer for ValidationTracer -where - H: crate::HistoryMode + 'static, - S: WriteStorage, -{ - fn vm_virtual_blocks( - &self, - ) -> Box> { - let params = self.params(); - Box::new(crate::vm_virtual_blocks::ValidationTracer::new( - crate::vm_virtual_blocks::ValidationTracerParams { - user_address: params.user_address, - paymaster_address: params.paymaster_address, - trusted_slots: params.trusted_slots, - trusted_addresses: params.trusted_addresses, - trusted_address_slots: params.trusted_address_slots, - computational_gas_limit: params.computational_gas_limit, - }, - self.result.clone(), - )) - } -} - -impl IntoVmRefundsEnhancementTracer for StorageInvocations -where - H: crate::HistoryMode, - S: WriteStorage, -{ - fn vm_refunds_enhancement( - &self, - ) -> Box> - { - Box::new(crate::vm_refunds_enhancement::StorageInvocations::new( - self.limit, - )) - } -} - -impl IntoVmRefundsEnhancementTracer for CallTracer -where - H: crate::HistoryMode + 'static, - S: WriteStorage, -{ - fn vm_refunds_enhancement( - &self, - ) -> Box> - { - Box::new(crate::vm_refunds_enhancement::CallTracer::new( - self.result.clone(), - H::VmVirtualBlocksRefundsEnhancement::default(), - )) - } -} - -impl IntoVmRefundsEnhancementTracer for ValidationTracer -where - H: crate::HistoryMode + 'static, - S: WriteStorage, -{ - fn vm_refunds_enhancement( - &self, - ) -> Box> - { - let params = self.params(); - Box::new( - crate::vm_refunds_enhancement::ValidationTracer::new( - crate::vm_refunds_enhancement::ValidationTracerParams { - user_address: params.user_address, - paymaster_address: params.paymaster_address, - trusted_slots: params.trusted_slots, - trusted_addresses: params.trusted_addresses, - trusted_address_slots: params.trusted_address_slots, - computational_gas_limit: params.computational_gas_limit, - }, - ) - .0, - ) - } -} diff --git a/core/lib/multivm/src/glue/tracer.rs b/core/lib/multivm/src/glue/tracers/mod.rs similarity index 55% rename from core/lib/multivm/src/glue/tracer.rs rename to core/lib/multivm/src/glue/tracers/mod.rs index cab66abc0b8..b9c0e083b84 100644 --- a/core/lib/multivm/src/glue/tracer.rs +++ b/core/lib/multivm/src/glue/tracers/mod.rs @@ -1,6 +1,6 @@ //! # Multivm Tracing //! -//! The MultiVM tracing module enables Tracers support for different versions of virtual machines +//! The MultiVM tracing module enables support for Tracers in different versions of virtual machines. //! //! ## Overview //! @@ -8,43 +8,37 @@ //! this module defines one primary trait: //! //! - `MultivmTracer`: This trait represents a tracer that can be converted into a tracer for -//! specific VM version. +//! a specific VM version. //! -//! Specific traits for each VM version, which supports Custom Tracers +//! Specific traits for each VM version, which support Custom Tracers: //! - `IntoLatestTracer`: This trait is responsible for converting a tracer //! into a form compatible with the latest VM version. //! It defines a method `latest` for obtaining a boxed tracer. //! -//! - `IntoVmVirtualBlocksTracer`:This trait is responsible for converting a tracer +//! - `IntoVmVirtualBlocksTracer`: This trait is responsible for converting a tracer //! into a form compatible with the vm_virtual_blocks version. //! It defines a method `vm_virtual_blocks` for obtaining a boxed tracer. //! -//! For `MultivmTracer` to be implemented, Tracer must implement all N currently +//! For `MultivmTracer` to be implemented, the Tracer must implement all N currently //! existing sub-traits. //! -//! Any tracer compatible with the current VM automatically -//! supports conversion into the latest VM, -//! but the remaining traits required by the MultivmTracer should be implemented manually. -//! If a certain tracer is not intended to be used with VMs other than the latest one, -//! these traits should still be implemented, but one may just write a panicking implementation. -//! //! ## Adding a new VM version //! //! To add support for one more VM version to MultivmTracer, one needs to: -//! - Create a new trait performing conversion to the specified VM tracer, e.g. `IntoTracer`. -//! - Provide implementations of this trait for all the structures that currently implement `MultivmTracer`. +//! - Create a new trait performing conversion to the specified VM tracer, e.g., `IntoTracer`. //! - Add this trait as a trait bound to the `MultivmTracer`. //! - Add this trait as a trait bound for `T` in `MultivmTracer` implementation. -//! - Integrate the newly added method to the MultiVM itself (e.g. add required tracer conversions where applicable). -mod implementations; - +//! — Implement the trait for `T` with a bound to `VmTracer` for a specific version. +//! use crate::HistoryMode; use zksync_state::WriteStorage; +pub type MultiVmTracerPointer = Box>; + pub trait MultivmTracer: IntoLatestTracer + IntoVmVirtualBlocksTracer + IntoVmRefundsEnhancementTracer { - fn into_boxed(self) -> Box> + fn into_tracer_pointer(self) -> MultiVmTracerPointer where Self: Sized + 'static, { @@ -53,13 +47,13 @@ pub trait MultivmTracer: } pub trait IntoLatestTracer { - fn latest(&self) -> Box>; + fn latest(&self) -> crate::vm_latest::TracerPointer; } pub trait IntoVmVirtualBlocksTracer { fn vm_virtual_blocks( &self, - ) -> Box>; + ) -> crate::vm_virtual_blocks::TracerPointer; } pub trait IntoVmRefundsEnhancementTracer { @@ -74,20 +68,46 @@ where H: HistoryMode, T: crate::vm_latest::VmTracer + Clone + 'static, { - fn latest(&self) -> Box> { + fn latest(&self) -> crate::vm_latest::TracerPointer { Box::new(self.clone()) } } -impl MultivmTracer for T +impl IntoVmVirtualBlocksTracer for T where S: WriteStorage, H: HistoryMode, - T: crate::vm_latest::VmTracer - + IntoLatestTracer - + IntoVmVirtualBlocksTracer - + IntoVmRefundsEnhancementTracer + T: crate::vm_virtual_blocks::VmTracer + Clone + 'static, +{ + fn vm_virtual_blocks( + &self, + ) -> crate::vm_virtual_blocks::TracerPointer { + Box::new(self.clone()) + } +} + +impl IntoVmRefundsEnhancementTracer for T +where + S: WriteStorage, + H: HistoryMode, + T: crate::vm_refunds_enhancement::VmTracer + Clone + 'static, +{ + fn vm_refunds_enhancement( + &self, + ) -> Box> + { + Box::new(self.clone()) + } +} + +impl MultivmTracer for T +where + S: WriteStorage, + H: HistoryMode, + T: IntoLatestTracer + + IntoVmVirtualBlocksTracer + + IntoVmRefundsEnhancementTracer, { } diff --git a/core/lib/multivm/src/glue/types/vm/vm_block_result.rs b/core/lib/multivm/src/glue/types/vm/vm_block_result.rs index cdbedfd4a75..827ac7fe82a 100644 --- a/core/lib/multivm/src/glue/types/vm/vm_block_result.rs +++ b/core/lib/multivm/src/glue/types/vm/vm_block_result.rs @@ -11,8 +11,8 @@ use crate::interface::{ // Bootloader memory required only for producing witnesses, // and server doesn't need to generate witnesses for old blocks -impl GlueFrom for crate::interface::FinishedL1Batch { - fn glue_from(value: crate::vm_m5::vm::VmBlockResult) -> Self { +impl GlueFrom for crate::interface::FinishedL1Batch { + fn glue_from(value: crate::vm_m5::vm_instance::VmBlockResult) -> Self { crate::interface::FinishedL1Batch { block_tip_execution_result: VmExecutionResultAndLogs { result: value.block_tip_result.revert_reason.glue_into(), @@ -48,8 +48,8 @@ impl GlueFrom for crate::interface::FinishedL1B } } -impl GlueFrom for crate::interface::FinishedL1Batch { - fn glue_from(value: crate::vm_m6::vm::VmBlockResult) -> Self { +impl GlueFrom for crate::interface::FinishedL1Batch { + fn glue_from(value: crate::vm_m6::vm_instance::VmBlockResult) -> Self { crate::interface::FinishedL1Batch { block_tip_execution_result: VmExecutionResultAndLogs { result: value.block_tip_result.revert_reason.glue_into(), @@ -85,8 +85,8 @@ impl GlueFrom for crate::interface::FinishedL1B } } -impl GlueFrom for crate::interface::FinishedL1Batch { - fn glue_from(value: crate::vm_1_3_2::vm::VmBlockResult) -> Self { +impl GlueFrom for crate::interface::FinishedL1Batch { + fn glue_from(value: crate::vm_1_3_2::vm_instance::VmBlockResult) -> Self { crate::interface::FinishedL1Batch { block_tip_execution_result: VmExecutionResultAndLogs { result: value.block_tip_result.revert_reason.glue_into(), @@ -128,8 +128,10 @@ impl GlueFrom for crate::interface::Finished } } -impl GlueFrom for crate::interface::VmExecutionResultAndLogs { - fn glue_from(value: crate::vm_1_3_2::vm::VmBlockResult) -> Self { +impl GlueFrom + for crate::interface::VmExecutionResultAndLogs +{ + fn glue_from(value: crate::vm_1_3_2::vm_instance::VmBlockResult) -> Self { let mut result = value .full_result .revert_reason @@ -167,8 +169,10 @@ impl GlueFrom for crate::interface::VmExecut } } -impl GlueFrom for crate::interface::VmExecutionResultAndLogs { - fn glue_from(value: crate::vm_m5::vm::VmBlockResult) -> Self { +impl GlueFrom + for crate::interface::VmExecutionResultAndLogs +{ + fn glue_from(value: crate::vm_m5::vm_instance::VmBlockResult) -> Self { let mut result = value .full_result .revert_reason @@ -195,8 +199,10 @@ impl GlueFrom for crate::interface::VmExecution } } -impl GlueFrom for crate::interface::VmExecutionResultAndLogs { - fn glue_from(value: crate::vm_m6::vm::VmBlockResult) -> Self { +impl GlueFrom + for crate::interface::VmExecutionResultAndLogs +{ + fn glue_from(value: crate::vm_m6::vm_instance::VmBlockResult) -> Self { let mut result = value .full_result .revert_reason diff --git a/core/lib/multivm/src/glue/types/vm/vm_partial_execution_result.rs b/core/lib/multivm/src/glue/types/vm/vm_partial_execution_result.rs index 9626468a2bb..4de727a04c1 100644 --- a/core/lib/multivm/src/glue/types/vm/vm_partial_execution_result.rs +++ b/core/lib/multivm/src/glue/types/vm/vm_partial_execution_result.rs @@ -1,9 +1,9 @@ use crate::glue::{GlueFrom, GlueInto}; -impl GlueFrom +impl GlueFrom for crate::interface::VmExecutionResultAndLogs { - fn glue_from(value: crate::vm_m5::vm::VmPartialExecutionResult) -> Self { + fn glue_from(value: crate::vm_m5::vm_instance::VmPartialExecutionResult) -> Self { Self { result: value.revert_reason.glue_into(), logs: value.logs.clone(), @@ -25,10 +25,10 @@ impl GlueFrom } } -impl GlueFrom +impl GlueFrom for crate::interface::VmExecutionResultAndLogs { - fn glue_from(value: crate::vm_m6::vm::VmPartialExecutionResult) -> Self { + fn glue_from(value: crate::vm_m6::vm_instance::VmPartialExecutionResult) -> Self { Self { result: value.revert_reason.glue_into(), logs: value.logs.clone(), @@ -48,10 +48,10 @@ impl GlueFrom } } -impl GlueFrom +impl GlueFrom for crate::interface::VmExecutionResultAndLogs { - fn glue_from(value: crate::vm_1_3_2::vm::VmPartialExecutionResult) -> Self { + fn glue_from(value: crate::vm_1_3_2::vm_instance::VmPartialExecutionResult) -> Self { Self { result: value.revert_reason.glue_into(), logs: value.logs.clone(), diff --git a/core/lib/multivm/src/glue/types/vm/vm_tx_execution_result.rs b/core/lib/multivm/src/glue/types/vm/vm_tx_execution_result.rs index 1390d9e9d58..10e422edbca 100644 --- a/core/lib/multivm/src/glue/types/vm/vm_tx_execution_result.rs +++ b/core/lib/multivm/src/glue/types/vm/vm_tx_execution_result.rs @@ -2,8 +2,8 @@ use crate::glue::{GlueFrom, GlueInto}; use crate::interface::{ExecutionResult, Refunds, TxRevertReason, VmExecutionResultAndLogs}; use zksync_types::tx::tx_execution_info::TxExecutionStatus; -impl GlueFrom for VmExecutionResultAndLogs { - fn glue_from(value: crate::vm_m5::vm::VmTxExecutionResult) -> Self { +impl GlueFrom for VmExecutionResultAndLogs { + fn glue_from(value: crate::vm_m5::vm_instance::VmTxExecutionResult) -> Self { let mut result: VmExecutionResultAndLogs = value.result.glue_into(); if result.result.is_failed() { assert_eq!(value.status, TxExecutionStatus::Failure); @@ -17,8 +17,8 @@ impl GlueFrom for VmExecutionResultAndLog } } -impl GlueFrom for VmExecutionResultAndLogs { - fn glue_from(value: crate::vm_m6::vm::VmTxExecutionResult) -> Self { +impl GlueFrom for VmExecutionResultAndLogs { + fn glue_from(value: crate::vm_m6::vm_instance::VmTxExecutionResult) -> Self { let mut result: VmExecutionResultAndLogs = value.result.glue_into(); if result.result.is_failed() { assert_eq!(value.status, TxExecutionStatus::Failure); @@ -32,8 +32,8 @@ impl GlueFrom for VmExecutionResultAndLog } } -impl GlueFrom for VmExecutionResultAndLogs { - fn glue_from(value: crate::vm_1_3_2::vm::VmTxExecutionResult) -> Self { +impl GlueFrom for VmExecutionResultAndLogs { + fn glue_from(value: crate::vm_1_3_2::vm_instance::VmTxExecutionResult) -> Self { let mut result: VmExecutionResultAndLogs = value.result.glue_into(); if result.result.is_failed() { assert_eq!(value.status, TxExecutionStatus::Failure); @@ -47,11 +47,11 @@ impl GlueFrom for VmExecutionResultAnd } } -impl GlueFrom> +impl GlueFrom> for VmExecutionResultAndLogs { fn glue_from( - value: Result, + value: Result, ) -> Self { match value { Ok(result) => result.glue_into(), @@ -76,11 +76,16 @@ impl GlueFrom> - for VmExecutionResultAndLogs +impl + GlueFrom< + Result, + > for VmExecutionResultAndLogs { fn glue_from( - value: Result, + value: Result< + crate::vm_1_3_2::vm_instance::VmTxExecutionResult, + crate::vm_1_3_2::TxRevertReason, + >, ) -> Self { match value { Ok(result) => result.glue_into(), @@ -105,11 +110,11 @@ impl GlueFrom> +impl GlueFrom> for VmExecutionResultAndLogs { fn glue_from( - value: Result, + value: Result, ) -> Self { match value { Ok(result) => result.glue_into(), diff --git a/core/lib/multivm/src/interface/mod.rs b/core/lib/multivm/src/interface/mod.rs index 75c088a0171..5cb1f5cd1d1 100644 --- a/core/lib/multivm/src/interface/mod.rs +++ b/core/lib/multivm/src/interface/mod.rs @@ -1,3 +1,9 @@ +pub(crate) mod traits; + +pub use traits::{ + tracers::dyn_tracers, + vm::{VmInterface, VmInterfaceHistoryEnabled}, +}; pub mod types; pub use types::{ @@ -9,4 +15,5 @@ pub use types::{ BootloaderMemory, CurrentExecutionState, ExecutionResult, FinishedL1Batch, L2Block, Refunds, VmExecutionResultAndLogs, VmExecutionStatistics, VmMemoryMetrics, }, + tracer, }; diff --git a/core/lib/multivm/src/interface/traits/mod.rs b/core/lib/multivm/src/interface/traits/mod.rs new file mode 100644 index 00000000000..a90c1c5281f --- /dev/null +++ b/core/lib/multivm/src/interface/traits/mod.rs @@ -0,0 +1,2 @@ +pub mod tracers; +pub mod vm; diff --git a/core/lib/multivm/src/interface/traits/tracers/dyn_tracers/mod.rs b/core/lib/multivm/src/interface/traits/tracers/dyn_tracers/mod.rs new file mode 100644 index 00000000000..bc34775e613 --- /dev/null +++ b/core/lib/multivm/src/interface/traits/tracers/dyn_tracers/mod.rs @@ -0,0 +1,2 @@ +pub mod vm_1_3_3; +pub mod vm_1_4_0; diff --git a/core/lib/multivm/src/interface/traits/tracers/dyn_tracers/vm_1_3_3.rs b/core/lib/multivm/src/interface/traits/tracers/dyn_tracers/vm_1_3_3.rs new file mode 100644 index 00000000000..2138dd086c0 --- /dev/null +++ b/core/lib/multivm/src/interface/traits/tracers/dyn_tracers/vm_1_3_3.rs @@ -0,0 +1,33 @@ +use zk_evm_1_3_3::abstractions::Memory; +use zk_evm_1_3_3::tracing::{ + AfterDecodingData, AfterExecutionData, BeforeExecutionData, VmLocalStateData, +}; +use zksync_state::StoragePtr; + +/// Version of zk_evm_1_3_3::Tracer suitable for dynamic dispatch. +pub trait DynTracer { + fn before_decoding(&mut self, _state: VmLocalStateData<'_>, _memory: &M) {} + fn after_decoding( + &mut self, + _state: VmLocalStateData<'_>, + _data: AfterDecodingData, + _memory: &M, + ) { + } + fn before_execution( + &mut self, + _state: VmLocalStateData<'_>, + _data: BeforeExecutionData, + _memory: &M, + _storage: StoragePtr, + ) { + } + fn after_execution( + &mut self, + _state: VmLocalStateData<'_>, + _data: AfterExecutionData, + _memory: &M, + _storage: StoragePtr, + ) { + } +} diff --git a/core/lib/multivm/src/interface/traits/tracers/dyn_tracers/vm_1_4_0.rs b/core/lib/multivm/src/interface/traits/tracers/dyn_tracers/vm_1_4_0.rs new file mode 100644 index 00000000000..61d7831393d --- /dev/null +++ b/core/lib/multivm/src/interface/traits/tracers/dyn_tracers/vm_1_4_0.rs @@ -0,0 +1,33 @@ +use zk_evm_1_4_0::abstractions::Memory; +use zk_evm_1_4_0::tracing::{ + AfterDecodingData, AfterExecutionData, BeforeExecutionData, VmLocalStateData, +}; +use zksync_state::StoragePtr; + +/// Version of zk_evm_1_3_3::Tracer suitable for dynamic dispatch. +pub trait DynTracer { + fn before_decoding(&mut self, _state: VmLocalStateData<'_>, _memory: &M) {} + fn after_decoding( + &mut self, + _state: VmLocalStateData<'_>, + _data: AfterDecodingData, + _memory: &M, + ) { + } + fn before_execution( + &mut self, + _state: VmLocalStateData<'_>, + _data: BeforeExecutionData, + _memory: &M, + _storage: StoragePtr, + ) { + } + fn after_execution( + &mut self, + _state: VmLocalStateData<'_>, + _data: AfterExecutionData, + _memory: &M, + _storage: StoragePtr, + ) { + } +} diff --git a/core/lib/multivm/src/interface/traits/tracers/mod.rs b/core/lib/multivm/src/interface/traits/tracers/mod.rs new file mode 100644 index 00000000000..f045674dfcf --- /dev/null +++ b/core/lib/multivm/src/interface/traits/tracers/mod.rs @@ -0,0 +1 @@ +pub mod dyn_tracers; diff --git a/core/lib/multivm/src/interface/traits/vm.rs b/core/lib/multivm/src/interface/traits/vm.rs new file mode 100644 index 00000000000..45daf770681 --- /dev/null +++ b/core/lib/multivm/src/interface/traits/vm.rs @@ -0,0 +1,128 @@ +//! This module contains traits for the VM interface. +//! All VMs should implement these traits to be used in our system. +//! The trait is generic over the storage type, allowing it to be used with any storage implementation. +//! Additionally, this trait is generic over HistoryMode, allowing it to be used with or without history. +//! +//! `TracerDispatcher` is an associated type used to dispatch tracers in VMs. +//! It manages tracers across different VM versions. +//! Even though we use the same interface for all VM versions, +//! we can now specify only the necessary trait bounds for each VM version. +//! +//! Generally speaking, in most cases, the tracer dispatcher is a wrapper around `Vec>`, +//! where `VmTracer` is a trait implemented for a specific VM version. +//! +//! Example usage: +//! ``` +//! use std::{ +//! cell::RefCell, +//! rc::Rc, +//! sync::Arc +//! }; +//! use once_cell::sync::OnceCell; +//! use multivm::{ +//! interface::{L1BatchEnv, SystemEnv, VmInterface}, +//! tracers::CallTracer , +//! vm_latest::ToTracerPointer +//! }; +//! use zksync_state::{InMemoryStorage, StorageView}; +//! use zksync_types::Transaction; +//! +//! // Prepare the environment for the VM. +//! let l1_batch_env = L1BatchEnv::new(); +//! let system_env = SystemEnv::default(); +//! // Create storage +//! let storage = Rc::new(RefCell::new(StorageView::new(InMemoryStorage::default()))); +//! // Instantiate VM with the desired version. +//! let mut vm = multivm::vm_latest::Vm::new(l1_batch_env, system_env, storage); +//! // Push a transaction to the VM. +//! let tx = Transaction::default(); +//! vm.push_transaction(tx); +//! // Instantiate a tracer. +//! let result = Arc::new(OnceCell::new()); +//! let call_tracer = CallTracer::new(result.clone()).into_tracer_pointer(); +//! // Inspect the transaction with a tracer. You can use either one tracer or a vector of tracers. +//! let result = vm.inspect(call_tracer.into(), multivm::interface::VmExecutionMode::OneTx); +//! +//! // To obtain the result of the entire batch, you can use the following code: +//! let result = vm.execute(multivm::interface::VmExecutionMode::Batch); +//! ``` + +use crate::interface::types::errors::BytecodeCompressionError; +use crate::interface::types::inputs::{L1BatchEnv, L2BlockEnv, SystemEnv, VmExecutionMode}; +use crate::interface::types::outputs::{ + BootloaderMemory, CurrentExecutionState, VmExecutionResultAndLogs, +}; + +use crate::vm_latest::HistoryEnabled; +use crate::HistoryMode; +use zksync_state::{StoragePtr, WriteStorage}; +use zksync_types::Transaction; +use zksync_utils::bytecode::CompressedBytecodeInfo; + +pub trait VmInterface { + type TracerDispatcher: Default; + + /// Initialize VM. + fn new(batch_env: L1BatchEnv, system_env: SystemEnv, storage: StoragePtr) -> Self; + + /// Push transaction to bootloader memory. + fn push_transaction(&mut self, tx: Transaction); + + /// Execute next VM step (either next transaction or bootloader or the whole batch). + fn execute(&mut self, execution_mode: VmExecutionMode) -> VmExecutionResultAndLogs { + self.inspect(Self::TracerDispatcher::default(), execution_mode) + } + + /// Execute next VM step (either next transaction or bootloader or the whole batch) + /// with custom tracers. + fn inspect( + &mut self, + dispatcher: Self::TracerDispatcher, + execution_mode: VmExecutionMode, + ) -> VmExecutionResultAndLogs; + + /// Get bootloader memory. + fn get_bootloader_memory(&self) -> BootloaderMemory; + + /// Get last transaction's compressed bytecodes. + fn get_last_tx_compressed_bytecodes(&self) -> Vec; + + /// Start a new L2 block. + fn start_new_l2_block(&mut self, l2_block_env: L2BlockEnv); + + /// Get the current state of the virtual machine. + fn get_current_execution_state(&self) -> CurrentExecutionState; + + /// Execute transaction with optional bytecode compression. + fn execute_transaction_with_bytecode_compression( + &mut self, + tx: Transaction, + with_compression: bool, + ) -> Result { + self.inspect_transaction_with_bytecode_compression( + Self::TracerDispatcher::default(), + tx, + with_compression, + ) + } + + /// Execute transaction with optional bytecode compression using custom tracers. + fn inspect_transaction_with_bytecode_compression( + &mut self, + tracer: Self::TracerDispatcher, + tx: Transaction, + with_compression: bool, + ) -> Result; +} + +/// Methods of VM requiring history manipulations. +pub trait VmInterfaceHistoryEnabled: VmInterface { + /// Create a snapshot of the current VM state and push it into memory. + fn make_snapshot(&mut self); + + /// Roll back VM state to the latest snapshot and destroy the snapshot. + fn rollback_to_the_latest_snapshot(&mut self); + + /// Pop the latest snapshot from memory and destroy it. + fn pop_snapshot_no_rollback(&mut self); +} diff --git a/core/lib/multivm/src/interface/types/mod.rs b/core/lib/multivm/src/interface/types/mod.rs index 702a43144d2..a70d0a59ead 100644 --- a/core/lib/multivm/src/interface/types/mod.rs +++ b/core/lib/multivm/src/interface/types/mod.rs @@ -1,3 +1,4 @@ pub mod errors; pub(crate) mod inputs; pub(crate) mod outputs; +pub mod tracer; diff --git a/core/lib/multivm/src/interface/types/tracer.rs b/core/lib/multivm/src/interface/types/tracer.rs new file mode 100644 index 00000000000..4221eddaf7a --- /dev/null +++ b/core/lib/multivm/src/interface/types/tracer.rs @@ -0,0 +1,39 @@ +use crate::interface::Halt; + +#[derive(Debug, Clone, PartialEq)] +pub enum TracerExecutionStopReason { + Finish, + Abort(Halt), +} + +#[derive(Debug, Clone, PartialEq)] +pub enum TracerExecutionStatus { + Continue, + Stop(TracerExecutionStopReason), +} + +impl TracerExecutionStatus { + /// Chose the stricter ExecutionStatus + /// If both statuses are Continue, then the result is Continue + /// If one of the statuses is Abort, then the result is Abort + /// If one of the statuses is Finish, then the result is Finish + pub fn stricter(&self, other: &Self) -> Self { + match (self, other) { + (Self::Continue, Self::Continue) => Self::Continue, + (Self::Stop(TracerExecutionStopReason::Abort(reason)), _) + | (_, Self::Stop(TracerExecutionStopReason::Abort(reason))) => { + Self::Stop(TracerExecutionStopReason::Abort(reason.clone())) + } + (Self::Stop(TracerExecutionStopReason::Finish), _) + | (_, Self::Stop(TracerExecutionStopReason::Finish)) => { + Self::Stop(TracerExecutionStopReason::Finish) + } + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum VmExecutionStopReason { + VmFinished, + TracerRequestedStop(TracerExecutionStopReason), +} diff --git a/core/lib/multivm/src/lib.rs b/core/lib/multivm/src/lib.rs index 51a8b8036fb..aadefe06a51 100644 --- a/core/lib/multivm/src/lib.rs +++ b/core/lib/multivm/src/lib.rs @@ -5,8 +5,10 @@ pub use crate::{ glue::{ - block_properties::BlockProperties, history_mode::HistoryMode, oracle_tools::OracleTools, - tracer::MultivmTracer, + block_properties::BlockProperties, + history_mode::HistoryMode, + oracle_tools::OracleTools, + tracers::{MultiVmTracerPointer, MultivmTracer}, }, vm_instance::VmInstance, }; @@ -16,6 +18,7 @@ mod glue; mod vm_instance; pub mod interface; +pub mod tracers; pub mod versions; pub use versions::vm_1_3_2; diff --git a/core/lib/multivm/src/tracers/call_tracer/mod.rs b/core/lib/multivm/src/tracers/call_tracer/mod.rs new file mode 100644 index 00000000000..90343a53bf6 --- /dev/null +++ b/core/lib/multivm/src/tracers/call_tracer/mod.rs @@ -0,0 +1,41 @@ +use once_cell::sync::OnceCell; +use std::sync::Arc; +use zksync_types::vm_trace::Call; + +pub mod vm_latest; +pub mod vm_refunds_enhancement; +pub mod vm_virtual_blocks; + +#[derive(Debug, Clone)] +pub struct CallTracer { + stack: Vec, + result: Arc>>, +} + +#[derive(Debug, Clone)] +struct FarcallAndNearCallCount { + farcall: Call, + near_calls_after: usize, +} + +impl CallTracer { + pub fn new(result: Arc>>) -> Self { + Self { + stack: vec![], + result, + } + } + + fn extract_result(&mut self) -> Vec { + std::mem::take(&mut self.stack) + .into_iter() + .map(|x| x.farcall) + .collect() + } + + fn store_result(&mut self) { + let result = self.extract_result(); + let cell = self.result.as_ref(); + cell.set(result).unwrap(); + } +} diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/call.rs b/core/lib/multivm/src/tracers/call_tracer/vm_latest/mod.rs similarity index 77% rename from core/lib/multivm/src/versions/vm_latest/tracers/call.rs rename to core/lib/multivm/src/tracers/call_tracer/vm_latest/mod.rs index 61c40e5b3be..2b6fc144bd4 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/call.rs +++ b/core/lib/multivm/src/tracers/call_tracer/vm_latest/mod.rs @@ -1,49 +1,24 @@ -use once_cell::sync::OnceCell; -use std::marker::PhantomData; -use std::sync::Arc; - -use zk_evm_1_4_0::tracing::{AfterExecutionData, VmLocalStateData}; -use zk_evm_1_4_0::zkevm_opcode_defs::{ - FarCallABI, FarCallOpcode, FatPointer, Opcode, RetOpcode, - CALL_IMPLICIT_CALLDATA_FAT_PTR_REGISTER, RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER, +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 crate::interface::VmRevertReason; use zksync_state::{StoragePtr, WriteStorage}; use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; use zksync_types::vm_trace::{Call, CallType}; +use zksync_types::FarCallOpcode; use zksync_types::U256; -use crate::vm_latest::old_vm::history_recorder::HistoryMode; -use crate::vm_latest::old_vm::memory::SimpleMemory; -use crate::vm_latest::tracers::traits::{DynTracer, VmTracer}; -use crate::vm_latest::types::internals::ZkSyncVmState; -use crate::vm_latest::{BootloaderState, VmExecutionStopReason}; - -#[derive(Debug, Clone)] -pub struct CallTracer { - stack: Vec, - pub result: Arc>>, - _phantom: PhantomData H>, -} - -#[derive(Debug, Clone)] -struct FarcallAndNearCallCount { - farcall: Call, - near_calls_after: usize, -} - -impl CallTracer { - pub fn new(resulted_stack: Arc>>, _history: H) -> Self { - Self { - stack: vec![], - result: resulted_stack, - _phantom: PhantomData, - } - } -} +use crate::interface::{ + tracer::VmExecutionStopReason, traits::tracers::dyn_tracers::vm_1_4_0::DynTracer, + VmRevertReason, +}; +use crate::tracers::call_tracer::{CallTracer, FarcallAndNearCallCount}; +use crate::vm_latest::{BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState}; -impl DynTracer for CallTracer { +impl DynTracer> for CallTracer { fn after_execution( &mut self, state: VmLocalStateData<'_>, @@ -75,43 +50,35 @@ impl DynTracer for CallTracer { ..Default::default() }; - self.handle_far_call_op_code(state, data, memory, &mut current_call); + self.handle_far_call_op_code_latest(state, memory, &mut current_call); self.stack.push(FarcallAndNearCallCount { farcall: current_call, near_calls_after: 0, }); } Opcode::Ret(ret_code) => { - self.handle_ret_op_code(state, data, memory, ret_code); + self.handle_ret_op_code_latest(state, memory, ret_code); } _ => {} }; } } -impl VmTracer for CallTracer { +impl VmTracer for CallTracer { fn after_vm_execution( &mut self, _state: &mut ZkSyncVmState, _bootloader_state: &BootloaderState, _stop_reason: VmExecutionStopReason, ) { - self.result - .set( - std::mem::take(&mut self.stack) - .into_iter() - .map(|x| x.farcall) - .collect(), - ) - .expect("Result is already set"); + self.store_result() } } -impl CallTracer { - fn handle_far_call_op_code( +impl CallTracer { + fn handle_far_call_op_code_latest( &mut self, state: VmLocalStateData<'_>, - _data: AfterExecutionData, memory: &SimpleMemory, current_call: &mut Call, ) { @@ -164,7 +131,7 @@ impl CallTracer { current_call.gas = current.ergs_remaining; } - fn save_output( + fn save_output_latest( &mut self, state: VmLocalStateData<'_>, memory: &SimpleMemory, @@ -208,10 +175,9 @@ impl CallTracer { } } - fn handle_ret_op_code( + fn handle_ret_op_code_latest( &mut self, state: VmLocalStateData<'_>, - _data: AfterExecutionData, memory: &SimpleMemory, ret_opcode: RetOpcode, ) { @@ -230,7 +196,7 @@ impl CallTracer { .parent_gas .saturating_sub(state.vm_local_state.callstack.current.ergs_remaining); - self.save_output(state, memory, ret_opcode, &mut current_call.farcall); + self.save_output_latest(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 diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/call.rs b/core/lib/multivm/src/tracers/call_tracer/vm_refunds_enhancement/mod.rs similarity index 76% rename from core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/call.rs rename to core/lib/multivm/src/tracers/call_tracer/vm_refunds_enhancement/mod.rs index 557d0fc5890..43dd363dcea 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/call.rs +++ b/core/lib/multivm/src/tracers/call_tracer/vm_refunds_enhancement/mod.rs @@ -1,49 +1,26 @@ -use once_cell::sync::OnceCell; -use std::marker::PhantomData; -use std::sync::Arc; - -use zk_evm_1_3_3::tracing::{AfterExecutionData, VmLocalStateData}; -use zk_evm_1_3_3::zkevm_opcode_defs::{ - FarCallABI, FarCallOpcode, FatPointer, Opcode, RetOpcode, - CALL_IMPLICIT_CALLDATA_FAT_PTR_REGISTER, RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER, +use zk_evm_1_3_3::{ + tracing::{AfterExecutionData, VmLocalStateData}, + zkevm_opcode_defs::{ + FarCallABI, FatPointer, Opcode, RetOpcode, CALL_IMPLICIT_CALLDATA_FAT_PTR_REGISTER, + RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER, + }, }; - -use crate::interface::VmRevertReason; use zksync_state::{StoragePtr, WriteStorage}; use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; use zksync_types::vm_trace::{Call, CallType}; +use zksync_types::FarCallOpcode; use zksync_types::U256; -use crate::vm_refunds_enhancement::old_vm::history_recorder::HistoryMode; -use crate::vm_refunds_enhancement::old_vm::memory::SimpleMemory; -use crate::vm_refunds_enhancement::tracers::traits::{DynTracer, VmTracer}; -use crate::vm_refunds_enhancement::types::internals::ZkSyncVmState; -use crate::vm_refunds_enhancement::{BootloaderState, VmExecutionStopReason}; - -#[derive(Debug, Clone)] -pub struct CallTracer { - stack: Vec, - pub result: Arc>>, - _phantom: PhantomData H>, -} - -#[derive(Debug, Clone)] -struct FarcallAndNearCallCount { - farcall: Call, - near_calls_after: usize, -} - -impl CallTracer { - pub fn new(resulted_stack: Arc>>, _history: H) -> Self { - Self { - stack: vec![], - result: resulted_stack, - _phantom: PhantomData, - } - } -} +use crate::interface::{ + tracer::VmExecutionStopReason, traits::tracers::dyn_tracers::vm_1_3_3::DynTracer, + VmRevertReason, +}; +use crate::tracers::call_tracer::{CallTracer, FarcallAndNearCallCount}; +use crate::vm_refunds_enhancement::{ + BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState, +}; -impl DynTracer for CallTracer { +impl DynTracer> for CallTracer { fn after_execution( &mut self, state: VmLocalStateData<'_>, @@ -75,43 +52,35 @@ impl DynTracer for CallTracer { ..Default::default() }; - self.handle_far_call_op_code(state, data, memory, &mut current_call); + self.handle_far_call_op_code_refunds_enhancement(state, memory, &mut current_call); self.stack.push(FarcallAndNearCallCount { farcall: current_call, near_calls_after: 0, }); } Opcode::Ret(ret_code) => { - self.handle_ret_op_code(state, data, memory, ret_code); + self.handle_ret_op_code_refunds_enhancement(state, memory, ret_code); } _ => {} }; } } -impl VmTracer for CallTracer { +impl VmTracer for CallTracer { fn after_vm_execution( &mut self, _state: &mut ZkSyncVmState, _bootloader_state: &BootloaderState, _stop_reason: VmExecutionStopReason, ) { - self.result - .set( - std::mem::take(&mut self.stack) - .into_iter() - .map(|x| x.farcall) - .collect(), - ) - .expect("Result is already set"); + self.store_result() } } -impl CallTracer { - fn handle_far_call_op_code( +impl CallTracer { + fn handle_far_call_op_code_refunds_enhancement( &mut self, state: VmLocalStateData<'_>, - _data: AfterExecutionData, memory: &SimpleMemory, current_call: &mut Call, ) { @@ -164,7 +133,7 @@ impl CallTracer { current_call.gas = current.ergs_remaining; } - fn save_output( + fn save_output_refunds_enhancement( &mut self, state: VmLocalStateData<'_>, memory: &SimpleMemory, @@ -208,10 +177,9 @@ impl CallTracer { } } - fn handle_ret_op_code( + fn handle_ret_op_code_refunds_enhancement( &mut self, state: VmLocalStateData<'_>, - _data: AfterExecutionData, memory: &SimpleMemory, ret_opcode: RetOpcode, ) { @@ -230,7 +198,7 @@ impl CallTracer { .parent_gas .saturating_sub(state.vm_local_state.callstack.current.ergs_remaining); - self.save_output(state, memory, ret_opcode, &mut current_call.farcall); + self.save_output_refunds_enhancement(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 diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/call.rs b/core/lib/multivm/src/tracers/call_tracer/vm_virtual_blocks/mod.rs similarity index 78% rename from core/lib/multivm/src/versions/vm_virtual_blocks/tracers/call.rs rename to core/lib/multivm/src/tracers/call_tracer/vm_virtual_blocks/mod.rs index 89a406a6b5d..c78593b40e7 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/call.rs +++ b/core/lib/multivm/src/tracers/call_tracer/vm_virtual_blocks/mod.rs @@ -1,49 +1,23 @@ -use once_cell::sync::OnceCell; -use std::marker::PhantomData; -use std::sync::Arc; - use zk_evm_1_3_3::tracing::{AfterExecutionData, VmLocalStateData}; use zk_evm_1_3_3::zkevm_opcode_defs::{ - FarCallABI, FarCallOpcode, FatPointer, Opcode, RetOpcode, - CALL_IMPLICIT_CALLDATA_FAT_PTR_REGISTER, RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER, + FarCallABI, FatPointer, Opcode, RetOpcode, CALL_IMPLICIT_CALLDATA_FAT_PTR_REGISTER, + RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER, }; - -use crate::interface::{VmExecutionResultAndLogs, VmRevertReason}; use zksync_state::{StoragePtr, WriteStorage}; use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; use zksync_types::vm_trace::{Call, CallType}; +use zksync_types::FarCallOpcode; use zksync_types::U256; -use crate::vm_virtual_blocks::old_vm::history_recorder::HistoryMode; -use crate::vm_virtual_blocks::old_vm::memory::SimpleMemory; -use crate::vm_virtual_blocks::tracers::traits::{ - DynTracer, ExecutionEndTracer, ExecutionProcessing, VmTracer, +use crate::interface::{ + dyn_tracers::vm_1_3_3::DynTracer, VmExecutionResultAndLogs, VmRevertReason, +}; +use crate::tracers::call_tracer::{CallTracer, FarcallAndNearCallCount}; +use crate::vm_virtual_blocks::{ + ExecutionEndTracer, ExecutionProcessing, HistoryMode, SimpleMemory, VmTracer, }; -#[derive(Debug, Clone)] -pub struct CallTracer { - stack: Vec, - result: Arc>>, - _phantom: PhantomData H>, -} - -#[derive(Debug, Clone)] -struct FarcallAndNearCallCount { - farcall: Call, - near_calls_after: usize, -} - -impl CallTracer { - pub fn new(resulted_stack: Arc>>, _history: H) -> Self { - Self { - stack: vec![], - result: resulted_stack, - _phantom: PhantomData, - } - } -} - -impl DynTracer for CallTracer { +impl DynTracer> for CallTracer { fn after_execution( &mut self, state: VmLocalStateData<'_>, @@ -75,39 +49,32 @@ impl DynTracer for CallTracer { ..Default::default() }; - self.handle_far_call_op_code(state, data, memory, &mut current_call); + self.handle_far_call_op_code_virtual_blocks(state, data, memory, &mut current_call); self.stack.push(FarcallAndNearCallCount { farcall: current_call, near_calls_after: 0, }); } Opcode::Ret(ret_code) => { - self.handle_ret_op_code(state, data, memory, ret_code); + self.handle_ret_op_code_virtual_blocks(state, data, memory, ret_code); } _ => {} }; } } -impl ExecutionEndTracer for CallTracer {} +impl ExecutionEndTracer for CallTracer {} -impl ExecutionProcessing for CallTracer {} +impl ExecutionProcessing for CallTracer {} -impl VmTracer for CallTracer { +impl VmTracer for CallTracer { fn save_results(&mut self, _result: &mut VmExecutionResultAndLogs) { - self.result - .set( - std::mem::take(&mut self.stack) - .into_iter() - .map(|x| x.farcall) - .collect(), - ) - .expect("Result is already set"); + self.store_result() } } -impl CallTracer { - fn handle_far_call_op_code( +impl CallTracer { + fn handle_far_call_op_code_virtual_blocks( &mut self, state: VmLocalStateData<'_>, _data: AfterExecutionData, @@ -163,7 +130,7 @@ impl CallTracer { current_call.gas = current.ergs_remaining; } - fn save_output( + fn save_output_virtual_blocks( &mut self, state: VmLocalStateData<'_>, memory: &SimpleMemory, @@ -207,7 +174,7 @@ impl CallTracer { } } - fn handle_ret_op_code( + fn handle_ret_op_code_virtual_blocks( &mut self, state: VmLocalStateData<'_>, _data: AfterExecutionData, @@ -229,7 +196,7 @@ impl CallTracer { .parent_gas .saturating_sub(state.vm_local_state.callstack.current.ergs_remaining); - self.save_output(state, memory, ret_opcode, &mut current_call.farcall); + self.save_output_virtual_blocks(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 diff --git a/core/lib/multivm/src/tracers/mod.rs b/core/lib/multivm/src/tracers/mod.rs new file mode 100644 index 00000000000..a67cc43fb10 --- /dev/null +++ b/core/lib/multivm/src/tracers/mod.rs @@ -0,0 +1,6 @@ +pub mod call_tracer; +pub mod storage_invocation; +pub mod validator; + +pub use call_tracer::CallTracer; +pub use storage_invocation::StorageInvocations; diff --git a/core/lib/multivm/src/tracers/storage_invocation/mod.rs b/core/lib/multivm/src/tracers/storage_invocation/mod.rs new file mode 100644 index 00000000000..3816d2a07a1 --- /dev/null +++ b/core/lib/multivm/src/tracers/storage_invocation/mod.rs @@ -0,0 +1,17 @@ +pub mod vm_latest; +pub mod vm_refunds_enhancement; +pub mod vm_virtual_blocks; + +/// Tracer responsible for calculating the number of storage invocations and +/// stopping the VM execution if the limit is reached. +#[derive(Debug, Default, Clone)] +pub struct StorageInvocations { + pub limit: usize, + pub current: usize, +} + +impl StorageInvocations { + pub fn new(limit: usize) -> Self { + Self { limit, current: 0 } + } +} diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/storage_invocations.rs b/core/lib/multivm/src/tracers/storage_invocation/vm_latest/mod.rs similarity index 50% rename from core/lib/multivm/src/versions/vm_latest/tracers/storage_invocations.rs rename to core/lib/multivm/src/tracers/storage_invocation/vm_latest/mod.rs index 7c5311ccdab..0490ec34107 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/storage_invocations.rs +++ b/core/lib/multivm/src/tracers/storage_invocation/vm_latest/mod.rs @@ -1,26 +1,13 @@ -use crate::interface::Halt; -use crate::vm_latest::bootloader_state::BootloaderState; -use crate::vm_latest::old_vm::history_recorder::HistoryMode; -use crate::vm_latest::tracers::traits::{ - DynTracer, TracerExecutionStatus, TracerExecutionStopReason, VmTracer, +use crate::interface::{ + tracer::{TracerExecutionStatus, TracerExecutionStopReason}, + traits::tracers::dyn_tracers::vm_1_4_0::DynTracer, + Halt, }; -use crate::vm_latest::types::internals::ZkSyncVmState; +use crate::tracers::storage_invocation::StorageInvocations; +use crate::vm_latest::{BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState}; use zksync_state::WriteStorage; -#[derive(Debug, Default, Clone)] -pub struct StorageInvocations { - pub limit: usize, -} - -impl StorageInvocations { - pub fn new(limit: usize) -> Self { - Self { limit } - } -} - -/// Tracer responsible for calculating the number of storage invocations and -/// stopping the VM execution if the limit is reached. -impl DynTracer for StorageInvocations {} +impl DynTracer> for StorageInvocations {} impl VmTracer for StorageInvocations { fn finish_cycle( diff --git a/core/lib/multivm/src/tracers/storage_invocation/vm_refunds_enhancement/mod.rs b/core/lib/multivm/src/tracers/storage_invocation/vm_refunds_enhancement/mod.rs new file mode 100644 index 00000000000..fe4fc33418d --- /dev/null +++ b/core/lib/multivm/src/tracers/storage_invocation/vm_refunds_enhancement/mod.rs @@ -0,0 +1,30 @@ +use crate::interface::tracer::{TracerExecutionStatus, TracerExecutionStopReason}; +use crate::interface::{traits::tracers::dyn_tracers::vm_1_3_3::DynTracer, Halt}; +use crate::tracers::storage_invocation::StorageInvocations; +use crate::vm_refunds_enhancement::VmTracer; +use crate::vm_refunds_enhancement::{BootloaderState, HistoryMode, SimpleMemory, ZkSyncVmState}; +use zksync_state::WriteStorage; + +impl DynTracer> for StorageInvocations {} + +impl VmTracer for StorageInvocations { + fn finish_cycle( + &mut self, + state: &mut ZkSyncVmState, + _bootloader_state: &mut BootloaderState, + ) -> TracerExecutionStatus { + let current = state + .storage + .storage + .get_ptr() + .borrow() + .missed_storage_invocations(); + + if current >= self.limit { + return TracerExecutionStatus::Stop(TracerExecutionStopReason::Abort( + Halt::TracerCustom("Storage invocations limit reached".to_string()), + )); + } + TracerExecutionStatus::Continue + } +} diff --git a/core/lib/multivm/src/tracers/storage_invocation/vm_virtual_blocks/mod.rs b/core/lib/multivm/src/tracers/storage_invocation/vm_virtual_blocks/mod.rs new file mode 100644 index 00000000000..023b6f376cd --- /dev/null +++ b/core/lib/multivm/src/tracers/storage_invocation/vm_virtual_blocks/mod.rs @@ -0,0 +1,32 @@ +use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; +use crate::tracers::storage_invocation::StorageInvocations; +use crate::vm_virtual_blocks::{ + BootloaderState, ExecutionEndTracer, ExecutionProcessing, HistoryMode, SimpleMemory, VmTracer, + ZkSyncVmState, +}; +use zksync_state::WriteStorage; + +impl ExecutionEndTracer for StorageInvocations { + fn should_stop_execution(&self) -> bool { + self.current >= self.limit + } +} + +impl DynTracer> for StorageInvocations {} + +impl ExecutionProcessing for StorageInvocations { + fn after_cycle( + &mut self, + state: &mut ZkSyncVmState, + _bootloader_state: &mut BootloaderState, + ) { + self.current = state + .storage + .storage + .get_ptr() + .borrow() + .missed_storage_invocations(); + } +} + +impl VmTracer for StorageInvocations {} diff --git a/core/lib/multivm/src/tracers/validator/mod.rs b/core/lib/multivm/src/tracers/validator/mod.rs new file mode 100644 index 00000000000..26d3b0ad926 --- /dev/null +++ b/core/lib/multivm/src/tracers/validator/mod.rs @@ -0,0 +1,220 @@ +mod types; +mod vm_latest; +mod vm_refunds_enhancement; +mod vm_virtual_blocks; + +use std::sync::Arc; +use std::{collections::HashSet, marker::PhantomData}; + +use once_cell::sync::OnceCell; + +use zksync_state::{StoragePtr, WriteStorage}; +use zksync_system_constants::{ + ACCOUNT_CODE_STORAGE_ADDRESS, BOOTLOADER_ADDRESS, CONTRACT_DEPLOYER_ADDRESS, + L2_ETH_TOKEN_ADDRESS, MSG_VALUE_SIMULATOR_ADDRESS, SYSTEM_CONTEXT_ADDRESS, +}; + +use zksync_types::{ + vm_trace::ViolatedValidationRule, web3::signing::keccak256, AccountTreeId, Address, StorageKey, + H256, U256, +}; +use zksync_utils::{be_bytes_to_safe_address, u256_to_account_address, u256_to_h256}; + +use crate::tracers::validator::types::{NewTrustedValidationItems, ValidationTracerMode}; +pub use crate::tracers::validator::types::{ValidationError, ValidationTracerParams}; + +/// Tracer that is used to ensure that the validation adheres to all the rules +/// to prevent DDoS attacks on the server. +#[derive(Debug, Clone)] +pub struct ValidationTracer { + validation_mode: ValidationTracerMode, + auxilary_allowed_slots: HashSet, + + user_address: Address, + #[allow(dead_code)] + paymaster_address: Address, + should_stop_execution: bool, + trusted_slots: HashSet<(Address, U256)>, + trusted_addresses: HashSet
, + trusted_address_slots: HashSet<(Address, U256)>, + computational_gas_used: u32, + computational_gas_limit: u32, + pub result: Arc>, + _marker: PhantomData H>, +} + +type ValidationRoundResult = Result; + +impl ValidationTracer { + pub fn new(params: ValidationTracerParams) -> (Self, Arc>) { + let result = Arc::new(OnceCell::new()); + ( + Self { + validation_mode: ValidationTracerMode::NoValidation, + auxilary_allowed_slots: Default::default(), + + should_stop_execution: false, + user_address: params.user_address, + paymaster_address: params.paymaster_address, + trusted_slots: params.trusted_slots, + trusted_addresses: params.trusted_addresses, + trusted_address_slots: params.trusted_address_slots, + computational_gas_used: 0, + computational_gas_limit: params.computational_gas_limit, + result: result.clone(), + _marker: Default::default(), + }, + result, + ) + } + + fn process_validation_round_result(&mut self, result: ValidationRoundResult) { + match result { + Ok(NewTrustedValidationItems { + new_allowed_slots, + new_trusted_addresses, + }) => { + self.auxilary_allowed_slots.extend(new_allowed_slots); + self.trusted_addresses.extend(new_trusted_addresses); + } + Err(err) => { + if self.result.get().is_some() { + tracing::trace!("Validation error is already set, skipping"); + return; + } + self.result.set(err).expect("Result should be empty"); + } + } + } + + // Checks whether such storage access is acceptable. + fn is_allowed_storage_read( + &self, + storage: StoragePtr, + address: Address, + key: U256, + msg_sender: Address, + ) -> bool { + // If there are no restrictions, all storage reads are valid. + // We also don't support the paymaster validation for now. + if matches!( + self.validation_mode, + ValidationTracerMode::NoValidation | ValidationTracerMode::PaymasterTxValidation + ) { + return true; + } + + // The pair of MSG_VALUE_SIMULATOR_ADDRESS & L2_ETH_TOKEN_ADDRESS simulates the behavior of transfering ETH + // that is safe for the DDoS protection rules. + if valid_eth_token_call(address, msg_sender) { + return true; + } + + if self.trusted_slots.contains(&(address, key)) + || self.trusted_addresses.contains(&address) + || self.trusted_address_slots.contains(&(address, key)) + { + return true; + } + + if touches_allowed_context(address, key) { + return true; + } + + // The user is allowed to touch its own slots or slots semantically related to him. + let valid_users_slot = address == self.user_address + || u256_to_account_address(&key) == self.user_address + || self.auxilary_allowed_slots.contains(&u256_to_h256(key)); + if valid_users_slot { + return true; + } + + if is_constant_code_hash(address, key, storage) { + return true; + } + + false + } + + // Used to remember user-related fields (its balance/allowance/etc). + // Note that it assumes that the length of the calldata is 64 bytes. + fn slot_to_add_from_keccak_call( + &self, + calldata: &[u8], + validated_address: Address, + ) -> Option { + assert_eq!(calldata.len(), 64); + + let (potential_address_bytes, potential_position_bytes) = calldata.split_at(32); + let potential_address = be_bytes_to_safe_address(potential_address_bytes); + + // If the validation_address is equal to the potential_address, + // then it is a request that could be used for mapping of kind mapping(address => ...). + // + // If the potential_position_bytes were already allowed before, then this keccak might be used + // for ERC-20 allowance or any other of mapping(address => mapping(...)) + if potential_address == Some(validated_address) + || self + .auxilary_allowed_slots + .contains(&H256::from_slice(potential_position_bytes)) + { + // This is request that could be used for mapping of kind mapping(address => ...) + + // We could theoretically wait for the slot number to be returned by the + // keccak256 precompile itself, but this would complicate the code even further + // so let's calculate it here. + let slot = keccak256(calldata); + + // Adding this slot to the allowed ones + Some(H256(slot)) + } else { + None + } + } + + pub fn params(&self) -> ValidationTracerParams { + ValidationTracerParams { + user_address: self.user_address, + paymaster_address: self.paymaster_address, + trusted_slots: self.trusted_slots.clone(), + trusted_addresses: self.trusted_addresses.clone(), + trusted_address_slots: self.trusted_address_slots.clone(), + computational_gas_limit: self.computational_gas_limit, + } + } +} + +fn touches_allowed_context(address: Address, key: U256) -> bool { + // Context is not touched at all + if address != SYSTEM_CONTEXT_ADDRESS { + return false; + } + + // Only chain_id is allowed to be touched. + key == U256::from(0u32) +} + +fn is_constant_code_hash( + address: Address, + key: U256, + storage: StoragePtr, +) -> bool { + if address != ACCOUNT_CODE_STORAGE_ADDRESS { + // Not a code hash + return false; + } + + let value = storage.borrow_mut().read_value(&StorageKey::new( + AccountTreeId::new(address), + u256_to_h256(key), + )); + + value != H256::zero() +} + +fn valid_eth_token_call(address: Address, msg_sender: Address) -> bool { + let is_valid_caller = msg_sender == MSG_VALUE_SIMULATOR_ADDRESS + || msg_sender == CONTRACT_DEPLOYER_ADDRESS + || msg_sender == BOOTLOADER_ADDRESS; + address == L2_ETH_TOKEN_ADDRESS && is_valid_caller +} diff --git a/core/lib/multivm/src/tracers/validator/types.rs b/core/lib/multivm/src/tracers/validator/types.rs new file mode 100644 index 00000000000..eb80e6f1650 --- /dev/null +++ b/core/lib/multivm/src/tracers/validator/types.rs @@ -0,0 +1,57 @@ +use crate::interface::Halt; +use std::collections::HashSet; +use std::fmt::Display; +use zksync_types::vm_trace::ViolatedValidationRule; +use zksync_types::{Address, H256, U256}; + +#[derive(Debug, Clone, Eq, PartialEq, Copy)] +#[allow(clippy::enum_variant_names)] +pub(super) enum ValidationTracerMode { + /// Should be activated when the transaction is being validated by user. + UserTxValidation, + /// Should be activated when the transaction is being validated by the paymaster. + PaymasterTxValidation, + /// Is a state when there are no restrictions on the execution. + NoValidation, +} + +#[derive(Debug, Clone, Default)] +pub(super) struct NewTrustedValidationItems { + pub(super) new_allowed_slots: Vec, + pub(super) new_trusted_addresses: Vec
, +} + +#[derive(Debug, Clone)] +pub struct ValidationTracerParams { + pub user_address: Address, + pub paymaster_address: Address, + /// Slots that are trusted (i.e. the user can access them). + pub trusted_slots: HashSet<(Address, U256)>, + /// Trusted addresses (the user can access any slots on these addresses). + pub trusted_addresses: HashSet
, + /// Slots, that are trusted and the value of them is the new trusted address. + /// They are needed to work correctly with beacon proxy, where the address of the implementation is + /// stored in the beacon. + pub trusted_address_slots: HashSet<(Address, U256)>, + /// Number of computational gas that validation step is allowed to use. + pub computational_gas_limit: u32, +} + +#[derive(Debug, Clone)] +pub enum ValidationError { + FailedTx(Halt), + ViolatedRule(ViolatedValidationRule), +} + +impl Display for ValidationError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::FailedTx(revert_reason) => { + write!(f, "Validation revert: {}", revert_reason) + } + Self::ViolatedRule(rule) => { + write!(f, "Violated validation rules: {}", rule) + } + } + } +} diff --git a/core/lib/multivm/src/tracers/validator/vm_latest/mod.rs b/core/lib/multivm/src/tracers/validator/vm_latest/mod.rs new file mode 100644 index 00000000000..4d5ff43ec47 --- /dev/null +++ b/core/lib/multivm/src/tracers/validator/vm_latest/mod.rs @@ -0,0 +1,201 @@ +use zk_evm_1_4_0::{ + tracing::{BeforeExecutionData, VmLocalStateData}, + zkevm_opcode_defs::{ContextOpcode, FarCallABI, LogOpcode, Opcode}, +}; + +use zksync_state::{StoragePtr, WriteStorage}; +use zksync_system_constants::KECCAK256_PRECOMPILE_ADDRESS; + +use crate::HistoryMode; +use zksync_types::{ + get_code_key, vm_trace::ViolatedValidationRule, AccountTreeId, StorageKey, H256, +}; +use zksync_utils::{h256_to_account_address, u256_to_account_address, u256_to_h256}; + +use crate::vm_latest::tracers::utils::{ + computational_gas_price, get_calldata_page_via_abi, print_debug_if_needed, VmHook, +}; + +use crate::interface::{ + traits::tracers::dyn_tracers::vm_1_4_0::DynTracer, + types::tracer::{TracerExecutionStatus, TracerExecutionStopReason}, + Halt, +}; +use crate::tracers::validator::{ + types::{NewTrustedValidationItems, ValidationTracerMode}, + {ValidationRoundResult, ValidationTracer}, +}; + +use crate::vm_latest::{BootloaderState, SimpleMemory, VmTracer, ZkSyncVmState}; + +impl ValidationTracer { + fn check_user_restrictions_vm_latest( + &mut self, + state: VmLocalStateData<'_>, + data: BeforeExecutionData, + memory: &SimpleMemory, + storage: StoragePtr, + ) -> ValidationRoundResult { + if self.computational_gas_used > self.computational_gas_limit { + return Err(ViolatedValidationRule::TookTooManyComputationalGas( + self.computational_gas_limit, + )); + } + + let opcode_variant = data.opcode.variant; + match opcode_variant.opcode { + Opcode::FarCall(_) => { + let packed_abi = data.src0_value.value; + let call_destination_value = data.src1_value.value; + + let called_address = u256_to_account_address(&call_destination_value); + let far_call_abi = FarCallABI::from_u256(packed_abi); + + if called_address == KECCAK256_PRECOMPILE_ADDRESS + && far_call_abi.memory_quasi_fat_pointer.length == 64 + { + let calldata_page = get_calldata_page_via_abi( + &far_call_abi, + state.vm_local_state.callstack.current.base_memory_page, + ); + let calldata = memory.read_unaligned_bytes( + calldata_page as usize, + far_call_abi.memory_quasi_fat_pointer.start as usize, + 64, + ); + + let slot_to_add = + self.slot_to_add_from_keccak_call(&calldata, self.user_address); + + if let Some(slot) = slot_to_add { + return Ok(NewTrustedValidationItems { + new_allowed_slots: vec![slot], + ..Default::default() + }); + } + } else if called_address != self.user_address { + let code_key = get_code_key(&called_address); + let code = storage.borrow_mut().read_value(&code_key); + + if code == H256::zero() { + // The users are not allowed to call contracts with no code + return Err(ViolatedValidationRule::CalledContractWithNoCode( + called_address, + )); + } + } + } + Opcode::Context(context) => { + match context { + ContextOpcode::Meta => { + return Err(ViolatedValidationRule::TouchedUnallowedContext); + } + ContextOpcode::ErgsLeft => { + // TODO (SMA-1168): implement the correct restrictions for the gas left opcode. + } + _ => {} + } + } + Opcode::Log(LogOpcode::StorageRead) => { + let key = data.src0_value.value; + let this_address = state.vm_local_state.callstack.current.this_address; + let msg_sender = state.vm_local_state.callstack.current.msg_sender; + + if !self.is_allowed_storage_read(storage.clone(), this_address, key, msg_sender) { + return Err(ViolatedValidationRule::TouchedUnallowedStorageSlots( + this_address, + key, + )); + } + + if self.trusted_address_slots.contains(&(this_address, key)) { + let storage_key = + StorageKey::new(AccountTreeId::new(this_address), u256_to_h256(key)); + + let value = storage.borrow_mut().read_value(&storage_key); + + return Ok(NewTrustedValidationItems { + new_trusted_addresses: vec![h256_to_account_address(&value)], + ..Default::default() + }); + } + } + _ => {} + } + + Ok(Default::default()) + } +} + +impl DynTracer> + for ValidationTracer +{ + fn before_execution( + &mut self, + state: VmLocalStateData<'_>, + data: BeforeExecutionData, + memory: &SimpleMemory, + storage: StoragePtr, + ) { + // For now, we support only validations for users. + if let ValidationTracerMode::UserTxValidation = self.validation_mode { + self.computational_gas_used = self + .computational_gas_used + .saturating_add(computational_gas_price(state, &data)); + + let validation_round_result = + self.check_user_restrictions_vm_latest(state, data, memory, storage); + self.process_validation_round_result(validation_round_result); + } + + let hook = VmHook::from_opcode_memory(&state, &data); + print_debug_if_needed(&hook, &state, memory); + + let current_mode = self.validation_mode; + match (current_mode, hook) { + (ValidationTracerMode::NoValidation, VmHook::AccountValidationEntered) => { + // Account validation can be entered when there is no prior validation (i.e. "nested" validations are not allowed) + self.validation_mode = ValidationTracerMode::UserTxValidation; + } + (ValidationTracerMode::NoValidation, VmHook::PaymasterValidationEntered) => { + // Paymaster validation can be entered when there is no prior validation (i.e. "nested" validations are not allowed) + self.validation_mode = ValidationTracerMode::PaymasterTxValidation; + } + (_, VmHook::AccountValidationEntered | VmHook::PaymasterValidationEntered) => { + panic!( + "Unallowed transition inside the validation tracer. Mode: {:#?}, hook: {:#?}", + self.validation_mode, hook + ); + } + (_, VmHook::NoValidationEntered) => { + // Validation can be always turned off + self.validation_mode = ValidationTracerMode::NoValidation; + } + (_, VmHook::ValidationStepEndeded) => { + // The validation step has ended. + self.should_stop_execution = true; + } + (_, _) => { + // The hook is not relevant to the validation tracer. Ignore. + } + } + } +} + +impl VmTracer for ValidationTracer { + fn finish_cycle( + &mut self, + _state: &mut ZkSyncVmState, + _bootloader_state: &mut BootloaderState, + ) -> TracerExecutionStatus { + if self.should_stop_execution { + return TracerExecutionStatus::Stop(TracerExecutionStopReason::Finish); + } + if let Some(result) = self.result.get() { + return TracerExecutionStatus::Stop(TracerExecutionStopReason::Abort( + Halt::TracerCustom(format!("Validation error: {:#?}", result)), + )); + } + TracerExecutionStatus::Continue + } +} diff --git a/core/lib/multivm/src/tracers/validator/vm_refunds_enhancement/mod.rs b/core/lib/multivm/src/tracers/validator/vm_refunds_enhancement/mod.rs new file mode 100644 index 00000000000..ec4e95e5630 --- /dev/null +++ b/core/lib/multivm/src/tracers/validator/vm_refunds_enhancement/mod.rs @@ -0,0 +1,204 @@ +use zk_evm_1_3_3::{ + tracing::{BeforeExecutionData, VmLocalStateData}, + zkevm_opcode_defs::{ContextOpcode, FarCallABI, LogOpcode, Opcode}, +}; + +use zksync_state::{StoragePtr, WriteStorage}; +use zksync_system_constants::KECCAK256_PRECOMPILE_ADDRESS; + +use crate::HistoryMode; +use zksync_types::{ + get_code_key, vm_trace::ViolatedValidationRule, AccountTreeId, StorageKey, H256, +}; +use zksync_utils::{h256_to_account_address, u256_to_account_address, u256_to_h256}; + +use crate::interface::{ + traits::tracers::dyn_tracers::vm_1_3_3::DynTracer, + types::tracer::{TracerExecutionStatus, TracerExecutionStopReason}, + Halt, +}; +use crate::tracers::validator::{ + types::{NewTrustedValidationItems, ValidationTracerMode}, + {ValidationRoundResult, ValidationTracer}, +}; + +use crate::vm_refunds_enhancement::{ + tracers::utils::{ + computational_gas_price, get_calldata_page_via_abi, print_debug_if_needed, VmHook, + }, + BootloaderState, SimpleMemory, VmTracer, ZkSyncVmState, +}; + +impl ValidationTracer { + fn check_user_restrictions_vm_refunds_enhancement( + &mut self, + state: VmLocalStateData<'_>, + data: BeforeExecutionData, + memory: &SimpleMemory, + storage: StoragePtr, + ) -> ValidationRoundResult { + if self.computational_gas_used > self.computational_gas_limit { + return Err(ViolatedValidationRule::TookTooManyComputationalGas( + self.computational_gas_limit, + )); + } + + let opcode_variant = data.opcode.variant; + match opcode_variant.opcode { + Opcode::FarCall(_) => { + let packed_abi = data.src0_value.value; + let call_destination_value = data.src1_value.value; + + let called_address = u256_to_account_address(&call_destination_value); + let far_call_abi = FarCallABI::from_u256(packed_abi); + + if called_address == KECCAK256_PRECOMPILE_ADDRESS + && far_call_abi.memory_quasi_fat_pointer.length == 64 + { + let calldata_page = get_calldata_page_via_abi( + &far_call_abi, + state.vm_local_state.callstack.current.base_memory_page, + ); + let calldata = memory.read_unaligned_bytes( + calldata_page as usize, + far_call_abi.memory_quasi_fat_pointer.start as usize, + 64, + ); + + let slot_to_add = + self.slot_to_add_from_keccak_call(&calldata, self.user_address); + + if let Some(slot) = slot_to_add { + return Ok(NewTrustedValidationItems { + new_allowed_slots: vec![slot], + ..Default::default() + }); + } + } else if called_address != self.user_address { + let code_key = get_code_key(&called_address); + let code = storage.borrow_mut().read_value(&code_key); + + if code == H256::zero() { + // The users are not allowed to call contracts with no code + return Err(ViolatedValidationRule::CalledContractWithNoCode( + called_address, + )); + } + } + } + Opcode::Context(context) => { + match context { + ContextOpcode::Meta => { + return Err(ViolatedValidationRule::TouchedUnallowedContext); + } + ContextOpcode::ErgsLeft => { + // TODO (SMA-1168): implement the correct restrictions for the gas left opcode. + } + _ => {} + } + } + Opcode::Log(LogOpcode::StorageRead) => { + let key = data.src0_value.value; + let this_address = state.vm_local_state.callstack.current.this_address; + let msg_sender = state.vm_local_state.callstack.current.msg_sender; + + if !self.is_allowed_storage_read(storage.clone(), this_address, key, msg_sender) { + return Err(ViolatedValidationRule::TouchedUnallowedStorageSlots( + this_address, + key, + )); + } + + if self.trusted_address_slots.contains(&(this_address, key)) { + let storage_key = + StorageKey::new(AccountTreeId::new(this_address), u256_to_h256(key)); + + let value = storage.borrow_mut().read_value(&storage_key); + + return Ok(NewTrustedValidationItems { + new_trusted_addresses: vec![h256_to_account_address(&value)], + ..Default::default() + }); + } + } + _ => {} + } + + Ok(Default::default()) + } +} + +impl + DynTracer> for ValidationTracer +{ + fn before_execution( + &mut self, + state: VmLocalStateData<'_>, + data: BeforeExecutionData, + memory: &SimpleMemory, + storage: StoragePtr, + ) { + // For now, we support only validations for users. + if let ValidationTracerMode::UserTxValidation = self.validation_mode { + self.computational_gas_used = self + .computational_gas_used + .saturating_add(computational_gas_price(state, &data)); + + let validation_round_result = + self.check_user_restrictions_vm_refunds_enhancement(state, data, memory, storage); + self.process_validation_round_result(validation_round_result); + } + + let hook = VmHook::from_opcode_memory(&state, &data); + print_debug_if_needed(&hook, &state, memory); + + let current_mode = self.validation_mode; + match (current_mode, hook) { + (ValidationTracerMode::NoValidation, VmHook::AccountValidationEntered) => { + // Account validation can be entered when there is no prior validation (i.e. "nested" validations are not allowed) + self.validation_mode = ValidationTracerMode::UserTxValidation; + } + (ValidationTracerMode::NoValidation, VmHook::PaymasterValidationEntered) => { + // Paymaster validation can be entered when there is no prior validation (i.e. "nested" validations are not allowed) + self.validation_mode = ValidationTracerMode::PaymasterTxValidation; + } + (_, VmHook::AccountValidationEntered | VmHook::PaymasterValidationEntered) => { + panic!( + "Unallowed transition inside the validation tracer. Mode: {:#?}, hook: {:#?}", + self.validation_mode, hook + ); + } + (_, VmHook::NoValidationEntered) => { + // Validation can be always turned off + self.validation_mode = ValidationTracerMode::NoValidation; + } + (_, VmHook::ValidationStepEndeded) => { + // The validation step has ended. + self.should_stop_execution = true; + } + (_, _) => { + // The hook is not relevant to the validation tracer. Ignore. + } + } + } +} + +impl VmTracer + for ValidationTracer +{ + fn finish_cycle( + &mut self, + _state: &mut ZkSyncVmState, + _bootloader_state: &mut BootloaderState, + ) -> TracerExecutionStatus { + if self.should_stop_execution { + return TracerExecutionStatus::Stop(TracerExecutionStopReason::Finish); + } + if let Some(result) = self.result.get() { + return TracerExecutionStatus::Stop(TracerExecutionStopReason::Abort( + Halt::TracerCustom(format!("Validation error: {:#?}", result)), + )); + } + TracerExecutionStatus::Continue + } +} diff --git a/core/lib/multivm/src/tracers/validator/vm_virtual_blocks/mod.rs b/core/lib/multivm/src/tracers/validator/vm_virtual_blocks/mod.rs new file mode 100644 index 00000000000..d2155f4ecf8 --- /dev/null +++ b/core/lib/multivm/src/tracers/validator/vm_virtual_blocks/mod.rs @@ -0,0 +1,192 @@ +use zk_evm_1_3_3::{ + tracing::{BeforeExecutionData, VmLocalStateData}, + zkevm_opcode_defs::{ContextOpcode, FarCallABI, LogOpcode, Opcode}, +}; + +use zksync_state::{StoragePtr, WriteStorage}; +use zksync_system_constants::KECCAK256_PRECOMPILE_ADDRESS; + +use crate::HistoryMode; +use zksync_types::vm_trace::ViolatedValidationRule; +use zksync_types::{get_code_key, AccountTreeId, StorageKey, H256}; +use zksync_utils::{h256_to_account_address, u256_to_account_address, u256_to_h256}; + +use crate::vm_virtual_blocks::tracers::utils::{ + computational_gas_price, get_calldata_page_via_abi, print_debug_if_needed, VmHook, +}; +use crate::vm_virtual_blocks::SimpleMemory; +use crate::vm_virtual_blocks::{ExecutionEndTracer, ExecutionProcessing, VmTracer}; + +use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; +use crate::interface::VmExecutionResultAndLogs; +use crate::tracers::validator::types::{NewTrustedValidationItems, ValidationTracerMode}; +use crate::tracers::validator::{ValidationRoundResult, ValidationTracer}; + +impl ValidationTracer { + fn check_user_restrictions_vm_virtual_blocks( + &mut self, + state: VmLocalStateData<'_>, + data: BeforeExecutionData, + memory: &SimpleMemory, + storage: StoragePtr, + ) -> ValidationRoundResult { + if self.computational_gas_used > self.computational_gas_limit { + return Err(ViolatedValidationRule::TookTooManyComputationalGas( + self.computational_gas_limit, + )); + } + + let opcode_variant = data.opcode.variant; + match opcode_variant.opcode { + Opcode::FarCall(_) => { + let packed_abi = data.src0_value.value; + let call_destination_value = data.src1_value.value; + + let called_address = u256_to_account_address(&call_destination_value); + let far_call_abi = FarCallABI::from_u256(packed_abi); + + if called_address == KECCAK256_PRECOMPILE_ADDRESS + && far_call_abi.memory_quasi_fat_pointer.length == 64 + { + let calldata_page = get_calldata_page_via_abi( + &far_call_abi, + state.vm_local_state.callstack.current.base_memory_page, + ); + let calldata = memory.read_unaligned_bytes( + calldata_page as usize, + far_call_abi.memory_quasi_fat_pointer.start as usize, + 64, + ); + + let slot_to_add = + self.slot_to_add_from_keccak_call(&calldata, self.user_address); + + if let Some(slot) = slot_to_add { + return Ok(NewTrustedValidationItems { + new_allowed_slots: vec![slot], + ..Default::default() + }); + } + } else if called_address != self.user_address { + let code_key = get_code_key(&called_address); + let code = storage.borrow_mut().read_value(&code_key); + + if code == H256::zero() { + // The users are not allowed to call contracts with no code + return Err(ViolatedValidationRule::CalledContractWithNoCode( + called_address, + )); + } + } + } + Opcode::Context(context) => { + match context { + ContextOpcode::Meta => { + return Err(ViolatedValidationRule::TouchedUnallowedContext); + } + ContextOpcode::ErgsLeft => { + // TODO (SMA-1168): implement the correct restrictions for the gas left opcode. + } + _ => {} + } + } + Opcode::Log(LogOpcode::StorageRead) => { + let key = data.src0_value.value; + let this_address = state.vm_local_state.callstack.current.this_address; + let msg_sender = state.vm_local_state.callstack.current.msg_sender; + + if !self.is_allowed_storage_read(storage.clone(), this_address, key, msg_sender) { + return Err(ViolatedValidationRule::TouchedUnallowedStorageSlots( + this_address, + key, + )); + } + + if self.trusted_address_slots.contains(&(this_address, key)) { + let storage_key = + StorageKey::new(AccountTreeId::new(this_address), u256_to_h256(key)); + + let value = storage.borrow_mut().read_value(&storage_key); + + return Ok(NewTrustedValidationItems { + new_trusted_addresses: vec![h256_to_account_address(&value)], + ..Default::default() + }); + } + } + _ => {} + } + + Ok(Default::default()) + } +} + +impl DynTracer> + for ValidationTracer +{ + fn before_execution( + &mut self, + state: VmLocalStateData<'_>, + data: BeforeExecutionData, + memory: &SimpleMemory, + storage: StoragePtr, + ) { + // For now, we support only validations for users. + if let ValidationTracerMode::UserTxValidation = self.validation_mode { + self.computational_gas_used = self + .computational_gas_used + .saturating_add(computational_gas_price(state, &data)); + + let validation_round_result = + self.check_user_restrictions_vm_virtual_blocks(state, data, memory, storage); + self.process_validation_round_result(validation_round_result); + } + + let hook = VmHook::from_opcode_memory(&state, &data); + print_debug_if_needed(&hook, &state, memory); + + let current_mode = self.validation_mode; + match (current_mode, hook) { + (ValidationTracerMode::NoValidation, VmHook::AccountValidationEntered) => { + // Account validation can be entered when there is no prior validation (i.e. "nested" validations are not allowed) + self.validation_mode = ValidationTracerMode::UserTxValidation; + } + (ValidationTracerMode::NoValidation, VmHook::PaymasterValidationEntered) => { + // Paymaster validation can be entered when there is no prior validation (i.e. "nested" validations are not allowed) + self.validation_mode = ValidationTracerMode::PaymasterTxValidation; + } + (_, VmHook::AccountValidationEntered | VmHook::PaymasterValidationEntered) => { + panic!( + "Unallowed transition inside the validation tracer. Mode: {:#?}, hook: {:#?}", + self.validation_mode, hook + ); + } + (_, VmHook::NoValidationEntered) => { + // Validation can be always turned off + self.validation_mode = ValidationTracerMode::NoValidation; + } + (_, VmHook::ValidationStepEndeded) => { + // The validation step has ended. + self.should_stop_execution = true; + } + (_, _) => { + // The hook is not relevant to the validation tracer. Ignore. + } + } + } +} + +impl ExecutionEndTracer for ValidationTracer { + fn should_stop_execution(&self) -> bool { + self.should_stop_execution || self.result.get().is_some() + } +} + +impl ExecutionProcessing + for ValidationTracer +{ +} + +impl VmTracer for ValidationTracer { + fn save_results(&mut self, _result: &mut VmExecutionResultAndLogs) {} +} diff --git a/core/lib/multivm/src/versions/vm_1_3_2/mod.rs b/core/lib/multivm/src/versions/vm_1_3_2/mod.rs index 4c46b3554a7..b2635d59715 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/mod.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/mod.rs @@ -13,7 +13,7 @@ mod refunds; pub mod test_utils; pub mod transaction_data; pub mod utils; -pub mod vm; +pub mod vm_instance; pub mod vm_with_bootloader; #[cfg(test)] @@ -23,7 +23,7 @@ pub use errors::TxRevertReason; pub use history_recorder::{HistoryDisabled, HistoryEnabled, HistoryMode}; pub use oracle_tools::OracleTools; pub use oracles::storage::StorageOracle; -pub use vm::{VmBlockResult, VmExecutionResult, VmInstance}; +pub use vm_instance::{VmBlockResult, VmExecutionResult, VmInstance}; pub use zk_evm_1_3_3; pub use zk_evm_1_3_3::block_properties::BlockProperties; pub use zksync_types::vm_trace::VmExecutionTrace; diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/one_tx.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/one_tx.rs index 81d2bc9142a..a9349ea2035 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/one_tx.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/one_tx.rs @@ -6,7 +6,7 @@ use crate::vm_1_3_2::{ utils::{gas_spent_on_bytecodes_and_long_messages_this_opcode, VmHook}, BootloaderTracer, ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, }, - vm::get_vm_hook_params, + vm_instance::get_vm_hook_params, }; use crate::vm_1_3_2::oracles::tracer::{CallTracer, StorageInvocationTracer}; diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/transaction_result.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/transaction_result.rs index b5ab33a5ae2..215c66bfa74 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/transaction_result.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/transaction_result.rs @@ -12,7 +12,7 @@ use crate::vm_1_3_2::oracles::tracer::{ CallTracer, ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, StorageInvocationTracer, }; -use crate::vm_1_3_2::vm::get_vm_hook_params; +use crate::vm_1_3_2::vm_instance::get_vm_hook_params; use crate::vm_1_3_2::{ history_recorder::HistoryMode, oracles::tracer::utils::{ diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/utils.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/utils.rs index 3ff8f1f2300..2914faf5120 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/utils.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/utils.rs @@ -1,7 +1,7 @@ use crate::vm_1_3_2::history_recorder::HistoryMode; use crate::vm_1_3_2::memory::SimpleMemory; use crate::vm_1_3_2::utils::{aux_heap_page_from_base, heap_page_from_base}; -use crate::vm_1_3_2::vm::{get_vm_hook_params, VM_HOOK_POSITION}; +use crate::vm_1_3_2::vm_instance::{get_vm_hook_params, VM_HOOK_POSITION}; use crate::vm_1_3_2::vm_with_bootloader::BOOTLOADER_HEAP_PAGE; use zk_evm_1_3_3::aux_structures::MemoryPage; diff --git a/core/lib/multivm/src/versions/vm_1_3_2/test_utils.rs b/core/lib/multivm/src/versions/vm_1_3_2/test_utils.rs index 9a03c6d7da9..6738b070482 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/test_utils.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/test_utils.rs @@ -32,7 +32,7 @@ use crate::vm_1_3_2::{ AppDataFrameManagerWithHistory, HistoryEnabled, HistoryMode, HistoryRecorder, }, memory::SimpleMemory, - vm::ZkSyncVmState, + vm_instance::ZkSyncVmState, VmInstance, }; diff --git a/core/lib/multivm/src/versions/vm_1_3_2/vm.rs b/core/lib/multivm/src/versions/vm_1_3_2/vm_instance.rs similarity index 100% rename from core/lib/multivm/src/versions/vm_1_3_2/vm.rs rename to core/lib/multivm/src/versions/vm_1_3_2/vm_instance.rs diff --git a/core/lib/multivm/src/versions/vm_1_3_2/vm_with_bootloader.rs b/core/lib/multivm/src/versions/vm_1_3_2/vm_with_bootloader.rs index bd15543c918..5642df6745d 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/vm_with_bootloader.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/vm_with_bootloader.rs @@ -35,7 +35,7 @@ use crate::vm_1_3_2::{ utils::{ code_page_candidate_from_base, heap_page_from_base, BLOCK_GAS_LIMIT, INITIAL_BASE_PAGE, }, - vm::ZkSyncVmState, + vm_instance::ZkSyncVmState, OracleTools, VmInstance, }; diff --git a/core/lib/multivm/src/versions/vm_latest/implementation/bytecode.rs b/core/lib/multivm/src/versions/vm_latest/implementation/bytecode.rs index 9a12500cd85..83a7be74897 100644 --- a/core/lib/multivm/src/versions/vm_latest/implementation/bytecode.rs +++ b/core/lib/multivm/src/versions/vm_latest/implementation/bytecode.rs @@ -1,11 +1,13 @@ use itertools::Itertools; +use crate::interface::VmInterface; +use crate::HistoryMode; use zksync_state::{StoragePtr, WriteStorage}; use zksync_types::U256; use zksync_utils::bytecode::{compress_bytecode, hash_bytecode, CompressedBytecodeInfo}; use zksync_utils::bytes_to_be_words; -use crate::vm_latest::{HistoryMode, Vm}; +use crate::vm_latest::Vm; impl Vm { /// Checks the last transaction has successfully published compressed bytecodes and returns `true` if there is at least one is still unknown. diff --git a/core/lib/multivm/src/versions/vm_latest/implementation/execution.rs b/core/lib/multivm/src/versions/vm_latest/implementation/execution.rs index e14103b8074..1b3197f57b9 100644 --- a/core/lib/multivm/src/versions/vm_latest/implementation/execution.rs +++ b/core/lib/multivm/src/versions/vm_latest/implementation/execution.rs @@ -1,22 +1,21 @@ +use crate::HistoryMode; use zk_evm_1_4_0::aux_structures::Timestamp; use zksync_state::WriteStorage; -use crate::interface::{VmExecutionMode, VmExecutionResultAndLogs}; -use crate::vm_latest::old_vm::{ - history_recorder::HistoryMode, - utils::{vm_may_have_ended_inner, VmExecutionResult}, +use crate::interface::{ + types::tracer::{TracerExecutionStatus, VmExecutionStopReason}, + VmExecutionMode, VmExecutionResultAndLogs, }; -use crate::vm_latest::tracers::{ - traits::{TracerExecutionStatus, VmTracer}, - DefaultExecutionTracer, PubdataTracer, RefundsTracer, +use crate::vm_latest::{ + old_vm::utils::{vm_may_have_ended_inner, VmExecutionResult}, + tracers::{dispatcher::TracerDispatcher, DefaultExecutionTracer, PubdataTracer, RefundsTracer}, + vm::Vm, }; -use crate::vm_latest::vm::Vm; -use crate::vm_latest::VmExecutionStopReason; impl Vm { pub(crate) fn inspect_inner( &mut self, - tracers: Vec>>, + dispatcher: TracerDispatcher, execution_mode: VmExecutionMode, ) -> VmExecutionResultAndLogs { let mut enable_refund_tracer = false; @@ -27,7 +26,7 @@ impl Vm { } let (_, result) = - self.inspect_and_collect_results(tracers, execution_mode, enable_refund_tracer); + self.inspect_and_collect_results(dispatcher, execution_mode, enable_refund_tracer); result } @@ -35,20 +34,21 @@ impl Vm { /// Collect the result from the default tracers. fn inspect_and_collect_results( &mut self, - tracers: Vec>>, + dispatcher: TracerDispatcher, execution_mode: VmExecutionMode, with_refund_tracer: bool, ) -> (VmExecutionStopReason, VmExecutionResultAndLogs) { let refund_tracers = with_refund_tracer.then_some(RefundsTracer::new(self.batch_env.clone())); - let mut tx_tracer: DefaultExecutionTracer = DefaultExecutionTracer::new( - self.system_env.default_validation_computational_gas_limit, - execution_mode, - tracers, - self.storage.clone(), - refund_tracers, - Some(PubdataTracer::new(self.batch_env.clone(), execution_mode)), - ); + let mut tx_tracer: DefaultExecutionTracer = + DefaultExecutionTracer::new( + self.system_env.default_validation_computational_gas_limit, + execution_mode, + dispatcher, + self.storage.clone(), + refund_tracers, + Some(PubdataTracer::new(self.batch_env.clone(), execution_mode)), + ); let timestamp_initial = Timestamp(self.state.local_state.timestamp); let cycles_initial = self.state.local_state.monotonic_cycle_counter; @@ -92,7 +92,7 @@ impl Vm { /// Execute vm with given tracers until the stop reason is reached. fn execute_with_default_tracer( &mut self, - tracer: &mut DefaultExecutionTracer, + tracer: &mut DefaultExecutionTracer, ) -> VmExecutionStopReason { tracer.initialize_tracer(&mut self.state); let result = loop { diff --git a/core/lib/multivm/src/versions/vm_latest/implementation/gas.rs b/core/lib/multivm/src/versions/vm_latest/implementation/gas.rs index 059485df246..c970cd4e5d2 100644 --- a/core/lib/multivm/src/versions/vm_latest/implementation/gas.rs +++ b/core/lib/multivm/src/versions/vm_latest/implementation/gas.rs @@ -1,6 +1,6 @@ +use crate::HistoryMode; use zksync_state::WriteStorage; -use crate::vm_latest::old_vm::history_recorder::HistoryMode; use crate::vm_latest::tracers::DefaultExecutionTracer; use crate::vm_latest::vm::Vm; @@ -19,7 +19,7 @@ impl Vm { pub(crate) fn calculate_computational_gas_used( &self, - tracer: &DefaultExecutionTracer, + tracer: &DefaultExecutionTracer, gas_remaining_before: u32, spent_pubdata_counter_before: u32, ) -> u32 { diff --git a/core/lib/multivm/src/versions/vm_latest/implementation/logs.rs b/core/lib/multivm/src/versions/vm_latest/implementation/logs.rs index d65a002a3df..c468cf87817 100644 --- a/core/lib/multivm/src/versions/vm_latest/implementation/logs.rs +++ b/core/lib/multivm/src/versions/vm_latest/implementation/logs.rs @@ -1,12 +1,13 @@ use zk_evm_1_4_0::aux_structures::Timestamp; use zksync_state::WriteStorage; - use zksync_types::event::extract_l2tol1logs_from_l1_messenger; + +use crate::HistoryMode; use zksync_types::l2_to_l1_log::{L2ToL1Log, SystemL2ToL1Log, UserL2ToL1Log}; use zksync_types::VmEvent; use crate::interface::types::outputs::VmExecutionLogs; -use crate::vm_latest::old_vm::history_recorder::HistoryMode; + use crate::vm_latest::old_vm::utils::precompile_calls_count_after_timestamp; use crate::vm_latest::utils::logs; use crate::vm_latest::vm::Vm; diff --git a/core/lib/multivm/src/versions/vm_latest/implementation/statistics.rs b/core/lib/multivm/src/versions/vm_latest/implementation/statistics.rs index afa57309a31..71ecc1a203d 100644 --- a/core/lib/multivm/src/versions/vm_latest/implementation/statistics.rs +++ b/core/lib/multivm/src/versions/vm_latest/implementation/statistics.rs @@ -1,10 +1,10 @@ use zk_evm_1_4_0::aux_structures::Timestamp; use zksync_state::WriteStorage; +use crate::HistoryMode; use zksync_types::U256; use crate::interface::{VmExecutionStatistics, VmMemoryMetrics}; -use crate::vm_latest::old_vm::history_recorder::HistoryMode; use crate::vm_latest::tracers::DefaultExecutionTracer; use crate::vm_latest::vm::Vm; @@ -18,7 +18,7 @@ impl Vm { &self, timestamp_initial: Timestamp, cycles_initial: u32, - tracer: &DefaultExecutionTracer, + tracer: &DefaultExecutionTracer, gas_remaining_before: u32, gas_remaining_after: u32, spent_pubdata_counter_before: u32, diff --git a/core/lib/multivm/src/versions/vm_latest/implementation/tx.rs b/core/lib/multivm/src/versions/vm_latest/implementation/tx.rs index 16087aa4eb0..6def1da0f5d 100644 --- a/core/lib/multivm/src/versions/vm_latest/implementation/tx.rs +++ b/core/lib/multivm/src/versions/vm_latest/implementation/tx.rs @@ -1,11 +1,11 @@ use crate::vm_latest::constants::BOOTLOADER_HEAP_PAGE; use crate::vm_latest::implementation::bytecode::{bytecode_to_factory_dep, compress_bytecodes}; +use crate::HistoryMode; use zk_evm_1_4_0::aux_structures::Timestamp; use zksync_state::WriteStorage; use zksync_types::l1::is_l1_tx_type; use zksync_types::Transaction; -use crate::vm_latest::old_vm::history_recorder::HistoryMode; use crate::vm_latest::types::internals::TransactionData; use crate::vm_latest::vm::Vm; diff --git a/core/lib/multivm/src/versions/vm_latest/mod.rs b/core/lib/multivm/src/versions/vm_latest/mod.rs index a044980e96e..49cd7111f6f 100644 --- a/core/lib/multivm/src/versions/vm_latest/mod.rs +++ b/core/lib/multivm/src/versions/vm_latest/mod.rs @@ -6,10 +6,8 @@ pub use old_vm::{ pub use oracles::storage::StorageOracle; pub use tracers::{ - call::CallTracer, - traits::{BoxedTracer, DynTracer, TracerExecutionStatus, TracerExecutionStopReason, VmTracer}, - utils::VmExecutionStopReason, - StorageInvocations, ValidationError, ValidationTracer, ValidationTracerParams, + dispatcher::TracerDispatcher, + traits::{ToTracerPointer, TracerPointer, VmTracer}, }; pub use crate::interface::types::{ @@ -30,7 +28,7 @@ mod bootloader_state; mod implementation; mod old_vm; mod oracles; -mod tracers; +pub(crate) mod tracers; mod types; mod vm; diff --git a/core/lib/multivm/src/versions/vm_latest/tests/bootloader.rs b/core/lib/multivm/src/versions/vm_latest/tests/bootloader.rs index 4b877592bba..b2763f358be 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/bootloader.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/bootloader.rs @@ -1,6 +1,6 @@ use zksync_types::U256; -use crate::interface::{Halt, TxExecutionMode, VmExecutionMode}; +use crate::interface::{Halt, TxExecutionMode, VmExecutionMode, VmInterface}; use crate::vm_latest::constants::BOOTLOADER_HEAP_PAGE; use crate::vm_latest::tests::tester::VmTesterBuilder; use crate::vm_latest::tests::utils::{ diff --git a/core/lib/multivm/src/versions/vm_latest/tests/bytecode_publishing.rs b/core/lib/multivm/src/versions/vm_latest/tests/bytecode_publishing.rs index e0d83924bc0..e574a881d91 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/bytecode_publishing.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/bytecode_publishing.rs @@ -1,7 +1,7 @@ use zksync_types::event::extract_long_l2_to_l1_messages; use zksync_utils::bytecode::compress_bytecode; -use crate::interface::{TxExecutionMode, VmExecutionMode}; +use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; use crate::vm_latest::tests::tester::{DeployContractsTx, TxType, VmTesterBuilder}; use crate::vm_latest::tests::utils::read_test_contract; use crate::vm_latest::HistoryEnabled; diff --git a/core/lib/multivm/src/versions/vm_latest/tests/call_tracer.rs b/core/lib/multivm/src/versions/vm_latest/tests/call_tracer.rs index ca55fa4a182..e5b1ce15fcd 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/call_tracer.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/call_tracer.rs @@ -1,8 +1,9 @@ -use crate::interface::{TxExecutionMode, VmExecutionMode}; +use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; +use crate::tracers::CallTracer; use crate::vm_latest::constants::BLOCK_GAS_LIMIT; use crate::vm_latest::tests::tester::VmTesterBuilder; use crate::vm_latest::tests::utils::{read_max_depth_contract, read_test_contract}; -use crate::vm_latest::{CallTracer, HistoryEnabled}; +use crate::vm_latest::{HistoryEnabled, ToTracerPointer}; use once_cell::sync::OnceCell; use std::sync::Arc; use zksync_types::{Address, Execute}; @@ -34,11 +35,9 @@ fn test_max_depth() { ); let result = Arc::new(OnceCell::new()); - let call_tracer = CallTracer::new(result.clone(), HistoryEnabled); + let call_tracer = CallTracer::new(result.clone()).into_tracer_pointer(); vm.vm.push_transaction(tx); - let res = vm - .vm - .inspect(vec![Box::new(call_tracer)], VmExecutionMode::OneTx); + let res = vm.vm.inspect(call_tracer.into(), VmExecutionMode::OneTx); assert!(result.get().is_some()); assert!(res.result.is_failed()); } @@ -71,11 +70,9 @@ fn test_basic_behavior() { ); let result = Arc::new(OnceCell::new()); - let call_tracer = CallTracer::new(result.clone(), HistoryEnabled); + let call_tracer = CallTracer::new(result.clone()).into_tracer_pointer(); vm.vm.push_transaction(tx); - let res = vm - .vm - .inspect(vec![Box::new(call_tracer)], VmExecutionMode::OneTx); + let res = vm.vm.inspect(call_tracer.into(), VmExecutionMode::OneTx); let call_tracer_result = result.get().unwrap(); diff --git a/core/lib/multivm/src/versions/vm_latest/tests/default_aa.rs b/core/lib/multivm/src/versions/vm_latest/tests/default_aa.rs index cb0ecb81ca1..b31e32270d9 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/default_aa.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/default_aa.rs @@ -4,7 +4,7 @@ use zksync_types::system_contracts::{DEPLOYMENT_NONCE_INCREMENT, TX_NONCE_INCREM use zksync_types::{get_code_key, get_known_code_key, get_nonce_key, AccountTreeId, U256}; use zksync_utils::u256_to_h256; -use crate::interface::{TxExecutionMode, VmExecutionMode}; +use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; use crate::vm_latest::tests::tester::{DeployContractsTx, TxType, VmTesterBuilder}; use crate::vm_latest::tests::utils::{get_balance, read_test_contract, verify_required_storage}; use crate::vm_latest::HistoryEnabled; diff --git a/core/lib/multivm/src/versions/vm_latest/tests/gas_limit.rs b/core/lib/multivm/src/versions/vm_latest/tests/gas_limit.rs index 30d4dca39cf..6bebffeacee 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/gas_limit.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/gas_limit.rs @@ -6,7 +6,7 @@ use crate::vm_latest::constants::{ }; use crate::vm_latest::tests::tester::VmTesterBuilder; -use crate::interface::TxExecutionMode; +use crate::interface::{TxExecutionMode, VmInterface}; use crate::vm_latest::HistoryDisabled; /// Checks that `TX_GAS_LIMIT_OFFSET` constant is correct. diff --git a/core/lib/multivm/src/versions/vm_latest/tests/get_used_contracts.rs b/core/lib/multivm/src/versions/vm_latest/tests/get_used_contracts.rs index 931c54d9a36..688711d5a9c 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/get_used_contracts.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/get_used_contracts.rs @@ -2,6 +2,7 @@ use std::collections::{HashMap, HashSet}; use itertools::Itertools; +use crate::HistoryMode; use zksync_state::WriteStorage; use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; use zksync_test_account::Account; @@ -9,10 +10,10 @@ use zksync_types::{Execute, U256}; use zksync_utils::bytecode::hash_bytecode; use zksync_utils::h256_to_u256; -use crate::interface::{TxExecutionMode, VmExecutionMode}; +use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; use crate::vm_latest::tests::tester::{TxType, VmTesterBuilder}; use crate::vm_latest::tests::utils::{read_test_contract, BASE_SYSTEM_CONTRACTS}; -use crate::vm_latest::{HistoryDisabled, HistoryMode, Vm}; +use crate::vm_latest::{HistoryDisabled, Vm}; #[test] fn test_get_used_contracts() { diff --git a/core/lib/multivm/src/versions/vm_latest/tests/is_write_initial.rs b/core/lib/multivm/src/versions/vm_latest/tests/is_write_initial.rs index 80438fec9b1..d40f9109dcb 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/is_write_initial.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/is_write_initial.rs @@ -1,7 +1,7 @@ use zksync_state::ReadStorage; use zksync_types::get_nonce_key; -use crate::interface::{TxExecutionMode, VmExecutionMode}; +use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; use crate::vm_latest::tests::tester::{Account, TxType, VmTesterBuilder}; use crate::vm_latest::tests::utils::read_test_contract; use crate::vm_latest::HistoryDisabled; diff --git a/core/lib/multivm/src/versions/vm_latest/tests/l1_tx_execution.rs b/core/lib/multivm/src/versions/vm_latest/tests/l1_tx_execution.rs index c4d7a16070e..5c1bdbad58a 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/l1_tx_execution.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/l1_tx_execution.rs @@ -4,7 +4,7 @@ use zksync_types::storage_writes_deduplicator::StorageWritesDeduplicator; use zksync_types::{get_code_key, get_known_code_key, U256}; use zksync_utils::u256_to_h256; -use crate::interface::{TxExecutionMode, VmExecutionMode}; +use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; use crate::vm_latest::tests::tester::{TxType, VmTesterBuilder}; use crate::vm_latest::tests::utils::{ read_test_contract, verify_required_storage, BASE_SYSTEM_CONTRACTS, diff --git a/core/lib/multivm/src/versions/vm_latest/tests/l2_blocks.rs b/core/lib/multivm/src/versions/vm_latest/tests/l2_blocks.rs index 64bcd766783..4fd4e0207d4 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/l2_blocks.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/l2_blocks.rs @@ -3,14 +3,17 @@ //! The description for each of the tests can be found in the corresponding `.yul` file. //! -use crate::interface::{ExecutionResult, Halt, L2BlockEnv, TxExecutionMode, VmExecutionMode}; +use crate::interface::{ + ExecutionResult, Halt, L2BlockEnv, TxExecutionMode, VmExecutionMode, VmInterface, +}; use crate::vm_latest::constants::{ BOOTLOADER_HEAP_PAGE, TX_OPERATOR_L2_BLOCK_INFO_OFFSET, TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO, }; use crate::vm_latest::tests::tester::default_l1_batch; use crate::vm_latest::tests::tester::VmTesterBuilder; use crate::vm_latest::utils::l2_blocks::get_l2_block_hash_key; -use crate::vm_latest::{HistoryEnabled, HistoryMode, Vm}; +use crate::vm_latest::{HistoryEnabled, Vm}; +use crate::HistoryMode; use zk_evm_1_4_0::aux_structures::Timestamp; use zksync_state::WriteStorage; use zksync_system_constants::REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_BYTE; diff --git a/core/lib/multivm/src/versions/vm_latest/tests/nonce_holder.rs b/core/lib/multivm/src/versions/vm_latest/tests/nonce_holder.rs index a83fcf345bf..dedaae5c933 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/nonce_holder.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/nonce_holder.rs @@ -1,8 +1,8 @@ use zksync_types::{Execute, Nonce}; -use crate::interface::TxExecutionMode; use crate::interface::VmRevertReason; use crate::interface::{ExecutionResult, Halt, TxRevertReason, VmExecutionMode}; +use crate::interface::{TxExecutionMode, VmInterface}; use crate::vm_latest::tests::tester::{Account, VmTesterBuilder}; use crate::vm_latest::tests::utils::read_nonce_holder_tester; use crate::vm_latest::types::internals::TransactionData; diff --git a/core/lib/multivm/src/versions/vm_latest/tests/refunds.rs b/core/lib/multivm/src/versions/vm_latest/tests/refunds.rs index 4bbbcb436d5..9d4afcdb317 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/refunds.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/refunds.rs @@ -1,4 +1,4 @@ -use crate::interface::{TxExecutionMode, VmExecutionMode}; +use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; use crate::vm_latest::tests::tester::{DeployContractsTx, TxType, VmTesterBuilder}; use crate::vm_latest::tests::utils::read_test_contract; diff --git a/core/lib/multivm/src/versions/vm_latest/tests/require_eip712.rs b/core/lib/multivm/src/versions/vm_latest/tests/require_eip712.rs index 0d8e51b6055..ad1d405a075 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/require_eip712.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/require_eip712.rs @@ -13,7 +13,7 @@ use zksync_types::{ AccountTreeId, Address, Eip712Domain, Execute, L2ChainId, Nonce, Transaction, U256, }; -use crate::interface::{TxExecutionMode, VmExecutionMode}; +use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; use crate::vm_latest::tests::tester::{Account, VmTester, VmTesterBuilder}; use crate::vm_latest::tests::utils::read_many_owners_custom_account_contract; use crate::vm_latest::HistoryDisabled; diff --git a/core/lib/multivm/src/versions/vm_latest/tests/rollbacks.rs b/core/lib/multivm/src/versions/vm_latest/tests/rollbacks.rs index d5dd7d70a7a..343d30dcd95 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/rollbacks.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/rollbacks.rs @@ -6,15 +6,16 @@ use zksync_contracts::test_contracts::LoadnextContractExecutionParams; use zksync_state::WriteStorage; use zksync_types::{get_nonce_key, Execute, U256}; -use crate::interface::{TxExecutionMode, VmExecutionMode}; +use crate::interface::dyn_tracers::vm_1_4_0::DynTracer; +use crate::interface::tracer::{TracerExecutionStatus, TracerExecutionStopReason}; +use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface, VmInterfaceHistoryEnabled}; use crate::vm_latest::tests::tester::{ DeployContractsTx, TransactionTestInfo, TxModifier, TxType, VmTesterBuilder, }; use crate::vm_latest::tests::utils::read_test_contract; use crate::vm_latest::types::internals::ZkSyncVmState; use crate::vm_latest::{ - BootloaderState, DynTracer, HistoryEnabled, HistoryMode, TracerExecutionStatus, - TracerExecutionStopReason, VmTracer, + BootloaderState, HistoryEnabled, HistoryMode, SimpleMemory, ToTracerPointer, VmTracer, }; #[test] @@ -157,7 +158,7 @@ struct MaxRecursionTracer { /// Tracer responsible for calculating the number of storage invocations and /// stopping the VM execution if the limit is reached. -impl DynTracer for MaxRecursionTracer {} +impl DynTracer> for MaxRecursionTracer {} impl VmTracer for MaxRecursionTracer { fn finish_cycle( @@ -223,9 +224,11 @@ fn test_layered_rollback() { vm.vm.push_transaction(loadnext_transaction.clone()); vm.vm.inspect( - vec![Box::new(MaxRecursionTracer { + MaxRecursionTracer { max_recursion_depth: 15, - })], + } + .into_tracer_pointer() + .into(), VmExecutionMode::OneTx, ); @@ -254,6 +257,6 @@ fn test_layered_rollback() { ); vm.vm.push_transaction(loadnext_transaction); - let result = vm.vm.inspect(vec![], VmExecutionMode::OneTx); + let result = vm.vm.execute(VmExecutionMode::OneTx); assert!(!result.result.is_failed(), "transaction must not fail"); } diff --git a/core/lib/multivm/src/versions/vm_latest/tests/simple_execution.rs b/core/lib/multivm/src/versions/vm_latest/tests/simple_execution.rs index ea5cf34e9a6..9f0c855b459 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/simple_execution.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/simple_execution.rs @@ -1,4 +1,4 @@ -use crate::interface::{ExecutionResult, VmExecutionMode}; +use crate::interface::{ExecutionResult, VmExecutionMode, VmInterface}; use crate::vm_latest::tests::tester::{TxType, VmTesterBuilder}; use crate::vm_latest::HistoryDisabled; diff --git a/core/lib/multivm/src/versions/vm_latest/tests/tester/inner_state.rs b/core/lib/multivm/src/versions/vm_latest/tests/tester/inner_state.rs index b9888f111b1..ec9ffe785f9 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/tester/inner_state.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/tester/inner_state.rs @@ -9,6 +9,7 @@ use zksync_types::{StorageKey, StorageLogQuery, StorageValue, U256}; use crate::vm_latest::old_vm::event_sink::InMemoryEventSink; use crate::vm_latest::old_vm::history_recorder::{AppDataFrameManagerWithHistory, HistoryRecorder}; use crate::vm_latest::{HistoryEnabled, HistoryMode, SimpleMemory, Vm}; +use crate::HistoryMode as CommonHistoryMode; #[derive(Clone, Debug)] pub(crate) struct ModifiedKeysMap(HashMap); @@ -71,9 +72,9 @@ pub(crate) struct VmInstanceInnerState { local_state: VmLocalState, } -impl Vm { +impl Vm { // Dump inner state of the VM. - pub(crate) fn dump_inner_state(&self) -> VmInstanceInnerState { + pub(crate) fn dump_inner_state(&self) -> VmInstanceInnerState { let event_sink = self.state.event_sink.clone(); let precompile_processor_state = PrecompileProcessorTestInnerState { timestamp_history: self.state.precompiles_processor.timestamp_history.clone(), diff --git a/core/lib/multivm/src/versions/vm_latest/tests/tester/transaction_test_info.rs b/core/lib/multivm/src/versions/vm_latest/tests/tester/transaction_test_info.rs index 6172cc166a1..6fdfa7955e0 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/tester/transaction_test_info.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/tester/transaction_test_info.rs @@ -1,10 +1,10 @@ use zksync_types::{ExecuteTransactionCommon, Transaction}; -use crate::interface::VmRevertReason; use crate::interface::{ CurrentExecutionState, ExecutionResult, Halt, TxRevertReason, VmExecutionMode, VmExecutionResultAndLogs, }; +use crate::interface::{VmInterface, VmInterfaceHistoryEnabled, VmRevertReason}; use crate::vm_latest::tests::tester::vm_tester::VmTester; use crate::vm_latest::HistoryEnabled; diff --git a/core/lib/multivm/src/versions/vm_latest/tests/tester/vm_tester.rs b/core/lib/multivm/src/versions/vm_latest/tests/tester/vm_tester.rs index 8518f99cf6f..c62855cd192 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/tester/vm_tester.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/tester/vm_tester.rs @@ -1,3 +1,4 @@ +use std::marker::PhantomData; use zksync_contracts::BaseSystemContracts; use zksync_state::{InMemoryStorage, StoragePtr, StorageView, WriteStorage}; @@ -14,13 +15,14 @@ use zksync_utils::u256_to_h256; use crate::vm_latest::constants::BLOCK_GAS_LIMIT; use crate::interface::{ - L1BatchEnv, L2Block, L2BlockEnv, SystemEnv, TxExecutionMode, VmExecutionMode, + L1BatchEnv, L2Block, L2BlockEnv, SystemEnv, TxExecutionMode, VmExecutionMode, VmInterface, }; use crate::vm_latest::tests::tester::Account; use crate::vm_latest::tests::tester::TxType; use crate::vm_latest::tests::utils::read_test_contract; use crate::vm_latest::utils::l2_blocks::load_last_l2_block; -use crate::vm_latest::{HistoryMode, Vm}; +use crate::vm_latest::Vm; +use crate::HistoryMode; pub(crate) type InMemoryStorageView = StorageView; @@ -32,7 +34,7 @@ pub(crate) struct VmTester { pub(crate) test_contract: Option
, pub(crate) rich_accounts: Vec, pub(crate) custom_contracts: Vec, - history_mode: H, + _phantom: std::marker::PhantomData, } impl VmTester { @@ -89,12 +91,7 @@ impl VmTester { }; } - let vm = Vm::new( - l1_batch, - self.vm.system_env.clone(), - self.storage.clone(), - self.history_mode.clone(), - ); + let vm = Vm::new(l1_batch, self.vm.system_env.clone(), self.storage.clone()); if self.test_contract.is_some() { self.deploy_test_contract(); @@ -107,34 +104,33 @@ impl VmTester { pub(crate) type ContractsToDeploy = (Vec, Address, bool); pub(crate) struct VmTesterBuilder { - history_mode: H, storage: Option, l1_batch_env: Option, system_env: SystemEnv, deployer: Option, rich_accounts: Vec, custom_contracts: Vec, + _phantom: PhantomData, } impl Clone for VmTesterBuilder { fn clone(&self) -> Self { Self { - history_mode: self.history_mode.clone(), storage: None, l1_batch_env: self.l1_batch_env.clone(), system_env: self.system_env.clone(), deployer: self.deployer.clone(), rich_accounts: self.rich_accounts.clone(), custom_contracts: self.custom_contracts.clone(), + _phantom: Default::default(), } } } #[allow(dead_code)] impl VmTesterBuilder { - pub(crate) fn new(history_mode: H) -> Self { + pub(crate) fn new(_: H) -> Self { Self { - history_mode, storage: None, l1_batch_env: None, system_env: SystemEnv { @@ -149,6 +145,7 @@ impl VmTesterBuilder { deployer: None, rich_accounts: vec![], custom_contracts: vec![], + _phantom: Default::default(), } } @@ -230,12 +227,7 @@ impl VmTesterBuilder { } let fee_account = l1_batch_env.fee_account; - let vm = Vm::new( - l1_batch_env, - self.system_env, - storage_ptr.clone(), - self.history_mode.clone(), - ); + let vm = Vm::new(l1_batch_env, self.system_env, storage_ptr.clone()); VmTester { vm, @@ -245,7 +237,7 @@ impl VmTesterBuilder { test_contract: None, rich_accounts: self.rich_accounts.clone(), custom_contracts: self.custom_contracts.clone(), - history_mode: self.history_mode, + _phantom: Default::default(), } } } diff --git a/core/lib/multivm/src/versions/vm_latest/tests/upgrade.rs b/core/lib/multivm/src/versions/vm_latest/tests/upgrade.rs index b321b33344d..65780114e9a 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/upgrade.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/upgrade.rs @@ -14,7 +14,9 @@ use zksync_contracts::{deployer_contract, load_contract, load_sys_contract, read use zksync_state::WriteStorage; use zksync_test_account::TxType; -use crate::interface::{ExecutionResult, Halt, TxExecutionMode, VmExecutionMode}; +use crate::interface::{ + ExecutionResult, Halt, TxExecutionMode, VmExecutionMode, VmInterface, VmInterfaceHistoryEnabled, +}; use crate::vm_latest::tests::tester::VmTesterBuilder; use crate::vm_latest::tests::utils::verify_required_storage; use crate::vm_latest::HistoryEnabled; diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs b/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs index 32155b7d1b4..bfcf331243c 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs @@ -1,5 +1,7 @@ use std::fmt::{Debug, Formatter}; +use std::marker::PhantomData; +use crate::interface::tracer::{TracerExecutionStopReason, VmExecutionStopReason}; use crate::interface::{Halt, VmExecutionMode}; use zk_evm_1_4_0::{ tracing::{ @@ -12,26 +14,26 @@ use zk_evm_1_4_0::{ use zksync_state::{StoragePtr, WriteStorage}; use zksync_types::Timestamp; +use crate::interface::traits::tracers::dyn_tracers::vm_1_4_0::DynTracer; +use crate::interface::types::tracer::TracerExecutionStatus; use crate::vm_latest::bootloader_state::utils::apply_l2_block; use crate::vm_latest::bootloader_state::BootloaderState; use crate::vm_latest::constants::BOOTLOADER_HEAP_PAGE; use crate::vm_latest::old_vm::history_recorder::HistoryMode; use crate::vm_latest::old_vm::memory::SimpleMemory; -use crate::vm_latest::tracers::traits::{ - DynTracer, TracerExecutionStatus, TracerExecutionStopReason, VmTracer, -}; +use crate::vm_latest::tracers::dispatcher::TracerDispatcher; use crate::vm_latest::tracers::utils::{ computational_gas_price, gas_spent_on_bytecodes_and_long_messages_this_opcode, print_debug_if_needed, VmHook, }; use crate::vm_latest::tracers::{RefundsTracer, ResultTracer}; use crate::vm_latest::types::internals::ZkSyncVmState; -use crate::vm_latest::VmExecutionStopReason; +use crate::vm_latest::VmTracer; use super::PubdataTracer; /// Default tracer for the VM. It manages the other tracers execution and stop the vm when needed. -pub(crate) struct DefaultExecutionTracer { +pub(crate) struct DefaultExecutionTracer { tx_has_been_processed: bool, execution_mode: VmExecutionMode, @@ -51,18 +53,19 @@ pub(crate) struct DefaultExecutionTracer { // memory at the end of the batch. Its separation from the custom tracer // ensures static dispatch, enhancing performance by avoiding dynamic dispatch overhead. pub(crate) pubdata_tracer: Option, - pub(crate) custom_tracers: Vec>>, + pub(crate) dispatcher: TracerDispatcher, ret_from_the_bootloader: Option, storage: StoragePtr, + _phantom: PhantomData, } -impl Debug for DefaultExecutionTracer { +impl Debug for DefaultExecutionTracer { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("DefaultExecutionTracer").finish() } } -impl Tracer for DefaultExecutionTracer { +impl Tracer for DefaultExecutionTracer { const CALL_BEFORE_DECODING: bool = false; const CALL_AFTER_DECODING: bool = true; const CALL_BEFORE_EXECUTION: bool = true; @@ -82,7 +85,7 @@ impl Tracer for DefaultExecutionTracer { data: AfterDecodingData, memory: &Self::SupportedMemory, ) { - >::after_decoding( + >>::after_decoding( &mut self.result_tracer, state, data, @@ -90,14 +93,22 @@ impl Tracer for DefaultExecutionTracer { ); if let Some(refund_tracer) = &mut self.refund_tracer { - >::after_decoding(refund_tracer, state, data, memory); + >>::after_decoding( + refund_tracer, + state, + data, + memory, + ); } if let Some(pubdata_tracer) = &mut self.pubdata_tracer { - >::after_decoding(pubdata_tracer, state, data, memory); - } - for tracer in self.custom_tracers.iter_mut() { - tracer.after_decoding(state, data, memory) + >>::after_decoding( + pubdata_tracer, + state, + data, + memory, + ); } + self.dispatcher.after_decoding(state, data, memory) } fn before_execution( @@ -131,12 +142,11 @@ impl Tracer for DefaultExecutionTracer { if let Some(refund_tracer) = &mut self.refund_tracer { refund_tracer.before_execution(state, data, memory, self.storage.clone()); } + self.dispatcher + .before_execution(state, data, memory, self.storage.clone()); if let Some(pubdata_tracer) = &mut self.pubdata_tracer { pubdata_tracer.before_execution(state, data, memory, self.storage.clone()); } - for tracer in self.custom_tracers.iter_mut() { - tracer.before_execution(state, data, memory, self.storage.clone()); - } } fn after_execution( @@ -164,12 +174,11 @@ impl Tracer for DefaultExecutionTracer { if let Some(refund_tracer) = &mut self.refund_tracer { refund_tracer.after_execution(state, data, memory, self.storage.clone()) } + self.dispatcher + .after_execution(state, data, memory, self.storage.clone()); if let Some(pubdata_tracer) = &mut self.pubdata_tracer { pubdata_tracer.after_execution(state, data, memory, self.storage.clone()) } - for tracer in self.custom_tracers.iter_mut() { - tracer.after_execution(state, data, memory, self.storage.clone()); - } } } @@ -177,7 +186,7 @@ impl DefaultExecutionTracer { pub(crate) fn new( computational_gas_limit: u32, execution_mode: VmExecutionMode, - custom_tracers: Vec>>, + dispatcher: TracerDispatcher, storage: StoragePtr, refund_tracer: Option, pubdata_tracer: Option, @@ -192,10 +201,11 @@ impl DefaultExecutionTracer { final_batch_info_requested: false, result_tracer: ResultTracer::new(execution_mode), refund_tracer, + dispatcher, pubdata_tracer, - custom_tracers, ret_from_the_bootloader: None, storage, + _phantom: Default::default(), } } @@ -246,23 +256,19 @@ impl DefaultExecutionTracer { } } -impl DynTracer for DefaultExecutionTracer {} - -impl VmTracer for DefaultExecutionTracer { - fn initialize_tracer(&mut self, state: &mut ZkSyncVmState) { +impl DefaultExecutionTracer { + pub(crate) fn initialize_tracer(&mut self, state: &mut ZkSyncVmState) { self.result_tracer.initialize_tracer(state); if let Some(refund_tracer) = &mut self.refund_tracer { refund_tracer.initialize_tracer(state); } + self.dispatcher.initialize_tracer(state); if let Some(pubdata_tracer) = &mut self.pubdata_tracer { pubdata_tracer.initialize_tracer(state); } - for processor in self.custom_tracers.iter_mut() { - processor.initialize_tracer(state); - } } - fn finish_cycle( + pub(crate) fn finish_cycle( &mut self, state: &mut ZkSyncVmState, bootloader_state: &mut BootloaderState, @@ -277,20 +283,19 @@ impl VmTracer for DefaultExecutionTracer< .finish_cycle(state, bootloader_state) .stricter(&result); } + result = self + .dispatcher + .finish_cycle(state, bootloader_state) + .stricter(&result); if let Some(pubdata_tracer) = &mut self.pubdata_tracer { result = pubdata_tracer .finish_cycle(state, bootloader_state) .stricter(&result); } - for processor in self.custom_tracers.iter_mut() { - result = processor - .finish_cycle(state, bootloader_state) - .stricter(&result); - } result.stricter(&self.should_stop_execution()) } - fn after_vm_execution( + pub(crate) fn after_vm_execution( &mut self, state: &mut ZkSyncVmState, bootloader_state: &BootloaderState, @@ -302,12 +307,11 @@ impl VmTracer for DefaultExecutionTracer< if let Some(refund_tracer) = &mut self.refund_tracer { refund_tracer.after_vm_execution(state, bootloader_state, stop_reason.clone()); } + self.dispatcher + .after_vm_execution(state, bootloader_state, stop_reason.clone()); if let Some(pubdata_tracer) = &mut self.pubdata_tracer { pubdata_tracer.after_vm_execution(state, bootloader_state, stop_reason.clone()); } - for processor in self.custom_tracers.iter_mut() { - processor.after_vm_execution(state, bootloader_state, stop_reason.clone()); - } } } diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/dispatcher.rs b/core/lib/multivm/src/versions/vm_latest/tracers/dispatcher.rs new file mode 100644 index 00000000000..72124d8f661 --- /dev/null +++ b/core/lib/multivm/src/versions/vm_latest/tracers/dispatcher.rs @@ -0,0 +1,115 @@ +use crate::interface::dyn_tracers::vm_1_4_0::DynTracer; +use crate::interface::tracer::{TracerExecutionStatus, VmExecutionStopReason}; +use crate::vm_latest::{ + BootloaderState, HistoryMode, SimpleMemory, TracerPointer, VmTracer, ZkSyncVmState, +}; +use zk_evm_1_4_0::tracing::{ + AfterDecodingData, AfterExecutionData, BeforeExecutionData, VmLocalStateData, +}; +use zksync_state::{StoragePtr, WriteStorage}; + +/// Tracer dispatcher is a tracer that can dispatch calls to multiple tracers. +pub struct TracerDispatcher { + tracers: Vec>, +} + +impl From> for TracerDispatcher { + fn from(value: TracerPointer) -> Self { + Self { + tracers: vec![value], + } + } +} + +impl From>> for TracerDispatcher { + fn from(value: Vec>) -> Self { + Self { tracers: value } + } +} + +impl Default for TracerDispatcher { + fn default() -> Self { + Self { tracers: vec![] } + } +} + +impl DynTracer> for TracerDispatcher { + #[inline(always)] + fn before_decoding(&mut self, _state: VmLocalStateData<'_>, _memory: &SimpleMemory) { + for tracer in self.tracers.iter_mut() { + tracer.before_decoding(_state, _memory); + } + } + + #[inline(always)] + fn after_decoding( + &mut self, + _state: VmLocalStateData<'_>, + _data: AfterDecodingData, + _memory: &SimpleMemory, + ) { + for tracer in self.tracers.iter_mut() { + tracer.after_decoding(_state, _data, _memory); + } + } + + #[inline(always)] + fn before_execution( + &mut self, + _state: VmLocalStateData<'_>, + _data: BeforeExecutionData, + _memory: &SimpleMemory, + _storage: StoragePtr, + ) { + for tracer in self.tracers.iter_mut() { + tracer.before_execution(_state, _data, _memory, _storage.clone()); + } + } + + #[inline(always)] + fn after_execution( + &mut self, + _state: VmLocalStateData<'_>, + _data: AfterExecutionData, + _memory: &SimpleMemory, + _storage: StoragePtr, + ) { + for tracer in self.tracers.iter_mut() { + tracer.after_execution(_state, _data, _memory, _storage.clone()); + } + } +} + +impl VmTracer for TracerDispatcher { + fn initialize_tracer(&mut self, _state: &mut ZkSyncVmState) { + for tracer in self.tracers.iter_mut() { + tracer.initialize_tracer(_state); + } + } + + /// Run after each vm execution cycle + #[inline(always)] + fn finish_cycle( + &mut self, + _state: &mut ZkSyncVmState, + _bootloader_state: &mut BootloaderState, + ) -> TracerExecutionStatus { + let mut result = TracerExecutionStatus::Continue; + for tracer in self.tracers.iter_mut() { + result = result.stricter(&tracer.finish_cycle(_state, _bootloader_state)); + } + result + } + + /// Run after the vm execution + fn after_vm_execution( + &mut self, + _state: &mut ZkSyncVmState, + _bootloader_state: &BootloaderState, + _stop_reason: VmExecutionStopReason, + ) { + for tracer in self.tracers.iter_mut() { + tracer.after_vm_execution(_state, _bootloader_state, _stop_reason.clone()); + } + } +} diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/mod.rs b/core/lib/multivm/src/versions/vm_latest/tracers/mod.rs index 3ac64deaabb..33d043de6eb 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/mod.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/mod.rs @@ -2,16 +2,12 @@ pub(crate) use default_tracers::DefaultExecutionTracer; pub(crate) use pubdata_tracer::PubdataTracer; pub(crate) use refunds::RefundsTracer; pub(crate) use result_tracer::ResultTracer; -pub use storage_invocations::StorageInvocations; -pub use validation::{ValidationError, ValidationTracer, ValidationTracerParams}; pub(crate) mod default_tracers; pub(crate) mod pubdata_tracer; pub(crate) mod refunds; pub(crate) mod result_tracer; -pub(crate) mod call; -pub(crate) mod storage_invocations; +pub mod dispatcher; pub(crate) mod traits; pub(crate) mod utils; -pub(crate) mod validation; diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/pubdata_tracer.rs b/core/lib/multivm/src/versions/vm_latest/tracers/pubdata_tracer.rs index b43c96cb912..0c6dfa5611c 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/pubdata_tracer.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/pubdata_tracer.rs @@ -20,16 +20,12 @@ use crate::vm_latest::{ old_vm::{history_recorder::HistoryMode, memory::SimpleMemory}, types::internals::pubdata::PubdataInput, }; -use crate::{ - vm_latest::StorageOracle, - vm_latest::{constants::BOOTLOADER_HEAP_PAGE, TracerExecutionStatus}, -}; +use crate::{vm_latest::constants::BOOTLOADER_HEAP_PAGE, vm_latest::StorageOracle}; +use crate::interface::dyn_tracers::vm_1_4_0::DynTracer; +use crate::interface::tracer::{TracerExecutionStatus, TracerExecutionStopReason}; use crate::interface::types::inputs::L1BatchEnv; -use crate::vm_latest::tracers::{ - traits::{DynTracer, TracerExecutionStopReason, VmTracer}, - utils::VmHook, -}; +use crate::vm_latest::tracers::{traits::VmTracer, utils::VmHook}; use crate::vm_latest::types::internals::ZkSyncVmState; use crate::vm_latest::utils::logs::collect_events_and_l1_system_logs_after_timestamp; use crate::{ @@ -164,7 +160,7 @@ impl PubdataTracer { } } -impl DynTracer for PubdataTracer { +impl DynTracer> for PubdataTracer { fn before_execution( &mut self, state: VmLocalStateData<'_>, diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/refunds.rs b/core/lib/multivm/src/versions/vm_latest/tracers/refunds.rs index 23eca72e6ac..321fdae852f 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/refunds.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/refunds.rs @@ -1,5 +1,7 @@ use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; +use crate::interface::traits::tracers::dyn_tracers::vm_1_4_0::DynTracer; +use crate::interface::types::tracer::TracerExecutionStatus; use crate::interface::{L1BatchEnv, Refunds}; use zk_evm_1_4_0::{ aux_structures::Timestamp, @@ -27,11 +29,10 @@ use crate::vm_latest::old_vm::{ use crate::vm_latest::bootloader_state::BootloaderState; use crate::vm_latest::tracers::utils::gas_spent_on_bytecodes_and_long_messages_this_opcode; use crate::vm_latest::tracers::{ - traits::{DynTracer, VmTracer}, + traits::VmTracer, utils::{get_vm_hook_params, VmHook}, }; use crate::vm_latest::types::internals::ZkSyncVmState; -use crate::vm_latest::TracerExecutionStatus; /// Tracer responsible for collecting information about refunds. #[derive(Debug, Clone)] @@ -150,7 +151,7 @@ impl RefundsTracer { } } -impl DynTracer for RefundsTracer { +impl DynTracer> for RefundsTracer { fn before_execution( &mut self, state: VmLocalStateData<'_>, diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/result_tracer.rs b/core/lib/multivm/src/versions/vm_latest/tracers/result_tracer.rs index 9e3373d0d83..19c878dfc6a 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/result_tracer.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/result_tracer.rs @@ -5,25 +5,24 @@ use zk_evm_1_4_0::{ }; use zksync_state::{StoragePtr, WriteStorage}; -use crate::interface::{ExecutionResult, Halt, TxRevertReason, VmExecutionMode, VmRevertReason}; +use crate::interface::{ + tracer::VmExecutionStopReason, traits::tracers::dyn_tracers::vm_1_4_0::DynTracer, + types::tracer::TracerExecutionStopReason, ExecutionResult, Halt, TxRevertReason, + VmExecutionMode, VmRevertReason, +}; use zksync_types::U256; -use crate::vm_latest::bootloader_state::BootloaderState; -use crate::vm_latest::old_vm::{ - history_recorder::HistoryMode, - memory::SimpleMemory, - utils::{vm_may_have_ended_inner, VmExecutionResult}, -}; -use crate::vm_latest::tracers::{ - traits::{DynTracer, VmTracer}, - utils::{get_vm_hook_params, read_pointer, VmHook}, +use crate::vm_latest::{ + constants::{BOOTLOADER_HEAP_PAGE, RESULT_SUCCESS_FIRST_SLOT}, + old_vm::utils::{vm_may_have_ended_inner, VmExecutionResult}, + tracers::{ + traits::VmTracer, + utils::{get_vm_hook_params, read_pointer, VmHook}, + }, + types::internals::ZkSyncVmState, + BootloaderState, HistoryMode, SimpleMemory, }; -use crate::vm_latest::constants::{BOOTLOADER_HEAP_PAGE, RESULT_SUCCESS_FIRST_SLOT}; -use crate::vm_latest::tracers::traits::TracerExecutionStopReason; -use crate::vm_latest::types::internals::ZkSyncVmState; -use crate::vm_latest::VmExecutionStopReason; - #[derive(Debug, Clone)] enum Result { Error { error_reason: VmRevertReason }, @@ -56,7 +55,7 @@ fn current_frame_is_bootloader(local_state: &VmLocalState) -> bool { local_state.callstack.inner.len() == 1 } -impl DynTracer for ResultTracer { +impl DynTracer> for ResultTracer { fn after_decoding( &mut self, state: VmLocalStateData<'_>, diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/traits.rs b/core/lib/multivm/src/versions/vm_latest/tracers/traits.rs index 8ee9e6c06d8..a3970541bac 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/traits.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/traits.rs @@ -1,17 +1,16 @@ -use crate::interface::Halt; -use zk_evm_1_4_0::tracing::{ - AfterDecodingData, AfterExecutionData, BeforeExecutionData, VmLocalStateData, -}; -use zksync_state::{StoragePtr, WriteStorage}; +use crate::interface::dyn_tracers::vm_1_4_0::DynTracer; +use crate::interface::tracer::{TracerExecutionStatus, VmExecutionStopReason}; +use zksync_state::WriteStorage; use crate::vm_latest::bootloader_state::BootloaderState; use crate::vm_latest::old_vm::history_recorder::HistoryMode; use crate::vm_latest::old_vm::memory::SimpleMemory; use crate::vm_latest::types::internals::ZkSyncVmState; -use crate::vm_latest::VmExecutionStopReason; + +pub type TracerPointer = Box>; /// Run tracer for collecting data during the vm execution cycles -pub trait VmTracer: DynTracer { +pub trait VmTracer: DynTracer> { /// Initialize the tracer before the vm execution fn initialize_tracer(&mut self, _state: &mut ZkSyncVmState) {} /// Run after each vm execution cycle @@ -32,72 +31,12 @@ pub trait VmTracer: DynTracer { } } -/// Version of zk_evm_1_4_0::Tracer suitable for dynamic dispatch. -pub trait DynTracer { - fn before_decoding(&mut self, _state: VmLocalStateData<'_>, _memory: &SimpleMemory) {} - fn after_decoding( - &mut self, - _state: VmLocalStateData<'_>, - _data: AfterDecodingData, - _memory: &SimpleMemory, - ) { - } - fn before_execution( - &mut self, - _state: VmLocalStateData<'_>, - _data: BeforeExecutionData, - _memory: &SimpleMemory, - _storage: StoragePtr, - ) { - } - fn after_execution( - &mut self, - _state: VmLocalStateData<'_>, - _data: AfterExecutionData, - _memory: &SimpleMemory, - _storage: StoragePtr, - ) { - } +pub trait ToTracerPointer { + fn into_tracer_pointer(self) -> TracerPointer; } -pub trait BoxedTracer { - fn into_boxed(self) -> Box>; -} - -impl + 'static> BoxedTracer for T { - fn into_boxed(self) -> Box> { +impl + 'static> ToTracerPointer for T { + fn into_tracer_pointer(self) -> TracerPointer { Box::new(self) } } - -#[derive(Debug, Clone, PartialEq)] -pub enum TracerExecutionStopReason { - Finish, - Abort(Halt), -} - -#[derive(Debug, Clone, PartialEq)] -pub enum TracerExecutionStatus { - Continue, - Stop(TracerExecutionStopReason), -} - -impl TracerExecutionStatus { - /// Chose the stricter ExecutionStatus - /// If both statuses are Continue, then the result is Continue - /// If one of the statuses is Abort, then the result is Abort - /// If one of the statuses is Finish, then the result is Finish - pub fn stricter(&self, other: &Self) -> Self { - match (self, other) { - (Self::Continue, Self::Continue) => Self::Continue, - (Self::Stop(TracerExecutionStopReason::Abort(reason)), _) - | (_, Self::Stop(TracerExecutionStopReason::Abort(reason))) => { - Self::Stop(TracerExecutionStopReason::Abort(reason.clone())) - } - (Self::Stop(TracerExecutionStopReason::Finish), _) - | (_, Self::Stop(TracerExecutionStopReason::Finish)) => { - Self::Stop(TracerExecutionStopReason::Finish) - } - } - } -} diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/utils.rs b/core/lib/multivm/src/versions/vm_latest/tracers/utils.rs index b19268030d1..5b34eee4742 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/utils.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/utils.rs @@ -18,7 +18,6 @@ use crate::vm_latest::constants::{ use crate::vm_latest::old_vm::history_recorder::HistoryMode; use crate::vm_latest::old_vm::memory::SimpleMemory; use crate::vm_latest::old_vm::utils::{aux_heap_page_from_base, heap_page_from_base}; -use crate::vm_latest::tracers::traits::TracerExecutionStopReason; #[derive(Clone, Debug, Copy)] pub(crate) enum VmHook { @@ -220,9 +219,3 @@ pub(crate) fn get_vm_hook_params(memory: &SimpleMemory) -> Ve VM_HOOK_PARAMS_START_POSITION..VM_HOOK_PARAMS_START_POSITION + VM_HOOK_PARAMS_COUNT, ) } - -#[derive(Debug, Clone, PartialEq)] -pub enum VmExecutionStopReason { - VmFinished, - TracerRequestedStop(TracerExecutionStopReason), -} diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/validation/error.rs b/core/lib/multivm/src/versions/vm_latest/tracers/validation/error.rs deleted file mode 100644 index 4b9741ddaa5..00000000000 --- a/core/lib/multivm/src/versions/vm_latest/tracers/validation/error.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::interface::Halt; -use std::fmt::Display; -use zksync_types::vm_trace::ViolatedValidationRule; - -#[derive(Debug, Clone)] -pub enum ValidationError { - FailedTx(Halt), - ViolatedRule(ViolatedValidationRule), -} - -impl Display for ValidationError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::FailedTx(revert_reason) => { - write!(f, "Validation revert: {}", revert_reason) - } - Self::ViolatedRule(rule) => { - write!(f, "Violated validation rules: {}", rule) - } - } - } -} diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/validation/mod.rs b/core/lib/multivm/src/versions/vm_latest/tracers/validation/mod.rs deleted file mode 100644 index 5b393b060c6..00000000000 --- a/core/lib/multivm/src/versions/vm_latest/tracers/validation/mod.rs +++ /dev/null @@ -1,409 +0,0 @@ -mod error; -mod params; -mod types; - -use std::sync::Arc; -use std::{collections::HashSet, marker::PhantomData}; - -use once_cell::sync::OnceCell; -use zk_evm_1_4_0::{ - tracing::{BeforeExecutionData, VmLocalStateData}, - zkevm_opcode_defs::{ContextOpcode, FarCallABI, LogOpcode, Opcode}, -}; - -use zksync_state::{StoragePtr, WriteStorage}; -use zksync_system_constants::{ - ACCOUNT_CODE_STORAGE_ADDRESS, BOOTLOADER_ADDRESS, CONTRACT_DEPLOYER_ADDRESS, - KECCAK256_PRECOMPILE_ADDRESS, L2_ETH_TOKEN_ADDRESS, MSG_VALUE_SIMULATOR_ADDRESS, - SYSTEM_CONTEXT_ADDRESS, -}; - -use zksync_types::{ - get_code_key, vm_trace::ViolatedValidationRule, web3::signing::keccak256, AccountTreeId, - Address, StorageKey, H256, U256, -}; -use zksync_utils::{ - be_bytes_to_safe_address, h256_to_account_address, u256_to_account_address, u256_to_h256, -}; - -use crate::vm_latest::old_vm::history_recorder::HistoryMode; -use crate::vm_latest::old_vm::memory::SimpleMemory; -use crate::vm_latest::tracers::traits::{ - DynTracer, TracerExecutionStatus, TracerExecutionStopReason, VmTracer, -}; -use crate::vm_latest::tracers::utils::{ - computational_gas_price, get_calldata_page_via_abi, print_debug_if_needed, VmHook, -}; - -pub use error::ValidationError; -pub use params::ValidationTracerParams; - -use crate::interface::Halt; -use types::NewTrustedValidationItems; -use types::ValidationTracerMode; - -use crate::vm_latest::{BootloaderState, ZkSyncVmState}; - -/// Tracer that is used to ensure that the validation adheres to all the rules -/// to prevent DDoS attacks on the server. -#[derive(Debug, Clone)] -pub struct ValidationTracer { - validation_mode: ValidationTracerMode, - auxilary_allowed_slots: HashSet, - - user_address: Address, - #[allow(dead_code)] - paymaster_address: Address, - should_stop_execution: bool, - trusted_slots: HashSet<(Address, U256)>, - trusted_addresses: HashSet
, - trusted_address_slots: HashSet<(Address, U256)>, - computational_gas_used: u32, - computational_gas_limit: u32, - pub result: Arc>, - _marker: PhantomData H>, -} - -type ValidationRoundResult = Result; - -impl ValidationTracer { - pub fn new(params: ValidationTracerParams) -> (Self, Arc>) { - let result = Arc::new(OnceCell::new()); - ( - Self { - validation_mode: ValidationTracerMode::NoValidation, - auxilary_allowed_slots: Default::default(), - - should_stop_execution: false, - user_address: params.user_address, - paymaster_address: params.paymaster_address, - trusted_slots: params.trusted_slots, - trusted_addresses: params.trusted_addresses, - trusted_address_slots: params.trusted_address_slots, - computational_gas_used: 0, - computational_gas_limit: params.computational_gas_limit, - result: result.clone(), - _marker: Default::default(), - }, - result, - ) - } - - fn process_validation_round_result(&mut self, result: ValidationRoundResult) { - match result { - Ok(NewTrustedValidationItems { - new_allowed_slots, - new_trusted_addresses, - }) => { - self.auxilary_allowed_slots.extend(new_allowed_slots); - self.trusted_addresses.extend(new_trusted_addresses); - } - Err(err) => { - if self.result.get().is_some() { - tracing::trace!("Validation error is already set, skipping"); - return; - } - self.result.set(err).expect("Result should be empty"); - } - } - } - - // Checks whether such storage access is acceptable. - fn is_allowed_storage_read( - &self, - storage: StoragePtr, - address: Address, - key: U256, - msg_sender: Address, - ) -> bool { - // If there are no restrictions, all storage reads are valid. - // We also don't support the paymaster validation for now. - if matches!( - self.validation_mode, - ValidationTracerMode::NoValidation | ValidationTracerMode::PaymasterTxValidation - ) { - return true; - } - - // The pair of MSG_VALUE_SIMULATOR_ADDRESS & L2_ETH_TOKEN_ADDRESS simulates the behavior of transfering ETH - // that is safe for the DDoS protection rules. - if valid_eth_token_call(address, msg_sender) { - return true; - } - - if self.trusted_slots.contains(&(address, key)) - || self.trusted_addresses.contains(&address) - || self.trusted_address_slots.contains(&(address, key)) - { - return true; - } - - if touches_allowed_context(address, key) { - return true; - } - - // The user is allowed to touch its own slots or slots semantically related to him. - let valid_users_slot = address == self.user_address - || u256_to_account_address(&key) == self.user_address - || self.auxilary_allowed_slots.contains(&u256_to_h256(key)); - if valid_users_slot { - return true; - } - - if is_constant_code_hash(address, key, storage) { - return true; - } - - false - } - - // Used to remember user-related fields (its balance/allowance/etc). - // Note that it assumes that the length of the calldata is 64 bytes. - fn slot_to_add_from_keccak_call( - &self, - calldata: &[u8], - validated_address: Address, - ) -> Option { - assert_eq!(calldata.len(), 64); - - let (potential_address_bytes, potential_position_bytes) = calldata.split_at(32); - let potential_address = be_bytes_to_safe_address(potential_address_bytes); - - // If the validation_address is equal to the potential_address, - // then it is a request that could be used for mapping of kind mapping(address => ...). - // - // If the potential_position_bytes were already allowed before, then this keccak might be used - // for ERC-20 allowance or any other of mapping(address => mapping(...)) - if potential_address == Some(validated_address) - || self - .auxilary_allowed_slots - .contains(&H256::from_slice(potential_position_bytes)) - { - // This is request that could be used for mapping of kind mapping(address => ...) - - // We could theoretically wait for the slot number to be returned by the - // keccak256 precompile itself, but this would complicate the code even further - // so let's calculate it here. - let slot = keccak256(calldata); - - // Adding this slot to the allowed ones - Some(H256(slot)) - } else { - None - } - } - - pub fn params(&self) -> ValidationTracerParams { - ValidationTracerParams { - user_address: self.user_address, - paymaster_address: self.paymaster_address, - trusted_slots: self.trusted_slots.clone(), - trusted_addresses: self.trusted_addresses.clone(), - trusted_address_slots: self.trusted_address_slots.clone(), - computational_gas_limit: self.computational_gas_limit, - } - } - - fn check_user_restrictions( - &mut self, - state: VmLocalStateData<'_>, - data: BeforeExecutionData, - memory: &SimpleMemory, - storage: StoragePtr, - ) -> ValidationRoundResult { - if self.computational_gas_used > self.computational_gas_limit { - return Err(ViolatedValidationRule::TookTooManyComputationalGas( - self.computational_gas_limit, - )); - } - - let opcode_variant = data.opcode.variant; - match opcode_variant.opcode { - Opcode::FarCall(_) => { - let packed_abi = data.src0_value.value; - let call_destination_value = data.src1_value.value; - - let called_address = u256_to_account_address(&call_destination_value); - let far_call_abi = FarCallABI::from_u256(packed_abi); - - if called_address == KECCAK256_PRECOMPILE_ADDRESS - && far_call_abi.memory_quasi_fat_pointer.length == 64 - { - let calldata_page = get_calldata_page_via_abi( - &far_call_abi, - state.vm_local_state.callstack.current.base_memory_page, - ); - let calldata = memory.read_unaligned_bytes( - calldata_page as usize, - far_call_abi.memory_quasi_fat_pointer.start as usize, - 64, - ); - - let slot_to_add = - self.slot_to_add_from_keccak_call(&calldata, self.user_address); - - if let Some(slot) = slot_to_add { - return Ok(NewTrustedValidationItems { - new_allowed_slots: vec![slot], - ..Default::default() - }); - } - } else if called_address != self.user_address { - let code_key = get_code_key(&called_address); - let code = storage.borrow_mut().read_value(&code_key); - - if code == H256::zero() { - // The users are not allowed to call contracts with no code - return Err(ViolatedValidationRule::CalledContractWithNoCode( - called_address, - )); - } - } - } - Opcode::Context(context) => { - match context { - ContextOpcode::Meta => { - return Err(ViolatedValidationRule::TouchedUnallowedContext); - } - ContextOpcode::ErgsLeft => { - // TODO (SMA-1168): implement the correct restrictions for the gas left opcode. - } - _ => {} - } - } - Opcode::Log(LogOpcode::StorageRead) => { - let key = data.src0_value.value; - let this_address = state.vm_local_state.callstack.current.this_address; - let msg_sender = state.vm_local_state.callstack.current.msg_sender; - - if !self.is_allowed_storage_read(storage.clone(), this_address, key, msg_sender) { - return Err(ViolatedValidationRule::TouchedUnallowedStorageSlots( - this_address, - key, - )); - } - - if self.trusted_address_slots.contains(&(this_address, key)) { - let storage_key = - StorageKey::new(AccountTreeId::new(this_address), u256_to_h256(key)); - - let value = storage.borrow_mut().read_value(&storage_key); - - return Ok(NewTrustedValidationItems { - new_trusted_addresses: vec![h256_to_account_address(&value)], - ..Default::default() - }); - } - } - _ => {} - } - - Ok(Default::default()) - } -} - -impl DynTracer for ValidationTracer { - fn before_execution( - &mut self, - state: VmLocalStateData<'_>, - data: BeforeExecutionData, - memory: &SimpleMemory, - storage: StoragePtr, - ) { - // For now, we support only validations for users. - if let ValidationTracerMode::UserTxValidation = self.validation_mode { - self.computational_gas_used = self - .computational_gas_used - .saturating_add(computational_gas_price(state, &data)); - - let validation_round_result = - self.check_user_restrictions(state, data, memory, storage); - self.process_validation_round_result(validation_round_result); - } - - let hook = VmHook::from_opcode_memory(&state, &data); - print_debug_if_needed(&hook, &state, memory); - - let current_mode = self.validation_mode; - match (current_mode, hook) { - (ValidationTracerMode::NoValidation, VmHook::AccountValidationEntered) => { - // Account validation can be entered when there is no prior validation (i.e. "nested" validations are not allowed) - self.validation_mode = ValidationTracerMode::UserTxValidation; - } - (ValidationTracerMode::NoValidation, VmHook::PaymasterValidationEntered) => { - // Paymaster validation can be entered when there is no prior validation (i.e. "nested" validations are not allowed) - self.validation_mode = ValidationTracerMode::PaymasterTxValidation; - } - (_, VmHook::AccountValidationEntered | VmHook::PaymasterValidationEntered) => { - panic!( - "Unallowed transition inside the validation tracer. Mode: {:#?}, hook: {:#?}", - self.validation_mode, hook - ); - } - (_, VmHook::NoValidationEntered) => { - // Validation can be always turned off - self.validation_mode = ValidationTracerMode::NoValidation; - } - (_, VmHook::ValidationStepEndeded) => { - // The validation step has ended. - self.should_stop_execution = true; - } - (_, _) => { - // The hook is not relevant to the validation tracer. Ignore. - } - } - } -} - -impl VmTracer for ValidationTracer { - fn finish_cycle( - &mut self, - _state: &mut ZkSyncVmState, - _bootloader_state: &mut BootloaderState, - ) -> TracerExecutionStatus { - if self.should_stop_execution { - return TracerExecutionStatus::Stop(TracerExecutionStopReason::Finish); - } - if let Some(result) = self.result.get() { - return TracerExecutionStatus::Stop(TracerExecutionStopReason::Abort( - Halt::TracerCustom(format!("Validation error: {:#?}", result)), - )); - } - TracerExecutionStatus::Continue - } -} - -fn touches_allowed_context(address: Address, key: U256) -> bool { - // Context is not touched at all - if address != SYSTEM_CONTEXT_ADDRESS { - return false; - } - - // Only chain_id is allowed to be touched. - key == U256::from(0u32) -} - -fn is_constant_code_hash( - address: Address, - key: U256, - storage: StoragePtr, -) -> bool { - if address != ACCOUNT_CODE_STORAGE_ADDRESS { - // Not a code hash - return false; - } - - let value = storage.borrow_mut().read_value(&StorageKey::new( - AccountTreeId::new(address), - u256_to_h256(key), - )); - - value != H256::zero() -} - -fn valid_eth_token_call(address: Address, msg_sender: Address) -> bool { - let is_valid_caller = msg_sender == MSG_VALUE_SIMULATOR_ADDRESS - || msg_sender == CONTRACT_DEPLOYER_ADDRESS - || msg_sender == BOOTLOADER_ADDRESS; - address == L2_ETH_TOKEN_ADDRESS && is_valid_caller -} diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/validation/params.rs b/core/lib/multivm/src/versions/vm_latest/tracers/validation/params.rs deleted file mode 100644 index 1a4ced478b6..00000000000 --- a/core/lib/multivm/src/versions/vm_latest/tracers/validation/params.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::collections::HashSet; -use zksync_types::{Address, U256}; - -#[derive(Debug, Clone)] -pub struct ValidationTracerParams { - pub user_address: Address, - pub paymaster_address: Address, - /// Slots that are trusted (i.e. the user can access them). - pub trusted_slots: HashSet<(Address, U256)>, - /// Trusted addresses (the user can access any slots on these addresses). - pub trusted_addresses: HashSet
, - /// Slots, that are trusted and the value of them is the new trusted address. - /// They are needed to work correctly with beacon proxy, where the address of the implementation is - /// stored in the beacon. - pub trusted_address_slots: HashSet<(Address, U256)>, - /// Number of computational gas that validation step is allowed to use. - pub computational_gas_limit: u32, -} diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/validation/types.rs b/core/lib/multivm/src/versions/vm_latest/tracers/validation/types.rs deleted file mode 100644 index b9d44227992..00000000000 --- a/core/lib/multivm/src/versions/vm_latest/tracers/validation/types.rs +++ /dev/null @@ -1,18 +0,0 @@ -use zksync_types::{Address, H256}; - -#[derive(Debug, Clone, Eq, PartialEq, Copy)] -#[allow(clippy::enum_variant_names)] -pub(super) enum ValidationTracerMode { - /// Should be activated when the transaction is being validated by user. - UserTxValidation, - /// Should be activated when the transaction is being validated by the paymaster. - PaymasterTxValidation, - /// Is a state when there are no restrictions on the execution. - NoValidation, -} - -#[derive(Debug, Clone, Default)] -pub(super) struct NewTrustedValidationItems { - pub(super) new_allowed_slots: Vec, - pub(super) new_trusted_addresses: Vec
, -} diff --git a/core/lib/multivm/src/versions/vm_latest/vm.rs b/core/lib/multivm/src/versions/vm_latest/vm.rs index 437815a8f82..e9787b6ac46 100644 --- a/core/lib/multivm/src/versions/vm_latest/vm.rs +++ b/core/lib/multivm/src/versions/vm_latest/vm.rs @@ -1,18 +1,20 @@ +use crate::HistoryMode; use zksync_state::{StoragePtr, WriteStorage}; use zksync_types::l2_to_l1_log::{SystemL2ToL1Log, UserL2ToL1Log}; use zksync_types::{event::extract_l2tol1logs_from_l1_messenger, Transaction}; use zksync_utils::bytecode::CompressedBytecodeInfo; use crate::vm_latest::old_vm::events::merge_events; -use crate::vm_latest::old_vm::history_recorder::{HistoryEnabled, HistoryMode}; +use crate::vm_latest::old_vm::history_recorder::HistoryEnabled; -use crate::interface::BytecodeCompressionError; use crate::interface::{ BootloaderMemory, CurrentExecutionState, L1BatchEnv, L2BlockEnv, SystemEnv, VmExecutionMode, - VmExecutionResultAndLogs, + VmExecutionResultAndLogs, VmInterfaceHistoryEnabled, }; +use crate::interface::{BytecodeCompressionError, VmInterface}; use crate::vm_latest::bootloader_state::BootloaderState; -use crate::vm_latest::tracers::traits::VmTracer; +use crate::vm_latest::tracers::dispatcher::TracerDispatcher; + use crate::vm_latest::types::internals::{new_vm_state, VmSnapshot, ZkSyncVmState}; /// Main entry point for Virtual Machine integration. @@ -21,7 +23,7 @@ use crate::vm_latest::types::internals::{new_vm_state, VmSnapshot, ZkSyncVmState pub struct Vm { pub(crate) bootloader_state: BootloaderState, // Current state and oracles of virtual machine - pub(crate) state: ZkSyncVmState, + pub(crate) state: ZkSyncVmState, pub(crate) storage: StoragePtr, pub(crate) system_env: SystemEnv, pub(crate) batch_env: L1BatchEnv, @@ -31,8 +33,10 @@ pub struct Vm { } /// Public interface for VM -impl Vm { - pub fn new(batch_env: L1BatchEnv, system_env: SystemEnv, storage: StoragePtr, _: H) -> Self { +impl VmInterface for Vm { + type TracerDispatcher = TracerDispatcher; + + fn new(batch_env: L1BatchEnv, system_env: SystemEnv, storage: StoragePtr) -> Self { let (state, bootloader_state) = new_vm_state(storage.clone(), &system_env, &batch_env); Self { bootloader_state, @@ -46,43 +50,37 @@ impl Vm { } /// Push tx into memory for the future execution - pub fn push_transaction(&mut self, tx: Transaction) { - self.push_transaction_with_compression(tx, true) - } - - /// Execute VM with default tracers. The execution mode determines whether the VM will stop and - /// how the vm will be processed. - pub fn execute(&mut self, execution_mode: VmExecutionMode) -> VmExecutionResultAndLogs { - self.inspect(vec![], execution_mode) + fn push_transaction(&mut self, tx: Transaction) { + self.push_transaction_with_compression(tx, true); } /// Execute VM with custom tracers. - pub fn inspect( + fn inspect( &mut self, - tracers: Vec>>, + tracer: Self::TracerDispatcher, execution_mode: VmExecutionMode, ) -> VmExecutionResultAndLogs { - self.inspect_inner(tracers, execution_mode) + self.inspect_inner(tracer, execution_mode) } /// Get current state of bootloader memory. - pub fn get_bootloader_memory(&self) -> BootloaderMemory { + fn get_bootloader_memory(&self) -> BootloaderMemory { self.bootloader_state.bootloader_memory() } /// Get compressed bytecodes of the last executed transaction - pub fn get_last_tx_compressed_bytecodes(&self) -> Vec { + fn get_last_tx_compressed_bytecodes(&self) -> Vec { self.bootloader_state.get_last_tx_compressed_bytecodes() } - pub fn start_new_l2_block(&mut self, l2_block_env: L2BlockEnv) { + fn start_new_l2_block(&mut self, l2_block_env: L2BlockEnv) { self.bootloader_state.start_new_l2_block(l2_block_env); } /// Get current state of virtual machine. /// This method should be used only after the batch execution. /// Otherwise it can panic. - pub fn get_current_execution_state(&self) -> CurrentExecutionState { + fn get_current_execution_state(&self) -> CurrentExecutionState { let (deduplicated_events_logs, raw_events, l1_messages) = self.state.event_sink.flatten(); let events: Vec<_> = merge_events(raw_events) .into_iter() @@ -119,23 +117,16 @@ impl Vm { } /// Execute transaction with optional bytecode compression. - pub fn execute_transaction_with_bytecode_compression( - &mut self, - tx: Transaction, - with_compression: bool, - ) -> Result { - self.inspect_transaction_with_bytecode_compression(vec![], tx, with_compression) - } /// Inspect transaction with optional bytecode compression. - pub fn inspect_transaction_with_bytecode_compression( + fn inspect_transaction_with_bytecode_compression( &mut self, - tracers: Vec>>, + tracer: Self::TracerDispatcher, tx: Transaction, with_compression: bool, ) -> Result { self.push_transaction_with_compression(tx, with_compression); - let result = self.inspect(tracers, VmExecutionMode::OneTx); + let result = self.inspect_inner(tracer, VmExecutionMode::OneTx); if self.has_unpublished_bytecodes() { Err(BytecodeCompressionError::BytecodeCompressionFailed) } else { @@ -145,14 +136,14 @@ impl Vm { } /// Methods of vm, which required some history manipullations -impl Vm { +impl VmInterfaceHistoryEnabled for Vm { /// Create snapshot of current vm state and push it into the memory - pub fn make_snapshot(&mut self) { + fn make_snapshot(&mut self) { self.make_snapshot_inner() } /// Rollback vm state to the latest snapshot and destroy the snapshot - pub fn rollback_to_the_latest_snapshot(&mut self) { + fn rollback_to_the_latest_snapshot(&mut self) { let snapshot = self .snapshots .pop() @@ -161,7 +152,7 @@ impl Vm { } /// Pop the latest snapshot from the memory and destroy it - pub fn pop_snapshot_no_rollback(&mut self) { + fn pop_snapshot_no_rollback(&mut self) { self.snapshots .pop() .expect("Snapshot should be created before rolling it back"); diff --git a/core/lib/multivm/src/versions/vm_m5/mod.rs b/core/lib/multivm/src/versions/vm_m5/mod.rs index c501398ea1d..ba0964dc96e 100644 --- a/core/lib/multivm/src/versions/vm_m5/mod.rs +++ b/core/lib/multivm/src/versions/vm_m5/mod.rs @@ -15,7 +15,7 @@ pub mod storage; pub mod test_utils; pub mod transaction_data; pub mod utils; -pub mod vm; +pub mod vm_instance; pub mod vm_with_bootloader; #[cfg(test)] @@ -24,9 +24,9 @@ mod tests; pub use errors::TxRevertReason; pub use oracle_tools::OracleTools; pub use oracles::storage::StorageOracle; -pub use vm::VmBlockResult; -pub use vm::VmExecutionResult; -pub use vm::VmInstance; +pub use vm_instance::VmBlockResult; +pub use vm_instance::VmExecutionResult; +pub use vm_instance::VmInstance; pub use zk_evm_1_3_1; pub use zksync_types::vm_trace::VmExecutionTrace; diff --git a/core/lib/multivm/src/versions/vm_m5/oracle_tools.rs b/core/lib/multivm/src/versions/vm_m5/oracle_tools.rs index 47239c435d5..4858a23adb6 100644 --- a/core/lib/multivm/src/versions/vm_m5/oracle_tools.rs +++ b/core/lib/multivm/src/versions/vm_m5/oracle_tools.rs @@ -1,5 +1,5 @@ use crate::vm_m5::memory::SimpleMemory; -use crate::vm_m5::vm::MultiVMSubversion; +use crate::vm_m5::vm_instance::MultiVMSubversion; use std::fmt::Debug; diff --git a/core/lib/multivm/src/versions/vm_m5/oracles/storage.rs b/core/lib/multivm/src/versions/vm_m5/oracles/storage.rs index 8fcdc6bcba5..5d6836df18e 100644 --- a/core/lib/multivm/src/versions/vm_m5/oracles/storage.rs +++ b/core/lib/multivm/src/versions/vm_m5/oracles/storage.rs @@ -6,7 +6,7 @@ use crate::vm_m5::storage::{Storage, StoragePtr}; use crate::vm_m5::history_recorder::{ AppDataFrameManagerWithHistory, HashMapHistoryEvent, HistoryRecorder, StorageWrapper, }; -use crate::vm_m5::vm::MultiVMSubversion; +use crate::vm_m5::vm_instance::MultiVMSubversion; use zk_evm_1_3_1::abstractions::RefundedAmounts; use zk_evm_1_3_1::zkevm_opcode_defs::system_params::INITIAL_STORAGE_WRITE_PUBDATA_BYTES; diff --git a/core/lib/multivm/src/versions/vm_m5/oracles/tracer.rs b/core/lib/multivm/src/versions/vm_m5/oracles/tracer.rs index 7964e81ea53..d8a70bdaf64 100644 --- a/core/lib/multivm/src/versions/vm_m5/oracles/tracer.rs +++ b/core/lib/multivm/src/versions/vm_m5/oracles/tracer.rs @@ -9,7 +9,7 @@ use crate::vm_m5::{ memory::SimpleMemory, storage::StoragePtr, utils::{aux_heap_page_from_base, heap_page_from_base}, - vm::{get_vm_hook_params, VM_HOOK_POSITION}, + vm_instance::{get_vm_hook_params, VM_HOOK_POSITION}, vm_with_bootloader::BOOTLOADER_HEAP_PAGE, }; // use zk_evm_1_3_1::testing::memory::SimpleMemory; diff --git a/core/lib/multivm/src/versions/vm_m5/vm.rs b/core/lib/multivm/src/versions/vm_m5/vm_instance.rs similarity index 100% rename from core/lib/multivm/src/versions/vm_m5/vm.rs rename to core/lib/multivm/src/versions/vm_m5/vm_instance.rs diff --git a/core/lib/multivm/src/versions/vm_m5/vm_with_bootloader.rs b/core/lib/multivm/src/versions/vm_m5/vm_with_bootloader.rs index 115ca93ad95..6c39d6b7904 100644 --- a/core/lib/multivm/src/versions/vm_m5/vm_with_bootloader.rs +++ b/core/lib/multivm/src/versions/vm_m5/vm_with_bootloader.rs @@ -29,7 +29,7 @@ use crate::vm_m5::{ utils::{ code_page_candidate_from_base, heap_page_from_base, BLOCK_GAS_LIMIT, INITIAL_BASE_PAGE, }, - vm::{MultiVMSubversion, ZkSyncVmState}, + vm_instance::{MultiVMSubversion, ZkSyncVmState}, OracleTools, VmInstance, }; diff --git a/core/lib/multivm/src/versions/vm_m6/mod.rs b/core/lib/multivm/src/versions/vm_m6/mod.rs index b8a6f6f051d..cc798d6b58b 100644 --- a/core/lib/multivm/src/versions/vm_m6/mod.rs +++ b/core/lib/multivm/src/versions/vm_m6/mod.rs @@ -15,7 +15,7 @@ pub mod storage; pub mod test_utils; pub mod transaction_data; pub mod utils; -pub mod vm; +pub mod vm_instance; pub mod vm_with_bootloader; #[cfg(test)] @@ -25,7 +25,7 @@ pub use errors::TxRevertReason; pub use history_recorder::{HistoryDisabled, HistoryEnabled, HistoryMode}; pub use oracle_tools::OracleTools; pub use oracles::storage::StorageOracle; -pub use vm::{VmBlockResult, VmExecutionResult, VmInstance}; +pub use vm_instance::{VmBlockResult, VmExecutionResult, VmInstance}; pub use zk_evm_1_3_1; pub use zksync_types::vm_trace::VmExecutionTrace; diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/one_tx.rs b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/one_tx.rs index f27bfb5494e..d5fbb78c909 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/one_tx.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/one_tx.rs @@ -6,7 +6,7 @@ use crate::vm_m6::{ utils::{gas_spent_on_bytecodes_and_long_messages_this_opcode, VmHook}, BootloaderTracer, ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, }, - vm::get_vm_hook_params, + vm_instance::get_vm_hook_params, }; use crate::vm_m6::oracles::tracer::{CallTracer, StorageInvocationTracer}; diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/transaction_result.rs b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/transaction_result.rs index 661305fd3d8..a3e4391af24 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/transaction_result.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/transaction_result.rs @@ -12,7 +12,7 @@ use crate::vm_m6::oracles::tracer::{ CallTracer, ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, StorageInvocationTracer, }; -use crate::vm_m6::vm::get_vm_hook_params; +use crate::vm_m6::vm_instance::get_vm_hook_params; use crate::vm_m6::{ history_recorder::HistoryMode, oracles::tracer::utils::{ diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/utils.rs b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/utils.rs index 2ab2540d125..e2f1652e9b7 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/utils.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/utils.rs @@ -1,7 +1,7 @@ use crate::vm_m6::history_recorder::HistoryMode; use crate::vm_m6::memory::SimpleMemory; use crate::vm_m6::utils::{aux_heap_page_from_base, heap_page_from_base}; -use crate::vm_m6::vm::{get_vm_hook_params, VM_HOOK_POSITION}; +use crate::vm_m6::vm_instance::{get_vm_hook_params, VM_HOOK_POSITION}; use crate::vm_m6::vm_with_bootloader::BOOTLOADER_HEAP_PAGE; use zk_evm_1_3_1::aux_structures::MemoryPage; diff --git a/core/lib/multivm/src/versions/vm_m6/vm.rs b/core/lib/multivm/src/versions/vm_m6/vm_instance.rs similarity index 100% rename from core/lib/multivm/src/versions/vm_m6/vm.rs rename to core/lib/multivm/src/versions/vm_m6/vm_instance.rs diff --git a/core/lib/multivm/src/versions/vm_m6/vm_with_bootloader.rs b/core/lib/multivm/src/versions/vm_m6/vm_with_bootloader.rs index 35b78e92d1c..17b8e8957f1 100644 --- a/core/lib/multivm/src/versions/vm_m6/vm_with_bootloader.rs +++ b/core/lib/multivm/src/versions/vm_m6/vm_with_bootloader.rs @@ -32,7 +32,7 @@ use crate::vm_m6::{ utils::{ code_page_candidate_from_base, heap_page_from_base, BLOCK_GAS_LIMIT, INITIAL_BASE_PAGE, }, - vm::{MultiVMSubversion, ZkSyncVmState}, + vm_instance::{MultiVMSubversion, ZkSyncVmState}, OracleTools, VmInstance, }; diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/bytecode.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/bytecode.rs index 2092c03e06e..4b7e529fc5b 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/bytecode.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/bytecode.rs @@ -1,11 +1,13 @@ use itertools::Itertools; +use crate::interface::VmInterface; +use crate::HistoryMode; use zksync_state::{StoragePtr, WriteStorage}; use zksync_types::U256; use zksync_utils::bytecode::{compress_bytecode, hash_bytecode, CompressedBytecodeInfo}; use zksync_utils::bytes_to_be_words; -use crate::vm_refunds_enhancement::{HistoryMode, Vm}; +use crate::vm_refunds_enhancement::Vm; impl Vm { /// Checks the last transaction has successfully published compressed bytecodes and returns `true` if there is at least one is still unknown. diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/execution.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/execution.rs index 2691b5395d7..9e55180d66f 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/execution.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/execution.rs @@ -1,22 +1,20 @@ +use crate::HistoryMode; use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::WriteStorage; +use crate::interface::tracer::{TracerExecutionStatus, VmExecutionStopReason}; use crate::interface::{VmExecutionMode, VmExecutionResultAndLogs}; -use crate::vm_refunds_enhancement::old_vm::{ - history_recorder::HistoryMode, - utils::{vm_may_have_ended_inner, VmExecutionResult}, -}; +use crate::vm_refunds_enhancement::old_vm::utils::{vm_may_have_ended_inner, VmExecutionResult}; +use crate::vm_refunds_enhancement::tracers::dispatcher::TracerDispatcher; use crate::vm_refunds_enhancement::tracers::{ - traits::{TracerExecutionStatus, VmTracer}, - DefaultExecutionTracer, RefundsTracer, + traits::VmTracer, DefaultExecutionTracer, RefundsTracer, }; use crate::vm_refunds_enhancement::vm::Vm; -use crate::vm_refunds_enhancement::VmExecutionStopReason; impl Vm { pub(crate) fn inspect_inner( &mut self, - tracers: Vec>>, + dispatcher: TracerDispatcher, execution_mode: VmExecutionMode, ) -> VmExecutionResultAndLogs { let mut enable_refund_tracer = false; @@ -26,7 +24,7 @@ impl Vm { enable_refund_tracer = true; } let (_, result) = - self.inspect_and_collect_results(tracers, execution_mode, enable_refund_tracer); + self.inspect_and_collect_results(dispatcher, execution_mode, enable_refund_tracer); result } @@ -34,19 +32,20 @@ impl Vm { /// Collect the result from the default tracers. fn inspect_and_collect_results( &mut self, - tracers: Vec>>, + dispatcher: TracerDispatcher, execution_mode: VmExecutionMode, with_refund_tracer: bool, ) -> (VmExecutionStopReason, VmExecutionResultAndLogs) { let refund_tracers = with_refund_tracer.then_some(RefundsTracer::new(self.batch_env.clone())); - let mut tx_tracer: DefaultExecutionTracer = DefaultExecutionTracer::new( - self.system_env.default_validation_computational_gas_limit, - execution_mode, - tracers, - self.storage.clone(), - refund_tracers, - ); + let mut tx_tracer: DefaultExecutionTracer = + DefaultExecutionTracer::new( + self.system_env.default_validation_computational_gas_limit, + execution_mode, + dispatcher, + self.storage.clone(), + refund_tracers, + ); let timestamp_initial = Timestamp(self.state.local_state.timestamp); let cycles_initial = self.state.local_state.monotonic_cycle_counter; @@ -91,7 +90,7 @@ impl Vm { /// Execute vm with given tracers until the stop reason is reached. fn execute_with_default_tracer( &mut self, - tracer: &mut DefaultExecutionTracer, + tracer: &mut DefaultExecutionTracer, ) -> VmExecutionStopReason { tracer.initialize_tracer(&mut self.state); let result = loop { diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/gas.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/gas.rs index 7f85ba7dda3..cce9bfad699 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/gas.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/gas.rs @@ -1,6 +1,6 @@ +use crate::HistoryMode; use zksync_state::WriteStorage; -use crate::vm_refunds_enhancement::old_vm::history_recorder::HistoryMode; use crate::vm_refunds_enhancement::tracers::DefaultExecutionTracer; use crate::vm_refunds_enhancement::vm::Vm; @@ -19,7 +19,7 @@ impl Vm { pub(crate) fn calculate_computational_gas_used( &self, - tracer: &DefaultExecutionTracer, + tracer: &DefaultExecutionTracer, gas_remaining_before: u32, spent_pubdata_counter_before: u32, ) -> u32 { diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/logs.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/logs.rs index 327a054afc0..b8e8652f301 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/logs.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/logs.rs @@ -1,12 +1,12 @@ use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::WriteStorage; +use crate::HistoryMode; use zksync_types::l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}; use zksync_types::VmEvent; use crate::interface::types::outputs::VmExecutionLogs; use crate::vm_refunds_enhancement::old_vm::events::merge_events; -use crate::vm_refunds_enhancement::old_vm::history_recorder::HistoryMode; use crate::vm_refunds_enhancement::old_vm::utils::precompile_calls_count_after_timestamp; use crate::vm_refunds_enhancement::vm::Vm; diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/snapshots.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/snapshots.rs index d7cf0fbb85e..972d50e5d76 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/snapshots.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/snapshots.rs @@ -2,13 +2,12 @@ use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics use std::time::Duration; +use crate::vm_latest::HistoryEnabled; use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::WriteStorage; use crate::vm_refunds_enhancement::{ - old_vm::{history_recorder::HistoryEnabled, oracles::OracleWithHistory}, - types::internals::VmSnapshot, - vm::Vm, + old_vm::oracles::OracleWithHistory, types::internals::VmSnapshot, vm::Vm, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelSet, EncodeLabelValue)] diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/statistics.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/statistics.rs index a113a09be70..ce65cab2d49 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/statistics.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/statistics.rs @@ -1,10 +1,10 @@ use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::WriteStorage; +use crate::HistoryMode; use zksync_types::U256; use crate::interface::{VmExecutionStatistics, VmMemoryMetrics}; -use crate::vm_refunds_enhancement::old_vm::history_recorder::HistoryMode; use crate::vm_refunds_enhancement::tracers::DefaultExecutionTracer; use crate::vm_refunds_enhancement::vm::Vm; @@ -18,7 +18,7 @@ impl Vm { &self, timestamp_initial: Timestamp, cycles_initial: u32, - tracer: &DefaultExecutionTracer, + tracer: &DefaultExecutionTracer, gas_remaining_before: u32, gas_remaining_after: u32, spent_pubdata_counter_before: u32, diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/tx.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/tx.rs index 2d748040b47..d6fd4858870 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/tx.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/tx.rs @@ -7,9 +7,9 @@ use zksync_state::WriteStorage; use zksync_types::l1::is_l1_tx_type; use zksync_types::Transaction; -use crate::vm_refunds_enhancement::old_vm::history_recorder::HistoryMode; use crate::vm_refunds_enhancement::types::internals::TransactionData; use crate::vm_refunds_enhancement::vm::Vm; +use crate::HistoryMode; impl Vm { pub(crate) fn push_raw_transaction( diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/mod.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/mod.rs index 8cfe50ffbf3..146ea5e659f 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/mod.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/mod.rs @@ -5,12 +5,7 @@ pub use old_vm::{ pub use oracles::storage::StorageOracle; -pub use tracers::{ - call::CallTracer, - traits::{BoxedTracer, DynTracer, TracerExecutionStatus, TracerExecutionStopReason, VmTracer}, - utils::VmExecutionStopReason, - StorageInvocations, ValidationError, ValidationTracer, ValidationTracerParams, -}; +pub use tracers::traits::{TracerPointer, VmTracer}; pub use utils::transaction_encoding::TransactionVmExt; @@ -23,7 +18,7 @@ mod bootloader_state; mod implementation; mod old_vm; mod oracles; -mod tracers; +pub(crate) mod tracers; mod types; mod vm; diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/default_tracers.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/default_tracers.rs index f06a42af286..51fbf06d855 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/default_tracers.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/default_tracers.rs @@ -1,6 +1,11 @@ use std::fmt::{Debug, Formatter}; +use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; +use crate::interface::tracer::{ + TracerExecutionStatus, TracerExecutionStopReason, VmExecutionStopReason, +}; use crate::interface::{Halt, VmExecutionMode}; +use crate::vm_refunds_enhancement::VmTracer; use zk_evm_1_3_3::{ tracing::{ AfterDecodingData, AfterExecutionData, BeforeExecutionData, Tracer, VmLocalStateData, @@ -17,19 +22,16 @@ use crate::vm_refunds_enhancement::bootloader_state::BootloaderState; use crate::vm_refunds_enhancement::constants::BOOTLOADER_HEAP_PAGE; use crate::vm_refunds_enhancement::old_vm::history_recorder::HistoryMode; use crate::vm_refunds_enhancement::old_vm::memory::SimpleMemory; -use crate::vm_refunds_enhancement::tracers::traits::{ - DynTracer, TracerExecutionStatus, TracerExecutionStopReason, VmTracer, -}; +use crate::vm_refunds_enhancement::tracers::dispatcher::TracerDispatcher; use crate::vm_refunds_enhancement::tracers::utils::{ computational_gas_price, gas_spent_on_bytecodes_and_long_messages_this_opcode, print_debug_if_needed, VmHook, }; use crate::vm_refunds_enhancement::tracers::{RefundsTracer, ResultTracer}; use crate::vm_refunds_enhancement::types::internals::ZkSyncVmState; -use crate::vm_refunds_enhancement::VmExecutionStopReason; /// Default tracer for the VM. It manages the other tracers execution and stop the vm when needed. -pub(crate) struct DefaultExecutionTracer { +pub(crate) struct DefaultExecutionTracer { tx_has_been_processed: bool, execution_mode: VmExecutionMode, @@ -45,18 +47,18 @@ pub(crate) struct DefaultExecutionTracer { // ensures static dispatch, enhancing performance by avoiding dynamic dispatch overhead. // Additionally, being an internal tracer, it saves the results directly to VmResultAndLogs. pub(crate) refund_tracer: Option, - pub(crate) custom_tracers: Vec>>, + pub(crate) dispatcher: TracerDispatcher, ret_from_the_bootloader: Option, storage: StoragePtr, } -impl Debug for DefaultExecutionTracer { +impl Debug for DefaultExecutionTracer { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("DefaultExecutionTracer").finish() } } -impl Tracer for DefaultExecutionTracer { +impl Tracer for DefaultExecutionTracer { const CALL_BEFORE_DECODING: bool = false; const CALL_AFTER_DECODING: bool = true; const CALL_BEFORE_EXECUTION: bool = true; @@ -76,7 +78,7 @@ impl Tracer for DefaultExecutionTracer { data: AfterDecodingData, memory: &Self::SupportedMemory, ) { - >::after_decoding( + >>::after_decoding( &mut self.result_tracer, state, data, @@ -84,12 +86,15 @@ impl Tracer for DefaultExecutionTracer { ); if let Some(refund_tracer) = &mut self.refund_tracer { - >::after_decoding(refund_tracer, state, data, memory); + >>::after_decoding( + refund_tracer, + state, + data, + memory, + ); } - for tracer in self.custom_tracers.iter_mut() { - tracer.after_decoding(state, data, memory) - } + self.dispatcher.after_decoding(state, data, memory); } fn before_execution( @@ -123,9 +128,8 @@ impl Tracer for DefaultExecutionTracer { if let Some(refund_tracer) = &mut self.refund_tracer { refund_tracer.before_execution(state, data, memory, self.storage.clone()); } - for tracer in self.custom_tracers.iter_mut() { - tracer.before_execution(state, data, memory, self.storage.clone()); - } + self.dispatcher + .before_execution(state, data, memory, self.storage.clone()); } fn after_execution( @@ -153,9 +157,8 @@ impl Tracer for DefaultExecutionTracer { if let Some(refund_tracer) = &mut self.refund_tracer { refund_tracer.after_execution(state, data, memory, self.storage.clone()) } - for tracer in self.custom_tracers.iter_mut() { - tracer.after_execution(state, data, memory, self.storage.clone()); - } + self.dispatcher + .after_execution(state, data, memory, self.storage.clone()); } } @@ -163,7 +166,7 @@ impl DefaultExecutionTracer { pub(crate) fn new( computational_gas_limit: u32, execution_mode: VmExecutionMode, - custom_tracers: Vec>>, + dispatcher: TracerDispatcher, storage: StoragePtr, refund_tracer: Option, ) -> Self { @@ -177,7 +180,7 @@ impl DefaultExecutionTracer { final_batch_info_requested: false, result_tracer: ResultTracer::new(execution_mode), refund_tracer, - custom_tracers, + dispatcher, ret_from_the_bootloader: None, storage, } @@ -230,7 +233,10 @@ impl DefaultExecutionTracer { } } -impl DynTracer for DefaultExecutionTracer {} +impl DynTracer> + for DefaultExecutionTracer +{ +} impl VmTracer for DefaultExecutionTracer { fn initialize_tracer(&mut self, state: &mut ZkSyncVmState) { @@ -238,9 +244,7 @@ impl VmTracer for DefaultExecutionTracer< if let Some(refund_tracer) = &mut self.refund_tracer { refund_tracer.initialize_tracer(state); } - for processor in self.custom_tracers.iter_mut() { - processor.initialize_tracer(state); - } + self.dispatcher.initialize_tracer(state); } fn finish_cycle( @@ -258,11 +262,10 @@ impl VmTracer for DefaultExecutionTracer< .finish_cycle(state, bootloader_state) .stricter(&result); } - for processor in self.custom_tracers.iter_mut() { - result = processor - .finish_cycle(state, bootloader_state) - .stricter(&result); - } + result = self + .dispatcher + .finish_cycle(state, bootloader_state) + .stricter(&result); result.stricter(&self.should_stop_execution()) } @@ -278,9 +281,8 @@ impl VmTracer for DefaultExecutionTracer< if let Some(refund_tracer) = &mut self.refund_tracer { refund_tracer.after_vm_execution(state, bootloader_state, stop_reason.clone()); } - for processor in self.custom_tracers.iter_mut() { - processor.after_vm_execution(state, bootloader_state, stop_reason.clone()); - } + self.dispatcher + .after_vm_execution(state, bootloader_state, stop_reason.clone()); } } diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/dispatcher.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/dispatcher.rs new file mode 100644 index 00000000000..ec249eaa484 --- /dev/null +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/dispatcher.rs @@ -0,0 +1,115 @@ +use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; +use crate::interface::tracer::{TracerExecutionStatus, VmExecutionStopReason}; +use crate::vm_refunds_enhancement::{ + BootloaderState, HistoryMode, SimpleMemory, TracerPointer, VmTracer, ZkSyncVmState, +}; +use zk_evm_1_3_3::tracing::{ + AfterDecodingData, AfterExecutionData, BeforeExecutionData, VmLocalStateData, +}; +use zksync_state::{StoragePtr, WriteStorage}; + +/// Tracer dispatcher is a tracer that can dispatch calls to multiple tracers. +pub struct TracerDispatcher { + tracers: Vec>, +} + +impl From> for TracerDispatcher { + fn from(value: TracerPointer) -> Self { + Self { + tracers: vec![value], + } + } +} + +impl From>> for TracerDispatcher { + fn from(value: Vec>) -> Self { + Self { tracers: value } + } +} + +impl Default for TracerDispatcher { + fn default() -> Self { + Self { tracers: vec![] } + } +} + +impl DynTracer> for TracerDispatcher { + #[inline(always)] + fn before_decoding(&mut self, _state: VmLocalStateData<'_>, _memory: &SimpleMemory) { + for tracer in self.tracers.iter_mut() { + tracer.before_decoding(_state, _memory); + } + } + + #[inline(always)] + fn after_decoding( + &mut self, + _state: VmLocalStateData<'_>, + _data: AfterDecodingData, + _memory: &SimpleMemory, + ) { + for tracer in self.tracers.iter_mut() { + tracer.after_decoding(_state, _data, _memory); + } + } + + #[inline(always)] + fn before_execution( + &mut self, + _state: VmLocalStateData<'_>, + _data: BeforeExecutionData, + _memory: &SimpleMemory, + _storage: StoragePtr, + ) { + for tracer in self.tracers.iter_mut() { + tracer.before_execution(_state, _data, _memory, _storage.clone()); + } + } + + #[inline(always)] + fn after_execution( + &mut self, + _state: VmLocalStateData<'_>, + _data: AfterExecutionData, + _memory: &SimpleMemory, + _storage: StoragePtr, + ) { + for tracer in self.tracers.iter_mut() { + tracer.after_execution(_state, _data, _memory, _storage.clone()); + } + } +} + +impl VmTracer for TracerDispatcher { + fn initialize_tracer(&mut self, _state: &mut ZkSyncVmState) { + for tracer in self.tracers.iter_mut() { + tracer.initialize_tracer(_state); + } + } + + /// Run after each vm execution cycle + #[inline(always)] + fn finish_cycle( + &mut self, + _state: &mut ZkSyncVmState, + _bootloader_state: &mut BootloaderState, + ) -> TracerExecutionStatus { + let mut result = TracerExecutionStatus::Continue; + for tracer in self.tracers.iter_mut() { + result = result.stricter(&tracer.finish_cycle(_state, _bootloader_state)); + } + result + } + + /// Run after the vm execution + fn after_vm_execution( + &mut self, + _state: &mut ZkSyncVmState, + _bootloader_state: &BootloaderState, + _stop_reason: VmExecutionStopReason, + ) { + for tracer in self.tracers.iter_mut() { + tracer.after_vm_execution(_state, _bootloader_state, _stop_reason.clone()); + } + } +} diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/mod.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/mod.rs index 11fefedc85a..af17b77237e 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/mod.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/mod.rs @@ -1,15 +1,10 @@ pub(crate) use default_tracers::DefaultExecutionTracer; pub(crate) use refunds::RefundsTracer; pub(crate) use result_tracer::ResultTracer; -pub use storage_invocations::StorageInvocations; -pub use validation::{ValidationError, ValidationTracer, ValidationTracerParams}; pub(crate) mod default_tracers; +pub mod dispatcher; pub(crate) mod refunds; pub(crate) mod result_tracer; - -pub(crate) mod call; -pub(crate) mod storage_invocations; pub(crate) mod traits; pub(crate) mod utils; -pub(crate) mod validation; diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/refunds.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/refunds.rs index b451e720967..5256561b5eb 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/refunds.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/refunds.rs @@ -1,6 +1,5 @@ use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; -use crate::interface::{L1BatchEnv, Refunds}; use zk_evm_1_3_3::{ aux_structures::Timestamp, tracing::{BeforeExecutionData, VmLocalStateData}, @@ -16,22 +15,25 @@ use zksync_types::{ use zksync_utils::bytecode::bytecode_len_in_bytes; use zksync_utils::{ceil_div_u256, u256_to_h256}; +use crate::interface::{ + dyn_tracers::vm_1_3_3::DynTracer, tracer::TracerExecutionStatus, L1BatchEnv, Refunds, +}; use crate::vm_refunds_enhancement::constants::{ BOOTLOADER_HEAP_PAGE, OPERATOR_REFUNDS_OFFSET, TX_GAS_LIMIT_OFFSET, }; -use crate::vm_refunds_enhancement::old_vm::{ - events::merge_events, history_recorder::HistoryMode, memory::SimpleMemory, - utils::eth_price_per_pubdata_byte, -}; -use crate::vm_refunds_enhancement::bootloader_state::BootloaderState; -use crate::vm_refunds_enhancement::tracers::utils::gas_spent_on_bytecodes_and_long_messages_this_opcode; -use crate::vm_refunds_enhancement::tracers::{ - traits::{DynTracer, VmTracer}, - utils::{get_vm_hook_params, VmHook}, +use crate::vm_refunds_enhancement::{ + bootloader_state::BootloaderState, + old_vm::{ + events::merge_events, history_recorder::HistoryMode, memory::SimpleMemory, + utils::eth_price_per_pubdata_byte, + }, + tracers::{ + traits::VmTracer, + utils::{gas_spent_on_bytecodes_and_long_messages_this_opcode, get_vm_hook_params, VmHook}, + }, + types::internals::ZkSyncVmState, }; -use crate::vm_refunds_enhancement::types::internals::ZkSyncVmState; -use crate::vm_refunds_enhancement::TracerExecutionStatus; /// Tracer responsible for collecting information about refunds. #[derive(Debug, Clone)] @@ -150,7 +152,7 @@ impl RefundsTracer { } } -impl DynTracer for RefundsTracer { +impl DynTracer> for RefundsTracer { fn before_execution( &mut self, state: VmLocalStateData<'_>, diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/result_tracer.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/result_tracer.rs index da70d06418a..c0a8e5d6cc0 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/result_tracer.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/result_tracer.rs @@ -5,6 +5,8 @@ use zk_evm_1_3_3::{ }; use zksync_state::{StoragePtr, WriteStorage}; +use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; +use crate::interface::tracer::{TracerExecutionStopReason, VmExecutionStopReason}; use crate::interface::{ExecutionResult, Halt, TxRevertReason, VmExecutionMode, VmRevertReason}; use zksync_types::U256; @@ -14,15 +16,11 @@ use crate::vm_refunds_enhancement::old_vm::{ memory::SimpleMemory, utils::{vm_may_have_ended_inner, VmExecutionResult}, }; -use crate::vm_refunds_enhancement::tracers::{ - traits::{DynTracer, VmTracer}, - utils::{get_vm_hook_params, read_pointer, VmHook}, -}; +use crate::vm_refunds_enhancement::tracers::utils::{get_vm_hook_params, read_pointer, VmHook}; use crate::vm_refunds_enhancement::constants::{BOOTLOADER_HEAP_PAGE, RESULT_SUCCESS_FIRST_SLOT}; -use crate::vm_refunds_enhancement::tracers::traits::TracerExecutionStopReason; use crate::vm_refunds_enhancement::types::internals::ZkSyncVmState; -use crate::vm_refunds_enhancement::VmExecutionStopReason; +use crate::vm_refunds_enhancement::VmTracer; #[derive(Debug, Clone)] enum Result { @@ -56,7 +54,7 @@ fn current_frame_is_bootloader(local_state: &VmLocalState) -> bool { local_state.callstack.inner.len() == 1 } -impl DynTracer for ResultTracer { +impl DynTracer> for ResultTracer { fn after_decoding( &mut self, state: VmLocalStateData<'_>, diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/storage_invocations.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/storage_invocations.rs deleted file mode 100644 index 95d496c6a86..00000000000 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/storage_invocations.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::interface::Halt; -use crate::vm_refunds_enhancement::bootloader_state::BootloaderState; -use crate::vm_refunds_enhancement::old_vm::history_recorder::HistoryMode; -use crate::vm_refunds_enhancement::tracers::traits::{ - DynTracer, TracerExecutionStatus, TracerExecutionStopReason, VmTracer, -}; -use crate::vm_refunds_enhancement::types::internals::ZkSyncVmState; -use zksync_state::WriteStorage; - -#[derive(Debug, Default, Clone)] -pub struct StorageInvocations { - pub limit: usize, -} - -impl StorageInvocations { - pub fn new(limit: usize) -> Self { - Self { limit } - } -} - -/// Tracer responsible for calculating the number of storage invocations and -/// stopping the VM execution if the limit is reached. -impl DynTracer for StorageInvocations {} - -impl VmTracer for StorageInvocations { - fn finish_cycle( - &mut self, - state: &mut ZkSyncVmState, - _bootloader_state: &mut BootloaderState, - ) -> TracerExecutionStatus { - let current = state - .storage - .storage - .get_ptr() - .borrow() - .missed_storage_invocations(); - - if current >= self.limit { - return TracerExecutionStatus::Stop(TracerExecutionStopReason::Abort( - Halt::TracerCustom("Storage invocations limit reached".to_string()), - )); - } - TracerExecutionStatus::Continue - } -} diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/traits.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/traits.rs index af79da8a20c..13b295b9fe9 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/traits.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/traits.rs @@ -1,17 +1,16 @@ -use crate::interface::Halt; -use zk_evm_1_3_3::tracing::{ - AfterDecodingData, AfterExecutionData, BeforeExecutionData, VmLocalStateData, -}; -use zksync_state::{StoragePtr, WriteStorage}; +use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; +use crate::interface::tracer::{TracerExecutionStatus, VmExecutionStopReason}; +use zksync_state::WriteStorage; use crate::vm_refunds_enhancement::bootloader_state::BootloaderState; use crate::vm_refunds_enhancement::old_vm::history_recorder::HistoryMode; use crate::vm_refunds_enhancement::old_vm::memory::SimpleMemory; use crate::vm_refunds_enhancement::types::internals::ZkSyncVmState; -use crate::vm_refunds_enhancement::VmExecutionStopReason; + +pub type TracerPointer = Box>; /// Run tracer for collecting data during the vm execution cycles -pub trait VmTracer: DynTracer { +pub trait VmTracer: DynTracer> { /// Initialize the tracer before the vm execution fn initialize_tracer(&mut self, _state: &mut ZkSyncVmState) {} /// Run after each vm execution cycle @@ -32,72 +31,12 @@ pub trait VmTracer: DynTracer { } } -/// Version of zk_evm_1_3_3::Tracer suitable for dynamic dispatch. -pub trait DynTracer { - fn before_decoding(&mut self, _state: VmLocalStateData<'_>, _memory: &SimpleMemory) {} - fn after_decoding( - &mut self, - _state: VmLocalStateData<'_>, - _data: AfterDecodingData, - _memory: &SimpleMemory, - ) { - } - fn before_execution( - &mut self, - _state: VmLocalStateData<'_>, - _data: BeforeExecutionData, - _memory: &SimpleMemory, - _storage: StoragePtr, - ) { - } - fn after_execution( - &mut self, - _state: VmLocalStateData<'_>, - _data: AfterExecutionData, - _memory: &SimpleMemory, - _storage: StoragePtr, - ) { - } +pub trait ToTracerPointer { + fn into_tracer_pointer(self) -> TracerPointer; } -pub trait BoxedTracer { - fn into_boxed(self) -> Box>; -} - -impl + 'static> BoxedTracer for T { - fn into_boxed(self) -> Box> { +impl + 'static> ToTracerPointer for T { + fn into_tracer_pointer(self) -> TracerPointer { Box::new(self) } } - -#[derive(Debug, Clone, PartialEq)] -pub enum TracerExecutionStopReason { - Finish, - Abort(Halt), -} - -#[derive(Debug, Clone, PartialEq)] -pub enum TracerExecutionStatus { - Continue, - Stop(TracerExecutionStopReason), -} - -impl TracerExecutionStatus { - /// Chose the stricter ExecutionStatus - /// If both statuses are Continue, then the result is Continue - /// If one of the statuses is Abort, then the result is Abort - /// If one of the statuses is Finish, then the result is Finish - pub fn stricter(&self, other: &Self) -> Self { - match (self, other) { - (Self::Continue, Self::Continue) => Self::Continue, - (Self::Stop(TracerExecutionStopReason::Abort(reason)), _) - | (_, Self::Stop(TracerExecutionStopReason::Abort(reason))) => { - Self::Stop(TracerExecutionStopReason::Abort(reason.clone())) - } - (Self::Stop(TracerExecutionStopReason::Finish), _) - | (_, Self::Stop(TracerExecutionStopReason::Finish)) => { - Self::Stop(TracerExecutionStopReason::Finish) - } - } - } -} diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/utils.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/utils.rs index d561a522733..654c7300e4a 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/utils.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/utils.rs @@ -1,8 +1,9 @@ -use zk_evm_1_3_3::aux_structures::MemoryPage; -use zk_evm_1_3_3::zkevm_opcode_defs::{FarCallABI, FarCallForwardPageType}; use zk_evm_1_3_3::{ + aux_structures::MemoryPage, tracing::{BeforeExecutionData, VmLocalStateData}, - zkevm_opcode_defs::{FatPointer, LogOpcode, Opcode, UMAOpcode}, + zkevm_opcode_defs::{ + FarCallABI, FarCallForwardPageType, FatPointer, LogOpcode, Opcode, UMAOpcode, + }, }; use zksync_system_constants::{ @@ -15,10 +16,11 @@ use zksync_utils::u256_to_h256; use crate::vm_refunds_enhancement::constants::{ BOOTLOADER_HEAP_PAGE, VM_HOOK_PARAMS_COUNT, VM_HOOK_PARAMS_START_POSITION, VM_HOOK_POSITION, }; -use crate::vm_refunds_enhancement::old_vm::history_recorder::HistoryMode; -use crate::vm_refunds_enhancement::old_vm::memory::SimpleMemory; -use crate::vm_refunds_enhancement::old_vm::utils::{aux_heap_page_from_base, heap_page_from_base}; -use crate::vm_refunds_enhancement::tracers::traits::TracerExecutionStopReason; +use crate::vm_refunds_enhancement::old_vm::{ + history_recorder::HistoryMode, + memory::SimpleMemory, + utils::{aux_heap_page_from_base, heap_page_from_base}, +}; #[derive(Clone, Debug, Copy)] pub(crate) enum VmHook { @@ -217,9 +219,3 @@ pub(crate) fn get_vm_hook_params(memory: &SimpleMemory) -> Ve VM_HOOK_PARAMS_START_POSITION..VM_HOOK_PARAMS_START_POSITION + VM_HOOK_PARAMS_COUNT, ) } - -#[derive(Debug, Clone, PartialEq)] -pub enum VmExecutionStopReason { - VmFinished, - TracerRequestedStop(TracerExecutionStopReason), -} diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/validation/error.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/validation/error.rs deleted file mode 100644 index 4b9741ddaa5..00000000000 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/validation/error.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::interface::Halt; -use std::fmt::Display; -use zksync_types::vm_trace::ViolatedValidationRule; - -#[derive(Debug, Clone)] -pub enum ValidationError { - FailedTx(Halt), - ViolatedRule(ViolatedValidationRule), -} - -impl Display for ValidationError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::FailedTx(revert_reason) => { - write!(f, "Validation revert: {}", revert_reason) - } - Self::ViolatedRule(rule) => { - write!(f, "Violated validation rules: {}", rule) - } - } - } -} diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/validation/mod.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/validation/mod.rs deleted file mode 100644 index 187d61ba0ed..00000000000 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/validation/mod.rs +++ /dev/null @@ -1,409 +0,0 @@ -mod error; -mod params; -mod types; - -use std::sync::Arc; -use std::{collections::HashSet, marker::PhantomData}; - -use once_cell::sync::OnceCell; -use zk_evm_1_3_3::{ - tracing::{BeforeExecutionData, VmLocalStateData}, - zkevm_opcode_defs::{ContextOpcode, FarCallABI, LogOpcode, Opcode}, -}; - -use zksync_state::{StoragePtr, WriteStorage}; -use zksync_system_constants::{ - ACCOUNT_CODE_STORAGE_ADDRESS, BOOTLOADER_ADDRESS, CONTRACT_DEPLOYER_ADDRESS, - KECCAK256_PRECOMPILE_ADDRESS, L2_ETH_TOKEN_ADDRESS, MSG_VALUE_SIMULATOR_ADDRESS, - SYSTEM_CONTEXT_ADDRESS, -}; - -use zksync_types::{ - get_code_key, vm_trace::ViolatedValidationRule, web3::signing::keccak256, AccountTreeId, - Address, StorageKey, H256, U256, -}; -use zksync_utils::{ - be_bytes_to_safe_address, h256_to_account_address, u256_to_account_address, u256_to_h256, -}; - -use crate::vm_refunds_enhancement::old_vm::history_recorder::HistoryMode; -use crate::vm_refunds_enhancement::old_vm::memory::SimpleMemory; -use crate::vm_refunds_enhancement::tracers::traits::{ - DynTracer, TracerExecutionStatus, TracerExecutionStopReason, VmTracer, -}; -use crate::vm_refunds_enhancement::tracers::utils::{ - computational_gas_price, get_calldata_page_via_abi, print_debug_if_needed, VmHook, -}; - -pub use error::ValidationError; -pub use params::ValidationTracerParams; - -use crate::interface::Halt; -use types::NewTrustedValidationItems; -use types::ValidationTracerMode; - -use crate::vm_refunds_enhancement::{BootloaderState, ZkSyncVmState}; - -/// Tracer that is used to ensure that the validation adheres to all the rules -/// to prevent DDoS attacks on the server. -#[derive(Debug, Clone)] -pub struct ValidationTracer { - validation_mode: ValidationTracerMode, - auxilary_allowed_slots: HashSet, - - user_address: Address, - #[allow(dead_code)] - paymaster_address: Address, - should_stop_execution: bool, - trusted_slots: HashSet<(Address, U256)>, - trusted_addresses: HashSet
, - trusted_address_slots: HashSet<(Address, U256)>, - computational_gas_used: u32, - computational_gas_limit: u32, - pub result: Arc>, - _marker: PhantomData H>, -} - -type ValidationRoundResult = Result; - -impl ValidationTracer { - pub fn new(params: ValidationTracerParams) -> (Self, Arc>) { - let result = Arc::new(OnceCell::new()); - ( - Self { - validation_mode: ValidationTracerMode::NoValidation, - auxilary_allowed_slots: Default::default(), - - should_stop_execution: false, - user_address: params.user_address, - paymaster_address: params.paymaster_address, - trusted_slots: params.trusted_slots, - trusted_addresses: params.trusted_addresses, - trusted_address_slots: params.trusted_address_slots, - computational_gas_used: 0, - computational_gas_limit: params.computational_gas_limit, - result: result.clone(), - _marker: Default::default(), - }, - result, - ) - } - - fn process_validation_round_result(&mut self, result: ValidationRoundResult) { - match result { - Ok(NewTrustedValidationItems { - new_allowed_slots, - new_trusted_addresses, - }) => { - self.auxilary_allowed_slots.extend(new_allowed_slots); - self.trusted_addresses.extend(new_trusted_addresses); - } - Err(err) => { - if self.result.get().is_some() { - tracing::trace!("Validation error is already set, skipping"); - return; - } - self.result.set(err).expect("Result should be empty"); - } - } - } - - // Checks whether such storage access is acceptable. - fn is_allowed_storage_read( - &self, - storage: StoragePtr, - address: Address, - key: U256, - msg_sender: Address, - ) -> bool { - // If there are no restrictions, all storage reads are valid. - // We also don't support the paymaster validation for now. - if matches!( - self.validation_mode, - ValidationTracerMode::NoValidation | ValidationTracerMode::PaymasterTxValidation - ) { - return true; - } - - // The pair of MSG_VALUE_SIMULATOR_ADDRESS & L2_ETH_TOKEN_ADDRESS simulates the behavior of transfering ETH - // that is safe for the DDoS protection rules. - if valid_eth_token_call(address, msg_sender) { - return true; - } - - if self.trusted_slots.contains(&(address, key)) - || self.trusted_addresses.contains(&address) - || self.trusted_address_slots.contains(&(address, key)) - { - return true; - } - - if touches_allowed_context(address, key) { - return true; - } - - // The user is allowed to touch its own slots or slots semantically related to him. - let valid_users_slot = address == self.user_address - || u256_to_account_address(&key) == self.user_address - || self.auxilary_allowed_slots.contains(&u256_to_h256(key)); - if valid_users_slot { - return true; - } - - if is_constant_code_hash(address, key, storage) { - return true; - } - - false - } - - // Used to remember user-related fields (its balance/allowance/etc). - // Note that it assumes that the length of the calldata is 64 bytes. - fn slot_to_add_from_keccak_call( - &self, - calldata: &[u8], - validated_address: Address, - ) -> Option { - assert_eq!(calldata.len(), 64); - - let (potential_address_bytes, potential_position_bytes) = calldata.split_at(32); - let potential_address = be_bytes_to_safe_address(potential_address_bytes); - - // If the validation_address is equal to the potential_address, - // then it is a request that could be used for mapping of kind mapping(address => ...). - // - // If the potential_position_bytes were already allowed before, then this keccak might be used - // for ERC-20 allowance or any other of mapping(address => mapping(...)) - if potential_address == Some(validated_address) - || self - .auxilary_allowed_slots - .contains(&H256::from_slice(potential_position_bytes)) - { - // This is request that could be used for mapping of kind mapping(address => ...) - - // We could theoretically wait for the slot number to be returned by the - // keccak256 precompile itself, but this would complicate the code even further - // so let's calculate it here. - let slot = keccak256(calldata); - - // Adding this slot to the allowed ones - Some(H256(slot)) - } else { - None - } - } - - pub fn params(&self) -> ValidationTracerParams { - ValidationTracerParams { - user_address: self.user_address, - paymaster_address: self.paymaster_address, - trusted_slots: self.trusted_slots.clone(), - trusted_addresses: self.trusted_addresses.clone(), - trusted_address_slots: self.trusted_address_slots.clone(), - computational_gas_limit: self.computational_gas_limit, - } - } - - fn check_user_restrictions( - &mut self, - state: VmLocalStateData<'_>, - data: BeforeExecutionData, - memory: &SimpleMemory, - storage: StoragePtr, - ) -> ValidationRoundResult { - if self.computational_gas_used > self.computational_gas_limit { - return Err(ViolatedValidationRule::TookTooManyComputationalGas( - self.computational_gas_limit, - )); - } - - let opcode_variant = data.opcode.variant; - match opcode_variant.opcode { - Opcode::FarCall(_) => { - let packed_abi = data.src0_value.value; - let call_destination_value = data.src1_value.value; - - let called_address = u256_to_account_address(&call_destination_value); - let far_call_abi = FarCallABI::from_u256(packed_abi); - - if called_address == KECCAK256_PRECOMPILE_ADDRESS - && far_call_abi.memory_quasi_fat_pointer.length == 64 - { - let calldata_page = get_calldata_page_via_abi( - &far_call_abi, - state.vm_local_state.callstack.current.base_memory_page, - ); - let calldata = memory.read_unaligned_bytes( - calldata_page as usize, - far_call_abi.memory_quasi_fat_pointer.start as usize, - 64, - ); - - let slot_to_add = - self.slot_to_add_from_keccak_call(&calldata, self.user_address); - - if let Some(slot) = slot_to_add { - return Ok(NewTrustedValidationItems { - new_allowed_slots: vec![slot], - ..Default::default() - }); - } - } else if called_address != self.user_address { - let code_key = get_code_key(&called_address); - let code = storage.borrow_mut().read_value(&code_key); - - if code == H256::zero() { - // The users are not allowed to call contracts with no code - return Err(ViolatedValidationRule::CalledContractWithNoCode( - called_address, - )); - } - } - } - Opcode::Context(context) => { - match context { - ContextOpcode::Meta => { - return Err(ViolatedValidationRule::TouchedUnallowedContext); - } - ContextOpcode::ErgsLeft => { - // TODO (SMA-1168): implement the correct restrictions for the gas left opcode. - } - _ => {} - } - } - Opcode::Log(LogOpcode::StorageRead) => { - let key = data.src0_value.value; - let this_address = state.vm_local_state.callstack.current.this_address; - let msg_sender = state.vm_local_state.callstack.current.msg_sender; - - if !self.is_allowed_storage_read(storage.clone(), this_address, key, msg_sender) { - return Err(ViolatedValidationRule::TouchedUnallowedStorageSlots( - this_address, - key, - )); - } - - if self.trusted_address_slots.contains(&(this_address, key)) { - let storage_key = - StorageKey::new(AccountTreeId::new(this_address), u256_to_h256(key)); - - let value = storage.borrow_mut().read_value(&storage_key); - - return Ok(NewTrustedValidationItems { - new_trusted_addresses: vec![h256_to_account_address(&value)], - ..Default::default() - }); - } - } - _ => {} - } - - Ok(Default::default()) - } -} - -impl DynTracer for ValidationTracer { - fn before_execution( - &mut self, - state: VmLocalStateData<'_>, - data: BeforeExecutionData, - memory: &SimpleMemory, - storage: StoragePtr, - ) { - // For now, we support only validations for users. - if let ValidationTracerMode::UserTxValidation = self.validation_mode { - self.computational_gas_used = self - .computational_gas_used - .saturating_add(computational_gas_price(state, &data)); - - let validation_round_result = - self.check_user_restrictions(state, data, memory, storage); - self.process_validation_round_result(validation_round_result); - } - - let hook = VmHook::from_opcode_memory(&state, &data); - print_debug_if_needed(&hook, &state, memory); - - let current_mode = self.validation_mode; - match (current_mode, hook) { - (ValidationTracerMode::NoValidation, VmHook::AccountValidationEntered) => { - // Account validation can be entered when there is no prior validation (i.e. "nested" validations are not allowed) - self.validation_mode = ValidationTracerMode::UserTxValidation; - } - (ValidationTracerMode::NoValidation, VmHook::PaymasterValidationEntered) => { - // Paymaster validation can be entered when there is no prior validation (i.e. "nested" validations are not allowed) - self.validation_mode = ValidationTracerMode::PaymasterTxValidation; - } - (_, VmHook::AccountValidationEntered | VmHook::PaymasterValidationEntered) => { - panic!( - "Unallowed transition inside the validation tracer. Mode: {:#?}, hook: {:#?}", - self.validation_mode, hook - ); - } - (_, VmHook::NoValidationEntered) => { - // Validation can be always turned off - self.validation_mode = ValidationTracerMode::NoValidation; - } - (_, VmHook::ValidationStepEndeded) => { - // The validation step has ended. - self.should_stop_execution = true; - } - (_, _) => { - // The hook is not relevant to the validation tracer. Ignore. - } - } - } -} - -impl VmTracer for ValidationTracer { - fn finish_cycle( - &mut self, - _state: &mut ZkSyncVmState, - _bootloader_state: &mut BootloaderState, - ) -> TracerExecutionStatus { - if self.should_stop_execution { - return TracerExecutionStatus::Stop(TracerExecutionStopReason::Finish); - } - if let Some(result) = self.result.get() { - return TracerExecutionStatus::Stop(TracerExecutionStopReason::Abort( - Halt::TracerCustom(format!("Validation error: {:#?}", result)), - )); - } - TracerExecutionStatus::Continue - } -} - -fn touches_allowed_context(address: Address, key: U256) -> bool { - // Context is not touched at all - if address != SYSTEM_CONTEXT_ADDRESS { - return false; - } - - // Only chain_id is allowed to be touched. - key == U256::from(0u32) -} - -fn is_constant_code_hash( - address: Address, - key: U256, - storage: StoragePtr, -) -> bool { - if address != ACCOUNT_CODE_STORAGE_ADDRESS { - // Not a code hash - return false; - } - - let value = storage.borrow_mut().read_value(&StorageKey::new( - AccountTreeId::new(address), - u256_to_h256(key), - )); - - value != H256::zero() -} - -fn valid_eth_token_call(address: Address, msg_sender: Address) -> bool { - let is_valid_caller = msg_sender == MSG_VALUE_SIMULATOR_ADDRESS - || msg_sender == CONTRACT_DEPLOYER_ADDRESS - || msg_sender == BOOTLOADER_ADDRESS; - address == L2_ETH_TOKEN_ADDRESS && is_valid_caller -} diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/validation/params.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/validation/params.rs deleted file mode 100644 index 1a4ced478b6..00000000000 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/validation/params.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::collections::HashSet; -use zksync_types::{Address, U256}; - -#[derive(Debug, Clone)] -pub struct ValidationTracerParams { - pub user_address: Address, - pub paymaster_address: Address, - /// Slots that are trusted (i.e. the user can access them). - pub trusted_slots: HashSet<(Address, U256)>, - /// Trusted addresses (the user can access any slots on these addresses). - pub trusted_addresses: HashSet
, - /// Slots, that are trusted and the value of them is the new trusted address. - /// They are needed to work correctly with beacon proxy, where the address of the implementation is - /// stored in the beacon. - pub trusted_address_slots: HashSet<(Address, U256)>, - /// Number of computational gas that validation step is allowed to use. - pub computational_gas_limit: u32, -} diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/validation/types.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/validation/types.rs deleted file mode 100644 index b9d44227992..00000000000 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/validation/types.rs +++ /dev/null @@ -1,18 +0,0 @@ -use zksync_types::{Address, H256}; - -#[derive(Debug, Clone, Eq, PartialEq, Copy)] -#[allow(clippy::enum_variant_names)] -pub(super) enum ValidationTracerMode { - /// Should be activated when the transaction is being validated by user. - UserTxValidation, - /// Should be activated when the transaction is being validated by the paymaster. - PaymasterTxValidation, - /// Is a state when there are no restrictions on the execution. - NoValidation, -} - -#[derive(Debug, Clone, Default)] -pub(super) struct NewTrustedValidationItems { - pub(super) new_allowed_slots: Vec, - pub(super) new_trusted_addresses: Vec
, -} diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/vm.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/vm.rs index c9d7d2b06ab..d7edcfe7ff9 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/vm.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/vm.rs @@ -1,18 +1,19 @@ +use crate::HistoryMode; use zksync_state::{StoragePtr, WriteStorage}; use zksync_types::l2_to_l1_log::UserL2ToL1Log; use zksync_types::Transaction; use zksync_utils::bytecode::CompressedBytecodeInfo; use crate::vm_refunds_enhancement::old_vm::events::merge_events; -use crate::vm_refunds_enhancement::old_vm::history_recorder::{HistoryEnabled, HistoryMode}; use crate::interface::BytecodeCompressionError; use crate::interface::{ BootloaderMemory, CurrentExecutionState, L1BatchEnv, L2BlockEnv, SystemEnv, VmExecutionMode, - VmExecutionResultAndLogs, + VmExecutionResultAndLogs, VmInterface, VmInterfaceHistoryEnabled, }; +use crate::vm_latest::HistoryEnabled; use crate::vm_refunds_enhancement::bootloader_state::BootloaderState; -use crate::vm_refunds_enhancement::tracers::traits::VmTracer; +use crate::vm_refunds_enhancement::tracers::dispatcher::TracerDispatcher; use crate::vm_refunds_enhancement::types::internals::{new_vm_state, VmSnapshot, ZkSyncVmState}; /// Main entry point for Virtual Machine integration. @@ -21,7 +22,7 @@ use crate::vm_refunds_enhancement::types::internals::{new_vm_state, VmSnapshot, pub struct Vm { pub(crate) bootloader_state: BootloaderState, // Current state and oracles of virtual machine - pub(crate) state: ZkSyncVmState, + pub(crate) state: ZkSyncVmState, pub(crate) storage: StoragePtr, pub(crate) system_env: SystemEnv, pub(crate) batch_env: L1BatchEnv, @@ -31,8 +32,10 @@ pub struct Vm { } /// Public interface for VM -impl Vm { - pub fn new(batch_env: L1BatchEnv, system_env: SystemEnv, storage: StoragePtr, _: H) -> Self { +impl VmInterface for Vm { + type TracerDispatcher = TracerDispatcher; + + fn new(batch_env: L1BatchEnv, system_env: SystemEnv, storage: StoragePtr) -> Self { let (state, bootloader_state) = new_vm_state(storage.clone(), &system_env, &batch_env); Self { bootloader_state, @@ -46,43 +49,37 @@ impl Vm { } /// Push tx into memory for the future execution - pub fn push_transaction(&mut self, tx: Transaction) { + fn push_transaction(&mut self, tx: Transaction) { self.push_transaction_with_compression(tx, true) } - /// Execute VM with default tracers. The execution mode determines whether the VM will stop and - /// how the vm will be processed. - pub fn execute(&mut self, execution_mode: VmExecutionMode) -> VmExecutionResultAndLogs { - self.inspect(vec![], execution_mode) - } - /// Execute VM with custom tracers. - pub fn inspect( + fn inspect( &mut self, - tracers: Vec>>, + dispatcher: Self::TracerDispatcher, execution_mode: VmExecutionMode, ) -> VmExecutionResultAndLogs { - self.inspect_inner(tracers, execution_mode) + self.inspect_inner(dispatcher, execution_mode) } /// Get current state of bootloader memory. - pub fn get_bootloader_memory(&self) -> BootloaderMemory { + fn get_bootloader_memory(&self) -> BootloaderMemory { self.bootloader_state.bootloader_memory() } /// Get compressed bytecodes of the last executed transaction - pub fn get_last_tx_compressed_bytecodes(&self) -> Vec { + fn get_last_tx_compressed_bytecodes(&self) -> Vec { self.bootloader_state.get_last_tx_compressed_bytecodes() } - pub fn start_new_l2_block(&mut self, l2_block_env: L2BlockEnv) { + fn start_new_l2_block(&mut self, l2_block_env: L2BlockEnv) { self.bootloader_state.start_new_l2_block(l2_block_env); } /// Get current state of virtual machine. /// This method should be used only after the batch execution. /// Otherwise it can panic. - pub fn get_current_execution_state(&self) -> CurrentExecutionState { + fn get_current_execution_state(&self) -> CurrentExecutionState { let (deduplicated_events_logs, raw_events, l1_messages) = self.state.event_sink.flatten(); let events: Vec<_> = merge_events(raw_events) .into_iter() @@ -114,24 +111,15 @@ impl Vm { } } - /// Execute transaction with optional bytecode compression. - pub fn execute_transaction_with_bytecode_compression( - &mut self, - tx: Transaction, - with_compression: bool, - ) -> Result { - self.inspect_transaction_with_bytecode_compression(vec![], tx, with_compression) - } - /// Inspect transaction with optional bytecode compression. - pub fn inspect_transaction_with_bytecode_compression( + fn inspect_transaction_with_bytecode_compression( &mut self, - tracers: Vec>>, + dispatcher: Self::TracerDispatcher, tx: Transaction, with_compression: bool, ) -> Result { self.push_transaction_with_compression(tx, with_compression); - let result = self.inspect(tracers, VmExecutionMode::OneTx); + let result = self.inspect(dispatcher, VmExecutionMode::OneTx); if self.has_unpublished_bytecodes() { Err(BytecodeCompressionError::BytecodeCompressionFailed) } else { @@ -141,14 +129,14 @@ impl Vm { } /// Methods of vm, which required some history manipullations -impl Vm { +impl VmInterfaceHistoryEnabled for Vm { /// Create snapshot of current vm state and push it into the memory - pub fn make_snapshot(&mut self) { + fn make_snapshot(&mut self) { self.make_snapshot_inner() } /// Rollback vm state to the latest snapshot and destroy the snapshot - pub fn rollback_to_the_latest_snapshot(&mut self) { + fn rollback_to_the_latest_snapshot(&mut self) { let snapshot = self .snapshots .pop() @@ -157,7 +145,7 @@ impl Vm { } /// Pop the latest snapshot from the memory and destroy it - pub fn pop_snapshot_no_rollback(&mut self) { + fn pop_snapshot_no_rollback(&mut self) { self.snapshots .pop() .expect("Snapshot should be created before rolling it back"); diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/bytecode.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/bytecode.rs index d1ce1ce9d29..2ae53a48ef3 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/bytecode.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/bytecode.rs @@ -1,11 +1,13 @@ use itertools::Itertools; +use crate::interface::VmInterface; +use crate::HistoryMode; use zksync_state::{StoragePtr, WriteStorage}; use zksync_types::U256; use zksync_utils::bytecode::{compress_bytecode, hash_bytecode, CompressedBytecodeInfo}; use zksync_utils::bytes_to_be_words; -use crate::vm_virtual_blocks::{HistoryMode, Vm}; +use crate::vm_virtual_blocks::Vm; impl Vm { /// Checks the last transaction has successfully published compressed bytecodes and returns `true` if there is at least one is still unknown. diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/execution.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/execution.rs index b8ad5189e66..ac95312019d 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/execution.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/execution.rs @@ -1,31 +1,32 @@ +use crate::interface::tracer::{TracerExecutionStopReason, VmExecutionStopReason}; use crate::interface::{VmExecutionMode, VmExecutionResultAndLogs}; +use crate::HistoryMode; use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::WriteStorage; -use crate::vm_virtual_blocks::old_vm::{ - history_recorder::HistoryMode, - utils::{vm_may_have_ended_inner, VmExecutionResult}, -}; +use crate::vm_virtual_blocks::old_vm::utils::{vm_may_have_ended_inner, VmExecutionResult}; +use crate::vm_virtual_blocks::tracers::dispatcher::TracerDispatcher; use crate::vm_virtual_blocks::tracers::{ - traits::{BoxedTracer, ExecutionEndTracer, ExecutionProcessing, VmTracer}, + traits::{ExecutionEndTracer, VmTracer}, DefaultExecutionTracer, RefundsTracer, }; use crate::vm_virtual_blocks::vm::Vm; -use crate::vm_virtual_blocks::VmExecutionStopReason; impl Vm { pub(crate) fn inspect_inner( &mut self, - mut tracers: Vec>>, + tracer: TracerDispatcher, execution_mode: VmExecutionMode, ) -> VmExecutionResultAndLogs { + let mut enable_refund_tracer = false; if let VmExecutionMode::OneTx = execution_mode { // For correct results we have to include refunds tracer to the desired tracers - tracers.push(RefundsTracer::new(self.batch_env.clone()).into_boxed()); + enable_refund_tracer = true; // Move the pointer to the next transaction self.bootloader_state.move_tx_to_execute_pointer(); } - let (_, result) = self.inspect_and_collect_results(tracers, execution_mode); + let (_, result) = + self.inspect_and_collect_results(tracer, execution_mode, enable_refund_tracer); result } @@ -33,15 +34,20 @@ impl Vm { /// Collect the result from the default tracers. fn inspect_and_collect_results( &mut self, - tracers: Vec>>, + dispatcher: TracerDispatcher, execution_mode: VmExecutionMode, + enable_refund_tracer: bool, ) -> (VmExecutionStopReason, VmExecutionResultAndLogs) { - let mut tx_tracer: DefaultExecutionTracer = DefaultExecutionTracer::new( - self.system_env.default_validation_computational_gas_limit, - execution_mode, - tracers, - self.storage.clone(), - ); + let refund_tracer = + enable_refund_tracer.then_some(RefundsTracer::new(self.batch_env.clone())); + let mut tx_tracer: DefaultExecutionTracer = + DefaultExecutionTracer::new( + self.system_env.default_validation_computational_gas_limit, + execution_mode, + dispatcher, + refund_tracer, + self.storage.clone(), + ); let timestamp_initial = Timestamp(self.state.local_state.timestamp); let cycles_initial = self.state.local_state.monotonic_cycle_counter; @@ -70,19 +76,20 @@ impl Vm { result, logs, statistics, - refunds: Default::default(), + refunds: tx_tracer + .refund_tracer + .map(|r| r.get_refunds()) + .unwrap_or_default(), }; - for tracer in tx_tracer.custom_tracers.iter_mut() { - tracer.save_results(&mut result); - } + tx_tracer.dispatcher.save_results(&mut result); (stop_reason, result) } /// Execute vm with given tracers until the stop reason is reached. fn execute_with_default_tracer( &mut self, - tracer: &mut DefaultExecutionTracer, + tracer: &mut DefaultExecutionTracer, ) -> VmExecutionStopReason { tracer.initialize_tracer(&mut self.state); let result = loop { @@ -105,10 +112,12 @@ impl Vm { } if tracer.should_stop_execution() { - break VmExecutionStopReason::TracerRequestedStop; + break VmExecutionStopReason::TracerRequestedStop( + TracerExecutionStopReason::Finish, + ); } }; - tracer.after_vm_execution(&mut self.state, &self.bootloader_state, result); + tracer.after_vm_execution(&mut self.state, &self.bootloader_state, result.clone()); result } diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/gas.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/gas.rs index 1d6de3f0b6c..1f06ecb0827 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/gas.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/gas.rs @@ -1,6 +1,6 @@ +use crate::HistoryMode; use zksync_state::WriteStorage; -use crate::vm_virtual_blocks::old_vm::history_recorder::HistoryMode; use crate::vm_virtual_blocks::tracers::DefaultExecutionTracer; use crate::vm_virtual_blocks::vm::Vm; @@ -19,7 +19,7 @@ impl Vm { pub(crate) fn calculate_computational_gas_used( &self, - tracer: &DefaultExecutionTracer, + tracer: &DefaultExecutionTracer, gas_remaining_before: u32, spent_pubdata_counter_before: u32, ) -> u32 { diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/logs.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/logs.rs index f67b97ff875..a32f3a16572 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/logs.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/logs.rs @@ -2,11 +2,11 @@ use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::WriteStorage; use crate::interface::types::outputs::VmExecutionLogs; +use crate::HistoryMode; use zksync_types::l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}; use zksync_types::VmEvent; use crate::vm_virtual_blocks::old_vm::events::merge_events; -use crate::vm_virtual_blocks::old_vm::history_recorder::HistoryMode; use crate::vm_virtual_blocks::old_vm::utils::precompile_calls_count_after_timestamp; use crate::vm_virtual_blocks::vm::Vm; diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/snapshots.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/snapshots.rs index fa106740277..1a8ad6fefd2 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/snapshots.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/snapshots.rs @@ -2,13 +2,12 @@ use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics use std::time::Duration; +use crate::vm_latest::HistoryEnabled; use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::WriteStorage; use crate::vm_virtual_blocks::{ - old_vm::{history_recorder::HistoryEnabled, oracles::OracleWithHistory}, - types::internals::VmSnapshot, - vm::Vm, + old_vm::oracles::OracleWithHistory, types::internals::VmSnapshot, vm::Vm, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelSet, EncodeLabelValue)] diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/statistics.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/statistics.rs index 98bc5430bea..f05157f14a4 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/statistics.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/statistics.rs @@ -2,9 +2,9 @@ use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::WriteStorage; use crate::interface::{VmExecutionStatistics, VmMemoryMetrics}; +use crate::HistoryMode; use zksync_types::U256; -use crate::vm_virtual_blocks::old_vm::history_recorder::HistoryMode; use crate::vm_virtual_blocks::tracers::DefaultExecutionTracer; use crate::vm_virtual_blocks::vm::Vm; @@ -18,7 +18,7 @@ impl Vm { &self, timestamp_initial: Timestamp, cycles_initial: u32, - tracer: &DefaultExecutionTracer, + tracer: &DefaultExecutionTracer, gas_remaining_before: u32, gas_remaining_after: u32, spent_pubdata_counter_before: u32, diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/tx.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/tx.rs index da6bcd62c69..bfeeb56e022 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/tx.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/tx.rs @@ -2,12 +2,12 @@ use crate::vm_virtual_blocks::constants::BOOTLOADER_HEAP_PAGE; use crate::vm_virtual_blocks::implementation::bytecode::{ bytecode_to_factory_dep, compress_bytecodes, }; +use crate::HistoryMode; use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::WriteStorage; use zksync_types::l1::is_l1_tx_type; use zksync_types::Transaction; -use crate::vm_virtual_blocks::old_vm::history_recorder::HistoryMode; use crate::vm_virtual_blocks::types::internals::TransactionData; use crate::vm_virtual_blocks::vm::Vm; diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/mod.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/mod.rs index 744bd8e66fb..3a7a96e729d 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/mod.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/mod.rs @@ -5,10 +5,8 @@ pub use old_vm::{ }; pub use tracers::{ - call::CallTracer, - traits::{BoxedTracer, DynTracer, ExecutionEndTracer, ExecutionProcessing, VmTracer}, - utils::VmExecutionStopReason, - StorageInvocations, ValidationError, ValidationTracer, ValidationTracerParams, + dispatcher::TracerDispatcher, + traits::{ExecutionEndTracer, ExecutionProcessing, TracerPointer, VmTracer}, }; pub use types::internals::ZkSyncVmState; @@ -21,7 +19,7 @@ pub use vm::Vm; mod bootloader_state; mod implementation; mod old_vm; -mod tracers; +pub(crate) mod tracers; mod types; mod vm; diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/bootloader.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/bootloader.rs index 2219cc02014..5abbd1dde47 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/bootloader.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/bootloader.rs @@ -1,13 +1,13 @@ use zksync_types::U256; -use crate::interface::{ExecutionResult, Halt, TxExecutionMode, VmExecutionMode}; +use crate::interface::{ExecutionResult, Halt, TxExecutionMode, VmExecutionMode, VmInterface}; use crate::vm_virtual_blocks::constants::BOOTLOADER_HEAP_PAGE; use crate::vm_virtual_blocks::tests::tester::VmTesterBuilder; use crate::vm_virtual_blocks::tests::utils::{ get_bootloader, verify_required_memory, BASE_SYSTEM_CONTRACTS, }; -use crate::vm_virtual_blocks::HistoryEnabled; +use crate::vm_latest::HistoryEnabled; #[test] fn test_dummy_bootloader() { diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/bytecode_publishing.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/bytecode_publishing.rs index bec771dd4a7..773aa77e150 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/bytecode_publishing.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/bytecode_publishing.rs @@ -1,10 +1,10 @@ use zksync_types::event::extract_long_l2_to_l1_messages; use zksync_utils::bytecode::compress_bytecode; -use crate::interface::{TxExecutionMode, VmExecutionMode}; +use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; +use crate::vm_latest::HistoryEnabled; use crate::vm_virtual_blocks::tests::tester::{DeployContractsTx, TxType, VmTesterBuilder}; use crate::vm_virtual_blocks::tests::utils::read_test_contract; -use crate::vm_virtual_blocks::HistoryEnabled; #[test] fn test_bytecode_publishing() { diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/call_tracer.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/call_tracer.rs index ed9084b7193..7ee647ee1f7 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/call_tracer.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/call_tracer.rs @@ -1,8 +1,10 @@ -use crate::interface::{TxExecutionMode, VmExecutionMode}; +use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; +use crate::tracers::CallTracer; +use crate::vm_latest::HistoryEnabled; use crate::vm_virtual_blocks::constants::BLOCK_GAS_LIMIT; use crate::vm_virtual_blocks::tests::tester::VmTesterBuilder; use crate::vm_virtual_blocks::tests::utils::{read_max_depth_contract, read_test_contract}; -use crate::vm_virtual_blocks::{CallTracer, HistoryEnabled}; +use crate::vm_virtual_blocks::tracers::traits::ToTracerPointer; use once_cell::sync::OnceCell; use std::sync::Arc; use zksync_types::{Address, Execute}; @@ -34,11 +36,9 @@ fn test_max_depth() { ); let result = Arc::new(OnceCell::new()); - let call_tracer = CallTracer::new(result.clone(), HistoryEnabled); + let call_tracer = CallTracer::new(result.clone()).into_tracer_pointer(); vm.vm.push_transaction(tx); - let res = vm - .vm - .inspect(vec![Box::new(call_tracer)], VmExecutionMode::OneTx); + let res = vm.vm.inspect(call_tracer.into(), VmExecutionMode::OneTx); assert!(result.get().is_some()); assert!(res.result.is_failed()); } @@ -71,11 +71,12 @@ fn test_basic_behavior() { ); let result = Arc::new(OnceCell::new()); - let call_tracer = CallTracer::new(result.clone(), HistoryEnabled); + let call_tracer = CallTracer::new(result.clone()); vm.vm.push_transaction(tx); - let res = vm - .vm - .inspect(vec![Box::new(call_tracer)], VmExecutionMode::OneTx); + let res = vm.vm.inspect( + call_tracer.into_tracer_pointer().into(), + VmExecutionMode::OneTx, + ); let call_tracer_result = result.get().unwrap(); diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/default_aa.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/default_aa.rs index e7176c3ca74..02a69a6a5d2 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/default_aa.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/default_aa.rs @@ -4,12 +4,12 @@ use zksync_types::system_contracts::{DEPLOYMENT_NONCE_INCREMENT, TX_NONCE_INCREM use zksync_types::{get_code_key, get_known_code_key, get_nonce_key, AccountTreeId, U256}; use zksync_utils::u256_to_h256; -use crate::interface::{TxExecutionMode, VmExecutionMode}; +use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; +use crate::vm_latest::HistoryEnabled; use crate::vm_virtual_blocks::tests::tester::{DeployContractsTx, TxType, VmTesterBuilder}; use crate::vm_virtual_blocks::tests::utils::{ get_balance, read_test_contract, verify_required_storage, }; -use crate::vm_virtual_blocks::HistoryEnabled; #[test] fn test_default_aa_interaction() { diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/gas_limit.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/gas_limit.rs index 280f564901f..01ebe4c0d22 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/gas_limit.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/gas_limit.rs @@ -6,8 +6,8 @@ use crate::vm_virtual_blocks::constants::{ }; use crate::vm_virtual_blocks::tests::tester::VmTesterBuilder; -use crate::interface::TxExecutionMode; -use crate::vm_virtual_blocks::HistoryDisabled; +use crate::interface::{TxExecutionMode, VmInterface}; +use crate::vm_latest::HistoryDisabled; /// Checks that `TX_GAS_LIMIT_OFFSET` constant is correct. #[test] diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/get_used_contracts.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/get_used_contracts.rs index cbee2968586..496b7a847ed 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/get_used_contracts.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/get_used_contracts.rs @@ -2,6 +2,7 @@ use std::collections::{HashMap, HashSet}; use itertools::Itertools; +use crate::HistoryMode; use zksync_state::WriteStorage; use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; use zksync_test_account::Account; @@ -9,10 +10,11 @@ use zksync_types::{Execute, U256}; use zksync_utils::bytecode::hash_bytecode; use zksync_utils::h256_to_u256; -use crate::interface::{TxExecutionMode, VmExecutionMode}; +use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; +use crate::vm_latest::HistoryDisabled; use crate::vm_virtual_blocks::tests::tester::{TxType, VmTesterBuilder}; use crate::vm_virtual_blocks::tests::utils::{read_test_contract, BASE_SYSTEM_CONTRACTS}; -use crate::vm_virtual_blocks::{HistoryDisabled, HistoryMode, Vm}; +use crate::vm_virtual_blocks::Vm; #[test] fn test_get_used_contracts() { diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/is_write_initial.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/is_write_initial.rs index bc6bf491478..8ac93268774 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/is_write_initial.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/is_write_initial.rs @@ -1,10 +1,10 @@ use zksync_state::ReadStorage; use zksync_types::get_nonce_key; -use crate::interface::{TxExecutionMode, VmExecutionMode}; +use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; +use crate::vm_latest::HistoryDisabled; use crate::vm_virtual_blocks::tests::tester::{Account, TxType, VmTesterBuilder}; use crate::vm_virtual_blocks::tests::utils::read_test_contract; -use crate::vm_virtual_blocks::HistoryDisabled; #[test] fn test_is_write_initial_behaviour() { diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/l1_tx_execution.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/l1_tx_execution.rs index 3d2dcee1118..64d9f98ddb3 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/l1_tx_execution.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/l1_tx_execution.rs @@ -4,13 +4,13 @@ use zksync_types::storage_writes_deduplicator::StorageWritesDeduplicator; use zksync_types::{get_code_key, get_known_code_key, U256}; use zksync_utils::u256_to_h256; -use crate::interface::{TxExecutionMode, VmExecutionMode}; +use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; +use crate::vm_latest::HistoryEnabled; use crate::vm_virtual_blocks::tests::tester::{TxType, VmTesterBuilder}; use crate::vm_virtual_blocks::tests::utils::{ read_test_contract, verify_required_storage, BASE_SYSTEM_CONTRACTS, }; use crate::vm_virtual_blocks::types::internals::TransactionData; -use crate::vm_virtual_blocks::HistoryEnabled; #[test] fn test_l1_tx_execution() { diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/l2_blocks.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/l2_blocks.rs index 87a3351bb7d..aebb389ebaf 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/l2_blocks.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/l2_blocks.rs @@ -3,14 +3,18 @@ //! The description for each of the tests can be found in the corresponding `.yul` file. //! -use crate::interface::{ExecutionResult, Halt, L2BlockEnv, TxExecutionMode, VmExecutionMode}; +use crate::interface::{ + ExecutionResult, Halt, L2BlockEnv, TxExecutionMode, VmExecutionMode, VmInterface, +}; +use crate::vm_latest::HistoryEnabled; use crate::vm_virtual_blocks::constants::{ BOOTLOADER_HEAP_PAGE, TX_OPERATOR_L2_BLOCK_INFO_OFFSET, TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO, }; use crate::vm_virtual_blocks::tests::tester::default_l1_batch; use crate::vm_virtual_blocks::tests::tester::VmTesterBuilder; use crate::vm_virtual_blocks::utils::l2_blocks::get_l2_block_hash_key; -use crate::vm_virtual_blocks::{HistoryEnabled, HistoryMode, Vm}; +use crate::vm_virtual_blocks::Vm; +use crate::HistoryMode; use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::{ReadStorage, WriteStorage}; use zksync_system_constants::{ diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/nonce_holder.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/nonce_holder.rs index b38b85e7dff..162a3f46cb1 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/nonce_holder.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/nonce_holder.rs @@ -1,12 +1,13 @@ use zksync_types::{Execute, Nonce}; use crate::interface::{ - ExecutionResult, Halt, TxExecutionMode, TxRevertReason, VmExecutionMode, VmRevertReason, + ExecutionResult, Halt, TxExecutionMode, TxRevertReason, VmExecutionMode, VmInterface, + VmRevertReason, }; +use crate::vm_latest::HistoryEnabled; use crate::vm_virtual_blocks::tests::tester::{Account, VmTesterBuilder}; use crate::vm_virtual_blocks::tests::utils::read_nonce_holder_tester; use crate::vm_virtual_blocks::types::internals::TransactionData; -use crate::vm_virtual_blocks::HistoryEnabled; pub enum NonceHolderTestMode { SetValueUnderNonce, diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/refunds.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/refunds.rs index d84ca235c8f..d0b3b7cbee3 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/refunds.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/refunds.rs @@ -1,9 +1,9 @@ -use crate::interface::{TxExecutionMode, VmExecutionMode}; +use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; use crate::vm_virtual_blocks::tests::tester::{DeployContractsTx, TxType, VmTesterBuilder}; use crate::vm_virtual_blocks::tests::utils::read_test_contract; +use crate::vm_latest::HistoryEnabled; use crate::vm_virtual_blocks::types::internals::TransactionData; -use crate::vm_virtual_blocks::HistoryEnabled; #[test] fn test_predetermined_refunded_gas() { diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/require_eip712.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/require_eip712.rs index efaba1a424e..82c1a052792 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/require_eip712.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/require_eip712.rs @@ -11,10 +11,10 @@ use zksync_types::transaction_request::TransactionRequest; use zksync_types::utils::storage_key_for_standard_token_balance; use zksync_types::{AccountTreeId, Address, Eip712Domain, Execute, Nonce, Transaction, U256}; -use crate::interface::{TxExecutionMode, VmExecutionMode}; +use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; +use crate::vm_latest::HistoryDisabled; use crate::vm_virtual_blocks::tests::tester::{Account, VmTester, VmTesterBuilder}; use crate::vm_virtual_blocks::tests::utils::read_many_owners_custom_account_contract; -use crate::vm_virtual_blocks::HistoryDisabled; impl VmTester { pub(crate) fn get_eth_balance(&mut self, address: Address) -> U256 { diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/rollbacks.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/rollbacks.rs index 571c2530c83..240b7188377 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/rollbacks.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/rollbacks.rs @@ -6,11 +6,11 @@ use zksync_contracts::test_contracts::LoadnextContractExecutionParams; use zksync_types::{Execute, U256}; use crate::interface::TxExecutionMode; +use crate::vm_latest::HistoryEnabled; use crate::vm_virtual_blocks::tests::tester::{ DeployContractsTx, TransactionTestInfo, TxModifier, TxType, VmTesterBuilder, }; use crate::vm_virtual_blocks::tests::utils::read_test_contract; -use crate::vm_virtual_blocks::HistoryEnabled; #[test] fn test_vm_rollbacks() { diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/simple_execution.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/simple_execution.rs index 2aefbd49cd9..6b2237f5e59 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/simple_execution.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/simple_execution.rs @@ -1,6 +1,6 @@ -use crate::interface::{ExecutionResult, VmExecutionMode}; +use crate::interface::{ExecutionResult, VmExecutionMode, VmInterface}; +use crate::vm_latest::HistoryDisabled; use crate::vm_virtual_blocks::tests::tester::{TxType, VmTesterBuilder}; -use crate::vm_virtual_blocks::HistoryDisabled; #[test] fn estimate_fee() { diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/tester/inner_state.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/tester/inner_state.rs index 0a6d9c03afe..8105ca244d3 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/tester/inner_state.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/tester/inner_state.rs @@ -11,6 +11,7 @@ use crate::vm_virtual_blocks::old_vm::history_recorder::{ AppDataFrameManagerWithHistory, HistoryRecorder, }; use crate::vm_virtual_blocks::{HistoryEnabled, HistoryMode, SimpleMemory, Vm}; +use crate::HistoryMode as CommonHistoryMode; #[derive(Clone, Debug)] pub(crate) struct ModifiedKeysMap(HashMap); @@ -68,9 +69,9 @@ pub(crate) struct VmInstanceInnerState { local_state: VmLocalState, } -impl Vm { +impl Vm { // Dump inner state of the VM. - pub(crate) fn dump_inner_state(&self) -> VmInstanceInnerState { + pub(crate) fn dump_inner_state(&self) -> VmInstanceInnerState { let event_sink = self.state.event_sink.clone(); let precompile_processor_state = PrecompileProcessorTestInnerState { timestamp_history: self.state.precompiles_processor.timestamp_history.clone(), diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/tester/transaction_test_info.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/tester/transaction_test_info.rs index aa8678037ff..15d3d98ab1d 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/tester/transaction_test_info.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/tester/transaction_test_info.rs @@ -2,10 +2,10 @@ use zksync_types::{ExecuteTransactionCommon, Transaction}; use crate::interface::{ CurrentExecutionState, ExecutionResult, Halt, TxRevertReason, VmExecutionMode, - VmExecutionResultAndLogs, VmRevertReason, + VmExecutionResultAndLogs, VmInterface, VmInterfaceHistoryEnabled, VmRevertReason, }; +use crate::vm_latest::HistoryEnabled; use crate::vm_virtual_blocks::tests::tester::vm_tester::VmTester; -use crate::vm_virtual_blocks::HistoryEnabled; #[derive(Debug, Clone)] pub(crate) enum TxModifier { diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/tester/vm_tester.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/tester/vm_tester.rs index 978635c02a8..0283239e7a2 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/tester/vm_tester.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/tester/vm_tester.rs @@ -1,6 +1,8 @@ +use std::marker::PhantomData; use zksync_contracts::BaseSystemContracts; use zksync_state::{InMemoryStorage, StoragePtr, StorageView, WriteStorage}; +use crate::HistoryMode; use zksync_types::block::legacy_miniblock_hash; use zksync_types::helpers::unix_timestamp_ms; use zksync_types::utils::{deployed_address_create, storage_key_for_eth_balance}; @@ -13,13 +15,13 @@ use zksync_utils::u256_to_h256; use crate::vm_virtual_blocks::constants::BLOCK_GAS_LIMIT; -use crate::interface::TxExecutionMode; use crate::interface::{L1BatchEnv, L2Block, L2BlockEnv, SystemEnv, VmExecutionMode}; +use crate::interface::{TxExecutionMode, VmInterface}; use crate::vm_virtual_blocks::tests::tester::Account; use crate::vm_virtual_blocks::tests::tester::TxType; use crate::vm_virtual_blocks::tests::utils::read_test_contract; use crate::vm_virtual_blocks::utils::l2_blocks::load_last_l2_block; -use crate::vm_virtual_blocks::{HistoryMode, Vm}; +use crate::vm_virtual_blocks::Vm; pub(crate) type InMemoryStorageView = StorageView; @@ -31,7 +33,7 @@ pub(crate) struct VmTester { pub(crate) test_contract: Option
, pub(crate) rich_accounts: Vec, pub(crate) custom_contracts: Vec, - history_mode: H, + _phantom: PhantomData, } impl VmTester { @@ -88,12 +90,7 @@ impl VmTester { }; } - let vm = Vm::new( - l1_batch, - self.vm.system_env.clone(), - self.storage.clone(), - self.history_mode.clone(), - ); + let vm = Vm::new(l1_batch, self.vm.system_env.clone(), self.storage.clone()); if self.test_contract.is_some() { self.deploy_test_contract(); @@ -106,7 +103,7 @@ impl VmTester { pub(crate) type ContractsToDeploy = (Vec, Address, bool); pub(crate) struct VmTesterBuilder { - history_mode: H, + _phantom: PhantomData, storage: Option, l1_batch_env: Option, system_env: SystemEnv, @@ -118,7 +115,7 @@ pub(crate) struct VmTesterBuilder { impl Clone for VmTesterBuilder { fn clone(&self) -> Self { Self { - history_mode: self.history_mode.clone(), + _phantom: Default::default(), storage: None, l1_batch_env: self.l1_batch_env.clone(), system_env: self.system_env.clone(), @@ -131,9 +128,9 @@ impl Clone for VmTesterBuilder { #[allow(dead_code)] impl VmTesterBuilder { - pub(crate) fn new(history_mode: H) -> Self { + pub(crate) fn new(_: H) -> Self { Self { - history_mode, + _phantom: Default::default(), storage: None, l1_batch_env: None, system_env: SystemEnv { @@ -229,12 +226,7 @@ impl VmTesterBuilder { } let fee_account = l1_batch_env.fee_account; - let vm = Vm::new( - l1_batch_env, - self.system_env, - storage_ptr.clone(), - self.history_mode.clone(), - ); + let vm = Vm::new(l1_batch_env, self.system_env, storage_ptr.clone()); VmTester { vm, @@ -244,7 +236,7 @@ impl VmTesterBuilder { test_contract: None, rich_accounts: self.rich_accounts.clone(), custom_contracts: self.custom_contracts.clone(), - history_mode: self.history_mode, + _phantom: Default::default(), } } } diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/tracing_execution_error.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/tracing_execution_error.rs index 2f664452992..8258abe0685 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/tracing_execution_error.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/tracing_execution_error.rs @@ -1,13 +1,13 @@ use crate::interface::{TxExecutionMode, TxRevertReason, VmRevertReason}; use zksync_types::{Execute, H160}; +use crate::vm_latest::HistoryEnabled; use crate::vm_virtual_blocks::tests::tester::{ ExpectedError, TransactionTestInfo, VmTesterBuilder, }; use crate::vm_virtual_blocks::tests::utils::{ get_execute_error_calldata, read_error_contract, BASE_SYSTEM_CONTRACTS, }; -use crate::vm_virtual_blocks::HistoryEnabled; #[test] fn test_tracing_of_execution_errors() { diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/upgrade.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/upgrade.rs index f692f45ef7e..7ba8ec6dfc0 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/upgrade.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/upgrade.rs @@ -14,10 +14,12 @@ use zksync_contracts::{deployer_contract, load_contract, load_sys_contract, read use zksync_state::WriteStorage; use zksync_test_account::TxType; -use crate::interface::{ExecutionResult, Halt, TxExecutionMode, VmExecutionMode}; +use crate::interface::{ + ExecutionResult, Halt, TxExecutionMode, VmExecutionMode, VmInterface, VmInterfaceHistoryEnabled, +}; +use crate::vm_latest::HistoryEnabled; use crate::vm_virtual_blocks::tests::tester::VmTesterBuilder; use crate::vm_virtual_blocks::tests::utils::verify_required_storage; -use crate::vm_virtual_blocks::HistoryEnabled; use zksync_types::protocol_version::ProtocolUpgradeTxCommonData; use super::utils::read_test_contract; diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/default_tracers.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/default_tracers.rs index d1fe0f27870..5cc3c75bc17 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/default_tracers.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/default_tracers.rs @@ -1,5 +1,8 @@ use std::fmt::{Debug, Formatter}; +use std::marker::PhantomData; +use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; +use crate::interface::tracer::VmExecutionStopReason; use crate::interface::VmExecutionMode; use zk_evm_1_3_3::witness_trace::DummyTracer; use zk_evm_1_3_3::zkevm_opcode_defs::{Opcode, RetOpcode}; @@ -17,19 +20,17 @@ use crate::vm_virtual_blocks::bootloader_state::BootloaderState; use crate::vm_virtual_blocks::constants::BOOTLOADER_HEAP_PAGE; use crate::vm_virtual_blocks::old_vm::history_recorder::HistoryMode; use crate::vm_virtual_blocks::old_vm::memory::SimpleMemory; -use crate::vm_virtual_blocks::tracers::traits::{ - DynTracer, ExecutionEndTracer, ExecutionProcessing, VmTracer, -}; +use crate::vm_virtual_blocks::tracers::dispatcher::TracerDispatcher; +use crate::vm_virtual_blocks::tracers::traits::{ExecutionEndTracer, ExecutionProcessing}; use crate::vm_virtual_blocks::tracers::utils::{ computational_gas_price, gas_spent_on_bytecodes_and_long_messages_this_opcode, print_debug_if_needed, VmHook, }; -use crate::vm_virtual_blocks::tracers::ResultTracer; +use crate::vm_virtual_blocks::tracers::{RefundsTracer, ResultTracer}; use crate::vm_virtual_blocks::types::internals::ZkSyncVmState; -use crate::vm_virtual_blocks::VmExecutionStopReason; /// Default tracer for the VM. It manages the other tracers execution and stop the vm when needed. -pub(crate) struct DefaultExecutionTracer { +pub(crate) struct DefaultExecutionTracer { tx_has_been_processed: bool, execution_mode: VmExecutionMode, @@ -41,30 +42,27 @@ pub(crate) struct DefaultExecutionTracer { in_account_validation: bool, final_batch_info_requested: bool, pub(crate) result_tracer: ResultTracer, - pub(crate) custom_tracers: Vec>>, + pub(crate) refund_tracer: Option, + pub(crate) dispatcher: TracerDispatcher, ret_from_the_bootloader: Option, storage: StoragePtr, + _phantom: PhantomData, } -impl Debug for DefaultExecutionTracer { +impl Debug for DefaultExecutionTracer { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("DefaultExecutionTracer").finish() } } -impl Tracer for DefaultExecutionTracer { - const CALL_BEFORE_DECODING: bool = true; +impl Tracer for DefaultExecutionTracer { + const CALL_BEFORE_DECODING: bool = false; const CALL_AFTER_DECODING: bool = true; const CALL_BEFORE_EXECUTION: bool = true; const CALL_AFTER_EXECUTION: bool = true; type SupportedMemory = SimpleMemory; - fn before_decoding(&mut self, state: VmLocalStateData<'_>, memory: &Self::SupportedMemory) { - >::before_decoding(&mut self.result_tracer, state, memory); - for tracer in self.custom_tracers.iter_mut() { - tracer.before_decoding(state, memory) - } - } + fn before_decoding(&mut self, _state: VmLocalStateData<'_>, _memory: &Self::SupportedMemory) {} fn after_decoding( &mut self, @@ -72,15 +70,19 @@ impl Tracer for DefaultExecutionTracer { data: AfterDecodingData, memory: &Self::SupportedMemory, ) { - >::after_decoding( + >>::after_decoding( &mut self.result_tracer, state, data, memory, ); - for tracer in self.custom_tracers.iter_mut() { - tracer.after_decoding(state, data, memory) + if let Some(refund) = &mut self.refund_tracer { + >>::after_decoding( + refund, state, data, memory, + ); } + + self.dispatcher.after_decoding(state, data, memory); } fn before_execution( @@ -110,9 +112,12 @@ impl Tracer for DefaultExecutionTracer { gas_spent_on_bytecodes_and_long_messages_this_opcode(&state, &data); self.result_tracer .before_execution(state, data, memory, self.storage.clone()); - for tracer in self.custom_tracers.iter_mut() { - tracer.before_execution(state, data, memory, self.storage.clone()); + if let Some(refund) = &mut self.refund_tracer { + refund.before_execution(state, data, memory, self.storage.clone()); } + + self.dispatcher + .before_execution(state, data, memory, self.storage.clone()); } fn after_execution( @@ -137,9 +142,12 @@ impl Tracer for DefaultExecutionTracer { self.result_tracer .after_execution(state, data, memory, self.storage.clone()); - for tracer in self.custom_tracers.iter_mut() { - tracer.after_execution(state, data, memory, self.storage.clone()); + if let Some(refund) = &mut self.refund_tracer { + refund.after_execution(state, data, memory, self.storage.clone()); } + + self.dispatcher + .after_execution(state, data, memory, self.storage.clone()); } } @@ -151,9 +159,7 @@ impl ExecutionEndTracer for DefaultExecution VmExecutionMode::Bootloader => self.ret_from_the_bootloader == Some(RetOpcode::Ok), }; should_stop = should_stop || self.validation_run_out_of_gas(); - for tracer in self.custom_tracers.iter() { - should_stop = should_stop || tracer.should_stop_execution(); - } + should_stop = should_stop || self.dispatcher.should_stop_execution(); should_stop } } @@ -162,7 +168,8 @@ impl DefaultExecutionTracer { pub(crate) fn new( computational_gas_limit: u32, execution_mode: VmExecutionMode, - custom_tracers: Vec>>, + dispatcher: TracerDispatcher, + refund_tracer: Option, storage: StoragePtr, ) -> Self { Self { @@ -174,9 +181,11 @@ impl DefaultExecutionTracer { in_account_validation: false, final_batch_info_requested: false, result_tracer: ResultTracer::new(execution_mode), - custom_tracers, + refund_tracer, + dispatcher, ret_from_the_bootloader: None, storage, + _phantom: Default::default(), } } @@ -209,48 +218,51 @@ impl DefaultExecutionTracer { } } -impl DynTracer for DefaultExecutionTracer {} - -impl ExecutionProcessing for DefaultExecutionTracer { - fn initialize_tracer(&mut self, state: &mut ZkSyncVmState) { +impl DefaultExecutionTracer { + pub(crate) fn initialize_tracer(&mut self, state: &mut ZkSyncVmState) { self.result_tracer.initialize_tracer(state); - for processor in self.custom_tracers.iter_mut() { - processor.initialize_tracer(state); + if let Some(refund) = &mut self.refund_tracer { + refund.initialize_tracer(state); } + self.dispatcher.initialize_tracer(state); } - fn before_cycle(&mut self, state: &mut ZkSyncVmState) { + pub(crate) fn before_cycle(&mut self, state: &mut ZkSyncVmState) { self.result_tracer.before_cycle(state); - for processor in self.custom_tracers.iter_mut() { - processor.before_cycle(state); + if let Some(refund) = &mut self.refund_tracer { + refund.before_cycle(state); } + self.dispatcher.before_cycle(state); } - fn after_cycle( + pub(crate) fn after_cycle( &mut self, state: &mut ZkSyncVmState, bootloader_state: &mut BootloaderState, ) { self.result_tracer.after_cycle(state, bootloader_state); - for processor in self.custom_tracers.iter_mut() { - processor.after_cycle(state, bootloader_state); + if let Some(refund) = &mut self.refund_tracer { + refund.after_cycle(state, bootloader_state); } + self.dispatcher.after_cycle(state, bootloader_state); if self.final_batch_info_requested { self.set_fictive_l2_block(state, bootloader_state) } } - fn after_vm_execution( + pub(crate) fn after_vm_execution( &mut self, state: &mut ZkSyncVmState, bootloader_state: &BootloaderState, stop_reason: VmExecutionStopReason, ) { self.result_tracer - .after_vm_execution(state, bootloader_state, stop_reason); - for processor in self.custom_tracers.iter_mut() { - processor.after_vm_execution(state, bootloader_state, stop_reason); + .after_vm_execution(state, bootloader_state, stop_reason.clone()); + if let Some(refund) = &mut self.refund_tracer { + refund.after_vm_execution(state, bootloader_state, stop_reason.clone()); } + self.dispatcher + .after_vm_execution(state, bootloader_state, stop_reason.clone()); } } diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/dispatcher.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/dispatcher.rs new file mode 100644 index 00000000000..713d2df250f --- /dev/null +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/dispatcher.rs @@ -0,0 +1,127 @@ +use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; +use crate::interface::tracer::VmExecutionStopReason; +use crate::interface::VmExecutionResultAndLogs; +use crate::vm_virtual_blocks::TracerPointer; +use crate::vm_virtual_blocks::{ + BootloaderState, ExecutionEndTracer, ExecutionProcessing, HistoryMode, SimpleMemory, VmTracer, + ZkSyncVmState, +}; + +use zk_evm_1_3_3::tracing::{ + AfterDecodingData, AfterExecutionData, BeforeExecutionData, VmLocalStateData, +}; +use zksync_state::{StoragePtr, WriteStorage}; + +impl From> for TracerDispatcher { + fn from(value: TracerPointer) -> Self { + Self { + tracers: vec![value], + } + } +} + +impl From>> for TracerDispatcher { + fn from(value: Vec>) -> Self { + Self { tracers: value } + } +} + +pub struct TracerDispatcher { + tracers: Vec>, +} + +impl Default for TracerDispatcher { + fn default() -> Self { + Self { tracers: vec![] } + } +} +impl DynTracer> for TracerDispatcher { + fn before_decoding(&mut self, _state: VmLocalStateData<'_>, _memory: &SimpleMemory) { + for tracer in self.tracers.iter_mut() { + tracer.before_decoding(_state, _memory); + } + } + + fn after_decoding( + &mut self, + _state: VmLocalStateData<'_>, + _data: AfterDecodingData, + _memory: &SimpleMemory, + ) { + for tracer in self.tracers.iter_mut() { + tracer.after_decoding(_state, _data, _memory); + } + } + + fn before_execution( + &mut self, + _state: VmLocalStateData<'_>, + _data: BeforeExecutionData, + _memory: &SimpleMemory, + _storage: StoragePtr, + ) { + for tracer in self.tracers.iter_mut() { + tracer.before_execution(_state, _data, _memory, _storage.clone()); + } + } + fn after_execution( + &mut self, + _state: VmLocalStateData<'_>, + _data: AfterExecutionData, + _memory: &SimpleMemory, + _storage: StoragePtr, + ) { + for tracer in self.tracers.iter_mut() { + tracer.after_execution(_state, _data, _memory, _storage.clone()); + } + } +} + +impl ExecutionEndTracer for TracerDispatcher { + fn should_stop_execution(&self) -> bool { + let mut result = false; + for tracer in self.tracers.iter() { + result |= tracer.should_stop_execution(); + } + result + } +} + +impl ExecutionProcessing for TracerDispatcher { + fn initialize_tracer(&mut self, _state: &mut ZkSyncVmState) { + for tracer in self.tracers.iter_mut() { + tracer.initialize_tracer(_state); + } + } + + /// Run after each vm execution cycle + fn after_cycle( + &mut self, + _state: &mut ZkSyncVmState, + _bootloader_state: &mut BootloaderState, + ) { + for tracer in self.tracers.iter_mut() { + tracer.after_cycle(_state, _bootloader_state); + } + } + + /// Run after the vm execution + fn after_vm_execution( + &mut self, + _state: &mut ZkSyncVmState, + _bootloader_state: &BootloaderState, + _stop_reason: VmExecutionStopReason, + ) { + for tracer in self.tracers.iter_mut() { + tracer.after_vm_execution(_state, _bootloader_state, _stop_reason.clone()); + } + } +} + +impl VmTracer for TracerDispatcher { + fn save_results(&mut self, _result: &mut VmExecutionResultAndLogs) { + for tracer in self.tracers.iter_mut() { + tracer.save_results(_result); + } + } +} diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/mod.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/mod.rs index 11fefedc85a..5447c4519e0 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/mod.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/mod.rs @@ -1,15 +1,11 @@ pub(crate) use default_tracers::DefaultExecutionTracer; pub(crate) use refunds::RefundsTracer; pub(crate) use result_tracer::ResultTracer; -pub use storage_invocations::StorageInvocations; -pub use validation::{ValidationError, ValidationTracer, ValidationTracerParams}; pub(crate) mod default_tracers; pub(crate) mod refunds; pub(crate) mod result_tracer; -pub(crate) mod call; -pub(crate) mod storage_invocations; +pub mod dispatcher; pub(crate) mod traits; pub(crate) mod utils; -pub(crate) mod validation; diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/refunds.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/refunds.rs index d8db7a2cbaf..6496e13172a 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/refunds.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/refunds.rs @@ -2,6 +2,7 @@ use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics use std::collections::HashMap; +use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; use crate::interface::{L1BatchEnv, Refunds, VmExecutionResultAndLogs}; use zk_evm_1_3_3::{ aux_structures::Timestamp, @@ -29,7 +30,7 @@ use crate::vm_virtual_blocks::old_vm::{ }; use crate::vm_virtual_blocks::tracers::utils::gas_spent_on_bytecodes_and_long_messages_this_opcode; use crate::vm_virtual_blocks::tracers::{ - traits::{DynTracer, ExecutionEndTracer, ExecutionProcessing, VmTracer}, + traits::{ExecutionEndTracer, ExecutionProcessing, VmTracer}, utils::{get_vm_hook_params, VmHook}, }; use crate::vm_virtual_blocks::types::internals::ZkSyncVmState; @@ -67,9 +68,13 @@ impl RefundsTracer { l1_batch, } } -} + pub(crate) fn get_refunds(&self) -> Refunds { + Refunds { + gas_refunded: self.refund_gas, + operator_suggested_refund: self.operator_refund.unwrap_or_default(), + } + } -impl RefundsTracer { fn requested_refund(&self) -> Option { self.pending_operator_refund } @@ -140,7 +145,7 @@ impl RefundsTracer { } } -impl DynTracer for RefundsTracer { +impl DynTracer> for RefundsTracer { fn before_execution( &mut self, state: VmLocalStateData<'_>, diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/result_tracer.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/result_tracer.rs index 798b1a1e42c..1f566fea567 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/result_tracer.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/result_tracer.rs @@ -5,6 +5,8 @@ use zk_evm_1_3_3::{ }; use zksync_state::{StoragePtr, WriteStorage}; +use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; +use crate::interface::tracer::VmExecutionStopReason; use crate::interface::{ ExecutionResult, Halt, TxRevertReason, VmExecutionMode, VmExecutionResultAndLogs, VmRevertReason, @@ -18,13 +20,12 @@ use crate::vm_virtual_blocks::old_vm::{ utils::{vm_may_have_ended_inner, VmExecutionResult}, }; use crate::vm_virtual_blocks::tracers::{ - traits::{DynTracer, ExecutionEndTracer, ExecutionProcessing, VmTracer}, + traits::{ExecutionEndTracer, ExecutionProcessing, VmTracer}, utils::{get_vm_hook_params, read_pointer, VmHook}, }; use crate::vm_virtual_blocks::types::internals::ZkSyncVmState; use crate::vm_virtual_blocks::constants::{BOOTLOADER_HEAP_PAGE, RESULT_SUCCESS_FIRST_SLOT}; -use crate::vm_virtual_blocks::VmExecutionStopReason; #[derive(Debug, Clone)] enum Result { @@ -58,7 +59,7 @@ fn current_frame_is_bootloader(local_state: &VmLocalState) -> bool { local_state.callstack.inner.len() == 1 } -impl DynTracer for ResultTracer { +impl DynTracer> for ResultTracer { fn after_decoding( &mut self, state: VmLocalStateData<'_>, @@ -119,7 +120,7 @@ impl ExecutionProcessing for ResultTracer // One of the tracers above has requested to stop the execution. // If it was the correct stop we already have the result, // otherwise it can be out of gas error - VmExecutionStopReason::TracerRequestedStop => { + VmExecutionStopReason::TracerRequestedStop(_) => { match self.execution_mode { VmExecutionMode::OneTx => self.vm_stopped_execution(state, bootloader_state), VmExecutionMode::Batch => self.vm_finished_execution(state), diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/storage_invocations.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/storage_invocations.rs deleted file mode 100644 index b7e86e08c13..00000000000 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/storage_invocations.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::vm_virtual_blocks::bootloader_state::BootloaderState; -use crate::vm_virtual_blocks::old_vm::history_recorder::HistoryMode; -use crate::vm_virtual_blocks::tracers::traits::{ - DynTracer, ExecutionEndTracer, ExecutionProcessing, VmTracer, -}; -use crate::vm_virtual_blocks::types::internals::ZkSyncVmState; -use zksync_state::WriteStorage; - -#[derive(Debug, Default, Clone)] -pub struct StorageInvocations { - limit: usize, - current: usize, -} - -impl StorageInvocations { - pub fn new(limit: usize) -> Self { - Self { limit, current: 0 } - } -} - -/// Tracer responsible for calculating the number of storage invocations and -/// stopping the VM execution if the limit is reached. -impl DynTracer for StorageInvocations {} - -impl ExecutionEndTracer for StorageInvocations { - fn should_stop_execution(&self) -> bool { - self.current >= self.limit - } -} - -impl ExecutionProcessing for StorageInvocations { - fn after_cycle( - &mut self, - state: &mut ZkSyncVmState, - _bootloader_state: &mut BootloaderState, - ) { - self.current = state - .storage - .storage - .get_ptr() - .borrow() - .missed_storage_invocations(); - } -} - -impl VmTracer for StorageInvocations {} diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/traits.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/traits.rs index 119b867575f..3045e6f8319 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/traits.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/traits.rs @@ -1,18 +1,17 @@ +use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; +use crate::interface::tracer::VmExecutionStopReason; use crate::interface::VmExecutionResultAndLogs; -use zk_evm_1_3_3::tracing::{ - AfterDecodingData, AfterExecutionData, BeforeExecutionData, VmLocalStateData, -}; -use zksync_state::{StoragePtr, WriteStorage}; +use zksync_state::WriteStorage; use crate::vm_virtual_blocks::bootloader_state::BootloaderState; use crate::vm_virtual_blocks::old_vm::history_recorder::HistoryMode; use crate::vm_virtual_blocks::old_vm::memory::SimpleMemory; use crate::vm_virtual_blocks::types::internals::ZkSyncVmState; -use crate::vm_virtual_blocks::VmExecutionStopReason; +pub type TracerPointer = Box>; /// Run tracer for collecting data during the vm execution cycles pub trait ExecutionProcessing: - DynTracer + ExecutionEndTracer + DynTracer> + ExecutionEndTracer { fn initialize_tracer(&mut self, _state: &mut ZkSyncVmState) {} fn before_cycle(&mut self, _state: &mut ZkSyncVmState) {} @@ -39,47 +38,19 @@ pub trait ExecutionEndTracer { } } -/// Version of zk_evm_1_3_3::Tracer suitable for dynamic dispatch. -pub trait DynTracer { - fn before_decoding(&mut self, _state: VmLocalStateData<'_>, _memory: &SimpleMemory) {} - fn after_decoding( - &mut self, - _state: VmLocalStateData<'_>, - _data: AfterDecodingData, - _memory: &SimpleMemory, - ) { - } - fn before_execution( - &mut self, - _state: VmLocalStateData<'_>, - _data: BeforeExecutionData, - _memory: &SimpleMemory, - _storage: StoragePtr, - ) { - } - fn after_execution( - &mut self, - _state: VmLocalStateData<'_>, - _data: AfterExecutionData, - _memory: &SimpleMemory, - _storage: StoragePtr, - ) { - } -} - /// Save the results of the vm execution. pub trait VmTracer: - DynTracer + ExecutionEndTracer + ExecutionProcessing + Send + DynTracer> + ExecutionEndTracer + ExecutionProcessing { fn save_results(&mut self, _result: &mut VmExecutionResultAndLogs) {} } -pub trait BoxedTracer { - fn into_boxed(self) -> Box>; +pub trait ToTracerPointer { + fn into_tracer_pointer(self) -> TracerPointer; } -impl + 'static> BoxedTracer for T { - fn into_boxed(self) -> Box> { +impl + 'static> ToTracerPointer for T { + fn into_tracer_pointer(self) -> TracerPointer { Box::new(self) } } diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/utils.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/utils.rs index a2698e85c10..abf8714bbe9 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/utils.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/utils.rs @@ -216,9 +216,3 @@ pub(crate) fn get_vm_hook_params(memory: &SimpleMemory) -> Ve VM_HOOK_PARAMS_START_POSITION..VM_HOOK_PARAMS_START_POSITION + VM_HOOK_PARAMS_COUNT, ) } - -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum VmExecutionStopReason { - VmFinished, - TracerRequestedStop, -} diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/validation/error.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/validation/error.rs deleted file mode 100644 index 4b9741ddaa5..00000000000 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/validation/error.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::interface::Halt; -use std::fmt::Display; -use zksync_types::vm_trace::ViolatedValidationRule; - -#[derive(Debug, Clone)] -pub enum ValidationError { - FailedTx(Halt), - ViolatedRule(ViolatedValidationRule), -} - -impl Display for ValidationError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::FailedTx(revert_reason) => { - write!(f, "Validation revert: {}", revert_reason) - } - Self::ViolatedRule(rule) => { - write!(f, "Violated validation rules: {}", rule) - } - } - } -} diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/validation/mod.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/validation/mod.rs deleted file mode 100644 index f883680b8e8..00000000000 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/validation/mod.rs +++ /dev/null @@ -1,389 +0,0 @@ -mod error; -mod params; -mod types; - -use std::sync::Arc; -use std::{collections::HashSet, marker::PhantomData}; - -use once_cell::sync::OnceCell; -use zk_evm_1_3_3::{ - tracing::{BeforeExecutionData, VmLocalStateData}, - zkevm_opcode_defs::{ContextOpcode, FarCallABI, LogOpcode, Opcode}, -}; - -use zksync_state::{StoragePtr, WriteStorage}; -use zksync_system_constants::{ - ACCOUNT_CODE_STORAGE_ADDRESS, BOOTLOADER_ADDRESS, CONTRACT_DEPLOYER_ADDRESS, - KECCAK256_PRECOMPILE_ADDRESS, L2_ETH_TOKEN_ADDRESS, MSG_VALUE_SIMULATOR_ADDRESS, - SYSTEM_CONTEXT_ADDRESS, -}; - -use zksync_types::{ - get_code_key, web3::signing::keccak256, AccountTreeId, Address, StorageKey, H256, U256, -}; -use zksync_utils::{ - be_bytes_to_safe_address, h256_to_account_address, u256_to_account_address, u256_to_h256, -}; - -use crate::vm_virtual_blocks::old_vm::history_recorder::HistoryMode; -use crate::vm_virtual_blocks::old_vm::memory::SimpleMemory; -use crate::vm_virtual_blocks::tracers::traits::{ - DynTracer, ExecutionEndTracer, ExecutionProcessing, VmTracer, -}; -use crate::vm_virtual_blocks::tracers::utils::{ - computational_gas_price, get_calldata_page_via_abi, print_debug_if_needed, VmHook, -}; - -pub use error::ValidationError; -pub use params::ValidationTracerParams; - -use crate::interface::VmExecutionResultAndLogs; -use types::NewTrustedValidationItems; -use types::ValidationTracerMode; -use zksync_types::vm_trace::ViolatedValidationRule; - -/// Tracer that is used to ensure that the validation adheres to all the rules -/// to prevent DDoS attacks on the server. -#[derive(Debug, Clone)] -pub struct ValidationTracer { - validation_mode: ValidationTracerMode, - auxilary_allowed_slots: HashSet, - - user_address: Address, - #[allow(dead_code)] - paymaster_address: Address, - should_stop_execution: bool, - trusted_slots: HashSet<(Address, U256)>, - trusted_addresses: HashSet
, - trusted_address_slots: HashSet<(Address, U256)>, - computational_gas_used: u32, - computational_gas_limit: u32, - result: Arc>, - _marker: PhantomData H>, -} - -type ValidationRoundResult = Result; - -impl ValidationTracer { - pub fn new( - params: ValidationTracerParams, - result: Arc>, - ) -> Self { - Self { - validation_mode: ValidationTracerMode::NoValidation, - auxilary_allowed_slots: Default::default(), - - should_stop_execution: false, - user_address: params.user_address, - paymaster_address: params.paymaster_address, - trusted_slots: params.trusted_slots, - trusted_addresses: params.trusted_addresses, - trusted_address_slots: params.trusted_address_slots, - computational_gas_used: 0, - computational_gas_limit: params.computational_gas_limit, - result, - _marker: Default::default(), - } - } - - fn process_validation_round_result(&mut self, result: ValidationRoundResult) { - match result { - Ok(NewTrustedValidationItems { - new_allowed_slots, - new_trusted_addresses, - }) => { - self.auxilary_allowed_slots.extend(new_allowed_slots); - self.trusted_addresses.extend(new_trusted_addresses); - } - Err(err) => { - if self.result.get().is_some() { - tracing::trace!("Validation error is already set, skipping"); - return; - } - self.result.set(err).expect("Result should be empty"); - } - } - } - - // Checks whether such storage access is acceptable. - fn is_allowed_storage_read( - &self, - storage: StoragePtr, - address: Address, - key: U256, - msg_sender: Address, - ) -> bool { - // If there are no restrictions, all storage reads are valid. - // We also don't support the paymaster validation for now. - if matches!( - self.validation_mode, - ValidationTracerMode::NoValidation | ValidationTracerMode::PaymasterTxValidation - ) { - return true; - } - - // The pair of MSG_VALUE_SIMULATOR_ADDRESS & L2_ETH_TOKEN_ADDRESS simulates the behavior of transfering ETH - // that is safe for the DDoS protection rules. - if valid_eth_token_call(address, msg_sender) { - return true; - } - - if self.trusted_slots.contains(&(address, key)) - || self.trusted_addresses.contains(&address) - || self.trusted_address_slots.contains(&(address, key)) - { - return true; - } - - if touches_allowed_context(address, key) { - return true; - } - - // The user is allowed to touch its own slots or slots semantically related to him. - let valid_users_slot = address == self.user_address - || u256_to_account_address(&key) == self.user_address - || self.auxilary_allowed_slots.contains(&u256_to_h256(key)); - if valid_users_slot { - return true; - } - - if is_constant_code_hash(address, key, storage) { - return true; - } - - false - } - - // Used to remember user-related fields (its balance/allowance/etc). - // Note that it assumes that the length of the calldata is 64 bytes. - fn slot_to_add_from_keccak_call( - &self, - calldata: &[u8], - validated_address: Address, - ) -> Option { - assert_eq!(calldata.len(), 64); - - let (potential_address_bytes, potential_position_bytes) = calldata.split_at(32); - let potential_address = be_bytes_to_safe_address(potential_address_bytes); - - // If the validation_address is equal to the potential_address, - // then it is a request that could be used for mapping of kind mapping(address => ...). - // - // If the potential_position_bytes were already allowed before, then this keccak might be used - // for ERC-20 allowance or any other of mapping(address => mapping(...)) - if potential_address == Some(validated_address) - || self - .auxilary_allowed_slots - .contains(&H256::from_slice(potential_position_bytes)) - { - // This is request that could be used for mapping of kind mapping(address => ...) - - // We could theoretically wait for the slot number to be returned by the - // keccak256 precompile itself, but this would complicate the code even further - // so let's calculate it here. - let slot = keccak256(calldata); - - // Adding this slot to the allowed ones - Some(H256(slot)) - } else { - None - } - } - - fn check_user_restrictions( - &mut self, - state: VmLocalStateData<'_>, - data: BeforeExecutionData, - memory: &SimpleMemory, - storage: StoragePtr, - ) -> ValidationRoundResult { - if self.computational_gas_used > self.computational_gas_limit { - return Err(ViolatedValidationRule::TookTooManyComputationalGas( - self.computational_gas_limit, - )); - } - - let opcode_variant = data.opcode.variant; - match opcode_variant.opcode { - Opcode::FarCall(_) => { - let packed_abi = data.src0_value.value; - let call_destination_value = data.src1_value.value; - - let called_address = u256_to_account_address(&call_destination_value); - let far_call_abi = FarCallABI::from_u256(packed_abi); - - if called_address == KECCAK256_PRECOMPILE_ADDRESS - && far_call_abi.memory_quasi_fat_pointer.length == 64 - { - let calldata_page = get_calldata_page_via_abi( - &far_call_abi, - state.vm_local_state.callstack.current.base_memory_page, - ); - let calldata = memory.read_unaligned_bytes( - calldata_page as usize, - far_call_abi.memory_quasi_fat_pointer.start as usize, - 64, - ); - - let slot_to_add = - self.slot_to_add_from_keccak_call(&calldata, self.user_address); - - if let Some(slot) = slot_to_add { - return Ok(NewTrustedValidationItems { - new_allowed_slots: vec![slot], - ..Default::default() - }); - } - } else if called_address != self.user_address { - let code_key = get_code_key(&called_address); - let code = storage.borrow_mut().read_value(&code_key); - - if code == H256::zero() { - // The users are not allowed to call contracts with no code - return Err(ViolatedValidationRule::CalledContractWithNoCode( - called_address, - )); - } - } - } - Opcode::Context(context) => { - match context { - ContextOpcode::Meta => { - return Err(ViolatedValidationRule::TouchedUnallowedContext); - } - ContextOpcode::ErgsLeft => { - // TODO (SMA-1168): implement the correct restrictions for the gas left opcode. - } - _ => {} - } - } - Opcode::Log(LogOpcode::StorageRead) => { - let key = data.src0_value.value; - let this_address = state.vm_local_state.callstack.current.this_address; - let msg_sender = state.vm_local_state.callstack.current.msg_sender; - - if !self.is_allowed_storage_read(storage.clone(), this_address, key, msg_sender) { - return Err(ViolatedValidationRule::TouchedUnallowedStorageSlots( - this_address, - key, - )); - } - - if self.trusted_address_slots.contains(&(this_address, key)) { - let storage_key = - StorageKey::new(AccountTreeId::new(this_address), u256_to_h256(key)); - - let value = storage.borrow_mut().read_value(&storage_key); - - return Ok(NewTrustedValidationItems { - new_trusted_addresses: vec![h256_to_account_address(&value)], - ..Default::default() - }); - } - } - _ => {} - } - - Ok(Default::default()) - } -} - -impl DynTracer for ValidationTracer { - fn before_execution( - &mut self, - state: VmLocalStateData<'_>, - data: BeforeExecutionData, - memory: &SimpleMemory, - storage: StoragePtr, - ) { - // For now, we support only validations for users. - if let ValidationTracerMode::UserTxValidation = self.validation_mode { - self.computational_gas_used = self - .computational_gas_used - .saturating_add(computational_gas_price(state, &data)); - - let validation_round_result = - self.check_user_restrictions(state, data, memory, storage); - self.process_validation_round_result(validation_round_result); - } - - let hook = VmHook::from_opcode_memory(&state, &data); - print_debug_if_needed(&hook, &state, memory); - - let current_mode = self.validation_mode; - match (current_mode, hook) { - (ValidationTracerMode::NoValidation, VmHook::AccountValidationEntered) => { - // Account validation can be entered when there is no prior validation (i.e. "nested" validations are not allowed) - self.validation_mode = ValidationTracerMode::UserTxValidation; - } - (ValidationTracerMode::NoValidation, VmHook::PaymasterValidationEntered) => { - // Paymaster validation can be entered when there is no prior validation (i.e. "nested" validations are not allowed) - self.validation_mode = ValidationTracerMode::PaymasterTxValidation; - } - (_, VmHook::AccountValidationEntered | VmHook::PaymasterValidationEntered) => { - panic!( - "Unallowed transition inside the validation tracer. Mode: {:#?}, hook: {:#?}", - self.validation_mode, hook - ); - } - (_, VmHook::NoValidationEntered) => { - // Validation can be always turned off - self.validation_mode = ValidationTracerMode::NoValidation; - } - (_, VmHook::ValidationStepEndeded) => { - // The validation step has ended. - self.should_stop_execution = true; - } - (_, _) => { - // The hook is not relevant to the validation tracer. Ignore. - } - } - } -} - -impl ExecutionEndTracer for ValidationTracer { - fn should_stop_execution(&self) -> bool { - self.should_stop_execution || self.result.get().is_some() - } -} - -impl ExecutionProcessing for ValidationTracer {} - -impl VmTracer for ValidationTracer { - fn save_results(&mut self, _result: &mut VmExecutionResultAndLogs) {} -} - -fn touches_allowed_context(address: Address, key: U256) -> bool { - // Context is not touched at all - if address != SYSTEM_CONTEXT_ADDRESS { - return false; - } - - // Only chain_id is allowed to be touched. - key == U256::from(0u32) -} - -fn is_constant_code_hash( - address: Address, - key: U256, - storage: StoragePtr, -) -> bool { - if address != ACCOUNT_CODE_STORAGE_ADDRESS { - // Not a code hash - return false; - } - - let value = storage.borrow_mut().read_value(&StorageKey::new( - AccountTreeId::new(address), - u256_to_h256(key), - )); - - value != H256::zero() -} - -fn valid_eth_token_call(address: Address, msg_sender: Address) -> bool { - let is_valid_caller = msg_sender == MSG_VALUE_SIMULATOR_ADDRESS - || msg_sender == CONTRACT_DEPLOYER_ADDRESS - || msg_sender == BOOTLOADER_ADDRESS; - address == L2_ETH_TOKEN_ADDRESS && is_valid_caller -} diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/validation/params.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/validation/params.rs deleted file mode 100644 index 1a4ced478b6..00000000000 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/validation/params.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::collections::HashSet; -use zksync_types::{Address, U256}; - -#[derive(Debug, Clone)] -pub struct ValidationTracerParams { - pub user_address: Address, - pub paymaster_address: Address, - /// Slots that are trusted (i.e. the user can access them). - pub trusted_slots: HashSet<(Address, U256)>, - /// Trusted addresses (the user can access any slots on these addresses). - pub trusted_addresses: HashSet
, - /// Slots, that are trusted and the value of them is the new trusted address. - /// They are needed to work correctly with beacon proxy, where the address of the implementation is - /// stored in the beacon. - pub trusted_address_slots: HashSet<(Address, U256)>, - /// Number of computational gas that validation step is allowed to use. - pub computational_gas_limit: u32, -} diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/validation/types.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/validation/types.rs deleted file mode 100644 index b9d44227992..00000000000 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/validation/types.rs +++ /dev/null @@ -1,18 +0,0 @@ -use zksync_types::{Address, H256}; - -#[derive(Debug, Clone, Eq, PartialEq, Copy)] -#[allow(clippy::enum_variant_names)] -pub(super) enum ValidationTracerMode { - /// Should be activated when the transaction is being validated by user. - UserTxValidation, - /// Should be activated when the transaction is being validated by the paymaster. - PaymasterTxValidation, - /// Is a state when there are no restrictions on the execution. - NoValidation, -} - -#[derive(Debug, Clone, Default)] -pub(super) struct NewTrustedValidationItems { - pub(super) new_allowed_slots: Vec, - pub(super) new_trusted_addresses: Vec
, -} diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/vm.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/vm.rs index 7bb5a6639df..cdd706ed0a1 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/vm.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/vm.rs @@ -1,17 +1,19 @@ use crate::interface::{ BootloaderMemory, BytecodeCompressionError, CurrentExecutionState, L1BatchEnv, L2BlockEnv, - SystemEnv, VmExecutionMode, VmExecutionResultAndLogs, + SystemEnv, VmExecutionMode, VmExecutionResultAndLogs, VmInterface, VmInterfaceHistoryEnabled, }; +use crate::vm_latest::HistoryEnabled; +use crate::HistoryMode; use zksync_state::{StoragePtr, WriteStorage}; use zksync_types::l2_to_l1_log::UserL2ToL1Log; use zksync_types::Transaction; use zksync_utils::bytecode::CompressedBytecodeInfo; use crate::vm_virtual_blocks::old_vm::events::merge_events; -use crate::vm_virtual_blocks::old_vm::history_recorder::{HistoryEnabled, HistoryMode}; use crate::vm_virtual_blocks::bootloader_state::BootloaderState; -use crate::vm_virtual_blocks::tracers::traits::VmTracer; +use crate::vm_virtual_blocks::tracers::dispatcher::TracerDispatcher; + use crate::vm_virtual_blocks::types::internals::{new_vm_state, VmSnapshot, ZkSyncVmState}; /// Main entry point for Virtual Machine integration. @@ -20,7 +22,7 @@ use crate::vm_virtual_blocks::types::internals::{new_vm_state, VmSnapshot, ZkSyn pub struct Vm { pub(crate) bootloader_state: BootloaderState, // Current state and oracles of virtual machine - pub(crate) state: ZkSyncVmState, + pub(crate) state: ZkSyncVmState, pub(crate) storage: StoragePtr, pub(crate) system_env: SystemEnv, pub(crate) batch_env: L1BatchEnv, @@ -30,8 +32,10 @@ pub struct Vm { } /// Public interface for VM -impl Vm { - pub fn new(batch_env: L1BatchEnv, system_env: SystemEnv, storage: StoragePtr, _: H) -> Self { +impl VmInterface for Vm { + type TracerDispatcher = TracerDispatcher; + + fn new(batch_env: L1BatchEnv, system_env: SystemEnv, storage: StoragePtr) -> Self { let (state, bootloader_state) = new_vm_state(storage.clone(), &system_env, &batch_env); Self { bootloader_state, @@ -45,43 +49,37 @@ impl Vm { } /// Push tx into memory for the future execution - pub fn push_transaction(&mut self, tx: Transaction) { + fn push_transaction(&mut self, tx: Transaction) { self.push_transaction_with_compression(tx, true) } - /// Execute VM with default tracers. The execution mode determines whether the VM will stop and - /// how the vm will be processed. - pub fn execute(&mut self, execution_mode: VmExecutionMode) -> VmExecutionResultAndLogs { - self.inspect(vec![], execution_mode) - } - /// Execute VM with custom tracers. - pub fn inspect( + fn inspect( &mut self, - tracers: Vec>>, + tracer: TracerDispatcher, execution_mode: VmExecutionMode, ) -> VmExecutionResultAndLogs { - self.inspect_inner(tracers, execution_mode) + self.inspect_inner(tracer, execution_mode) } /// Get current state of bootloader memory. - pub fn get_bootloader_memory(&self) -> BootloaderMemory { + fn get_bootloader_memory(&self) -> BootloaderMemory { self.bootloader_state.bootloader_memory() } /// Get compressed bytecodes of the last executed transaction - pub fn get_last_tx_compressed_bytecodes(&self) -> Vec { + fn get_last_tx_compressed_bytecodes(&self) -> Vec { self.bootloader_state.get_last_tx_compressed_bytecodes() } - pub fn start_new_l2_block(&mut self, l2_block_env: L2BlockEnv) { + fn start_new_l2_block(&mut self, l2_block_env: L2BlockEnv) { self.bootloader_state.start_new_l2_block(l2_block_env); } /// Get current state of virtual machine. /// This method should be used only after the batch execution. /// Otherwise it can panic. - pub fn get_current_execution_state(&self) -> CurrentExecutionState { + fn get_current_execution_state(&self) -> CurrentExecutionState { let (deduplicated_events_logs, raw_events, l1_messages) = self.state.event_sink.flatten(); let events: Vec<_> = merge_events(raw_events) .into_iter() @@ -113,24 +111,15 @@ impl Vm { } } - /// Execute transaction with optional bytecode compression. - pub fn execute_transaction_with_bytecode_compression( - &mut self, - tx: Transaction, - with_compression: bool, - ) -> Result { - self.inspect_transaction_with_bytecode_compression(vec![], tx, with_compression) - } - /// Inspect transaction with optional bytecode compression. - pub fn inspect_transaction_with_bytecode_compression( + fn inspect_transaction_with_bytecode_compression( &mut self, - tracers: Vec>>, + tracer: TracerDispatcher, tx: Transaction, with_compression: bool, ) -> Result { self.push_transaction_with_compression(tx, with_compression); - let result = self.inspect(tracers, VmExecutionMode::OneTx); + let result = self.inspect_inner(tracer, VmExecutionMode::OneTx); if self.has_unpublished_bytecodes() { Err(BytecodeCompressionError::BytecodeCompressionFailed) } else { @@ -140,14 +129,14 @@ impl Vm { } /// Methods of vm, which required some history manipullations -impl Vm { +impl VmInterfaceHistoryEnabled for Vm { /// Create snapshot of current vm state and push it into the memory - pub fn make_snapshot(&mut self) { + fn make_snapshot(&mut self) { self.make_snapshot_inner() } /// Rollback vm state to the latest snapshot and destroy the snapshot - pub fn rollback_to_the_latest_snapshot(&mut self) { + fn rollback_to_the_latest_snapshot(&mut self) { let snapshot = self .snapshots .pop() @@ -156,7 +145,7 @@ impl Vm { } /// Pop the latest snapshot from the memory and destroy it - pub fn pop_snapshot_no_rollback(&mut self) { + fn pop_snapshot_no_rollback(&mut self) { self.snapshots .pop() .expect("Snapshot should be created before rolling it back"); diff --git a/core/lib/multivm/src/vm_instance.rs b/core/lib/multivm/src/vm_instance.rs index 314eabaaae6..0cf4a992b96 100644 --- a/core/lib/multivm/src/vm_instance.rs +++ b/core/lib/multivm/src/vm_instance.rs @@ -1,13 +1,15 @@ use crate::interface::{ - FinishedL1Batch, L2BlockEnv, SystemEnv, TxExecutionMode, VmExecutionMode, VmMemoryMetrics, + FinishedL1Batch, L2BlockEnv, SystemEnv, TxExecutionMode, VmExecutionMode, VmInterface, + VmInterfaceHistoryEnabled, VmMemoryMetrics, }; + use std::collections::HashSet; use zksync_state::{ReadStorage, StorageView}; use zksync_utils::bytecode::{hash_bytecode, CompressedBytecodeInfo}; use crate::glue::history_mode::HistoryMode; -use crate::glue::tracer::MultivmTracer; +use crate::glue::tracers::MultivmTracer; use crate::glue::GlueInto; pub struct VmInstance { @@ -21,13 +23,9 @@ pub(crate) enum VmInstanceVersion { VmM5(Box>>), VmM6(Box, H::VmM6Mode>>), Vm1_3_2(Box, H::Vm1_3_2Mode>>), - VmVirtualBlocks(Box, H::VmVirtualBlocksMode>>), - VmVirtualBlocksRefundsEnhancement( - Box< - crate::vm_refunds_enhancement::Vm, H::VmVirtualBlocksRefundsEnhancement>, - >, - ), - VmBoojumIntegration(Box, H::VmBoojumIntegration>>), + VmVirtualBlocks(Box, H>>), + VmVirtualBlocksRefundsEnhancement(Box, H>>), + VmBoojumIntegration(Box, H>>), } impl VmInstance { @@ -210,26 +208,21 @@ impl VmInstance { tracers: Vec, H>>>, ) -> crate::interface::VmExecutionResultAndLogs { match &mut self.vm { - VmInstanceVersion::VmVirtualBlocks(vm) => vm - .inspect( - tracers - .into_iter() - .map(|tracer| tracer.vm_virtual_blocks()) - .collect(), - VmExecutionMode::OneTx.glue_into(), - ) - .glue_into(), - VmInstanceVersion::VmVirtualBlocksRefundsEnhancement(vm) => vm.inspect( - tracers + VmInstanceVersion::VmVirtualBlocks(vm) => { + let tracers: Vec<_> = tracers.into_iter().map(|t| t.vm_virtual_blocks()).collect(); + vm.inspect(tracers.into(), VmExecutionMode::OneTx) + } + VmInstanceVersion::VmVirtualBlocksRefundsEnhancement(vm) => { + let tracers: Vec<_> = tracers .into_iter() - .map(|tracer| tracer.vm_refunds_enhancement()) - .collect(), - VmExecutionMode::OneTx, - ), - VmInstanceVersion::VmBoojumIntegration(vm) => vm.inspect( - tracers.into_iter().map(|tracer| tracer.latest()).collect(), - VmExecutionMode::OneTx, - ), + .map(|t| t.vm_refunds_enhancement()) + .collect(); + vm.inspect(tracers.into(), VmExecutionMode::OneTx) + } + VmInstanceVersion::VmBoojumIntegration(vm) => { + let tracers: Vec<_> = tracers.into_iter().map(|t| t.latest()).collect(); + vm.inspect(tracers.into(), VmExecutionMode::OneTx) + } _ => self.execute_next_transaction(), } } @@ -403,31 +396,33 @@ impl VmInstance { crate::interface::BytecodeCompressionError, > { match &mut self.vm { - VmInstanceVersion::VmVirtualBlocks(vm) => vm - .inspect_transaction_with_bytecode_compression( - tracers - .into_iter() - .map(|tracer| tracer.vm_virtual_blocks()) - .collect(), + VmInstanceVersion::VmVirtualBlocks(vm) => { + let tracers: Vec<_> = tracers.into_iter().map(|t| t.vm_virtual_blocks()).collect(); + vm.inspect_transaction_with_bytecode_compression( + tracers.into(), tx, with_compression, ) - .glue_into(), - VmInstanceVersion::VmVirtualBlocksRefundsEnhancement(vm) => vm - .inspect_transaction_with_bytecode_compression( - tracers - .into_iter() - .map(|tracer| tracer.vm_refunds_enhancement()) - .collect(), + } + VmInstanceVersion::VmVirtualBlocksRefundsEnhancement(vm) => { + let tracers: Vec<_> = tracers + .into_iter() + .map(|t| t.vm_refunds_enhancement()) + .collect(); + vm.inspect_transaction_with_bytecode_compression( + tracers.into(), tx, with_compression, - ), - VmInstanceVersion::VmBoojumIntegration(vm) => vm - .inspect_transaction_with_bytecode_compression( - tracers.into_iter().map(|tracer| tracer.latest()).collect(), + ) + } + VmInstanceVersion::VmBoojumIntegration(vm) => { + let tracers: Vec<_> = tracers.into_iter().map(|t| t.latest()).collect(); + vm.inspect_transaction_with_bytecode_compression( + tracers.into(), tx, with_compression, - ), + ) + } _ => { self.last_tx_compressed_bytecodes = vec![]; self.execute_transaction_with_bytecode_compression(tx, with_compression) diff --git a/core/lib/zksync_core/src/api_server/execution_sandbox/execute.rs b/core/lib/zksync_core/src/api_server/execution_sandbox/execute.rs index 1f913ea2587..3b45636fa9c 100644 --- a/core/lib/zksync_core/src/api_server/execution_sandbox/execute.rs +++ b/core/lib/zksync_core/src/api_server/execution_sandbox/execute.rs @@ -3,7 +3,8 @@ use tracing::{span, Level}; use multivm::interface::{TxExecutionMode, VmExecutionResultAndLogs}; -use multivm::vm_latest::{constants::ETH_CALL_GAS_LIMIT, StorageInvocations}; +use multivm::tracers::StorageInvocations; +use multivm::vm_latest::constants::ETH_CALL_GAS_LIMIT; use multivm::MultivmTracer; use zksync_dal::ConnectionPool; @@ -169,7 +170,7 @@ async fn execute_tx_in_sandbox( let custom_tracers: Vec<_> = custom_tracers .into_iter() .map(|tracer| tracer.into_boxed()) - .chain(vec![storage_invocation_tracer.into_boxed()]) + .chain(vec![storage_invocation_tracer.into_tracer_pointer()]) .collect(); vm.inspect_next_transaction(custom_tracers) }, diff --git a/core/lib/zksync_core/src/api_server/execution_sandbox/tracers.rs b/core/lib/zksync_core/src/api_server/execution_sandbox/tracers.rs index 47df6676767..ac675eee707 100644 --- a/core/lib/zksync_core/src/api_server/execution_sandbox/tracers.rs +++ b/core/lib/zksync_core/src/api_server/execution_sandbox/tracers.rs @@ -1,6 +1,8 @@ -use multivm::vm_latest::{CallTracer, HistoryMode}; -use multivm::MultivmTracer; +use multivm::tracers::CallTracer; +use multivm::vm_latest::HistoryMode; +use multivm::{MultiVmTracerPointer, MultivmTracer}; use once_cell::sync::OnceCell; + use std::sync::Arc; use zksync_state::WriteStorage; use zksync_types::vm_trace::Call; @@ -17,9 +19,9 @@ impl ApiTracer { H: HistoryMode + multivm::HistoryMode + 'static, >( self, - ) -> Box> { + ) -> MultiVmTracerPointer { match self { - ApiTracer::CallTracer(tracer) => CallTracer::new(tracer, H::default()).into_boxed(), + ApiTracer::CallTracer(tracer) => CallTracer::new(tracer.clone()).into_tracer_pointer(), } } } diff --git a/core/lib/zksync_core/src/api_server/execution_sandbox/validate.rs b/core/lib/zksync_core/src/api_server/execution_sandbox/validate.rs index 2400ada0da5..214f7d13bcd 100644 --- a/core/lib/zksync_core/src/api_server/execution_sandbox/validate.rs +++ b/core/lib/zksync_core/src/api_server/execution_sandbox/validate.rs @@ -1,10 +1,12 @@ use multivm::interface::ExecutionResult; +use multivm::MultivmTracer; use std::collections::HashSet; -use multivm::vm_latest::{ - HistoryDisabled, StorageInvocations, ValidationError, ValidationTracer, ValidationTracerParams, +use multivm::tracers::{ + validator::{ValidationError, ValidationTracer, ValidationTracerParams}, + StorageInvocations, }; -use multivm::MultivmTracer; +use multivm::vm_latest::HistoryDisabled; use zksync_dal::{ConnectionPool, StorageProcessor}; use zksync_types::{l2::L2Tx, Transaction, TRUSTED_ADDRESS_SLOTS, TRUSTED_TOKEN_SLOTS, U256}; @@ -81,9 +83,9 @@ impl TxSharedArgs { ValidationTracer::::new(validation_params); let result = vm.inspect_next_transaction(vec![ - tracer.into_boxed(), + tracer.into_tracer_pointer(), StorageInvocations::new(execution_args.missed_storage_invocation_limit) - .into_boxed(), + .into_tracer_pointer(), ]); let result = match (result.result, validation_result.get()) { diff --git a/core/lib/zksync_core/src/api_server/tx_sender/result.rs b/core/lib/zksync_core/src/api_server/tx_sender/result.rs index 5222921fde8..b02049f014e 100644 --- a/core/lib/zksync_core/src/api_server/tx_sender/result.rs +++ b/core/lib/zksync_core/src/api_server/tx_sender/result.rs @@ -2,7 +2,7 @@ use crate::api_server::execution_sandbox::SandboxExecutionError; use thiserror::Error; use multivm::interface::{ExecutionResult, VmExecutionResultAndLogs}; -use multivm::vm_latest::ValidationError; +use multivm::tracers::validator::ValidationError; use zksync_types::l2::error::TxCheckError; use zksync_types::U256; diff --git a/core/lib/zksync_core/src/state_keeper/batch_executor/mod.rs b/core/lib/zksync_core/src/state_keeper/batch_executor/mod.rs index d116f03b1ff..4785acbf893 100644 --- a/core/lib/zksync_core/src/state_keeper/batch_executor/mod.rs +++ b/core/lib/zksync_core/src/state_keeper/batch_executor/mod.rs @@ -5,14 +5,16 @@ use tokio::{ task::JoinHandle, }; +use multivm::MultivmTracer; use std::{fmt, sync::Arc}; use multivm::interface::{ ExecutionResult, FinishedL1Batch, Halt, L1BatchEnv, L2BlockEnv, SystemEnv, VmExecutionResultAndLogs, }; -use multivm::vm_latest::{CallTracer, HistoryEnabled}; -use multivm::{MultivmTracer, VmInstance}; +use multivm::tracers::CallTracer; +use multivm::vm_latest::HistoryEnabled; +use multivm::VmInstance; use zksync_dal::ConnectionPool; use zksync_state::{ReadStorage, RocksdbStorage, StorageView}; use zksync_types::{vm_trace::Call, witness_block_state::WitnessBlockState, Transaction, U256}; @@ -456,13 +458,14 @@ impl BatchExecutor { vm.make_snapshot(); let call_tracer_result = Arc::new(OnceCell::default()); - let custom_tracers = if self.save_call_traces { - vec![CallTracer::new(call_tracer_result.clone(), HistoryEnabled).into_boxed()] + let tracer = if self.save_call_traces { + vec![CallTracer::new(call_tracer_result.clone()).into_tracer_pointer()] } else { vec![] }; + if let Ok(result) = - vm.inspect_transaction_with_bytecode_compression(custom_tracers, tx.clone(), true) + vm.inspect_transaction_with_bytecode_compression(tracer, tx.clone(), true) { let compressed_bytecodes = vm.get_last_tx_compressed_bytecodes(); vm.pop_snapshot_no_rollback(); @@ -473,16 +476,17 @@ impl BatchExecutor { .unwrap_or_default(); return (result, compressed_bytecodes, trace); } + vm.rollback_to_the_latest_snapshot(); let call_tracer_result = Arc::new(OnceCell::default()); - let custom_tracers = if self.save_call_traces { - vec![CallTracer::new(call_tracer_result.clone(), HistoryEnabled).into_boxed()] + let tracer = if self.save_call_traces { + vec![CallTracer::new(call_tracer_result.clone()).into_tracer_pointer()] } else { vec![] }; - vm.rollback_to_the_latest_snapshot(); + let result = vm - .inspect_transaction_with_bytecode_compression(custom_tracers, tx.clone(), false) + .inspect_transaction_with_bytecode_compression(tracer, tx.clone(), false) .expect("Compression can't fail if we don't apply it"); let compressed_bytecodes = vm.get_last_tx_compressed_bytecodes(); diff --git a/core/tests/vm-benchmark/harness/src/lib.rs b/core/tests/vm-benchmark/harness/src/lib.rs index d911addbf7c..b7da44aed92 100644 --- a/core/tests/vm-benchmark/harness/src/lib.rs +++ b/core/tests/vm-benchmark/harness/src/lib.rs @@ -1,4 +1,6 @@ -use multivm::interface::{L2BlockEnv, TxExecutionMode, VmExecutionMode, VmExecutionResultAndLogs}; +use multivm::interface::{ + L2BlockEnv, TxExecutionMode, VmExecutionMode, VmExecutionResultAndLogs, VmInterface, +}; use multivm::vm_latest::{constants::BLOCK_GAS_LIMIT, HistoryEnabled, Vm}; use once_cell::sync::Lazy; use std::{cell::RefCell, rc::Rc}; @@ -85,7 +87,6 @@ impl BenchmarkingVm { chain_id: L2ChainId::from(270), }, Rc::new(RefCell::new(StorageView::new(&*STORAGE))), - HistoryEnabled, )) }