Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(zk): create with either bytecode #631

Merged
merged 5 commits into from
Oct 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 39 additions & 35 deletions crates/cheatcodes/src/inspector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -953,22 +953,22 @@ impl Cheatcodes {
let mut zk_tx = if self.use_zk_vm {
to = Some(TxKind::Call(CONTRACT_DEPLOYER_ADDRESS.to_address()));
nonce = foundry_zksync_core::nonce(broadcast.new_origin, ecx_inner) as u64;
let contract = self
let init_code = input.init_code();
let find_contract = self
.dual_compiled_contracts
.find_by_evm_bytecode(&input.init_code().0)
.unwrap_or_else(|| {
panic!("failed finding contract for {:?}", input.init_code())
});
.find_bytecode(&init_code.0)
.unwrap_or_else(|| panic!("failed finding contract for {init_code:?}"));

let constructor_args = find_contract.constructor_args();
let contract = find_contract.contract();

let factory_deps =
self.dual_compiled_contracts.fetch_all_factory_deps(contract);

let constructor_input =
call_init_code[contract.evm_bytecode.len()..].to_vec();

let create_input = foundry_zksync_core::encode_create_params(
&input.scheme().unwrap_or(CreateScheme::Create),
contract.zk_bytecode_hash,
constructor_input,
constructor_args.to_vec(),
);
call_init_code = Bytes::from(create_input);

Expand Down Expand Up @@ -1107,20 +1107,30 @@ impl Cheatcodes {
}
}

if input.init_code().0 == DEFAULT_CREATE2_DEPLOYER_CODE {
let init_code = input.init_code();
if init_code.0 == DEFAULT_CREATE2_DEPLOYER_CODE {
info!("running create in EVM, instead of zkEVM (DEFAULT_CREATE2_DEPLOYER_CODE)");
return None
}

info!("running create in zkEVM");

let zk_contract = self
let find_contract = self
.dual_compiled_contracts
.find_by_evm_bytecode(&input.init_code().0)
.unwrap_or_else(|| panic!("failed finding contract for {:?}", input.init_code()));
.find_bytecode(&init_code.0)
.unwrap_or_else(|| panic!("failed finding contract for {init_code:?}"));

let constructor_args = find_contract.constructor_args();
let contract = find_contract.contract();

let factory_deps = self.dual_compiled_contracts.fetch_all_factory_deps(zk_contract);
tracing::debug!(contract = zk_contract.name, "using dual compiled contract");
let zk_create_input = foundry_zksync_core::encode_create_params(
&input.scheme().unwrap_or(CreateScheme::Create),
contract.zk_bytecode_hash,
constructor_args.to_vec(),
);

let factory_deps = self.dual_compiled_contracts.fetch_all_factory_deps(contract);
tracing::debug!(contract = contract.name, "using dual compiled contract");

let ccx = foundry_zksync_core::vm::CheatcodeTracerContext {
mocked_calls: self.mocked_calls.clone(),
Expand All @@ -1129,22 +1139,15 @@ impl Cheatcodes {
persisted_factory_deps: Some(&mut self.persisted_factory_deps),
paymaster_data: self.paymaster_params.take(),
};
let create_inputs = CreateInputs {
scheme: input.scheme().unwrap_or(CreateScheme::Create),
init_code: input.init_code(),
value: input.value(),
caller: input.caller(),
gas_limit: input.gas_limit(),
let zk_create = foundry_zksync_core::vm::ZkCreateInputs {
value: input.value().to_u256(),
msg_sender: input.caller(),
create_input: zk_create_input,
factory_deps,
};

let mut gas = Gas::new(input.gas_limit());
match foundry_zksync_core::vm::create::<_, DatabaseError>(
&create_inputs,
zk_contract,
factory_deps,
ecx,
ccx,
) {
match foundry_zksync_core::vm::create::<_, DatabaseError>(zk_create, ecx, ccx) {
Ok(result) => {
if let Some(recorded_logs) = &mut self.recorded_logs {
recorded_logs.extend(result.logs.clone().into_iter().map(|log| Vm::Log {
Expand All @@ -1161,8 +1164,8 @@ impl Cheatcodes {
state: self,
ecx: &mut ecx.inner,
precompiles: &mut ecx.precompiles,
gas_limit: create_inputs.gas_limit,
caller: create_inputs.caller,
gas_limit: input.gas_limit(),
caller: input.caller(),
},
decoded_log,
);
Expand Down Expand Up @@ -1436,19 +1439,20 @@ impl Cheatcodes {
call.bytecode_address = DEFAULT_CREATE2_DEPLOYER_ZKSYNC;

let (salt, init_code) = call.input.split_at(32);
let contract = self
let find_contract = self
.dual_compiled_contracts
.find_by_evm_bytecode(init_code)
.find_bytecode(init_code)
.unwrap_or_else(|| panic!("failed finding contract for {init_code:?}"));

factory_deps = self.dual_compiled_contracts.fetch_all_factory_deps(contract);
let constructor_args = find_contract.constructor_args();
let contract = find_contract.contract();

let constructor_input = init_code[contract.evm_bytecode.len()..].to_vec();
factory_deps = self.dual_compiled_contracts.fetch_all_factory_deps(contract);

let create_input = foundry_zksync_core::encode_create_params(
&CreateScheme::Create2 { salt: U256::from_be_slice(salt) },
contract.zk_bytecode_hash,
constructor_input,
constructor_args.to_vec(),
);

call.input = create_input.into();
Expand Down
48 changes: 48 additions & 0 deletions crates/zksync/compiler/src/zksolc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,28 @@ pub struct DualCompiledContract {
pub evm_bytecode: Vec<u8>,
}

/// Couple contract type with contract and init code
pub struct FindBytecodeResult<'a> {
r#type: ContractType,
contract: &'a DualCompiledContract,
init_code: &'a [u8],
}

impl<'a> FindBytecodeResult<'a> {
/// Retrieve the found contract
pub fn contract(self) -> &'a DualCompiledContract {
self.contract
}

/// Retrieve the correct constructor args
pub fn constructor_args(&self) -> &'a [u8] {
match self.r#type {
ContractType::ZK => &self.init_code[self.contract.zk_deployed_bytecode.len()..],
ContractType::EVM => &self.init_code[self.contract.evm_bytecode.len()..],
}
}
}

/// A collection of `[DualCompiledContract]`s
#[derive(Debug, Default, Clone)]
pub struct DualCompiledContracts {
Expand Down Expand Up @@ -195,6 +217,32 @@ impl DualCompiledContracts {
self.contracts.iter().find(|contract| code_hash == contract.zk_bytecode_hash)
}

/// Find a contract matching the given bytecode, whether it's EVM or ZK.
///
/// Will prioritize longest match
pub fn find_bytecode<'a: 'b, 'b>(
&'a self,
init_code: &'b [u8],
) -> Option<FindBytecodeResult<'b>> {
let evm = self.find_by_evm_bytecode(init_code).map(|evm| (ContractType::EVM, evm));
let zk = self.find_by_zk_deployed_bytecode(init_code).map(|evm| (ContractType::ZK, evm));

match (&evm, &zk) {
(Some((_, evm)), Some((_, zk))) => {
if zk.zk_deployed_bytecode.len() >= evm.evm_bytecode.len() {
Some(FindBytecodeResult { r#type: ContractType::ZK, contract: zk, init_code })
} else {
Some(FindBytecodeResult { r#type: ContractType::EVM, contract: zk, init_code })
}
}
_ => evm.or(zk).map(|(r#type, contract)| FindBytecodeResult {
r#type,
contract,
init_code,
}),
}
}

/// Finds a contract own and nested factory deps
pub fn fetch_all_factory_deps(&self, root: &DualCompiledContract) -> Vec<Vec<u8>> {
let mut visited = HashSet::new();
Expand Down
1 change: 0 additions & 1 deletion crates/zksync/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ exclude.workspace = true
foundry-common.workspace = true
foundry-evm-abi.workspace = true
foundry-cheatcodes-common.workspace = true
foundry-zksync-compiler.workspace = true
alloy-primitives.workspace = true
alloy-signer.workspace = true
alloy-network.workspace = true
Expand Down
4 changes: 3 additions & 1 deletion crates/zksync/core/src/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@ pub use farcall::{SELECTOR_CONTRACT_DEPLOYER_CREATE, SELECTOR_CONTRACT_DEPLOYER_
pub use inspect::{
batch_factory_dependencies, inspect, inspect_as_batch, ZKVMExecutionResult, ZKVMResult,
};
pub use runner::{balance, call, code_hash, create, encode_create_params, nonce, transact};
pub use runner::{
balance, call, code_hash, create, encode_create_params, nonce, transact, ZkCreateInputs,
};
pub use tracers::cheatcode::CheatcodeTracerContext;
35 changes: 23 additions & 12 deletions crates/zksync/core/src/vm/runner.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use alloy_primitives::hex;
use foundry_zksync_compiler::DualCompiledContract;
use itertools::Itertools;
use revm::{
interpreter::{CallInputs, CallScheme, CallValue, CreateInputs},
interpreter::{CallInputs, CallScheme, CallValue},
primitives::{Address, CreateScheme, Env, ResultAndState, TransactTo, B256, U256 as rU256},
Database, EvmContext, InnerEvmContext,
};
Expand Down Expand Up @@ -116,22 +115,34 @@ where
ZKVMData::new(ecx).get_tx_nonce(address).0
}

/// Executes a CREATE opcode on the ZK-VM.
/// EraVM equivalent of [`CreateInputs`]
pub struct ZkCreateInputs {
/// The current `msg.sender`
pub msg_sender: Address,
/// The encoded calldata input for `CONTRACT_DEPLOYER`
pub create_input: Vec<u8>,
/// Factory deps for the contract we are deploying
pub factory_deps: Vec<Vec<u8>>,
/// Value specified for the deployment
pub value: U256,
}

/// Executes a CREATE opcode on the EraVM.
///
/// * `call.init_code` should be valid EraVM's ContractDeployer input
pub fn create<DB, E>(
call: &CreateInputs,
contract: &DualCompiledContract,
factory_deps: Vec<Vec<u8>>,
inputs: ZkCreateInputs,
ecx: &mut EvmContext<DB>,
mut ccx: CheatcodeTracerContext,
) -> ZKVMResult<E>
where
DB: Database,
<DB as Database>::Error: Debug,
{
info!(?call, "create tx {}", hex::encode(&call.init_code));
let constructor_input = call.init_code[contract.evm_bytecode.len()..].to_vec();
let ZkCreateInputs { create_input, factory_deps, value, msg_sender } = inputs;

info!("create tx {}", hex::encode(&create_input));
let caller = ecx.env.tx.caller;
let calldata = encode_create_params(&call.scheme, contract.zk_bytecode_hash, constructor_input);
let nonce = ZKVMData::new(ecx).get_tx_nonce(caller);

let paymaster_params = if let Some(paymaster_data) = &ccx.paymaster_data {
Expand All @@ -148,7 +159,7 @@ where

let tx = L2Tx::new(
Some(CONTRACT_DEPLOYER_ADDRESS),
calldata,
create_input,
nonce,
Fee {
gas_limit,
Expand All @@ -157,14 +168,14 @@ where
gas_per_pubdata_limit: U256::from(20000),
},
caller.to_h160(),
call.value.to_u256(),
value,
factory_deps,
paymaster_params,
);

let call_ctx = CallContext {
tx_caller: ecx.env.tx.caller,
msg_sender: call.caller,
msg_sender,
contract: CONTRACT_DEPLOYER_ADDRESS.to_address(),
delegate_as: None,
block_number: ecx.env.block.number,
Expand Down