From c787deb8317243db5831c28aa4ee6219171a2534 Mon Sep 17 00:00:00 2001 From: Nisheeth Barthwal Date: Fri, 13 Sep 2024 16:20:29 +0200 Subject: [PATCH 01/12] take historical block count from env --- crates/zksync/core/src/vm/runner.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/crates/zksync/core/src/vm/runner.rs b/crates/zksync/core/src/vm/runner.rs index 835b1efca..ce10ccbb6 100644 --- a/crates/zksync/core/src/vm/runner.rs +++ b/crates/zksync/core/src/vm/runner.rs @@ -275,10 +275,12 @@ pub fn encode_create_params( signature.iter().copied().chain(params).collect() } -/// Get last 256 block hashes mapped to block numbers. This excludes the current block. +/// Get historical block hashes mapped to block numbers. This excludes the current block. fn get_historical_block_hashes(ecx: &mut EvmContext) -> HashMap { let mut block_hashes = HashMap::default(); - for i in 1..=256u32 { + let num_blocks = get_env_historical_block_count(); + tracing::debug!("fetching last {num_blocks} block hashes"); + for i in 1..=num_blocks { let (block_number, overflow) = ecx.env.block.number.overflowing_sub(alloy_primitives::U256::from(i)); if overflow { @@ -294,3 +296,16 @@ fn get_historical_block_hashes(ecx: &mut EvmContext) -> HashMa block_hashes } + +/// Get the number of historical blocks to fetch for mapping to block hashes from env. Default: `256`. +fn get_env_historical_block_count() -> u32 { + let name = "ZK_DEBUG_HISTORICAL_BLOCK_HASHES"; + std::env::var(name) + .map(|value| { + value.parse::().unwrap_or_else(|err| { + panic!("failed parsing env variable {}={}, {:?}", name, value, err) + }) + }) + .map(|num| num.min(256)) + .unwrap_or(256) +} From 503afc3e9e3b5ccf23570e649f617a75d8911317 Mon Sep 17 00:00:00 2001 From: Nisheeth Barthwal Date: Fri, 13 Sep 2024 16:23:34 +0200 Subject: [PATCH 02/12] fmt --- crates/zksync/core/src/vm/runner.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/zksync/core/src/vm/runner.rs b/crates/zksync/core/src/vm/runner.rs index ce10ccbb6..aa33c8527 100644 --- a/crates/zksync/core/src/vm/runner.rs +++ b/crates/zksync/core/src/vm/runner.rs @@ -297,7 +297,8 @@ fn get_historical_block_hashes(ecx: &mut EvmContext) -> HashMa block_hashes } -/// Get the number of historical blocks to fetch for mapping to block hashes from env. Default: `256`. +/// Get the number of historical blocks to fetch for mapping to block hashes from env. +/// Default: `256`. fn get_env_historical_block_count() -> u32 { let name = "ZK_DEBUG_HISTORICAL_BLOCK_HASHES"; std::env::var(name) From bc6da8f196841ed260b124bcc70e583d5d4e6c68 Mon Sep 17 00:00:00 2001 From: Nisheeth Barthwal Date: Fri, 13 Sep 2024 16:32:35 +0200 Subject: [PATCH 03/12] fmt --- crates/zksync/core/src/vm/runner.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/zksync/core/src/vm/runner.rs b/crates/zksync/core/src/vm/runner.rs index aa33c8527..291ab045f 100644 --- a/crates/zksync/core/src/vm/runner.rs +++ b/crates/zksync/core/src/vm/runner.rs @@ -297,7 +297,7 @@ fn get_historical_block_hashes(ecx: &mut EvmContext) -> HashMa block_hashes } -/// Get the number of historical blocks to fetch for mapping to block hashes from env. +/// Get the number of historical blocks to fetch, from the env. /// Default: `256`. fn get_env_historical_block_count() -> u32 { let name = "ZK_DEBUG_HISTORICAL_BLOCK_HASHES"; From c9af59210cd5d61ccb91f826f9d4fed16b8d54f7 Mon Sep 17 00:00:00 2001 From: Jrigada Date: Mon, 16 Sep 2024 10:35:08 -0300 Subject: [PATCH 04/12] Handle create2 factory usage in scripts --- crates/cheatcodes/src/inspector.rs | 54 ++++++++++++++++++++++++++--- crates/evm/core/src/constants.rs | 5 +++ crates/evm/core/src/utils.rs | 22 +++++++++--- crates/zksync/core/src/vm/runner.rs | 3 +- 4 files changed, 74 insertions(+), 10 deletions(-) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 616787055..49543e9c4 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -26,7 +26,10 @@ use foundry_config::Config; use foundry_evm_core::{ abi::{Vm::stopExpectSafeMemoryCall, HARDHAT_CONSOLE_ADDRESS}, backend::{DatabaseError, DatabaseExt, LocalForkId, RevertDiagnostic}, - constants::{CHEATCODE_ADDRESS, CHEATCODE_CONTRACT_HASH, DEFAULT_CREATE2_DEPLOYER_CODE}, + constants::{ + CHEATCODE_ADDRESS, CHEATCODE_CONTRACT_HASH, DEFAULT_CREATE2_DEPLOYER, + DEFAULT_CREATE2_DEPLOYER_CODE, DEFAULT_CREATE2_DEPLOYER_ZKSYNC, + }, decode::decode_console_log, utils::new_evm_with_existing_context, InspectorExt, @@ -34,7 +37,8 @@ use foundry_evm_core::{ use foundry_zksync_compiler::{DualCompiledContract, DualCompiledContracts}; use foundry_zksync_core::{ convert::{ConvertH160, ConvertH256, ConvertRU256, ConvertU256}, - get_account_code_key, get_balance_key, get_nonce_key, Call, ZkTransactionMetadata, + get_account_code_key, get_balance_key, get_nonce_key, hash_bytecode, Call, + ZkTransactionMetadata, }; use itertools::Itertools; use revm::{ @@ -1183,6 +1187,37 @@ impl Cheatcodes { return None; } + let mut create2_factory_deps = Vec::new(); + + if call.target_address == DEFAULT_CREATE2_DEPLOYER && self.use_zk_vm { + call.target_address = DEFAULT_CREATE2_DEPLOYER_ZKSYNC; + call.bytecode_address = DEFAULT_CREATE2_DEPLOYER_ZKSYNC; + + let (salt, init_code) = call.input.split_at(32); + let contract = self + .dual_compiled_contracts + .find_by_evm_bytecode(init_code) + .unwrap_or_else(|| panic!("failed finding contract for {:?}", init_code)); + + create2_factory_deps.push(contract.zk_deployed_bytecode.clone()); + + let factory_deps = self.dual_compiled_contracts.fetch_all_factory_deps(contract); + + // This is a hack to pass it to the call + self.persisted_factory_deps + .extend(factory_deps.into_iter().map(|v| (hash_bytecode(&v), v))); + + let constructor_input = init_code[contract.evm_bytecode.len()..].to_vec(); + + let create_input = foundry_zksync_core::encode_create_params( + &CreateScheme::Create2 { salt: U256::from_be_slice(salt) }, + contract.zk_bytecode_hash, + constructor_input, + ); + + call.input = create_input.into(); + } + // Handle expected calls // Grab the different calldatas expected. @@ -1307,7 +1342,13 @@ impl Cheatcodes { let zk_tx = if self.use_zk_vm { // We shouldn't need factory_deps for CALLs - Some(ZkTransactionMetadata { factory_deps: Default::default() }) + if call.target_address == DEFAULT_CREATE2_DEPLOYER_ZKSYNC { + Some(ZkTransactionMetadata { + factory_deps: create2_factory_deps.clone(), + }) + } else { + Some(ZkTransactionMetadata { factory_deps: Default::default() }) + } } else { None }; @@ -1415,7 +1456,12 @@ impl Cheatcodes { accesses: self.accesses.as_mut(), persisted_factory_deps: Some(&mut self.persisted_factory_deps), }; - if let Ok(result) = foundry_zksync_core::vm::call::<_, DatabaseError>(call, ecx, ccx) { + if let Ok(result) = foundry_zksync_core::vm::call::<_, DatabaseError>( + call, + ecx, + ccx, + create2_factory_deps.clone(), + ) { // append console logs from zkEVM to the current executor's LogTracer result.logs.iter().filter_map(decode_console_log).for_each(|decoded_log| { executor.console_log( diff --git a/crates/evm/core/src/constants.rs b/crates/evm/core/src/constants.rs index 713d03d87..3e25c299e 100644 --- a/crates/evm/core/src/constants.rs +++ b/crates/evm/core/src/constants.rs @@ -37,6 +37,11 @@ pub const DEFAULT_CREATE2_DEPLOYER_DEPLOYER: Address = address!("3fAB184622Dc19b6109349B94811493BF2a45362"); /// The default CREATE2 deployer. pub const DEFAULT_CREATE2_DEPLOYER: Address = address!("4e59b44847b379578588920ca78fbf26c0b4956c"); + +/// The default CREATE2 deployer for zksync (0x0000000000000000000000000000000000010000) +pub const DEFAULT_CREATE2_DEPLOYER_ZKSYNC: Address = + address!("0000000000000000000000000000000000010000"); + /// The initcode of the default CREATE2 deployer. pub const DEFAULT_CREATE2_DEPLOYER_CODE: &[u8] = &hex!("604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3"); /// The runtime code of the default CREATE2 deployer. diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index 76a738c52..c78c31daa 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -142,7 +142,7 @@ pub fn create2_handler_register>( .push((ctx.evm.journaled_state.depth(), call_inputs.clone())); // Sanity check that CREATE2 deployer exists. - let code_hash = ctx.evm.load_account(DEFAULT_CREATE2_DEPLOYER)?.0.info.code_hash; + let code_hash = ctx.evm.load_account(call_inputs.target_address)?.0.info.code_hash; if code_hash == KECCAK_EMPTY { return Ok(FrameOrResult::Result(FrameResult::Call(CallOutcome { result: InterpreterResult { @@ -184,17 +184,29 @@ pub fn create2_handler_register>( // Decode address from output. let address = match outcome.instruction_result() { - return_ok!() => Address::try_from(outcome.output().as_ref()) - .map_err(|_| { + return_ok!() => { + let output = outcome.output(); + + // Here we need to handle both evm and zksync return data to parse the + // address + if output.len() == 20 { + Some(Address::from_slice(output)) + } else if output.len() >= 32 { + // Address in the last 20 bytes of a 32-byte word due to the zksync + // return data + Some(Address::from_slice(&output[12..32])) + } else { outcome.result = InterpreterResult { result: InstructionResult::Revert, output: "invalid CREATE2 factory output".into(), gas: Gas::new(call_inputs.gas_limit), }; - }) - .ok(), + None + } + } _ => None, }; + frame .frame_data_mut() .interpreter diff --git a/crates/zksync/core/src/vm/runner.rs b/crates/zksync/core/src/vm/runner.rs index 291ab045f..4bd626576 100644 --- a/crates/zksync/core/src/vm/runner.rs +++ b/crates/zksync/core/src/vm/runner.rs @@ -174,6 +174,7 @@ pub fn call( call: &CallInputs, ecx: &mut EvmContext, mut ccx: CheatcodeTracerContext, + factory_deps: Vec>, ) -> ZKVMResult where DB: Database, @@ -200,7 +201,7 @@ where CallValue::Transfer(value) => value.to_u256(), _ => U256::zero(), }, - Default::default(), + factory_deps, PaymasterParams::default(), ); From 88a31e9e823633cb30da8e37e457f70e9f39ded3 Mon Sep 17 00:00:00 2001 From: Jrigada Date: Mon, 16 Sep 2024 10:49:57 -0300 Subject: [PATCH 05/12] Add test for create2 deployment in zksync --- crates/forge/tests/fixtures/zk/Create2.s.sol | 36 ++++++++++++++++++++ crates/forge/tests/it/zk/deploy.rs | 16 +++++++++ 2 files changed, 52 insertions(+) create mode 100644 crates/forge/tests/fixtures/zk/Create2.s.sol diff --git a/crates/forge/tests/fixtures/zk/Create2.s.sol b/crates/forge/tests/fixtures/zk/Create2.s.sol new file mode 100644 index 000000000..6fa5e2974 --- /dev/null +++ b/crates/forge/tests/fixtures/zk/Create2.s.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.18; + +import {Script} from "forge-std/Script.sol"; +import {Greeter} from "../src/Greeter.sol"; +import {CustomNumber} from "../src/CustomNumber.sol"; + +contract Create2Script is Script { + function run() external { + (bool success,) = address(vm).call(abi.encodeWithSignature("zkVm(bool)", true)); + require(success, "zkVm() call failed"); + + vm.startBroadcast(); + + // Deploy Greeter using create2 with a salt + bytes32 greeterSalt = bytes32("12345"); + Greeter greeter = new Greeter{salt: greeterSalt}(); + + // Verify Greeter deployment + require(address(greeter) != address(0), "Greeter deployment failed"); + + // Test Greeter functionality + string memory greeting = greeter.greeting("Alice"); + require(bytes(greeting).length > 0, "Greeter greeting failed"); + + // Deploy CustomNumber using create2 with a salt value + uint8 customNumberValue = 42; + CustomNumber customNumber = new CustomNumber{salt: "123" }(customNumberValue); + + // Verify CustomNumber deployment and initial value + require(address(customNumber) != address(0), "CustomNumber deployment failed"); + require(customNumber.number() == customNumberValue, "CustomNumber initial value mismatch"); + + vm.stopBroadcast(); + } +} diff --git a/crates/forge/tests/it/zk/deploy.rs b/crates/forge/tests/it/zk/deploy.rs index 4af3bb71e..40554b587 100644 --- a/crates/forge/tests/it/zk/deploy.rs +++ b/crates/forge/tests/it/zk/deploy.rs @@ -24,8 +24,24 @@ forgetest_async!(multiple_deployments_of_the_same_contract, |prj, cmd| { ); }); +forgetest_async!(can_deploy_via_create2, |prj, cmd| { + setup_deploy_prj(&mut prj); + run_zk_script_test( + prj.root(), + &mut cmd, + "./script/Create2.s.sol", + "Create2Script", + None, + 3, + Some(&["-vvvvv"]), + ); +}); + fn setup_deploy_prj(prj: &mut TestProject) { util::initialize(prj.root()); prj.add_script("Deploy.s.sol", include_str!("../../fixtures/zk/Deploy.s.sol")).unwrap(); + prj.add_script("Create2.s.sol", include_str!("../../fixtures/zk/Create2.s.sol")).unwrap(); prj.add_source("Greeter.sol", include_str!("../../../../../testdata/zk/Greeter.sol")).unwrap(); + prj.add_source("CustomNumber.sol", include_str!("../../../../../testdata/zk/CustomNumber.sol")) + .unwrap(); } From a3318f7165700ca9df02600852bc7130b5c33192 Mon Sep 17 00:00:00 2001 From: Jrigada Date: Mon, 16 Sep 2024 10:54:41 -0300 Subject: [PATCH 06/12] Cargo clippy --- crates/cheatcodes/src/inspector.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 49543e9c4..91ba80fc6 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -1197,7 +1197,7 @@ impl Cheatcodes { let contract = self .dual_compiled_contracts .find_by_evm_bytecode(init_code) - .unwrap_or_else(|| panic!("failed finding contract for {:?}", init_code)); + .unwrap_or_else(|| panic!("failed finding contract for {init_code:?}")); create2_factory_deps.push(contract.zk_deployed_bytecode.clone()); From 2b9dc44ca29a0640905c4caa3f7447e52a6fc45b Mon Sep 17 00:00:00 2001 From: Jrigada Date: Tue, 17 Sep 2024 08:08:57 -0300 Subject: [PATCH 07/12] change conditional in address parsing, separate test case and remove create2 factory deps --- crates/cheatcodes/src/inspector.rs | 24 ++++++-------------- crates/evm/core/src/constants.rs | 3 ++- crates/evm/core/src/utils.rs | 22 +++++++----------- crates/forge/tests/fixtures/zk/Create2.s.sol | 9 -------- crates/forge/tests/it/zk/create2.rs | 22 ++++++++++++++++++ crates/forge/tests/it/zk/deploy.rs | 15 ------------ crates/forge/tests/it/zk/mod.rs | 1 + 7 files changed, 40 insertions(+), 56 deletions(-) create mode 100644 crates/forge/tests/it/zk/create2.rs diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index fad56085f..3d2be90e4 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -37,7 +37,7 @@ use foundry_evm_core::{ use foundry_zksync_compiler::{DualCompiledContract, DualCompiledContracts}; use foundry_zksync_core::{ convert::{ConvertH160, ConvertH256, ConvertRU256, ConvertU256}, - get_account_code_key, get_balance_key, get_nonce_key, hash_bytecode, Call, + get_account_code_key, get_balance_key, get_nonce_key, Call, ZkTransactionMetadata, }; use itertools::Itertools; @@ -1254,7 +1254,7 @@ impl Cheatcodes { return None; } - let mut create2_factory_deps = Vec::new(); + let mut factory_deps = Vec::new(); if call.target_address == DEFAULT_CREATE2_DEPLOYER && self.use_zk_vm { call.target_address = DEFAULT_CREATE2_DEPLOYER_ZKSYNC; @@ -1266,13 +1266,7 @@ impl Cheatcodes { .find_by_evm_bytecode(init_code) .unwrap_or_else(|| panic!("failed finding contract for {init_code:?}")); - create2_factory_deps.push(contract.zk_deployed_bytecode.clone()); - - let factory_deps = self.dual_compiled_contracts.fetch_all_factory_deps(contract); - - // This is a hack to pass it to the call - self.persisted_factory_deps - .extend(factory_deps.into_iter().map(|v| (hash_bytecode(&v), v))); + factory_deps = self.dual_compiled_contracts.fetch_all_factory_deps(contract); let constructor_input = init_code[contract.evm_bytecode.len()..].to_vec(); @@ -1410,9 +1404,7 @@ impl Cheatcodes { let zk_tx = if self.use_zk_vm { // We shouldn't need factory_deps for CALLs if call.target_address == DEFAULT_CREATE2_DEPLOYER_ZKSYNC { - Some(ZkTransactionMetadata { - factory_deps: create2_factory_deps.clone(), - }) + Some(ZkTransactionMetadata { factory_deps: factory_deps.clone() }) } else { Some(ZkTransactionMetadata { factory_deps: Default::default() }) } @@ -1508,9 +1500,7 @@ impl Cheatcodes { } if self.use_zk_vm { - if let Some(result) = - self.try_call_in_zk(ecx, call, executor, &mut create2_factory_deps) - { + if let Some(result) = self.try_call_in_zk(ecx, call, executor, factory_deps) { return Some(result); } } @@ -1526,7 +1516,7 @@ impl Cheatcodes { ecx: &mut EvmContext, call: &mut CallInputs, executor: &mut impl CheatcodesExecutor, - create2_factory_deps: &mut [Vec], + create2_factory_deps: Vec>, ) -> Option where DB: DatabaseExt, @@ -1563,7 +1553,7 @@ impl Cheatcodes { call, ecx, ccx, - create2_factory_deps.to_owned(), + create2_factory_deps, ) { Ok(result) => { // append console logs from zkEVM to the current executor's LogTracer diff --git a/crates/evm/core/src/constants.rs b/crates/evm/core/src/constants.rs index 3e25c299e..9b29cb3ce 100644 --- a/crates/evm/core/src/constants.rs +++ b/crates/evm/core/src/constants.rs @@ -38,7 +38,8 @@ pub const DEFAULT_CREATE2_DEPLOYER_DEPLOYER: Address = /// The default CREATE2 deployer. pub const DEFAULT_CREATE2_DEPLOYER: Address = address!("4e59b44847b379578588920ca78fbf26c0b4956c"); -/// The default CREATE2 deployer for zksync (0x0000000000000000000000000000000000010000) +/// The default CREATE2 deployer for zkSync (0x0000000000000000000000000000000000010000) +/// See: https://github.com/zkSync-Community-Hub/zksync-developers/discussions/519 pub const DEFAULT_CREATE2_DEPLOYER_ZKSYNC: Address = address!("0000000000000000000000000000000000010000"); diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index c78c31daa..d82834aef 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -1,5 +1,8 @@ pub use crate::ic::*; -use crate::{constants::DEFAULT_CREATE2_DEPLOYER, InspectorExt}; +use crate::{ + constants::{DEFAULT_CREATE2_DEPLOYER, DEFAULT_CREATE2_DEPLOYER_ZKSYNC}, + InspectorExt, +}; use alloy_json_abi::{Function, JsonAbi}; use alloy_primitives::{Address, Selector, TxKind, U256}; use alloy_rpc_types::{Block, Transaction}; @@ -187,21 +190,12 @@ pub fn create2_handler_register>( return_ok!() => { let output = outcome.output(); - // Here we need to handle both evm and zksync return data to parse the - // address - if output.len() == 20 { - Some(Address::from_slice(output)) - } else if output.len() >= 32 { - // Address in the last 20 bytes of a 32-byte word due to the zksync - // return data + if call_inputs.target_address == DEFAULT_CREATE2_DEPLOYER_ZKSYNC { + // ZkSync: Address in the last 20 bytes of a 32-byte word Some(Address::from_slice(&output[12..32])) } else { - outcome.result = InterpreterResult { - result: InstructionResult::Revert, - output: "invalid CREATE2 factory output".into(), - gas: Gas::new(call_inputs.gas_limit), - }; - None + // Standard EVM: Full output as address + Some(Address::from_slice(output)) } } _ => None, diff --git a/crates/forge/tests/fixtures/zk/Create2.s.sol b/crates/forge/tests/fixtures/zk/Create2.s.sol index 6fa5e2974..4347a8e1c 100644 --- a/crates/forge/tests/fixtures/zk/Create2.s.sol +++ b/crates/forge/tests/fixtures/zk/Create2.s.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.18; import {Script} from "forge-std/Script.sol"; import {Greeter} from "../src/Greeter.sol"; -import {CustomNumber} from "../src/CustomNumber.sol"; contract Create2Script is Script { function run() external { @@ -23,14 +22,6 @@ contract Create2Script is Script { string memory greeting = greeter.greeting("Alice"); require(bytes(greeting).length > 0, "Greeter greeting failed"); - // Deploy CustomNumber using create2 with a salt value - uint8 customNumberValue = 42; - CustomNumber customNumber = new CustomNumber{salt: "123" }(customNumberValue); - - // Verify CustomNumber deployment and initial value - require(address(customNumber) != address(0), "CustomNumber deployment failed"); - require(customNumber.number() == customNumberValue, "CustomNumber initial value mismatch"); - vm.stopBroadcast(); } } diff --git a/crates/forge/tests/it/zk/create2.rs b/crates/forge/tests/it/zk/create2.rs new file mode 100644 index 000000000..94ec4894c --- /dev/null +++ b/crates/forge/tests/it/zk/create2.rs @@ -0,0 +1,22 @@ +use foundry_test_utils::{forgetest_async, util, TestProject}; + +use crate::test_helpers::run_zk_script_test; + +forgetest_async!(can_deploy_via_create2, |prj, cmd| { + setup_create2_prj(&mut prj); + run_zk_script_test( + prj.root(), + &mut cmd, + "./script/Create2.s.sol", + "Create2Script", + None, + 2, + Some(&["-vvvvv"]), + ); +}); + +fn setup_create2_prj(prj: &mut TestProject) { + util::initialize(prj.root()); + prj.add_script("Create2.s.sol", include_str!("../../fixtures/zk/Create2.s.sol")).unwrap(); + prj.add_source("Greeter.sol", include_str!("../../../../../testdata/zk/Greeter.sol")).unwrap(); +} diff --git a/crates/forge/tests/it/zk/deploy.rs b/crates/forge/tests/it/zk/deploy.rs index 40554b587..cb8973f4d 100644 --- a/crates/forge/tests/it/zk/deploy.rs +++ b/crates/forge/tests/it/zk/deploy.rs @@ -24,24 +24,9 @@ forgetest_async!(multiple_deployments_of_the_same_contract, |prj, cmd| { ); }); -forgetest_async!(can_deploy_via_create2, |prj, cmd| { - setup_deploy_prj(&mut prj); - run_zk_script_test( - prj.root(), - &mut cmd, - "./script/Create2.s.sol", - "Create2Script", - None, - 3, - Some(&["-vvvvv"]), - ); -}); - fn setup_deploy_prj(prj: &mut TestProject) { util::initialize(prj.root()); prj.add_script("Deploy.s.sol", include_str!("../../fixtures/zk/Deploy.s.sol")).unwrap(); - prj.add_script("Create2.s.sol", include_str!("../../fixtures/zk/Create2.s.sol")).unwrap(); - prj.add_source("Greeter.sol", include_str!("../../../../../testdata/zk/Greeter.sol")).unwrap(); prj.add_source("CustomNumber.sol", include_str!("../../../../../testdata/zk/CustomNumber.sol")) .unwrap(); } diff --git a/crates/forge/tests/it/zk/mod.rs b/crates/forge/tests/it/zk/mod.rs index c9bf37f79..d7337efb0 100644 --- a/crates/forge/tests/it/zk/mod.rs +++ b/crates/forge/tests/it/zk/mod.rs @@ -3,6 +3,7 @@ mod basic; mod cheats; mod contracts; mod create; +mod create2; mod deploy; mod factory; mod factory_deps; From 6b55ff18c6940c2c472f594ad0b7649c21a52fc9 Mon Sep 17 00:00:00 2001 From: Jrigada Date: Tue, 17 Sep 2024 08:32:39 -0300 Subject: [PATCH 08/12] Apply formatter --- crates/cheatcodes/src/inspector.rs | 12 +++--------- crates/zksync/core/src/vm/runner.rs | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 3d2be90e4..6d53e17aa 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -37,8 +37,7 @@ use foundry_evm_core::{ use foundry_zksync_compiler::{DualCompiledContract, DualCompiledContracts}; use foundry_zksync_core::{ convert::{ConvertH160, ConvertH256, ConvertRU256, ConvertU256}, - get_account_code_key, get_balance_key, get_nonce_key, Call, - ZkTransactionMetadata, + get_account_code_key, get_balance_key, get_nonce_key, Call, ZkTransactionMetadata, }; use itertools::Itertools; use revm::{ @@ -1516,7 +1515,7 @@ impl Cheatcodes { ecx: &mut EvmContext, call: &mut CallInputs, executor: &mut impl CheatcodesExecutor, - create2_factory_deps: Vec>, + factory_deps: Vec>, ) -> Option where DB: DatabaseExt, @@ -1549,12 +1548,7 @@ impl Cheatcodes { // We currently exhaust the entire gas for the call as zkEVM returns a very high amount // of gas that OOGs revm. let gas = Gas::new(call.gas_limit); - match foundry_zksync_core::vm::call::<_, DatabaseError>( - call, - ecx, - ccx, - create2_factory_deps, - ) { + match foundry_zksync_core::vm::call::<_, DatabaseError>(call, factory_deps, ecx, ccx) { Ok(result) => { // append console logs from zkEVM to the current executor's LogTracer result.logs.iter().filter_map(decode_console_log).for_each(|decoded_log| { diff --git a/crates/zksync/core/src/vm/runner.rs b/crates/zksync/core/src/vm/runner.rs index 4bd626576..520f65f1f 100644 --- a/crates/zksync/core/src/vm/runner.rs +++ b/crates/zksync/core/src/vm/runner.rs @@ -172,9 +172,9 @@ where /// Executes a CALL opcode on the ZK-VM. pub fn call( call: &CallInputs, + factory_deps: Vec>, ecx: &mut EvmContext, mut ccx: CheatcodeTracerContext, - factory_deps: Vec>, ) -> ZKVMResult where DB: Database, From a8c988ec8c8a6ae9c90d5e299dbb7f049e1ea288 Mon Sep 17 00:00:00 2001 From: Jrigada Date: Tue, 17 Sep 2024 08:41:27 -0300 Subject: [PATCH 09/12] Check which deployer we are using to retrieve the code hash --- crates/evm/core/src/utils.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index d82834aef..4f8930124 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -145,7 +145,13 @@ pub fn create2_handler_register>( .push((ctx.evm.journaled_state.depth(), call_inputs.clone())); // Sanity check that CREATE2 deployer exists. - let code_hash = ctx.evm.load_account(call_inputs.target_address)?.0.info.code_hash; + // We check which deployer we are using to separate the logic for zkSync and original + // foundry. + let code_hash = if call_inputs.target_address == DEFAULT_CREATE2_DEPLOYER { + ctx.evm.load_account(DEFAULT_CREATE2_DEPLOYER)?.0.info.code_hash + } else { + ctx.evm.load_account(call_inputs.target_address)?.0.info.code_hash + }; if code_hash == KECCAK_EMPTY { return Ok(FrameOrResult::Result(FrameResult::Call(CallOutcome { result: InterpreterResult { From 87194f6963accc942a4fe6a08df00e7456cdcc6f Mon Sep 17 00:00:00 2001 From: Juan Rigada <62958725+Jrigada@users.noreply.github.com> Date: Tue, 17 Sep 2024 08:47:06 -0300 Subject: [PATCH 10/12] Update crates/cheatcodes/src/inspector.rs Co-authored-by: Nisheeth Barthwal --- crates/cheatcodes/src/inspector.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 6d53e17aa..748203d0a 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -1499,7 +1499,7 @@ impl Cheatcodes { } if self.use_zk_vm { - if let Some(result) = self.try_call_in_zk(ecx, call, executor, factory_deps) { + if let Some(result) = self.try_call_in_zk(ecx, call, factory_deps, executor) { return Some(result); } } From d5798f7fb7c17936631fd5c7cebb41ab8d355ecb Mon Sep 17 00:00:00 2001 From: Jrigada Date: Tue, 17 Sep 2024 09:53:09 -0300 Subject: [PATCH 11/12] Migrate zksync create 2 deployer constant to zksync core --- crates/cheatcodes/src/inspector.rs | 7 ++++--- crates/evm/core/src/constants.rs | 5 ----- crates/evm/core/src/utils.rs | 6 ++---- crates/forge/tests/it/zk/deploy.rs | 1 + crates/zksync/core/src/lib.rs | 7 ++++++- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index e6d8715d0..d3acedfa5 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -28,7 +28,7 @@ use foundry_evm_core::{ backend::{DatabaseError, DatabaseExt, LocalForkId, RevertDiagnostic}, constants::{ CHEATCODE_ADDRESS, CHEATCODE_CONTRACT_HASH, DEFAULT_CREATE2_DEPLOYER, - DEFAULT_CREATE2_DEPLOYER_CODE, DEFAULT_CREATE2_DEPLOYER_ZKSYNC, + DEFAULT_CREATE2_DEPLOYER_CODE, }, decode::decode_console_log, utils::new_evm_with_existing_context, @@ -38,6 +38,7 @@ use foundry_zksync_compiler::{DualCompiledContract, DualCompiledContracts}; use foundry_zksync_core::{ convert::{ConvertH160, ConvertH256, ConvertRU256, ConvertU256}, get_account_code_key, get_balance_key, get_nonce_key, Call, ZkTransactionMetadata, + DEFAULT_CREATE2_DEPLOYER_ZKSYNC, }; use itertools::Itertools; use revm::{ @@ -1499,7 +1500,7 @@ impl Cheatcodes { } if self.use_zk_vm { - if let Some(result) = self.try_call_in_zk(ecx, call, factory_deps, executor) { + if let Some(result) = self.try_call_in_zk(factory_deps, ecx, call, executor) { return Some(result); } } @@ -1512,10 +1513,10 @@ impl Cheatcodes { /// handled in EVM. fn try_call_in_zk( &mut self, + factory_deps: Vec>, ecx: &mut EvmContext, call: &mut CallInputs, executor: &mut impl CheatcodesExecutor, - factory_deps: Vec>, ) -> Option where DB: DatabaseExt, diff --git a/crates/evm/core/src/constants.rs b/crates/evm/core/src/constants.rs index 9b29cb3ce..c62e9190f 100644 --- a/crates/evm/core/src/constants.rs +++ b/crates/evm/core/src/constants.rs @@ -38,11 +38,6 @@ pub const DEFAULT_CREATE2_DEPLOYER_DEPLOYER: Address = /// The default CREATE2 deployer. pub const DEFAULT_CREATE2_DEPLOYER: Address = address!("4e59b44847b379578588920ca78fbf26c0b4956c"); -/// The default CREATE2 deployer for zkSync (0x0000000000000000000000000000000000010000) -/// See: https://github.com/zkSync-Community-Hub/zksync-developers/discussions/519 -pub const DEFAULT_CREATE2_DEPLOYER_ZKSYNC: Address = - address!("0000000000000000000000000000000000010000"); - /// The initcode of the default CREATE2 deployer. pub const DEFAULT_CREATE2_DEPLOYER_CODE: &[u8] = &hex!("604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3"); /// The runtime code of the default CREATE2 deployer. diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index 4f8930124..c3b7c6862 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -1,12 +1,10 @@ pub use crate::ic::*; -use crate::{ - constants::{DEFAULT_CREATE2_DEPLOYER, DEFAULT_CREATE2_DEPLOYER_ZKSYNC}, - InspectorExt, -}; +use crate::{constants::DEFAULT_CREATE2_DEPLOYER, InspectorExt}; use alloy_json_abi::{Function, JsonAbi}; use alloy_primitives::{Address, Selector, TxKind, U256}; use alloy_rpc_types::{Block, Transaction}; use foundry_config::NamedChain; +use foundry_zksync_core::DEFAULT_CREATE2_DEPLOYER_ZKSYNC; use revm::{ db::WrapDatabaseRef, handler::register::EvmHandler, diff --git a/crates/forge/tests/it/zk/deploy.rs b/crates/forge/tests/it/zk/deploy.rs index cb8973f4d..1b2ef5a30 100644 --- a/crates/forge/tests/it/zk/deploy.rs +++ b/crates/forge/tests/it/zk/deploy.rs @@ -27,6 +27,7 @@ forgetest_async!(multiple_deployments_of_the_same_contract, |prj, cmd| { fn setup_deploy_prj(prj: &mut TestProject) { util::initialize(prj.root()); prj.add_script("Deploy.s.sol", include_str!("../../fixtures/zk/Deploy.s.sol")).unwrap(); + prj.add_source("Greeter.sol", include_str!("../../../../../testdata/zk/Greeter.sol")).unwrap(); prj.add_source("CustomNumber.sol", include_str!("../../../../../testdata/zk/CustomNumber.sol")) .unwrap(); } diff --git a/crates/zksync/core/src/lib.rs b/crates/zksync/core/src/lib.rs index 5173be1e0..b93dc4013 100644 --- a/crates/zksync/core/src/lib.rs +++ b/crates/zksync/core/src/lib.rs @@ -19,7 +19,7 @@ pub mod vm; pub mod state; use alloy_network::{AnyNetwork, TxSigner}; -use alloy_primitives::{Address, Bytes, U256 as rU256}; +use alloy_primitives::{address, Address, Bytes, U256 as rU256}; use alloy_provider::Provider; use alloy_rpc_types::TransactionRequest; use alloy_serde::WithOtherFields; @@ -54,6 +54,11 @@ pub const EMPTY_CODE: [u8; 32] = [0; 32]; /// The minimum possible address that is not reserved in the zkSync space. const MIN_VALID_ADDRESS: u32 = 2u32.pow(16); +/// The default CREATE2 deployer for zkSync (0x0000000000000000000000000000000000010000) +/// See: https://github.com/zkSync-Community-Hub/zksync-developers/discussions/519 +pub const DEFAULT_CREATE2_DEPLOYER_ZKSYNC: Address = + address!("0000000000000000000000000000000000010000"); + /// Returns the balance key for a provided account address. pub fn get_balance_key(address: Address) -> rU256 { storage_key_for_eth_balance(&address.to_h160()).key().to_ru256() From a4d534c0380980e6ea0764c4d105f8c78723fd25 Mon Sep 17 00:00:00 2001 From: Jrigada Date: Tue, 17 Sep 2024 11:24:26 -0300 Subject: [PATCH 12/12] Check for expected address in test and change conditional in utils --- crates/evm/core/src/utils.rs | 11 +++++++---- crates/forge/tests/fixtures/zk/Create2.s.sol | 17 +++++++++++++++++ crates/forge/tests/it/zk/create2.rs | 6 ++++++ crates/forge/tests/it/zk/deploy.rs | 2 -- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index c3b7c6862..d755b8abf 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -145,11 +145,12 @@ pub fn create2_handler_register>( // Sanity check that CREATE2 deployer exists. // We check which deployer we are using to separate the logic for zkSync and original // foundry. - let code_hash = if call_inputs.target_address == DEFAULT_CREATE2_DEPLOYER { - ctx.evm.load_account(DEFAULT_CREATE2_DEPLOYER)?.0.info.code_hash - } else { - ctx.evm.load_account(call_inputs.target_address)?.0.info.code_hash + let mut code_hash = ctx.evm.load_account(DEFAULT_CREATE2_DEPLOYER)?.0.info.code_hash; + + if call_inputs.target_address == DEFAULT_CREATE2_DEPLOYER_ZKSYNC { + code_hash = ctx.evm.load_account(call_inputs.target_address)?.0.info.code_hash; }; + if code_hash == KECCAK_EMPTY { return Ok(FrameOrResult::Result(FrameResult::Call(CallOutcome { result: InterpreterResult { @@ -196,6 +197,8 @@ pub fn create2_handler_register>( if call_inputs.target_address == DEFAULT_CREATE2_DEPLOYER_ZKSYNC { // ZkSync: Address in the last 20 bytes of a 32-byte word + // We want to error out if the address is not valid as + // Address::from_slice() does Some(Address::from_slice(&output[12..32])) } else { // Standard EVM: Full output as address diff --git a/crates/forge/tests/fixtures/zk/Create2.s.sol b/crates/forge/tests/fixtures/zk/Create2.s.sol index 4347a8e1c..9612f859b 100644 --- a/crates/forge/tests/fixtures/zk/Create2.s.sol +++ b/crates/forge/tests/fixtures/zk/Create2.s.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.18; import {Script} from "forge-std/Script.sol"; import {Greeter} from "../src/Greeter.sol"; +import {Create2Utils} from "../src/Create2Utils.sol"; contract Create2Script is Script { function run() external { @@ -18,10 +19,26 @@ contract Create2Script is Script { // Verify Greeter deployment require(address(greeter) != address(0), "Greeter deployment failed"); + // Verify the deployed address matches the expected address + bytes32 bytecodeHash = getBytecodeHash("zkout/Greeter.sol/Greeter.json"); + address expectedAddress = Create2Utils.computeCreate2Address( + address(0x0000000000000000000000000000000000010000), // DEFAULT_CREATE2_DEPLOYER_ZKSYNC + greeterSalt, + bytecodeHash, + keccak256(abi.encode()) + ); + + require(address(greeter) == expectedAddress, "Deployed address doesn't match expected address"); + // Test Greeter functionality string memory greeting = greeter.greeting("Alice"); require(bytes(greeting).length > 0, "Greeter greeting failed"); vm.stopBroadcast(); } + + function getBytecodeHash(string memory path) internal returns (bytes32 bytecodeHash) { + string memory artifact = vm.readFile(path); + bytecodeHash = vm.parseJsonBytes32(artifact, ".hash"); + } } diff --git a/crates/forge/tests/it/zk/create2.rs b/crates/forge/tests/it/zk/create2.rs index 94ec4894c..f46c2dcbf 100644 --- a/crates/forge/tests/it/zk/create2.rs +++ b/crates/forge/tests/it/zk/create2.rs @@ -1,9 +1,13 @@ +use foundry_config::fs_permissions::PathPermission; use foundry_test_utils::{forgetest_async, util, TestProject}; use crate::test_helpers::run_zk_script_test; forgetest_async!(can_deploy_via_create2, |prj, cmd| { setup_create2_prj(&mut prj); + let mut config = cmd.config(); + config.fs_permissions.add(PathPermission::read("./zkout")); + prj.write_config(config); run_zk_script_test( prj.root(), &mut cmd, @@ -19,4 +23,6 @@ fn setup_create2_prj(prj: &mut TestProject) { util::initialize(prj.root()); prj.add_script("Create2.s.sol", include_str!("../../fixtures/zk/Create2.s.sol")).unwrap(); prj.add_source("Greeter.sol", include_str!("../../../../../testdata/zk/Greeter.sol")).unwrap(); + prj.add_source("Create2Utils.sol", include_str!("../../../../../testdata/zk/Create2Utils.sol")) + .unwrap(); } diff --git a/crates/forge/tests/it/zk/deploy.rs b/crates/forge/tests/it/zk/deploy.rs index 1b2ef5a30..4af3bb71e 100644 --- a/crates/forge/tests/it/zk/deploy.rs +++ b/crates/forge/tests/it/zk/deploy.rs @@ -28,6 +28,4 @@ fn setup_deploy_prj(prj: &mut TestProject) { util::initialize(prj.root()); prj.add_script("Deploy.s.sol", include_str!("../../fixtures/zk/Deploy.s.sol")).unwrap(); prj.add_source("Greeter.sol", include_str!("../../../../../testdata/zk/Greeter.sol")).unwrap(); - prj.add_source("CustomNumber.sol", include_str!("../../../../../testdata/zk/CustomNumber.sol")) - .unwrap(); }