diff --git a/core/lib/multivm/src/tracers/mod.rs b/core/lib/multivm/src/tracers/mod.rs index 8d858ae03ed5..3090628fcac7 100644 --- a/core/lib/multivm/src/tracers/mod.rs +++ b/core/lib/multivm/src/tracers/mod.rs @@ -1,9 +1,11 @@ pub mod call_tracer; mod multivm_dispatcher; pub mod old_tracers; +pub mod prestate_tracer; pub mod storage_invocation; pub mod validator; pub use call_tracer::CallTracer; pub use multivm_dispatcher::TracerDispatcher; +pub use prestate_tracer::PrestateTracer; pub use storage_invocation::StorageInvocations; diff --git a/core/lib/multivm/src/tracers/prestate_tracer/mod.rs b/core/lib/multivm/src/tracers/prestate_tracer/mod.rs new file mode 100644 index 000000000000..9bc7bd021ce8 --- /dev/null +++ b/core/lib/multivm/src/tracers/prestate_tracer/mod.rs @@ -0,0 +1,167 @@ +use std::{collections::HashMap, fmt, sync::Arc}; + +use once_cell::sync::OnceCell; +use zksync_state::{StoragePtr, WriteStorage}; +use zksync_types::{ + get_code_key, get_nonce_key, web3::signing::keccak256, AccountTreeId, Address, StorageKey, + StorageValue, H160, H256, L2_ETH_TOKEN_ADDRESS, U256, +}; +use zksync_utils::{address_to_h256, h256_to_u256}; + +pub mod vm_1_4_1; +pub mod vm_latest; +pub mod vm_refunds_enhancement; +pub mod vm_virtual_blocks; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Account { + pub balance: Option, + pub code: Option, + pub nonce: Option, + pub storage: Option>, +} + +impl fmt::Display for Account { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "{{")?; + if let Some(balance) = self.balance { + writeln!(f, " balance: \"0x{:x}\",", balance)?; + } + if let Some(code) = &self.code { + writeln!(f, " code: \"{}\",", code)?; + } + if let Some(nonce) = self.nonce { + writeln!(f, " nonce: {},", nonce)?; + } + if let Some(storage) = &self.storage { + writeln!(f, " storage: {{")?; + for (key, value) in storage.iter() { + writeln!(f, " {}: \"{}\",", key, value)?; + } + writeln!(f, " }}")?; + } + writeln!(f, "}}") + } +} + +type State = HashMap; + +#[derive(Debug, Clone)] +pub struct PrestateTracer { + pub pre: State, + pub post: State, + pub config: PrestateTracerConfig, + pub result: Arc>, +} + +impl PrestateTracer { + pub fn new(diff_mode: bool, result: Arc>) -> Self { + Self { + pre: Default::default(), + post: Default::default(), + config: PrestateTracerConfig { diff_mode }, + result, + } + } +} + +#[derive(Debug, Clone)] +pub struct PrestateTracerConfig { + diff_mode: bool, +} + +pub fn process_modified_storage_keys( + prestate: State, + storage: &StoragePtr, +) -> HashMap +where + S: WriteStorage, +{ + let cloned_storage = &storage.clone(); + let mut initial_storage_ref = cloned_storage.as_ref().borrow_mut(); + + initial_storage_ref + .modified_storage_keys() + .clone() + .iter() + .filter(|k| !prestate.contains_key(k.0.account().address())) + .map(|k| { + ( + *(k.0.account().address()), + Account { + balance: Some(h256_to_u256( + initial_storage_ref.read_value(&get_balance_key(k.0.account())), + )), + code: Some(h256_to_u256( + initial_storage_ref.read_value(&get_code_key(k.0.account().address())), + )), + nonce: Some(h256_to_u256( + initial_storage_ref.read_value(&get_nonce_key(k.0.account().address())), + )), + storage: Some(get_storage_if_present( + k.0.account(), + initial_storage_ref.modified_storage_keys(), + )), + }, + ) + }) + .collect::() +} + +fn get_balance_key(account: &AccountTreeId) -> StorageKey { + let address_h256 = address_to_h256(account.address()); + let bytes = [address_h256.as_bytes(), &[0; 32]].concat(); + let balance_key: H256 = keccak256(&bytes).into(); + StorageKey::new(AccountTreeId::new(L2_ETH_TOKEN_ADDRESS), balance_key) +} + +fn get_storage_if_present( + account: &AccountTreeId, + modified_storage_keys: &HashMap, +) -> HashMap { + //check if there is a Storage Key struct with an account field that matches the account and return the key as the key and the Storage Value as the value + modified_storage_keys + .iter() + .filter(|(k, _)| k.account() == account) + .map(|(k, v)| (*k.key(), *v)) + .collect() +} + +fn process_result(result: &Arc>, mut pre: State, post: State) { + pre.retain(|k, v| { + if let Some(post_v) = post.get(k) { + if v != post_v { + return true; + } + } + false + }); + result.set((pre, post)).unwrap(); +} + +fn get_account_data( + account_key: &StorageKey, + state: &T, + storage: &HashMap, +) -> (Address, Account) { + let address = *(account_key.account().address()); + let balance = state.read_from_storage(&get_balance_key(account_key.account())); + let code = state.read_from_storage(&get_code_key(account_key.account().address())); + let nonce = state.read_from_storage(&get_nonce_key(account_key.account().address())); + let storage = get_storage_if_present(account_key.account(), storage); + + ( + address, + Account { + balance: Some(balance), + code: Some(code), + nonce: Some(nonce), + storage: Some(storage), + }, + ) +} + +// Define a trait that abstracts storage access +trait StorageAccess { + fn read_from_storage(&self, key: &StorageKey) -> U256; +} diff --git a/core/lib/multivm/src/tracers/prestate_tracer/vm_1_4_1/mod.rs b/core/lib/multivm/src/tracers/prestate_tracer/vm_1_4_1/mod.rs new file mode 100644 index 000000000000..f2080b2740fe --- /dev/null +++ b/core/lib/multivm/src/tracers/prestate_tracer/vm_1_4_1/mod.rs @@ -0,0 +1,59 @@ +use zk_evm_1_4_1::tracing::{BeforeExecutionData, VmLocalStateData}; +use zksync_state::{StoragePtr, WriteStorage}; +use zksync_types::StorageKey; + +use super::{ + get_account_data, process_modified_storage_keys, process_result, PrestateTracer, State, + StorageAccess, +}; +use crate::{ + interface::dyn_tracers::vm_1_4_1::DynTracer, + tracers::prestate_tracer::U256, + vm_1_4_1::{BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState}, +}; +impl DynTracer> for PrestateTracer { + fn before_execution( + &mut self, + _state: VmLocalStateData<'_>, + _data: BeforeExecutionData, + _memory: &SimpleMemory, + storage: StoragePtr, + ) { + if self.config.diff_mode { + self.pre + .extend(process_modified_storage_keys(self.pre.clone(), &storage)); + } + } +} + +impl VmTracer for PrestateTracer { + fn after_vm_execution( + &mut self, + state: &mut ZkSyncVmState, + _bootloader_state: &BootloaderState, + _stop_reason: crate::interface::tracer::VmExecutionStopReason, + ) { + let modified_storage_keys = state.storage.storage.inner().get_modified_storage_keys(); + if self.config.diff_mode { + self.post = modified_storage_keys + .iter() + .map(|k| get_account_data(k.0, state, &modified_storage_keys)) + .collect::(); + } else { + let read_keys = &state.storage.read_keys; + let map = read_keys.inner().clone(); + let res = map + .iter() + .map(|k| get_account_data(k.0, state, &modified_storage_keys)) + .collect::(); + self.post = res; + } + process_result(&self.result, self.pre.clone(), self.post.clone()); + } +} + +impl StorageAccess for ZkSyncVmState { + fn read_from_storage(&self, key: &StorageKey) -> U256 { + self.storage.storage.read_from_storage(key) + } +} diff --git a/core/lib/multivm/src/tracers/prestate_tracer/vm_latest/mod.rs b/core/lib/multivm/src/tracers/prestate_tracer/vm_latest/mod.rs new file mode 100644 index 000000000000..ceb11005456e --- /dev/null +++ b/core/lib/multivm/src/tracers/prestate_tracer/vm_latest/mod.rs @@ -0,0 +1,60 @@ +use zk_evm_1_4_1::tracing::{BeforeExecutionData, VmLocalStateData}; +use zksync_state::{StoragePtr, WriteStorage}; +use zksync_types::StorageKey; + +use super::{ + get_account_data, process_modified_storage_keys, process_result, PrestateTracer, State, + StorageAccess, +}; +use crate::{ + interface::dyn_tracers::vm_1_4_1::DynTracer, + tracers::prestate_tracer::U256, + vm_latest::{BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState}, +}; + +impl DynTracer> for PrestateTracer { + fn before_execution( + &mut self, + _state: VmLocalStateData<'_>, + _data: BeforeExecutionData, + _memory: &SimpleMemory, + storage: StoragePtr, + ) { + if self.config.diff_mode { + self.pre + .extend(process_modified_storage_keys(self.pre.clone(), &storage)); + } + } +} + +impl VmTracer for PrestateTracer { + fn after_vm_execution( + &mut self, + state: &mut ZkSyncVmState, + _bootloader_state: &BootloaderState, + _stop_reason: crate::interface::tracer::VmExecutionStopReason, + ) { + let modified_storage_keys = state.storage.storage.inner().get_modified_storage_keys(); + if self.config.diff_mode { + self.post = modified_storage_keys + .iter() + .map(|k| get_account_data(k.0, state, &modified_storage_keys)) + .collect::(); + } else { + let read_keys = &state.storage.read_keys; + let map = read_keys.inner().clone(); + let res = map + .iter() + .map(|k| get_account_data(k.0, state, &modified_storage_keys)) + .collect::(); + self.post = res; + } + process_result(&self.result, self.pre.clone(), self.post.clone()); + } +} + +impl StorageAccess for ZkSyncVmState { + fn read_from_storage(&self, key: &StorageKey) -> U256 { + self.storage.storage.read_from_storage(key) + } +} diff --git a/core/lib/multivm/src/tracers/prestate_tracer/vm_refunds_enhancement/mod.rs b/core/lib/multivm/src/tracers/prestate_tracer/vm_refunds_enhancement/mod.rs new file mode 100644 index 000000000000..970b0a8387ba --- /dev/null +++ b/core/lib/multivm/src/tracers/prestate_tracer/vm_refunds_enhancement/mod.rs @@ -0,0 +1,70 @@ +use std::collections::HashMap; + +use zk_evm_1_3_3::tracing::{BeforeExecutionData, VmLocalStateData}; +use zksync_state::{StoragePtr, WriteStorage}; +use zksync_types::{StorageKey, H256}; + +use super::{ + get_account_data, process_modified_storage_keys, process_result, PrestateTracer, State, + StorageAccess, +}; +use crate::{ + interface::dyn_tracers::vm_1_3_3::DynTracer, + tracers::prestate_tracer::U256, + vm_refunds_enhancement::{BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState}, +}; + +impl DynTracer> for PrestateTracer { + fn before_execution( + &mut self, + _state: VmLocalStateData<'_>, + _data: BeforeExecutionData, + _memory: &SimpleMemory, + storage: StoragePtr, + ) { + if self.config.diff_mode { + self.pre + .extend(process_modified_storage_keys(self.pre.clone(), &storage)); + } + } +} + +impl VmTracer for PrestateTracer { + fn after_vm_execution( + &mut self, + state: &mut ZkSyncVmState, + _bootloader_state: &BootloaderState, + _stop_reason: crate::interface::tracer::VmExecutionStopReason, + ) { + let modified_storage_keys = state.storage.storage.inner().get_modified_storage_keys(); + if self.config.diff_mode { + self.post = modified_storage_keys + .iter() + .map(|k| get_account_data(k.0, state, &modified_storage_keys)) + .collect::(); + } else { + let read_keys: &HashMap = &state + .storage + .storage + .inner() + .get_ptr() + .borrow() + .read_storage_keys() + .iter() + .map(|(k, v)| (*k, *v)) + .collect(); + let res = read_keys + .iter() + .map(|k| get_account_data(k.0, state, &modified_storage_keys)) + .collect::(); + self.post = res; + } + process_result(&self.result, self.pre.clone(), self.post.clone()); + } +} + +impl StorageAccess for ZkSyncVmState { + fn read_from_storage(&self, key: &StorageKey) -> U256 { + self.storage.storage.read_from_storage(key) + } +} diff --git a/core/lib/multivm/src/tracers/prestate_tracer/vm_virtual_blocks/mod.rs b/core/lib/multivm/src/tracers/prestate_tracer/vm_virtual_blocks/mod.rs new file mode 100644 index 000000000000..34c60a6bc07a --- /dev/null +++ b/core/lib/multivm/src/tracers/prestate_tracer/vm_virtual_blocks/mod.rs @@ -0,0 +1,76 @@ +use std::collections::HashMap; + +use zk_evm_1_3_3::tracing::{BeforeExecutionData, VmLocalStateData}; +use zksync_state::{StoragePtr, WriteStorage}; +use zksync_types::{StorageKey, H256}; + +use super::{ + get_account_data, process_modified_storage_keys, process_result, PrestateTracer, State, + StorageAccess, +}; +use crate::{ + interface::dyn_tracers::vm_1_3_3::DynTracer, + tracers::prestate_tracer::U256, + vm_virtual_blocks::{ + BootloaderState, ExecutionEndTracer, ExecutionProcessing, HistoryMode, SimpleMemory, + ZkSyncVmState, + }, +}; + +impl DynTracer> for PrestateTracer { + fn before_execution( + &mut self, + _state: VmLocalStateData<'_>, + _data: BeforeExecutionData, + _memory: &SimpleMemory, + storage: StoragePtr, + ) { + if self.config.diff_mode { + self.pre + .extend(process_modified_storage_keys(self.pre.clone(), &storage)); + } + } +} + +impl ExecutionEndTracer for PrestateTracer {} + +impl ExecutionProcessing for PrestateTracer { + fn after_vm_execution( + &mut self, + state: &mut ZkSyncVmState, + _bootloader_state: &BootloaderState, + _stop_reason: crate::interface::tracer::VmExecutionStopReason, + ) { + let modified_storage_keys = state.storage.storage.inner().get_modified_storage_keys(); + if self.config.diff_mode { + self.post = modified_storage_keys + .iter() + .map(|k| get_account_data(k.0, state, &modified_storage_keys)) + .collect::(); + } else { + let read_keys: &HashMap = &state + .storage + .storage + .inner() + .get_ptr() + .borrow() + .read_storage_keys() + .iter() + .map(|(k, v)| (*k, *v)) + .collect(); + + let res = read_keys + .iter() + .map(|k| get_account_data(k.0, state, &modified_storage_keys)) + .collect::(); + self.post = res; + } + process_result(&self.result, self.pre.clone(), self.post.clone()); + } +} + +impl StorageAccess for ZkSyncVmState { + fn read_from_storage(&self, key: &StorageKey) -> U256 { + self.storage.storage.read_from_storage(key) + } +} diff --git a/core/lib/multivm/src/versions/vm_1_4_1/old_vm/history_recorder.rs b/core/lib/multivm/src/versions/vm_1_4_1/old_vm/history_recorder.rs index 7a0b3f45edf3..e9e2d51632dc 100644 --- a/core/lib/multivm/src/versions/vm_1_4_1/old_vm/history_recorder.rs +++ b/core/lib/multivm/src/versions/vm_1_4_1/old_vm/history_recorder.rs @@ -6,7 +6,7 @@ use zk_evm_1_4_1::{ zkevm_opcode_defs::{self}, }; use zksync_state::{StoragePtr, WriteStorage}; -use zksync_types::{StorageKey, U256}; +use zksync_types::{StorageKey, H256, U256}; use zksync_utils::{h256_to_u256, u256_to_h256}; pub(crate) type MemoryWithHistory = HistoryRecorder; @@ -720,6 +720,15 @@ impl StorageWrapper { pub fn read_from_storage(&self, key: &StorageKey) -> U256 { h256_to_u256(self.storage_ptr.borrow_mut().read_value(key)) } + + pub fn get_modified_storage_keys(&self) -> HashMap { + self.storage_ptr + .borrow() + .modified_storage_keys() + .iter() + .map(|(k, v)| (*k, *v)) + .collect() + } } #[derive(Debug, Clone)] diff --git a/core/lib/multivm/src/versions/vm_latest/old_vm/history_recorder.rs b/core/lib/multivm/src/versions/vm_latest/old_vm/history_recorder.rs index d4c5b6367a9b..a8e7d46bba55 100644 --- a/core/lib/multivm/src/versions/vm_latest/old_vm/history_recorder.rs +++ b/core/lib/multivm/src/versions/vm_latest/old_vm/history_recorder.rs @@ -6,7 +6,7 @@ use zk_evm_1_4_1::{ zkevm_opcode_defs::{self}, }; use zksync_state::{StoragePtr, WriteStorage}; -use zksync_types::{StorageKey, U256}; +use zksync_types::{StorageKey, H256, U256}; use zksync_utils::{h256_to_u256, u256_to_h256}; pub(crate) type MemoryWithHistory = HistoryRecorder; @@ -720,6 +720,15 @@ impl StorageWrapper { pub fn read_from_storage(&self, key: &StorageKey) -> U256 { h256_to_u256(self.storage_ptr.borrow_mut().read_value(key)) } + + pub fn get_modified_storage_keys(&self) -> HashMap { + self.storage_ptr + .borrow() + .modified_storage_keys() + .iter() + .map(|(k, v)| (*k, *v)) + .collect() + } } #[derive(Debug, Clone)] diff --git a/core/lib/multivm/src/versions/vm_latest/tests/mod.rs b/core/lib/multivm/src/versions/vm_latest/tests/mod.rs index a07608121bc1..f3443f00e68b 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/mod.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/mod.rs @@ -13,6 +13,7 @@ mod l1_tx_execution; mod l2_blocks; mod nonce_holder; mod precompiles; +mod prestate_tracer; mod refunds; mod require_eip712; mod rollbacks; diff --git a/core/lib/multivm/src/versions/vm_latest/tests/prestate_tracer.rs b/core/lib/multivm/src/versions/vm_latest/tests/prestate_tracer.rs new file mode 100644 index 000000000000..86ae7f54972f --- /dev/null +++ b/core/lib/multivm/src/versions/vm_latest/tests/prestate_tracer.rs @@ -0,0 +1,143 @@ +use std::sync::Arc; + +use once_cell::sync::OnceCell; +use zksync_test_account::TxType; +use zksync_types::{utils::deployed_address_create, Execute, U256}; + +use crate::{ + interface::{TxExecutionMode, VmExecutionMode, VmInterface}, + tracers::PrestateTracer, + vm_latest::{ + constants::BLOCK_GAS_LIMIT, + tests::{tester::VmTesterBuilder, utils::read_simple_transfer_contract}, + HistoryEnabled, ToTracerPointer, + }, +}; + +#[test] +fn test_prestate_tracer() { + let mut vm = VmTesterBuilder::new(HistoryEnabled) + .with_empty_in_memory_storage() + .with_random_rich_accounts(1) + .with_deployer() + .with_gas_limit(BLOCK_GAS_LIMIT) + .with_execution_mode(TxExecutionMode::VerifyExecute) + .build(); + + vm.deploy_test_contract(); + let account = &mut vm.rich_accounts[0]; + + let tx1 = account.get_test_contract_transaction( + vm.test_contract.unwrap(), + false, + Default::default(), + true, + TxType::L2, + ); + vm.vm.push_transaction(tx1); + + let contract_address = vm.test_contract.unwrap(); + let prestate_tracer_result = Arc::new(OnceCell::default()); + let prestate_tracer = PrestateTracer::new(false, prestate_tracer_result.clone()); + let tracer_ptr = prestate_tracer.into_tracer_pointer(); + vm.vm.inspect(tracer_ptr.into(), VmExecutionMode::Batch); + + let prestate_result = Arc::try_unwrap(prestate_tracer_result) + .unwrap() + .take() + .unwrap_or_default(); + + assert!(prestate_result.1.contains_key(&contract_address)); +} + +#[test] +fn test_prestate_tracer_diff_mode() { + let mut vm = VmTesterBuilder::new(HistoryEnabled) + .with_empty_in_memory_storage() + .with_random_rich_accounts(1) + .with_deployer() + .with_gas_limit(BLOCK_GAS_LIMIT) + .with_execution_mode(TxExecutionMode::VerifyExecute) + .build(); + let contract = read_simple_transfer_contract(); + let tx = vm + .deployer + .as_mut() + .expect("You have to initialize builder with deployer") + .get_deploy_tx(&contract, None, TxType::L2) + .tx; + let nonce = tx.nonce().unwrap().0.into(); + vm.vm.push_transaction(tx); + vm.vm.execute(VmExecutionMode::OneTx); + let deployed_address = deployed_address_create(vm.deployer.as_ref().unwrap().address, nonce); + vm.test_contract = Some(deployed_address); + + // Deploy a second copy of the contract to see its appearance in the pre-state + let tx2 = vm + .deployer + .as_mut() + .expect("You have to initialize builder with deployer") + .get_deploy_tx(&contract, None, TxType::L2) + .tx; + let nonce2 = tx2.nonce().unwrap().0.into(); + vm.vm.push_transaction(tx2); + vm.vm.execute(VmExecutionMode::OneTx); + let deployed_address2 = deployed_address_create(vm.deployer.as_ref().unwrap().address, nonce2); + + let account = &mut vm.rich_accounts[0]; + + //enter ether to contract to see difference in the balance post execution + let tx0 = Execute { + contract_address: vm.test_contract.unwrap(), + calldata: Default::default(), + value: U256::from(100000), + factory_deps: None, + }; + + vm.vm + .push_transaction(account.get_l2_tx_for_execute(tx0.clone(), None)); + + let tx1 = Execute { + contract_address: deployed_address2, + calldata: Default::default(), + value: U256::from(200000), + factory_deps: None, + }; + + vm.vm + .push_transaction(account.get_l2_tx_for_execute(tx1, None)); + let prestate_tracer_result = Arc::new(OnceCell::default()); + let prestate_tracer = PrestateTracer::new(true, prestate_tracer_result.clone()); + let tracer_ptr = prestate_tracer.into_tracer_pointer(); + vm.vm + .inspect(tracer_ptr.into(), VmExecutionMode::Bootloader); + + let prestate_result = Arc::try_unwrap(prestate_tracer_result) + .unwrap() + .take() + .unwrap_or_default(); + + //assert that the pre-state contains both deployed contracts with balance zero + assert!(prestate_result.0.contains_key(&deployed_address)); + assert!(prestate_result.0.contains_key(&deployed_address2)); + assert_eq!( + prestate_result.0[&deployed_address].balance, + Some(U256::zero()) + ); + assert_eq!( + prestate_result.0[&deployed_address2].balance, + Some(U256::zero()) + ); + + //assert that the post-state contains both deployed contracts with the correct balance + assert!(prestate_result.1.contains_key(&deployed_address)); + assert!(prestate_result.1.contains_key(&deployed_address2)); + assert_eq!( + prestate_result.1[&deployed_address].balance, + Some(U256::from(100000)) + ); + assert_eq!( + prestate_result.1[&deployed_address2].balance, + Some(U256::from(200000)) + ); +} diff --git a/core/lib/multivm/src/versions/vm_latest/tests/utils.rs b/core/lib/multivm/src/versions/vm_latest/tests/utils.rs index 80d59ab709f5..eeab7a91f51f 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/utils.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/utils.rs @@ -81,6 +81,12 @@ pub(crate) fn read_error_contract() -> Vec { ) } +pub(crate) fn read_simple_transfer_contract() -> Vec { + read_bytecode( + "etc/contracts-test-data/artifacts-zk/contracts/simple-transfer/simple-transfer.sol/SimpleTransfer.json", + ) +} + pub(crate) fn get_execute_error_calldata() -> Vec { let test_contract = load_contract( "etc/contracts-test-data/artifacts-zk/contracts/error/error.sol/SimpleRequire.json", 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 old mode 100644 new mode 100755 index 0c61bae00a5d..020e78bbe161 --- a/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs @@ -67,7 +67,6 @@ pub(crate) struct DefaultExecutionTracer { // It only takes into account circuits that are generated for actual execution. It doesn't // take into account e.g circuits produced by the initial bootloader memory commitment. pub(crate) circuits_tracer: CircuitsTracer, - storage: StoragePtr, _phantom: PhantomData, } diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/mod.rs b/core/lib/multivm/src/versions/vm_latest/tracers/mod.rs old mode 100644 new mode 100755 diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/history_recorder.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/history_recorder.rs index e862f57898ae..682a8264faee 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/history_recorder.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/history_recorder.rs @@ -6,7 +6,7 @@ use zk_evm_1_3_3::{ zkevm_opcode_defs::{self}, }; use zksync_state::{StoragePtr, WriteStorage}; -use zksync_types::{StorageKey, U256}; +use zksync_types::{StorageKey, H256, U256}; use zksync_utils::{h256_to_u256, u256_to_h256}; pub(crate) type MemoryWithHistory = HistoryRecorder; @@ -720,6 +720,15 @@ impl StorageWrapper { pub fn read_from_storage(&self, key: &StorageKey) -> U256 { h256_to_u256(self.storage_ptr.borrow_mut().read_value(key)) } + + pub fn get_modified_storage_keys(&self) -> HashMap { + self.storage_ptr + .borrow() + .modified_storage_keys() + .iter() + .map(|(k, v)| (*k, *v)) + .collect() + } } #[derive(Debug, Clone)] diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/oracles/storage.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/oracles/storage.rs index 40b2d83030a2..59ed4f9450e5 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/oracles/storage.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/oracles/storage.rs @@ -12,15 +12,18 @@ use zksync_types::{ }; use zksync_utils::u256_to_h256; -use crate::vm_refunds_enhancement::{ - old_vm::{ - history_recorder::{ - AppDataFrameManagerWithHistory, HashMapHistoryEvent, HistoryEnabled, HistoryMode, - HistoryRecorder, StorageWrapper, VectorHistoryEvent, WithHistory, +use crate::{ + glue::GlueInto, + vm_refunds_enhancement::{ + old_vm::{ + history_recorder::{ + AppDataFrameManagerWithHistory, HashMapHistoryEvent, HistoryEnabled, HistoryMode, + HistoryRecorder, StorageWrapper, VectorHistoryEvent, WithHistory, + }, + oracles::OracleWithHistory, }, - oracles::OracleWithHistory, + utils::logs::StorageLogQuery, }, - utils::logs::StorageLogQuery, }; // While the storage does not support different shards, it was decided to write the @@ -104,7 +107,7 @@ impl StorageOracle { self.frames_stack.push_forward( Box::new(StorageLogQuery { - log_query: query, + log_query: query.glue_into(), log_type: StorageLogQueryType::Read, }), query.timestamp, diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/history_recorder.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/history_recorder.rs index baed63c14b89..664de3a90037 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/history_recorder.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/history_recorder.rs @@ -6,7 +6,7 @@ use zk_evm_1_3_3::{ zkevm_opcode_defs::{self}, }; use zksync_state::{StoragePtr, WriteStorage}; -use zksync_types::{StorageKey, U256}; +use zksync_types::{StorageKey, H256, U256}; use zksync_utils::{h256_to_u256, u256_to_h256}; pub(crate) type MemoryWithHistory = HistoryRecorder; @@ -716,6 +716,15 @@ impl StorageWrapper { pub fn read_from_storage(&self, key: &StorageKey) -> U256 { h256_to_u256(self.storage_ptr.borrow_mut().read_value(key)) } + + pub fn get_modified_storage_keys(&self) -> HashMap { + self.storage_ptr + .borrow() + .modified_storage_keys() + .iter() + .map(|(k, v)| (*k, *v)) + .collect() + } } #[derive(Debug, Clone)] diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/oracles/storage.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/oracles/storage.rs index b099930cbed4..423abfd1c4a2 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/oracles/storage.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/oracles/storage.rs @@ -13,12 +13,15 @@ use zksync_types::{ use zksync_utils::u256_to_h256; use super::OracleWithHistory; -use crate::vm_virtual_blocks::{ - old_vm::history_recorder::{ - AppDataFrameManagerWithHistory, HashMapHistoryEvent, HistoryEnabled, HistoryMode, - HistoryRecorder, StorageWrapper, WithHistory, +use crate::{ + glue::GlueInto, + vm_virtual_blocks::{ + old_vm::history_recorder::{ + AppDataFrameManagerWithHistory, HashMapHistoryEvent, HistoryEnabled, HistoryMode, + HistoryRecorder, StorageWrapper, WithHistory, + }, + utils::logs::StorageLogQuery, }, - utils::logs::StorageLogQuery, }; // While the storage does not support different shards, it was decided to write the @@ -83,7 +86,7 @@ impl StorageOracle { self.frames_stack.push_forward( Box::new(StorageLogQuery { - log_query: query, + log_query: query.glue_into(), log_type: StorageLogQueryType::Read, }), query.timestamp, diff --git a/core/lib/state/src/lib.rs b/core/lib/state/src/lib.rs index 84e487ad3f7a..a037ce2be804 100644 --- a/core/lib/state/src/lib.rs +++ b/core/lib/state/src/lib.rs @@ -64,6 +64,9 @@ pub trait ReadStorage: fmt::Debug { /// /// So far, this trait is implemented only for [`StorageView`]. pub trait WriteStorage: ReadStorage { + /// Returns the map with the key–value pairs read by this batch. + fn read_storage_keys(&self) -> &HashMap; + /// Sets the new value under a given key and returns the previous value. fn set_value(&mut self, key: StorageKey, value: StorageValue) -> StorageValue; diff --git a/core/lib/state/src/storage_view.rs b/core/lib/state/src/storage_view.rs index 543b41bc6576..2db96cb73fcc 100644 --- a/core/lib/state/src/storage_view.rs +++ b/core/lib/state/src/storage_view.rs @@ -60,6 +60,11 @@ impl StorageView { is_write_initial: self.initial_writes_cache.clone(), } } + + /// Returns the modified storage keys + pub fn modified_storage_keys(&self) -> &HashMap { + &self.modified_storage_keys + } } impl ReadStorage for Box @@ -175,6 +180,10 @@ impl ReadStorage for StorageView { } impl WriteStorage for StorageView { + fn read_storage_keys(&self) -> &HashMap { + &self.read_storage_keys + } + fn set_value(&mut self, key: StorageKey, value: StorageValue) -> StorageValue { let started_at = Instant::now(); self.metrics.set_value_storage_invocations += 1; diff --git a/etc/contracts-test-data/contracts/simple-transfer/simple-transfer.sol b/etc/contracts-test-data/contracts/simple-transfer/simple-transfer.sol new file mode 100644 index 000000000000..591e97cc1ae9 --- /dev/null +++ b/etc/contracts-test-data/contracts/simple-transfer/simple-transfer.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract SimpleTransfer { + address public owner; + + constructor() { + owner = msg.sender; // Set the contract creator as the owner + } + + // This function allows the contract to receive Ether with msg.data being empty + receive() external payable {} + + modifier onlyOwner() { + require(msg.sender == owner, "Only the owner can execute this."); + _; + } + + // Function to withdraw Ether to the owner's address + function withdraw(uint _amount) public onlyOwner { + require(address(this).balance >= _amount, "Insufficient balance in contract"); + payable(owner).transfer(_amount); + } + + // Function to transfer Ether from this contract to any address + function transfer(address _to, uint _amount) public onlyOwner { + require(address(this).balance >= _amount, "Insufficient balance in contract"); + payable(_to).transfer(_amount); + } + + // Function to check the contract's balance + function getBalance() public view returns (uint) { + return address(this).balance; + } +}