Skip to content

Commit f17912e

Browse files
authored
Merge pull request #631 from matter-labs/fix/zk-create-bytecode
fix(zk): create with either bytecode
2 parents 4e2a832 + 11d17c9 commit f17912e

File tree

5 files changed

+113
-49
lines changed

5 files changed

+113
-49
lines changed

crates/cheatcodes/src/inspector.rs

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -953,22 +953,22 @@ impl Cheatcodes {
953953
let mut zk_tx = if self.use_zk_vm {
954954
to = Some(TxKind::Call(CONTRACT_DEPLOYER_ADDRESS.to_address()));
955955
nonce = foundry_zksync_core::nonce(broadcast.new_origin, ecx_inner) as u64;
956-
let contract = self
956+
let init_code = input.init_code();
957+
let find_contract = self
957958
.dual_compiled_contracts
958-
.find_by_evm_bytecode(&input.init_code().0)
959-
.unwrap_or_else(|| {
960-
panic!("failed finding contract for {:?}", input.init_code())
961-
});
959+
.find_bytecode(&init_code.0)
960+
.unwrap_or_else(|| panic!("failed finding contract for {init_code:?}"));
961+
962+
let constructor_args = find_contract.constructor_args();
963+
let contract = find_contract.contract();
964+
962965
let factory_deps =
963966
self.dual_compiled_contracts.fetch_all_factory_deps(contract);
964967

965-
let constructor_input =
966-
call_init_code[contract.evm_bytecode.len()..].to_vec();
967-
968968
let create_input = foundry_zksync_core::encode_create_params(
969969
&input.scheme().unwrap_or(CreateScheme::Create),
970970
contract.zk_bytecode_hash,
971-
constructor_input,
971+
constructor_args.to_vec(),
972972
);
973973
call_init_code = Bytes::from(create_input);
974974

@@ -1107,20 +1107,30 @@ impl Cheatcodes {
11071107
}
11081108
}
11091109

1110-
if input.init_code().0 == DEFAULT_CREATE2_DEPLOYER_CODE {
1110+
let init_code = input.init_code();
1111+
if init_code.0 == DEFAULT_CREATE2_DEPLOYER_CODE {
11111112
info!("running create in EVM, instead of zkEVM (DEFAULT_CREATE2_DEPLOYER_CODE)");
11121113
return None
11131114
}
11141115

11151116
info!("running create in zkEVM");
11161117

1117-
let zk_contract = self
1118+
let find_contract = self
11181119
.dual_compiled_contracts
1119-
.find_by_evm_bytecode(&input.init_code().0)
1120-
.unwrap_or_else(|| panic!("failed finding contract for {:?}", input.init_code()));
1120+
.find_bytecode(&init_code.0)
1121+
.unwrap_or_else(|| panic!("failed finding contract for {init_code:?}"));
1122+
1123+
let constructor_args = find_contract.constructor_args();
1124+
let contract = find_contract.contract();
11211125

1122-
let factory_deps = self.dual_compiled_contracts.fetch_all_factory_deps(zk_contract);
1123-
tracing::debug!(contract = zk_contract.name, "using dual compiled contract");
1126+
let zk_create_input = foundry_zksync_core::encode_create_params(
1127+
&input.scheme().unwrap_or(CreateScheme::Create),
1128+
contract.zk_bytecode_hash,
1129+
constructor_args.to_vec(),
1130+
);
1131+
1132+
let factory_deps = self.dual_compiled_contracts.fetch_all_factory_deps(contract);
1133+
tracing::debug!(contract = contract.name, "using dual compiled contract");
11241134

11251135
let ccx = foundry_zksync_core::vm::CheatcodeTracerContext {
11261136
mocked_calls: self.mocked_calls.clone(),
@@ -1129,22 +1139,15 @@ impl Cheatcodes {
11291139
persisted_factory_deps: Some(&mut self.persisted_factory_deps),
11301140
paymaster_data: self.paymaster_params.take(),
11311141
};
1132-
let create_inputs = CreateInputs {
1133-
scheme: input.scheme().unwrap_or(CreateScheme::Create),
1134-
init_code: input.init_code(),
1135-
value: input.value(),
1136-
caller: input.caller(),
1137-
gas_limit: input.gas_limit(),
1142+
let zk_create = foundry_zksync_core::vm::ZkCreateInputs {
1143+
value: input.value().to_u256(),
1144+
msg_sender: input.caller(),
1145+
create_input: zk_create_input,
1146+
factory_deps,
11381147
};
11391148

11401149
let mut gas = Gas::new(input.gas_limit());
1141-
match foundry_zksync_core::vm::create::<_, DatabaseError>(
1142-
&create_inputs,
1143-
zk_contract,
1144-
factory_deps,
1145-
ecx,
1146-
ccx,
1147-
) {
1150+
match foundry_zksync_core::vm::create::<_, DatabaseError>(zk_create, ecx, ccx) {
11481151
Ok(result) => {
11491152
if let Some(recorded_logs) = &mut self.recorded_logs {
11501153
recorded_logs.extend(result.logs.clone().into_iter().map(|log| Vm::Log {
@@ -1161,8 +1164,8 @@ impl Cheatcodes {
11611164
state: self,
11621165
ecx: &mut ecx.inner,
11631166
precompiles: &mut ecx.precompiles,
1164-
gas_limit: create_inputs.gas_limit,
1165-
caller: create_inputs.caller,
1167+
gas_limit: input.gas_limit(),
1168+
caller: input.caller(),
11661169
},
11671170
decoded_log,
11681171
);
@@ -1436,19 +1439,20 @@ impl Cheatcodes {
14361439
call.bytecode_address = DEFAULT_CREATE2_DEPLOYER_ZKSYNC;
14371440

14381441
let (salt, init_code) = call.input.split_at(32);
1439-
let contract = self
1442+
let find_contract = self
14401443
.dual_compiled_contracts
1441-
.find_by_evm_bytecode(init_code)
1444+
.find_bytecode(init_code)
14421445
.unwrap_or_else(|| panic!("failed finding contract for {init_code:?}"));
14431446

1444-
factory_deps = self.dual_compiled_contracts.fetch_all_factory_deps(contract);
1447+
let constructor_args = find_contract.constructor_args();
1448+
let contract = find_contract.contract();
14451449

1446-
let constructor_input = init_code[contract.evm_bytecode.len()..].to_vec();
1450+
factory_deps = self.dual_compiled_contracts.fetch_all_factory_deps(contract);
14471451

14481452
let create_input = foundry_zksync_core::encode_create_params(
14491453
&CreateScheme::Create2 { salt: U256::from_be_slice(salt) },
14501454
contract.zk_bytecode_hash,
1451-
constructor_input,
1455+
constructor_args.to_vec(),
14521456
);
14531457

14541458
call.input = create_input.into();

crates/zksync/compiler/src/zksolc/mod.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,28 @@ pub struct DualCompiledContract {
4343
pub evm_bytecode: Vec<u8>,
4444
}
4545

46+
/// Couple contract type with contract and init code
47+
pub struct FindBytecodeResult<'a> {
48+
r#type: ContractType,
49+
contract: &'a DualCompiledContract,
50+
init_code: &'a [u8],
51+
}
52+
53+
impl<'a> FindBytecodeResult<'a> {
54+
/// Retrieve the found contract
55+
pub fn contract(self) -> &'a DualCompiledContract {
56+
self.contract
57+
}
58+
59+
/// Retrieve the correct constructor args
60+
pub fn constructor_args(&self) -> &'a [u8] {
61+
match self.r#type {
62+
ContractType::ZK => &self.init_code[self.contract.zk_deployed_bytecode.len()..],
63+
ContractType::EVM => &self.init_code[self.contract.evm_bytecode.len()..],
64+
}
65+
}
66+
}
67+
4668
/// A collection of `[DualCompiledContract]`s
4769
#[derive(Debug, Default, Clone)]
4870
pub struct DualCompiledContracts {
@@ -195,6 +217,32 @@ impl DualCompiledContracts {
195217
self.contracts.iter().find(|contract| code_hash == contract.zk_bytecode_hash)
196218
}
197219

220+
/// Find a contract matching the given bytecode, whether it's EVM or ZK.
221+
///
222+
/// Will prioritize longest match
223+
pub fn find_bytecode<'a: 'b, 'b>(
224+
&'a self,
225+
init_code: &'b [u8],
226+
) -> Option<FindBytecodeResult<'b>> {
227+
let evm = self.find_by_evm_bytecode(init_code).map(|evm| (ContractType::EVM, evm));
228+
let zk = self.find_by_zk_deployed_bytecode(init_code).map(|evm| (ContractType::ZK, evm));
229+
230+
match (&evm, &zk) {
231+
(Some((_, evm)), Some((_, zk))) => {
232+
if zk.zk_deployed_bytecode.len() >= evm.evm_bytecode.len() {
233+
Some(FindBytecodeResult { r#type: ContractType::ZK, contract: zk, init_code })
234+
} else {
235+
Some(FindBytecodeResult { r#type: ContractType::EVM, contract: zk, init_code })
236+
}
237+
}
238+
_ => evm.or(zk).map(|(r#type, contract)| FindBytecodeResult {
239+
r#type,
240+
contract,
241+
init_code,
242+
}),
243+
}
244+
}
245+
198246
/// Finds a contract own and nested factory deps
199247
pub fn fetch_all_factory_deps(&self, root: &DualCompiledContract) -> Vec<Vec<u8>> {
200248
let mut visited = HashSet::new();

crates/zksync/core/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ exclude.workspace = true
1515
foundry-common.workspace = true
1616
foundry-evm-abi.workspace = true
1717
foundry-cheatcodes-common.workspace = true
18-
foundry-zksync-compiler.workspace = true
1918
alloy-primitives.workspace = true
2019
alloy-signer.workspace = true
2120
alloy-network.workspace = true

crates/zksync/core/src/vm/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,7 @@ pub use farcall::{SELECTOR_CONTRACT_DEPLOYER_CREATE, SELECTOR_CONTRACT_DEPLOYER_
1010
pub use inspect::{
1111
batch_factory_dependencies, inspect, inspect_as_batch, ZKVMExecutionResult, ZKVMResult,
1212
};
13-
pub use runner::{balance, call, code_hash, create, encode_create_params, nonce, transact};
13+
pub use runner::{
14+
balance, call, code_hash, create, encode_create_params, nonce, transact, ZkCreateInputs,
15+
};
1416
pub use tracers::cheatcode::CheatcodeTracerContext;

crates/zksync/core/src/vm/runner.rs

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use alloy_primitives::hex;
2-
use foundry_zksync_compiler::DualCompiledContract;
32
use itertools::Itertools;
43
use revm::{
5-
interpreter::{CallInputs, CallScheme, CallValue, CreateInputs},
4+
interpreter::{CallInputs, CallScheme, CallValue},
65
primitives::{Address, CreateScheme, Env, ResultAndState, TransactTo, B256, U256 as rU256},
76
Database, EvmContext, InnerEvmContext,
87
};
@@ -116,22 +115,34 @@ where
116115
ZKVMData::new(ecx).get_tx_nonce(address).0
117116
}
118117

119-
/// Executes a CREATE opcode on the ZK-VM.
118+
/// EraVM equivalent of [`CreateInputs`]
119+
pub struct ZkCreateInputs {
120+
/// The current `msg.sender`
121+
pub msg_sender: Address,
122+
/// The encoded calldata input for `CONTRACT_DEPLOYER`
123+
pub create_input: Vec<u8>,
124+
/// Factory deps for the contract we are deploying
125+
pub factory_deps: Vec<Vec<u8>>,
126+
/// Value specified for the deployment
127+
pub value: U256,
128+
}
129+
130+
/// Executes a CREATE opcode on the EraVM.
131+
///
132+
/// * `call.init_code` should be valid EraVM's ContractDeployer input
120133
pub fn create<DB, E>(
121-
call: &CreateInputs,
122-
contract: &DualCompiledContract,
123-
factory_deps: Vec<Vec<u8>>,
134+
inputs: ZkCreateInputs,
124135
ecx: &mut EvmContext<DB>,
125136
mut ccx: CheatcodeTracerContext,
126137
) -> ZKVMResult<E>
127138
where
128139
DB: Database,
129140
<DB as Database>::Error: Debug,
130141
{
131-
info!(?call, "create tx {}", hex::encode(&call.init_code));
132-
let constructor_input = call.init_code[contract.evm_bytecode.len()..].to_vec();
142+
let ZkCreateInputs { create_input, factory_deps, value, msg_sender } = inputs;
143+
144+
info!("create tx {}", hex::encode(&create_input));
133145
let caller = ecx.env.tx.caller;
134-
let calldata = encode_create_params(&call.scheme, contract.zk_bytecode_hash, constructor_input);
135146
let nonce = ZKVMData::new(ecx).get_tx_nonce(caller);
136147

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

149160
let tx = L2Tx::new(
150161
Some(CONTRACT_DEPLOYER_ADDRESS),
151-
calldata,
162+
create_input,
152163
nonce,
153164
Fee {
154165
gas_limit,
@@ -157,14 +168,14 @@ where
157168
gas_per_pubdata_limit: U256::from(20000),
158169
},
159170
caller.to_h160(),
160-
call.value.to_u256(),
171+
value,
161172
factory_deps,
162173
paymaster_params,
163174
);
164175

165176
let call_ctx = CallContext {
166177
tx_caller: ecx.env.tx.caller,
167-
msg_sender: call.caller,
178+
msg_sender,
168179
contract: CONTRACT_DEPLOYER_ADDRESS.to_address(),
169180
delegate_as: None,
170181
block_number: ecx.env.block.number,

0 commit comments

Comments
 (0)