diff --git a/Cargo.lock b/Cargo.lock index 68c471cd..add29949 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -265,6 +265,7 @@ dependencies = [ "sha3", "solidity-adapter", "structopt", + "vm2", "web3", "which", "zkevm-assembly", @@ -473,6 +474,18 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enum_dispatch" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.65", +] + [[package]] name = "env_logger" version = "0.9.3" @@ -2575,6 +2588,16 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2266fcb904c50fb17fda4c9a751a1715629ecf8b21f4c9d78b4890fb71525d71" +[[package]] +name = "vm2" +version = "0.1.0" +dependencies = [ + "enum_dispatch", + "primitive-types", + "zk_evm_abstractions", + "zkevm_opcode_defs", +] + [[package]] name = "want" version = "0.3.1" diff --git a/compiler_tester/Cargo.toml b/compiler_tester/Cargo.toml index 6819f1e0..86fca4d0 100644 --- a/compiler_tester/Cargo.toml +++ b/compiler_tester/Cargo.toml @@ -44,7 +44,7 @@ zkevm-assembly = { git = "https://github.com/matter-labs/era-zkEVM-assembly", br zkevm_opcode_defs = { git = "https://github.com/matter-labs/era-zkevm_opcode_defs", branch = "v1.5.0" } zkevm_tester = { git = "https://github.com/matter-labs/era-zkevm_tester", branch = "v1.5.0" } -# vm2 = { git = "https://github.com/matter-labs/vm2", optional = true } +vm2 = { git = "https://github.com/matter-labs/vm2", optional = true } era-compiler-common = { git = "https://github.com/matter-labs/era-compiler-common", branch = "main" } era-compiler-llvm-context = { git = "https://github.com/matter-labs/era-compiler-llvm-context", branch = "main" } diff --git a/compiler_tester/src/vm/eravm/vm2_adapter.rs b/compiler_tester/src/vm/eravm/vm2_adapter.rs index 3bb6ab02..ff43b269 100644 --- a/compiler_tester/src/vm/eravm/vm2_adapter.rs +++ b/compiler_tester/src/vm/eravm/vm2_adapter.rs @@ -6,11 +6,13 @@ use std::collections::HashMap; -use web3::ethabi::Address; - +use crate::vm::execution_result::ExecutionResult; +use anyhow::anyhow; +use vm2::initial_decommit; use vm2::ExecutionEnd; +use vm2::Program; use vm2::World; -use zkevm_assembly::zkevm_opcode_defs::Assembly; +use zkevm_assembly::Assembly; use zkevm_opcode_defs::ethereum_types::{BigEndianHash, H256, U256}; use zkevm_tester::runners::compiler_tests::FullABIParams; use zkevm_tester::runners::compiler_tests::StorageKey; @@ -21,7 +23,6 @@ use crate::test::case::input::{ output::{event::Event, Output}, value::Value, }; -use crate::vm::eravm::execution_result::ExecutionResult; pub fn run_vm( contracts: HashMap, @@ -54,28 +55,34 @@ pub fn run_vm( r5_value: None, }, VmLaunchOption::ManualCallABI(abiparams) => abiparams, - x => return Err(anyhow::anyhow!("Unsupported launch option {x:?}")), + x => return Err(anyhow!("Unsupported launch option {x:?}")), }; for (_, contract) in contracts { let bytecode = contract.clone().compile_to_bytecode()?; - let hash = zkevm_assembly::zkevm_opcode_defs::bytecode_to_code_hash(&bytecode)?; + let hash = zkevm_assembly::zkevm_opcode_defs::bytecode_to_code_hash(&bytecode) + .map_err(|()| anyhow!("Failed to hash bytecode"))?; known_contracts.insert(U256::from_big_endian(&hash), contract); } let context = context.unwrap_or_default(); + let mut world = TestWorld { + storage, + contracts: known_contracts.clone(), + }; + let initial_program = initial_decommit(&mut world, entry_address); + let mut vm = vm2::VirtualMachine::new( - Box::new(TestWorld { - storage, - contracts: known_contracts.clone(), - }), entry_address, + initial_program, context.msg_sender, calldata.to_vec(), - u32::MAX, + // zkevm_tester subtracts this constant, I don't know why + u32::MAX - 0x80000000, vm2::Settings { - default_aa_code_hash, + default_aa_code_hash: default_aa_code_hash.into(), + evm_interpreter_code_hash: evm_interpreter_code_hash.into(), hook_address: 0, }, ); @@ -90,48 +97,62 @@ pub fn run_vm( vm.state.registers[4] = abi_params.r4_value.unwrap_or_default(); vm.state.registers[5] = abi_params.r5_value.unwrap_or_default(); - let output = match vm.run() { - ExecutionEnd::ProgramFinished(return_value) => Output { - return_data: chunk_return_data(&return_value), - exception: false, - events: merge_events(vm.world.events()), - }, + let mut storage_changes = HashMap::new(); + let mut deployed_contracts = HashMap::new(); + + let output = match vm.run(&mut world) { + ExecutionEnd::ProgramFinished(return_value) => { + // Only successful transactions can have side effects + // The VM doesn't undo side effects done in the initial frame + // because that would mess with the bootloader. + + storage_changes = vm + .world_diff + .get_storage_state() + .iter() + .map(|(&(address, key), value)| { + (StorageKey { address, key }, H256::from_uint(value)) + }) + .collect::>(); + deployed_contracts = vm + .world_diff + .get_storage_state() + .iter() + .filter_map(|((address, key), value)| { + if *address == *zkevm_assembly::zkevm_opcode_defs::system_params::DEPLOYER_SYSTEM_CONTRACT_ADDRESS { + let mut buffer = [0u8; 32]; + key.to_big_endian(&mut buffer); + let deployed_address = web3::ethabi::Address::from_slice(&buffer[12..]); + if let Some(code) = known_contracts.get(&value) { + Some((deployed_address, code.clone())) + } else { + None + } + } else { + None + } + }) + .collect::>(); + + Output { + return_data: chunk_return_data(&return_value), + exception: false, + events: merge_events(vm.world_diff.events()), + } + } ExecutionEnd::Reverted(return_value) => Output { return_data: chunk_return_data(&return_value), exception: true, events: vec![], }, - _panic => Output { + ExecutionEnd::Panicked => Output { return_data: vec![], exception: true, events: vec![], }, + ExecutionEnd::SuspendedOnHook { .. } => unreachable!(), }; - let storage_changes = vm - .world - .get_storage_changes() - .map(|((address, key), value)| (StorageKey { address, key }, H256::from_uint(&value))) - .collect::>(); - let deployed_contracts = vm - .world - .get_storage_changes() - .filter_map(|((address, key), value)| { - if address == *zkevm_assembly::zkevm_opcode_defs::system_params::DEPLOYER_SYSTEM_CONTRACT_ADDRESS { - let mut buffer = [0u8; 32]; - key.to_big_endian(&mut buffer); - let deployed_address = web3::ethabi::Address::from_slice(&buffer[12..]); - if let Some(code) = known_contracts.get(&value) { - Some((deployed_address, code.clone())) - } else { - None - } - } else { - None - } - }) - .collect::>(); - Ok(( ExecutionResult { output, @@ -150,17 +171,15 @@ struct TestWorld { } impl World for TestWorld { - fn decommit( - &mut self, - hash: U256, - ) -> (std::sync::Arc<[vm2::Instruction]>, std::sync::Arc<[U256]>) { - let bytecode = self + fn decommit(&mut self, hash: U256) -> Program { + let Some(bytecode) = self .contracts .get(&hash) - .unwrap() - .clone() - .compile_to_bytecode() - .unwrap(); + .map(|assembly| assembly.clone().compile_to_bytecode().unwrap()) + else { + // This case only happens when a contract fails to deploy but the assumes it did deploy + return Program::new(vec![vm2::Instruction::from_invalid()], vec![]); + }; let instructions = bytecode .iter() .flat_map(|x| { @@ -169,13 +188,12 @@ impl World for TestWorld { }) .collect::>(); - ( - vm2::decode::decode_program(&instructions, false).into(), + Program::new( + vm2::decode::decode_program(&instructions, false), bytecode .iter() .map(|x| U256::from_big_endian(x)) - .collect::>() - .into(), + .collect::>(), ) } @@ -193,8 +211,20 @@ impl World for TestWorld { .unwrap_or(U256::zero()) } - fn handle_hook(&mut self, _: u32) { - unreachable!() // There is no bootloader + fn cost_of_writing_storage( + &mut self, + contract: web3::types::H160, + key: U256, + new_value: U256, + ) -> u32 { + 0 + } + + fn is_free_storage_slot(&self, contract: &web3::types::H160, key: &U256) -> bool { + self.storage.contains_key(&StorageKey { + address: *contract, + key: *key, + }) } } @@ -231,6 +261,7 @@ fn merge_events(events: &[vm2::Event]) -> Vec { key, value, } = *message; + let tx_number = tx_number.into(); if !is_first { if let Some((mut remaining_data_length, mut remaining_topics, mut event)) =