Skip to content

Commit b57c83b

Browse files
nbaztecJrigada
andauthored
feat: support EVM interpreter for cast run (#1168)
* support evm interpreter * support cast * cleanup * fmt * remove print * clippy * update Cargo.lock * use evm-interpreter aware transact_with_env instead fo deploy_with_env * fmt * fmt * handle transact_to address for CALLs * fix test * add test * add test for cast run with evm interpreter * Add gas comparison and fix interpreter not being used * Remove force EVM interpreter override in cast run * fix conflict resolution * remove raw logs and fix test list --------- Co-authored-by: jrigada <[email protected]> Co-authored-by: Juan Rigada <[email protected]>
1 parent 11d2203 commit b57c83b

File tree

6 files changed

+117
-7
lines changed

6 files changed

+117
-7
lines changed

crates/cast/src/cmd/call.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ impl CallArgs {
297297
paymaster: Default::default(),
298298
paymaster_input: data.to_vec(),
299299
}),
300+
force_evm_interpreter: None,
300301
};
301302

302303
other_fields.insert(

crates/cast/src/cmd/run.rs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ pub struct RunArgs {
113113
#[arg(long = "zksync")]
114114
zk_force: bool,
115115

116+
/// Use ZKsync EVM interpreter.
117+
#[arg(long = "zk-evm-interpreter")]
118+
zk_evm_interpreter: bool,
119+
116120
/// Disables storage caching entirely.
117121
/// NOTE(zk) This is needed so tests don't cache anvil-zksync responses, could also be useful
118122
/// upstream.
@@ -131,6 +135,7 @@ impl RunArgs {
131135
let evm_opts = figment.extract::<EvmOpts>()?;
132136
let mut config = Config::from_provider(figment)?.sanitized();
133137
config.zksync.compile = self.zk_force;
138+
config.zksync.evm_interpreter = self.zk_evm_interpreter;
134139
config.no_storage_caching = self.no_storage_caching;
135140
let strategy = utils::get_executor_strategy(&config);
136141

@@ -319,7 +324,17 @@ impl RunArgs {
319324
})?;
320325
} else {
321326
trace!(tx=?tx.tx_hash(), "executing previous create transaction");
322-
if let Err(error) = executor.deploy_with_env(env.clone(), None) {
327+
if self.zk_force {
328+
if let Err(error) = executor.transact_with_env(env.clone()) {
329+
return Err(error).wrap_err_with(|| {
330+
format!(
331+
"Failed to deploy transaction: {:?} in block {}",
332+
tx.tx_hash(),
333+
env.evm_env.block_env.number
334+
)
335+
});
336+
}
337+
} else if let Err(error) = executor.deploy_with_env(env.clone(), None) {
323338
match error {
324339
// Reverted transactions should be skipped
325340
EvmError::Execution(_) => (),
@@ -380,7 +395,11 @@ impl RunArgs {
380395
TraceResult::try_from(executor.transact_with_env(env))?
381396
} else {
382397
trace!(tx=?tx.tx_hash(), "executing create transaction");
383-
TraceResult::try_from(executor.deploy_with_env(env, None))?
398+
if self.zk_force {
399+
TraceResult::try_from(executor.transact_with_env(env))?
400+
} else {
401+
TraceResult::try_from(executor.deploy_with_env(env, None))?
402+
}
384403
}
385404
};
386405

@@ -478,9 +497,9 @@ pub fn configure_zksync_tx_env(
478497
// Set basic transaction fields
479498
env.tx.caller = outer_tx.initiator_account().to_address();
480499

481-
env.tx.kind = TxKind::Call(
482-
outer_tx.recipient_account().expect("recipient_account not found in execute").to_address(),
483-
);
500+
let recipient_account = outer_tx.recipient_account();
501+
502+
env.tx.kind = TxKind::Call(recipient_account.unwrap_or_default().to_address());
484503
env.tx.gas_limit = match &outer_tx.common_data {
485504
ExecuteTransactionCommon::L2(l2) => l2.fee.gas_limit.as_u64(),
486505
_ => outer_tx.gas_limit().as_u64(),
@@ -497,10 +516,12 @@ pub fn configure_zksync_tx_env(
497516
ExecuteTransactionCommon::L2(common_data) => foundry_zksync_core::ZkTransactionMetadata {
498517
factory_deps: outer_tx.execute.factory_deps.clone(),
499518
paymaster_data: Some(common_data.paymaster_params.clone()),
519+
force_evm_interpreter: None,
500520
},
501521
_ => foundry_zksync_core::ZkTransactionMetadata {
502522
factory_deps: outer_tx.execute.factory_deps.clone(),
503523
paymaster_data: None,
524+
force_evm_interpreter: None,
504525
},
505526
}
506527
}

crates/cast/tests/cli/zk.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,89 @@ casttest!(test_zk_cast_run_with_call, async |prj, cmd| {
816816
);
817817
});
818818

819+
casttest!(test_zk_cast_run_with_evm_interpreter, async |prj, cmd| {
820+
let node = ZkSyncNode::start().await;
821+
let url = node.url();
822+
823+
let (_, private_key) = ZkSyncNode::rich_wallets()
824+
.next()
825+
.map(|(addr, pk, _)| (addr, pk))
826+
.expect("No rich wallets available");
827+
828+
prj.add_script(
829+
"Counter",
830+
r#"
831+
contract Counter {
832+
uint256 public number;
833+
834+
function increment() public {
835+
number++;
836+
}
837+
}
838+
839+
"#,
840+
);
841+
842+
// Deploy the contract
843+
let output = cmd
844+
.forge_fuse()
845+
.args([
846+
"create",
847+
"Counter",
848+
"--rpc-url",
849+
&url,
850+
"--private-key",
851+
private_key,
852+
"--zksync",
853+
"--broadcast",
854+
])
855+
.assert_success()
856+
.get_output()
857+
.stdout_lossy();
858+
859+
let output = output.trim().parse::<String>().unwrap();
860+
assert!(output.contains("success"));
861+
862+
let re = Regex::new(r"Deployed to: (\w+)\nTransaction hash: (\w+)").expect("invalid regex");
863+
let caps = re.captures(&output).expect("failed getting captures");
864+
let deployed_addr = caps.get(1).expect("failed getting deployed address").as_str();
865+
866+
// Call a function to generate a transaction
867+
let output = cmd
868+
.cast_fuse()
869+
.args([
870+
"send",
871+
deployed_addr,
872+
"increment()",
873+
"--rpc-url",
874+
&url,
875+
"--private-key",
876+
private_key,
877+
"--zksync",
878+
])
879+
.assert_success()
880+
.get_output()
881+
.stdout_lossy();
882+
883+
let re = Regex::new(r"transactionHash\s+(\w+)").expect("invalid regex");
884+
let caps = re.captures(&output).expect("failed getting capture");
885+
let tx_hash = caps.get(1).expect("failed getting tx hash").as_str();
886+
887+
let output = cmd
888+
.cast_fuse()
889+
.args(["run", "--rpc-url", &url, "--zksync", tx_hash, "--no-storage-caching"])
890+
.assert_success()
891+
.get_output()
892+
.stdout_lossy();
893+
894+
assert!(
895+
output.contains(&format!("{deployed_addr}::increment()")) && output.contains("Return"),
896+
"trace missing or incomplete, got:\n{output}"
897+
);
898+
899+
assert!(output.contains("Traces:"), "trace missing, got:\n{output}");
900+
});
901+
819902
casttest!(test_zk_cast_run_with_create_transaction_on_sepolia, async |_prj, cmd| {
820903
let node =
821904
ZkSyncNode::start_with_fork(Fork::new("https://sepolia.era.zksync.dev".to_string())).await;

crates/strategy/zksync/src/executor/runner/libraries.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ impl ZksyncExecutorStrategyRunner {
222222

223223
let ctx = get_context(executor.strategy.context.as_mut());
224224

225+
// TODO(zk): adapt this to conditionally use EVM interpreter?
225226
let (code, create_scheme, to) = match kind {
226227
DeployLibKind::Create(bytes) => {
227228
(bytes, CreateScheme::Create, CONTRACT_DEPLOYER_ADDRESS)
@@ -254,7 +255,8 @@ impl ZksyncExecutorStrategyRunner {
254255
// persist existing paymaster data (TODO(zk): is this needed?)
255256
let paymaster_data =
256257
ctx.transaction_context.take().and_then(|metadata| metadata.paymaster_data);
257-
let metadata = ZkTransactionMetadata { factory_deps, paymaster_data };
258+
let metadata =
259+
ZkTransactionMetadata { factory_deps, paymaster_data, force_evm_interpreter: None };
258260
ctx.transaction_context = Some(metadata.clone());
259261

260262
let result = executor.transact_raw(from, to, create_params.clone(), value)?;

crates/zksync/core/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,14 @@ pub struct ZkTransactionMetadata {
111111
pub factory_deps: Vec<Vec<u8>>,
112112
/// Paymaster data for ZK transactions.
113113
pub paymaster_data: Option<PaymasterParams>,
114+
/// Use EVM interpreter overriding the strategy configuration.
115+
pub force_evm_interpreter: Option<bool>,
114116
}
115117

116118
impl ZkTransactionMetadata {
117119
/// Create a new [`ZkTransactionMetadata`] with the given factory deps
118120
pub fn new(factory_deps: Vec<Vec<u8>>, paymaster_data: Option<PaymasterParams>) -> Self {
119-
Self { factory_deps, paymaster_data }
121+
Self { factory_deps, paymaster_data, force_evm_interpreter: None } // TODO(zk): make it configurable
120122
}
121123
}
122124
/// Estimated gas from a ZK network.

zksync-tests

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ cast::cli:
1111
zk::test_zk_cast_run_with_call_transaction_on_sepolia
1212
zk::test_zk_cast_run_with_create
1313
zk::test_zk_cast_run_with_create_transaction_on_sepolia
14+
zk::test_zk_cast_run_with_evm_interpreter
1415
zk::test_zk_cast_using_paymaster
1516
zk::test_zk_cast_without_paymaster
1617
forge:

0 commit comments

Comments
 (0)