From 87b02e3ab5c1f61d59dd0f0eefa9ec33a7b55488 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Wed, 4 Sep 2024 09:36:02 +0300 Subject: [PATCH] feat: Integrate tracers and implement circuits tracer in vm2 (#2653) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Integrates tracers support into the codebase and implements the circuits tracer. ## Why ❔ Tracers are required for some VM applications, e.g. to determine batch seal criteria and for tracing calls. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --------- Co-authored-by: Joonatan Saarhelo --- Cargo.lock | 11 +- Cargo.toml | 2 +- core/lib/multivm/src/versions/shadow.rs | 5 + .../src/versions/vm_fast/circuits_tracer.rs | 157 ++++++++++++++++++ core/lib/multivm/src/versions/vm_fast/mod.rs | 1 + .../src/versions/vm_fast/tests/code_oracle.rs | 10 +- .../tests/tester/transaction_test_info.rs | 10 +- .../src/versions/vm_fast/tests/utils.rs | 5 +- core/lib/multivm/src/versions/vm_fast/vm.rs | 56 +++---- .../lib/vm_interface/src/storage/in_memory.rs | 2 +- .../state_keeper/src/executor/tests/mod.rs | 22 +++ .../state_keeper/src/executor/tests/tester.rs | 26 ++- core/tests/test_account/src/lib.rs | 2 +- .../contracts/failed-call/failed_call.sol | 24 +++ infrastructure/zk/src/prover_setup.ts | 6 +- prover/Cargo.lock | 11 +- 16 files changed, 301 insertions(+), 49 deletions(-) create mode 100644 core/lib/multivm/src/versions/vm_fast/circuits_tracer.rs create mode 100644 etc/contracts-test-data/contracts/failed-call/failed_call.sol diff --git a/Cargo.lock b/Cargo.lock index e57c437d4bf..cfa18534528 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1981,6 +1981,14 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "eravm-stable-interface" +version = "0.1.0" +source = "git+https://github.com/matter-labs/vm2.git?rev=4ef15d46410ffc11744771a3a6c7c09dd9470c90#4ef15d46410ffc11744771a3a6c7c09dd9470c90" +dependencies = [ + "primitive-types", +] + [[package]] name = "errno" version = "0.3.9" @@ -7307,9 +7315,10 @@ dependencies = [ [[package]] name = "vm2" version = "0.1.0" -source = "git+https://github.com/matter-labs/vm2.git?rev=2276b7b5af520fca0477bdafe43781b51896d235#2276b7b5af520fca0477bdafe43781b51896d235" +source = "git+https://github.com/matter-labs/vm2.git?rev=4ef15d46410ffc11744771a3a6c7c09dd9470c90#4ef15d46410ffc11744771a3a6c7c09dd9470c90" dependencies = [ "enum_dispatch", + "eravm-stable-interface", "primitive-types", "zk_evm_abstractions 0.150.4", "zkevm_opcode_defs 0.150.4", diff --git a/Cargo.toml b/Cargo.toml index 334c85870f2..7d28cd7fe21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -217,7 +217,7 @@ zk_evm_1_4_1 = { package = "zk_evm", version = "0.141.0" } zk_evm_1_5_0 = { package = "zk_evm", version = "=0.150.4" } # New VM; pinned to a specific commit because of instability -vm2 = { git = "https://github.com/matter-labs/vm2.git", rev = "2276b7b5af520fca0477bdafe43781b51896d235" } +vm2 = { git = "https://github.com/matter-labs/vm2.git", rev = "4ef15d46410ffc11744771a3a6c7c09dd9470c90" } # Consensus dependencies. zksync_concurrency = "=0.1.0-rc.11" diff --git a/core/lib/multivm/src/versions/shadow.rs b/core/lib/multivm/src/versions/shadow.rs index 7394c461750..32a4463c425 100644 --- a/core/lib/multivm/src/versions/shadow.rs +++ b/core/lib/multivm/src/versions/shadow.rs @@ -165,6 +165,11 @@ impl DivergenceErrors { let shadow_logs = UniqueStorageLogs::new(&shadow_result.logs.storage_logs); self.check_match("logs.storage_logs", &main_logs, &shadow_logs); self.check_match("refunds", &main_result.refunds, &shadow_result.refunds); + self.check_match( + "statistics.circuit_statistic", + &main_result.statistics.circuit_statistic, + &shadow_result.statistics.circuit_statistic, + ); self.check_match( "gas_remaining", &main_result.statistics.gas_remaining, diff --git a/core/lib/multivm/src/versions/vm_fast/circuits_tracer.rs b/core/lib/multivm/src/versions/vm_fast/circuits_tracer.rs new file mode 100644 index 00000000000..061d91be60b --- /dev/null +++ b/core/lib/multivm/src/versions/vm_fast/circuits_tracer.rs @@ -0,0 +1,157 @@ +use circuit_sequencer_api_1_5_0::{geometry_config::get_geometry_config, toolset::GeometryConfig}; +use vm2::{CycleStats, Opcode, OpcodeType, StateInterface, Tracer}; +use zksync_vm_interface::CircuitStatistic; + +use crate::vm_latest::tracers::circuits_capacity::*; + +#[derive(Debug, Default, Clone, PartialEq)] +pub(crate) struct CircuitsTracer { + main_vm_cycles: u32, + ram_permutation_cycles: u32, + storage_application_cycles: u32, + storage_sorter_cycles: u32, + code_decommitter_cycles: u32, + code_decommitter_sorter_cycles: u32, + log_demuxer_cycles: u32, + events_sorter_cycles: u32, + keccak256_cycles: u32, + ecrecover_cycles: u32, + sha256_cycles: u32, + secp256k1_verify_cycles: u32, + transient_storage_checker_cycles: u32, +} + +impl Tracer for CircuitsTracer { + fn after_instruction(&mut self, _state: &mut S) { + self.main_vm_cycles += 1; + + match OP::VALUE { + Opcode::Nop + | Opcode::Add + | Opcode::Sub + | Opcode::Mul + | Opcode::Div + | Opcode::Jump + | Opcode::Xor + | Opcode::And + | Opcode::Or + | Opcode::ShiftLeft + | Opcode::ShiftRight + | Opcode::RotateLeft + | Opcode::RotateRight + | Opcode::PointerAdd + | Opcode::PointerSub + | Opcode::PointerPack + | Opcode::PointerShrink => { + self.ram_permutation_cycles += RICH_ADDRESSING_OPCODE_RAM_CYCLES; + } + Opcode::This + | Opcode::Caller + | Opcode::CodeAddress + | Opcode::ContextMeta + | Opcode::ErgsLeft + | Opcode::SP + | Opcode::ContextU128 + | Opcode::SetContextU128 + | Opcode::AuxMutating0 + | Opcode::IncrementTxNumber + | Opcode::Ret(_) + | Opcode::NearCall => { + self.ram_permutation_cycles += AVERAGE_OPCODE_RAM_CYCLES; + } + Opcode::StorageRead => { + self.ram_permutation_cycles += STORAGE_READ_RAM_CYCLES; + self.log_demuxer_cycles += STORAGE_READ_LOG_DEMUXER_CYCLES; + self.storage_sorter_cycles += STORAGE_READ_STORAGE_SORTER_CYCLES; + } + Opcode::TransientStorageRead => { + self.ram_permutation_cycles += TRANSIENT_STORAGE_READ_RAM_CYCLES; + self.log_demuxer_cycles += TRANSIENT_STORAGE_READ_LOG_DEMUXER_CYCLES; + self.transient_storage_checker_cycles += + TRANSIENT_STORAGE_READ_TRANSIENT_STORAGE_CHECKER_CYCLES; + } + Opcode::StorageWrite => { + self.ram_permutation_cycles += STORAGE_WRITE_RAM_CYCLES; + self.log_demuxer_cycles += STORAGE_WRITE_LOG_DEMUXER_CYCLES; + self.storage_sorter_cycles += STORAGE_WRITE_STORAGE_SORTER_CYCLES; + } + Opcode::TransientStorageWrite => { + self.ram_permutation_cycles += TRANSIENT_STORAGE_WRITE_RAM_CYCLES; + self.log_demuxer_cycles += TRANSIENT_STORAGE_WRITE_LOG_DEMUXER_CYCLES; + self.transient_storage_checker_cycles += + TRANSIENT_STORAGE_WRITE_TRANSIENT_STORAGE_CHECKER_CYCLES; + } + Opcode::L2ToL1Message | Opcode::Event => { + self.ram_permutation_cycles += EVENT_RAM_CYCLES; + self.log_demuxer_cycles += EVENT_LOG_DEMUXER_CYCLES; + self.events_sorter_cycles += EVENT_EVENTS_SORTER_CYCLES; + } + Opcode::PrecompileCall => { + self.ram_permutation_cycles += PRECOMPILE_RAM_CYCLES; + self.log_demuxer_cycles += PRECOMPILE_LOG_DEMUXER_CYCLES; + } + Opcode::Decommit => { + // Note, that for decommit the log demuxer circuit is not used. + self.ram_permutation_cycles += LOG_DECOMMIT_RAM_CYCLES; + self.code_decommitter_sorter_cycles += LOG_DECOMMIT_DECOMMITTER_SORTER_CYCLES; + } + Opcode::FarCall(_) => { + self.ram_permutation_cycles += FAR_CALL_RAM_CYCLES; + self.code_decommitter_sorter_cycles += FAR_CALL_CODE_DECOMMITTER_SORTER_CYCLES; + self.storage_sorter_cycles += FAR_CALL_STORAGE_SORTER_CYCLES; + self.log_demuxer_cycles += FAR_CALL_LOG_DEMUXER_CYCLES; + } + Opcode::AuxHeapWrite | Opcode::HeapWrite /* StaticMemoryWrite */ => { + self.ram_permutation_cycles += UMA_WRITE_RAM_CYCLES; + } + Opcode::AuxHeapRead | Opcode::HeapRead | Opcode::PointerRead /* StaticMemoryRead */ => { + self.ram_permutation_cycles += UMA_READ_RAM_CYCLES; + } + } + } + + fn on_extra_prover_cycles(&mut self, stats: CycleStats) { + match stats { + CycleStats::Keccak256(cycles) => self.keccak256_cycles += cycles, + CycleStats::Sha256(cycles) => self.sha256_cycles += cycles, + CycleStats::EcRecover(cycles) => self.ecrecover_cycles += cycles, + CycleStats::Secp256k1Verify(cycles) => self.secp256k1_verify_cycles += cycles, + CycleStats::Decommit(cycles) => self.code_decommitter_cycles += cycles, + CycleStats::StorageRead => self.storage_application_cycles += 1, + CycleStats::StorageWrite => self.storage_application_cycles += 2, + } + } +} + +impl CircuitsTracer { + pub(crate) fn circuit_statistic(&self) -> CircuitStatistic { + CircuitStatistic { + main_vm: self.main_vm_cycles as f32 / GEOMETRY_CONFIG.cycles_per_vm_snapshot as f32, + ram_permutation: self.ram_permutation_cycles as f32 + / GEOMETRY_CONFIG.cycles_per_ram_permutation as f32, + storage_application: self.storage_application_cycles as f32 + / GEOMETRY_CONFIG.cycles_per_storage_application as f32, + storage_sorter: self.storage_sorter_cycles as f32 + / GEOMETRY_CONFIG.cycles_per_storage_sorter as f32, + code_decommitter: self.code_decommitter_cycles as f32 + / GEOMETRY_CONFIG.cycles_per_code_decommitter as f32, + code_decommitter_sorter: self.code_decommitter_sorter_cycles as f32 + / GEOMETRY_CONFIG.cycles_code_decommitter_sorter as f32, + log_demuxer: self.log_demuxer_cycles as f32 + / GEOMETRY_CONFIG.cycles_per_log_demuxer as f32, + events_sorter: self.events_sorter_cycles as f32 + / GEOMETRY_CONFIG.cycles_per_events_or_l1_messages_sorter as f32, + keccak256: self.keccak256_cycles as f32 + / GEOMETRY_CONFIG.cycles_per_keccak256_circuit as f32, + ecrecover: self.ecrecover_cycles as f32 + / GEOMETRY_CONFIG.cycles_per_ecrecover_circuit as f32, + sha256: self.sha256_cycles as f32 / GEOMETRY_CONFIG.cycles_per_sha256_circuit as f32, + secp256k1_verify: self.secp256k1_verify_cycles as f32 + / GEOMETRY_CONFIG.cycles_per_secp256r1_verify_circuit as f32, + transient_storage_checker: self.transient_storage_checker_cycles as f32 + / GEOMETRY_CONFIG.cycles_per_transient_storage_sorter as f32, + } + } +} + +const GEOMETRY_CONFIG: GeometryConfig = get_geometry_config(); diff --git a/core/lib/multivm/src/versions/vm_fast/mod.rs b/core/lib/multivm/src/versions/vm_fast/mod.rs index 4deb6b9dbf7..f0d8bafe69e 100644 --- a/core/lib/multivm/src/versions/vm_fast/mod.rs +++ b/core/lib/multivm/src/versions/vm_fast/mod.rs @@ -2,6 +2,7 @@ pub use self::vm::Vm; mod bootloader_state; mod bytecode; +mod circuits_tracer; mod events; mod glue; mod hook; diff --git a/core/lib/multivm/src/versions/vm_fast/tests/code_oracle.rs b/core/lib/multivm/src/versions/vm_fast/tests/code_oracle.rs index 946ad0c38b0..29df17d7293 100644 --- a/core/lib/multivm/src/versions/vm_fast/tests/code_oracle.rs +++ b/core/lib/multivm/src/versions/vm_fast/tests/code_oracle.rs @@ -207,11 +207,11 @@ fn refunds_in_code_oracle() { let account = &mut vm.rich_accounts[0]; if decommit { - let (_, is_fresh) = vm - .vm - .inner - .world_diff - .decommit_opcode(&mut vm.vm.world, h256_to_u256(normal_zkevm_bytecode_hash)); + let (_, is_fresh) = vm.vm.inner.world_diff.decommit_opcode( + &mut vm.vm.world, + &mut vm.vm.tracer, + h256_to_u256(normal_zkevm_bytecode_hash), + ); assert!(is_fresh); } diff --git a/core/lib/multivm/src/versions/vm_fast/tests/tester/transaction_test_info.rs b/core/lib/multivm/src/versions/vm_fast/tests/tester/transaction_test_info.rs index 0d8c6b20764..5b8f0cb0b10 100644 --- a/core/lib/multivm/src/versions/vm_fast/tests/tester/transaction_test_info.rs +++ b/core/lib/multivm/src/versions/vm_fast/tests/tester/transaction_test_info.rs @@ -7,7 +7,7 @@ use crate::{ VmExecutionMode, VmExecutionResultAndLogs, VmInterface, VmInterfaceExt, VmInterfaceHistoryEnabled, VmRevertReason, }, - vm_fast::Vm, + vm_fast::{circuits_tracer::CircuitsTracer, vm::World, Vm}, }; #[derive(Debug, Clone)] @@ -185,14 +185,14 @@ impl TransactionTestInfo { // TODO this doesn't include all the state of ModifiedWorld #[derive(Debug, PartialEq)] -struct VmStateDump { - state: vm2::State, +struct VmStateDump { + state: vm2::State>, storage_writes: Vec<((H160, U256), U256)>, events: Box<[vm2::Event]>, } -impl Vm { - fn dump_state(&self) -> VmStateDump { +impl Vm { + fn dump_state(&self) -> VmStateDump { VmStateDump { state: self.inner.state.clone(), storage_writes: self diff --git a/core/lib/multivm/src/versions/vm_fast/tests/utils.rs b/core/lib/multivm/src/versions/vm_fast/tests/utils.rs index d696aa582d6..d75ae12c30c 100644 --- a/core/lib/multivm/src/versions/vm_fast/tests/utils.rs +++ b/core/lib/multivm/src/versions/vm_fast/tests/utils.rs @@ -17,7 +17,10 @@ use crate::interface::storage::ReadStorage; pub(crate) static BASE_SYSTEM_CONTRACTS: Lazy = Lazy::new(BaseSystemContracts::load_from_disk); -pub(crate) fn verify_required_memory(state: &State, required_values: Vec<(U256, HeapId, u32)>) { +pub(crate) fn verify_required_memory( + state: &State, + required_values: Vec<(U256, HeapId, u32)>, +) { for (required_value, memory_page, cell) in required_values { let current_value = state.heaps[memory_page].read_u256(cell * 32); assert_eq!(current_value, required_value); diff --git a/core/lib/multivm/src/versions/vm_fast/vm.rs b/core/lib/multivm/src/versions/vm_fast/vm.rs index 3a01a10d187..d40ea075f19 100644 --- a/core/lib/multivm/src/versions/vm_fast/vm.rs +++ b/core/lib/multivm/src/versions/vm_fast/vm.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, fmt}; use vm2::{ decode::decode_program, fat_pointer::FatPointer, instruction_handlers::HeapInterface, - ExecutionEnd, Program, Settings, VirtualMachine, + ExecutionEnd, Program, Settings, Tracer, VirtualMachine, }; use zk_evm_1_5_0::zkevm_opcode_defs::system_params::INITIAL_FRAME_FORMAL_EH_LOCATION; use zksync_contracts::SystemContractCode; @@ -23,6 +23,7 @@ use zksync_utils::{bytecode::hash_bytecode, h256_to_u256, u256_to_h256}; use super::{ bootloader_state::{BootloaderState, BootloaderStateSnapshot}, bytecode::compress_bytecodes, + circuits_tracer::CircuitsTracer, hook::Hook, initial_bootloader_memory::bootloader_initial_memory, transaction_data::TransactionData, @@ -55,14 +56,14 @@ use crate::{ const VM_VERSION: MultiVMSubversion = MultiVMSubversion::IncreasedBootloaderMemory; pub struct Vm { - pub(crate) world: World, - pub(crate) inner: VirtualMachine, - suspended_at: u16, + pub(crate) world: World, + pub(crate) inner: VirtualMachine>, gas_for_account_validation: u32, pub(crate) bootloader_state: BootloaderState, pub(crate) batch_env: L1BatchEnv, pub(crate) system_env: SystemEnv, snapshot: Option, + pub(crate) tracer: CircuitsTracer, } impl Vm { @@ -79,14 +80,8 @@ impl Vm { let mut pubdata_before = self.inner.world_diff.pubdata() as u32; let result = loop { - let hook = match self.inner.resume_from(self.suspended_at, &mut self.world) { - ExecutionEnd::SuspendedOnHook { - hook, - pc_to_resume_from, - } => { - self.suspended_at = pc_to_resume_from; - hook - } + let hook = match self.inner.run(&mut self.world, &mut self.tracer) { + ExecutionEnd::SuspendedOnHook(hook) => hook, ExecutionEnd::ProgramFinished(output) => break ExecutionResult::Success { output }, ExecutionEnd::Reverted(output) => { break match TxRevertReason::parse_error(&output) { @@ -394,7 +389,6 @@ impl Vm { let mut me = Self { world: World::new(storage, program_cache), inner, - suspended_at: 0, gas_for_account_validation: system_env.default_validation_computational_gas_limit, bootloader_state: BootloaderState::new( system_env.execution_mode, @@ -404,6 +398,7 @@ impl Vm { system_env, batch_env, snapshot: None, + tracer: CircuitsTracer::default(), }; me.write_to_bootloader_heap(bootloader_memory); @@ -470,6 +465,8 @@ impl VmInterface for Vm { track_refunds = true; } + self.tracer = Default::default(); + let start = self.inner.world_diff.snapshot(); let pubdata_before = self.inner.world_diff.pubdata(); @@ -525,6 +522,9 @@ impl VmInterface for Vm { }; let pubdata_after = self.inner.world_diff.pubdata(); + + let circuit_statistic = self.tracer.circuit_statistic(); + VmExecutionResultAndLogs { result, logs, @@ -537,7 +537,7 @@ impl VmInterface for Vm { computational_gas_used: 0, total_log_queries: 0, pubdata_published: (pubdata_after - pubdata_before).max(0) as u32, - circuit_statistic: Default::default(), + circuit_statistic, }, refunds, } @@ -599,7 +599,6 @@ impl VmInterface for Vm { struct VmSnapshot { vm_snapshot: vm2::Snapshot, bootloader_snapshot: BootloaderStateSnapshot, - suspended_at: u16, gas_for_account_validation: u32, } @@ -614,7 +613,6 @@ impl VmInterfaceHistoryEnabled for Vm { self.snapshot = Some(VmSnapshot { vm_snapshot: self.inner.snapshot(), bootloader_snapshot: self.bootloader_state.get_snapshot(), - suspended_at: self.suspended_at, gas_for_account_validation: self.gas_for_account_validation, }); } @@ -623,13 +621,11 @@ impl VmInterfaceHistoryEnabled for Vm { let VmSnapshot { vm_snapshot, bootloader_snapshot, - suspended_at, gas_for_account_validation, } = self.snapshot.take().expect("no snapshots to rollback to"); self.inner.rollback(vm_snapshot); self.bootloader_state.apply_snapshot(bootloader_snapshot); - self.suspended_at = suspended_at; self.gas_for_account_validation = gas_for_account_validation; self.delete_history_if_appropriate(); @@ -644,7 +640,6 @@ impl VmInterfaceHistoryEnabled for Vm { impl fmt::Debug for Vm { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Vm") - .field("suspended_at", &self.suspended_at) .field( "gas_for_account_validation", &self.gas_for_account_validation, @@ -659,16 +654,16 @@ impl fmt::Debug for Vm { } } -#[derive(Debug)] -pub(crate) struct World { +#[derive(Debug, Clone)] +pub(crate) struct World { pub(crate) storage: S, // TODO (PLA-1008): Store `Program`s in an LRU cache - program_cache: HashMap, + program_cache: HashMap>>, pub(crate) bytecode_cache: HashMap>, } -impl World { - fn new(storage: S, program_cache: HashMap) -> Self { +impl World { + fn new(storage: S, program_cache: HashMap>>) -> Self { Self { storage, program_cache, @@ -677,7 +672,7 @@ impl World { } } -impl vm2::World for World { +impl vm2::World for World { fn decommit_code(&mut self, hash: U256) -> Vec { self.decommit(hash) .code_page() @@ -691,7 +686,7 @@ impl vm2::World for World { .collect() } - fn decommit(&mut self, hash: U256) -> Program { + fn decommit(&mut self, hash: U256) -> Program> { self.program_cache .entry(hash) .or_insert_with(|| { @@ -703,7 +698,9 @@ impl vm2::World for World { }) .clone() } +} +impl vm2::StorageInterface for World { fn read_storage(&mut self, contract: H160, key: U256) -> Option { let key = &StorageKey::new(AccountTreeId::new(contract), u256_to_h256(key)); if self.storage.is_write_initial(key) { @@ -748,7 +745,7 @@ impl vm2::World for World { } } -fn bytecode_to_program(bytecode: &[u8]) -> Program { +fn bytecode_to_program>(bytecode: &[u8]) -> Program { Program::new( decode_program( &bytecode @@ -764,7 +761,10 @@ fn bytecode_to_program(bytecode: &[u8]) -> Program { ) } -fn convert_system_contract_code(code: &SystemContractCode, is_bootloader: bool) -> (U256, Program) { +fn convert_system_contract_code>( + code: &SystemContractCode, + is_bootloader: bool, +) -> (U256, Program) { ( h256_to_u256(code.hash), Program::new( diff --git a/core/lib/vm_interface/src/storage/in_memory.rs b/core/lib/vm_interface/src/storage/in_memory.rs index d4b5e57f1fa..6a8b5643345 100644 --- a/core/lib/vm_interface/src/storage/in_memory.rs +++ b/core/lib/vm_interface/src/storage/in_memory.rs @@ -12,7 +12,7 @@ use super::ReadStorage; pub const IN_MEMORY_STORAGE_DEFAULT_NETWORK_ID: u32 = 270; /// In-memory storage. -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct InMemoryStorage { state: HashMap, factory_deps: HashMap>, diff --git a/core/node/state_keeper/src/executor/tests/mod.rs b/core/node/state_keeper/src/executor/tests/mod.rs index 90ce236a38f..6fa4522d43f 100644 --- a/core/node/state_keeper/src/executor/tests/mod.rs +++ b/core/node/state_keeper/src/executor/tests/mod.rs @@ -2,6 +2,7 @@ use assert_matches::assert_matches; use test_casing::{test_casing, Product}; +use tester::AccountFailedCall; use zksync_dal::{ConnectionPool, Core}; use zksync_multivm::interface::{BatchTransactionExecutionResult, ExecutionResult, Halt}; use zksync_test_account::Account; @@ -300,6 +301,27 @@ async fn deploy_and_call_loadtest(vm_mode: FastVmMode) { executor.finish_batch().await.unwrap(); } +#[test_casing(3, FAST_VM_MODES)] +#[tokio::test] +async fn deploy_failedcall(vm_mode: FastVmMode) { + let connection_pool = ConnectionPool::::constrained_test_pool(1).await; + let mut alice = Account::random(); + + let mut tester = Tester::new(connection_pool, vm_mode); + tester.genesis().await; + tester.fund(&[alice.address()]).await; + let mut executor = tester + .create_batch_executor(StorageType::AsyncRocksdbCache) + .await; + + let tx = alice.deploy_failedcall_tx(); + + let execute_tx = executor.execute_tx(tx.tx).await.unwrap(); + assert_executed(&execute_tx); + + executor.finish_batch().await.unwrap(); +} + /// Checks that a tx that is reverted by the VM still can be included into a batch. #[test_casing(3, FAST_VM_MODES)] #[tokio::test] diff --git a/core/node/state_keeper/src/executor/tests/tester.rs b/core/node/state_keeper/src/executor/tests/tester.rs index a00d9ca5ec1..8256435f2f5 100644 --- a/core/node/state_keeper/src/executor/tests/tester.rs +++ b/core/node/state_keeper/src/executor/tests/tester.rs @@ -6,7 +6,10 @@ use std::{collections::HashMap, fmt::Debug, sync::Arc}; use tempfile::TempDir; use tokio::{sync::watch, task::JoinHandle}; use zksync_config::configs::chain::StateKeeperConfig; -use zksync_contracts::{get_loadnext_contract, test_contracts::LoadnextContractExecutionParams}; +use zksync_contracts::{ + get_loadnext_contract, load_contract, read_bytecode, + test_contracts::LoadnextContractExecutionParams, TestContract, +}; use zksync_dal::{ConnectionPool, Core, CoreDal}; use zksync_multivm::{ interface::{ @@ -262,9 +265,8 @@ impl Tester { /// Adds funds for specified account list. /// Expects genesis to be performed (i.e. `setup_storage` called beforehand). pub(super) async fn fund(&self, addresses: &[Address]) { - let mut storage = self.pool.connection_tagged("state_keeper").await.unwrap(); - let eth_amount = U256::from(10u32).pow(U256::from(32)); //10^32 wei + let mut storage = self.pool.connection_tagged("state_keeper").await.unwrap(); for address in addresses { let key = storage_key_for_standard_token_balance( @@ -336,6 +338,24 @@ pub trait AccountLoadNextExecutable { ) -> Transaction; } +pub trait AccountFailedCall { + fn deploy_failedcall_tx(&mut self) -> DeployContractsTx; +} + +impl AccountFailedCall for Account { + fn deploy_failedcall_tx(&mut self) -> DeployContractsTx { + let bytecode = read_bytecode( + "etc/contracts-test-data/artifacts-zk/contracts/failed-call/failed_call.sol/FailedCall.json"); + let failedcall_contract = TestContract { + bytecode, + contract: load_contract("etc/contracts-test-data/artifacts-zk/contracts/failed-call/failed_call.sol/FailedCall.json"), + factory_deps: vec![], + }; + + self.get_deploy_tx(&failedcall_contract.bytecode, None, TxType::L2) + } +} + impl AccountLoadNextExecutable for Account { fn deploy_loadnext_tx(&mut self) -> DeployContractsTx { let loadnext_contract = get_loadnext_contract(); diff --git a/core/tests/test_account/src/lib.rs b/core/tests/test_account/src/lib.rs index e259ce209c6..28e3d609e63 100644 --- a/core/tests/test_account/src/lib.rs +++ b/core/tests/test_account/src/lib.rs @@ -89,7 +89,7 @@ impl Account { pub fn default_fee() -> Fee { Fee { - gas_limit: U256::from(2000000000u32), + gas_limit: U256::from(2_000_000_000u32), max_fee_per_gas: U256::from(BASE_FEE), max_priority_fee_per_gas: U256::from(100), gas_per_pubdata_limit: U256::from(DEFAULT_L2_TX_GAS_PER_PUBDATA_BYTE), diff --git a/etc/contracts-test-data/contracts/failed-call/failed_call.sol b/etc/contracts-test-data/contracts/failed-call/failed_call.sol new file mode 100644 index 00000000000..7a8f43fbd89 --- /dev/null +++ b/etc/contracts-test-data/contracts/failed-call/failed_call.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +pragma solidity ^0.8.0; + +contract FailedCall { + bool public success; + bytes1 public data_first_byte; + + constructor() { + address MSG_VALUE_SIMULATOR = 0x0000000000000000000000000000000000008009; + + while (gasleft() > 20000) { + // Burn gas so that there's about 20k left before the external call. + } + + // This call fails because MSG_VALUE_SIMULATOR forcibly takes 27k gas + (bool s, bytes memory data) = MSG_VALUE_SIMULATOR.call( + abi.encodeWithSignature("deadBeef()") + ); + + success = s; + data_first_byte = data[0]; + } +} diff --git a/infrastructure/zk/src/prover_setup.ts b/infrastructure/zk/src/prover_setup.ts index b5bd4c828ae..0ef3515cc75 100644 --- a/infrastructure/zk/src/prover_setup.ts +++ b/infrastructure/zk/src/prover_setup.ts @@ -30,7 +30,8 @@ export async function setupProver(proverType: ProverType) { } else { env.modify( 'FRI_PROVER_SETUP_DATA_PATH', - `${process.env.ZKSYNC_HOME}/etc/hyperchains/prover-keys/${process.env.ZKSYNC_ENV}/${proverType === ProverType.GPU ? 'gpu' : 'cpu' + `${process.env.ZKSYNC_HOME}/etc/hyperchains/prover-keys/${process.env.ZKSYNC_ENV}/${ + proverType === ProverType.GPU ? 'gpu' : 'cpu' }/`, process.env.ENV_FILE! ); @@ -97,7 +98,8 @@ async function setupProverKeys(proverType: ProverType) { env.modify( 'FRI_PROVER_SETUP_DATA_PATH', - `${process.env.ZKSYNC_HOME}/etc/hyperchains/prover-keys/${process.env.ZKSYNC_ENV}/${proverType === ProverType.GPU ? 'gpu' : 'cpu' + `${process.env.ZKSYNC_HOME}/etc/hyperchains/prover-keys/${process.env.ZKSYNC_ENV}/${ + proverType === ProverType.GPU ? 'gpu' : 'cpu' }/`, process.env.ENV_FILE! ); diff --git a/prover/Cargo.lock b/prover/Cargo.lock index 09b13a80e39..fe6f04d74c8 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -1880,6 +1880,14 @@ dependencies = [ "serde_json", ] +[[package]] +name = "eravm-stable-interface" +version = "0.1.0" +source = "git+https://github.com/matter-labs/vm2.git?rev=4ef15d46410ffc11744771a3a6c7c09dd9470c90#4ef15d46410ffc11744771a3a6c7c09dd9470c90" +dependencies = [ + "primitive-types", +] + [[package]] name = "errno" version = "0.3.9" @@ -6816,9 +6824,10 @@ dependencies = [ [[package]] name = "vm2" version = "0.1.0" -source = "git+https://github.com/matter-labs/vm2.git?rev=2276b7b5af520fca0477bdafe43781b51896d235#2276b7b5af520fca0477bdafe43781b51896d235" +source = "git+https://github.com/matter-labs/vm2.git?rev=4ef15d46410ffc11744771a3a6c7c09dd9470c90#4ef15d46410ffc11744771a3a6c7c09dd9470c90" dependencies = [ "enum_dispatch", + "eravm-stable-interface", "primitive-types", "zk_evm_abstractions 0.150.4", "zkevm_opcode_defs 0.150.4",