Skip to content

Commit

Permalink
Merge branch 'nish-zksync-sizes' of github.com:matter-labs/foundry-zk…
Browse files Browse the repository at this point in the history
…sync into nish-zksync-sizes
  • Loading branch information
nbaztec committed Aug 7, 2024
2 parents c6dcbc9 + 192b54f commit c3bcf49
Show file tree
Hide file tree
Showing 12 changed files with 159 additions and 132 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This repository enhances Foundry to support zkSync Era, enabling Solidity-based

> 🔧 **Fork Notice:** This is a Foundry fork with added zkSync support.
>
> ⚠️ **Alpha Stage:** The project is in alpha, so you might encounter issues.
> ⚠️ **Alpha Stage:** The project is in alpha, so you might encounter issues. For more information, please review [Limitations](#limitations) section.
>
> 🐞 **Found an Issue?** Please report it to help us improve.
Expand Down Expand Up @@ -59,6 +59,7 @@ While `foundry-zksync` is **alpha stage**, there are some limitations to be awar
- **Contract Verification**: Currently contract verification via the `--verify` flag do not work as expected but will be added shortly.
- **Specific Foundry Features**: Currently features such as `--gas-report`, `--coverage` may not work as intended. We are actively working on providing support for these feature types.
- **Solc Compatibility**: `zksolc` requires a `solc` binary to be run as a child process. The version/path to use for each can be specified by the `zksolc` and `solc` options in `foundry.toml`. Not all `solc` versions are supported by all `zksolc` versions, compiling with a `solc` version higher than the one supported may lead to unexpected errors. [Read the docs](https://docs.zksync.io/zk-stack/components/compiler/toolchain/solidity.html#limitations) about version limitations and check the [zksolc changelog](https://github.com/matter-labs/era-compiler-solidity/blob/main/CHANGELOG.md) to see the latest supported `solc` version.
- **Windows Compatibility**: Windows is not officially supported yet. The reported issues would be investigated on a best-effort basis.
For the most effective use of our library, we recommend familiarizing yourself with these features and limitations.
Expand Down
40 changes: 23 additions & 17 deletions crates/cheatcodes/src/inspector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ use foundry_evm_core::{
};
use foundry_zksync_compiler::{DualCompiledContract, DualCompiledContracts};
use foundry_zksync_core::{
convert::{ConvertAddress, ConvertH160, ConvertH256, ConvertRU256, ConvertU256},
ZkTransactionMetadata,
convert::{ConvertH160, ConvertH256, ConvertRU256, ConvertU256},
get_account_code_key, get_balance_key, get_nonce_key, ZkTransactionMetadata,
};
use itertools::Itertools;
use revm::{
Expand All @@ -60,8 +60,7 @@ use std::{
};
use zksync_types::{
block::{pack_block_info, unpack_block_info},
get_code_key, get_nonce_key,
utils::{decompose_full_nonce, nonces_to_full_nonce, storage_key_for_eth_balance},
utils::{decompose_full_nonce, nonces_to_full_nonce},
ACCOUNT_CODE_STORAGE_ADDRESS, CONTRACT_DEPLOYER_ADDRESS, CURRENT_VIRTUAL_BLOCK_INFO_POSITION,
H256, KNOWN_CODES_STORAGE_ADDRESS, L2_BASE_TOKEN_ADDRESS, NONCE_HOLDER_ADDRESS,
SYSTEM_CONTEXT_ADDRESS,
Expand Down Expand Up @@ -281,14 +280,22 @@ impl Cheatcodes {
evm_deployed_bytecode: Bytecode::new_raw(empty_bytes.clone()).bytecode().to_vec(),
evm_bytecode: Bytecode::new_raw(empty_bytes.clone()).bytecode().to_vec(),
});

let cheatcodes_bytecode = {
let mut bytecode = CHEATCODE_ADDRESS.abi_encode_packed();
bytecode.append(&mut [0; 12].to_vec());
Bytes::from(bytecode)
};
dual_compiled_contracts.push(DualCompiledContract {
name: String::from("CheatcodeBytecode"),
zk_bytecode_hash,
zk_deployed_bytecode: zk_deployed_bytecode.clone(),
// we put a different bytecode hash here so when importing back to EVM
// we avoid collision with EmptyEVMBytecode for the cheatcodes
zk_bytecode_hash: foundry_zksync_core::hash_bytecode(CHEATCODE_CONTRACT_HASH.as_ref()),
zk_deployed_bytecode: cheatcodes_bytecode.to_vec(),
zk_factory_deps: Default::default(),
evm_bytecode_hash: CHEATCODE_CONTRACT_HASH,
evm_deployed_bytecode: Bytecode::new_raw(empty_bytes.clone()).bytecode().to_vec(),
evm_bytecode: Bytecode::new_raw(empty_bytes).bytecode().to_vec(),
evm_deployed_bytecode: cheatcodes_bytecode.to_vec(),
evm_bytecode: cheatcodes_bytecode.to_vec(),
});

let mut persisted_factory_deps = HashMap::new();
Expand Down Expand Up @@ -476,16 +483,15 @@ impl Cheatcodes {
for address in data.db.persistent_accounts().into_iter().chain([data.env.tx.caller]) {
info!(?address, "importing to evm state");

let zk_address = address.to_h160();
let balance_key = storage_key_for_eth_balance(&zk_address).key().to_ru256();
let nonce_key = get_nonce_key(&zk_address).key().to_ru256();
let balance_key = get_balance_key(address);
let nonce_key = get_nonce_key(address);

let (balance, _) = data.sload(balance_account, balance_key).unwrap_or_default();
let (full_nonce, _) = data.sload(nonce_account, nonce_key).unwrap_or_default();
let (tx_nonce, _deployment_nonce) = decompose_full_nonce(full_nonce.to_u256());
let nonce = tx_nonce.as_u64();

let account_code_key = get_code_key(&zk_address).key().to_ru256();
let account_code_key = get_account_code_key(address);
let (code_hash, code) = data
.sload(account_code_account, account_code_key)
.map(|(value, _)| value)
Expand Down Expand Up @@ -551,21 +557,21 @@ impl Cheatcodes {

let account = journaled_account(data, address).expect("failed to load account");
let info = &account.info;
let zk_address = address.to_h160();

let balance_key = storage_key_for_eth_balance(&zk_address).key().to_ru256();
let nonce_key = get_nonce_key(&zk_address).key().to_ru256();
let balance_key = get_balance_key(address);
l2_eth_storage.insert(balance_key, EvmStorageSlot::new(info.balance));

// TODO we need to find a proper way to handle deploy nonces instead of replicating
let full_nonce = nonces_to_full_nonce(info.nonce.into(), info.nonce.into());

let nonce_key = get_nonce_key(address);
nonce_storage.insert(nonce_key, EvmStorageSlot::new(full_nonce.to_ru256()));

if let Some(contract) = self.dual_compiled_contracts.iter().find(|contract| {
info.code_hash != KECCAK_EMPTY && info.code_hash == contract.evm_bytecode_hash
}) {
account_code_storage.insert(
zk_address.to_h256().to_ru256(),
get_account_code_key(address),
EvmStorageSlot::new(contract.zk_bytecode_hash.to_ru256()),
);
known_codes_storage
Expand All @@ -584,7 +590,7 @@ impl Cheatcodes {
},
);
} else {
tracing::debug!("no zk contract found for {:?}", info.code_hash)
tracing::debug!(code_hash = ?info.code_hash, ?address, "no zk contract found")
}
}

Expand Down
66 changes: 1 addition & 65 deletions crates/config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,10 @@ use foundry_compilers::{
multi::{MultiCompiler, MultiCompilerSettings},
solc::{Solc, SolcCompiler},
vyper::{Vyper, VyperSettings},
zksolc::ZkSolc,
Compiler,
},
error::SolcError,
zksolc::ZkSolcSettings,
zksync::config::ZkSolcConfig,
ConfigurableArtifacts, Project, ProjectPathsConfig,
};
use inflector::Inflector;
Expand Down Expand Up @@ -822,36 +820,12 @@ impl Config {
builder = builder.sparse_output(filter);
}

let mut project = builder.build(self.compiler()?)?;
let project = builder.build(self.compiler()?)?;

if self.force {
self.cleanup(&project)?;
}

// Set up zksolc project values
// TODO: maybe some of these could be included
// when setting up the builder for the sake of consistency (requires dedicated
// builder methods)
project.zksync_zksolc_config = ZkSolcConfig { settings: self.zksync_zksolc_settings()? };

if let Some(zksolc) = self.zksync_ensure_zksolc()? {
project.zksync_zksolc = zksolc;
} else {
// TODO: we automatically install a zksolc version
// if none is found, but maybe we should mirror auto detect settings
// as done with solc
if !self.offline {
let default_version = Version::new(1, 4, 1);
let mut zksolc = ZkSolc::find_installed_version(&default_version)?;
if zksolc.is_none() {
ZkSolc::blocking_install(&default_version)?;
zksolc = ZkSolc::find_installed_version(&default_version)?;
}
project.zksync_zksolc = zksolc
.unwrap_or_else(|| panic!("Could not install zksolc v{}", default_version));
}
}

Ok(project)
}

Expand Down Expand Up @@ -911,44 +885,6 @@ impl Config {
Ok(None)
}

/// Ensures that the configured version is installed if explicitly set
///
/// If `zksolc` is [`SolcReq::Version`] then this will download and install the solc version if
/// it's missing, unless the `offline` flag is enabled, in which case an error is thrown.
///
/// If `zksolc` is [`SolcReq::Local`] then this will ensure that the path exists.
fn zksync_ensure_zksolc(&self) -> Result<Option<ZkSolc>, SolcError> {
if let Some(ref zksolc) = self.zksync.zksolc {
let zksolc = match zksolc {
SolcReq::Version(version) => {
let mut zksolc = ZkSolc::find_installed_version(version)?;
if zksolc.is_none() {
if self.offline {
return Err(SolcError::msg(format!(
"can't install missing zksolc {version} in offline mode"
)))
}
ZkSolc::blocking_install(version)?;
zksolc = ZkSolc::find_installed_version(version)?;
}
zksolc
}
SolcReq::Local(zksolc) => {
if !zksolc.is_file() {
return Err(SolcError::msg(format!(
"`zksolc` {} does not exist",
zksolc.display()
)))
}
Some(ZkSolc::new(zksolc))
}
};
return Ok(zksolc)
}

Ok(None)
}

/// Returns the [SpecId] derived from the configured [EvmVersion]
#[inline]
pub fn evm_spec_id(&self) -> SpecId {
Expand Down
12 changes: 6 additions & 6 deletions crates/evm/core/src/backend/fork_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ impl CachedForkType {

let is_zk_url = foundry_common::provider::try_get_http_provider(fork_url)
.map(|provider| {
let is_zk_url =
futures::executor::block_on(provider.raw_request("zks_L1ChainId".into(), ()))
.map(|_: String| true)
.unwrap_or_default();

is_zk_url
tokio::task::block_in_place(move || {
tokio::runtime::Handle::current()
.block_on(provider.raw_request::<_, String>("zks_L1ChainId".into(), ()))
.map(|_| true)
})
.unwrap_or_default()
})
.unwrap_or_default();

Expand Down
62 changes: 36 additions & 26 deletions crates/evm/core/src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ use alloy_rpc_types::{Block, BlockNumberOrTag, BlockTransactions, Transaction};
use alloy_serde::WithOtherFields;
use eyre::Context;
use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE};
use foundry_zksync_core::{convert::ConvertH160, L2_BASE_TOKEN_ADDRESS};
use foundry_zksync_core::{
convert::ConvertH160, ACCOUNT_CODE_STORAGE_ADDRESS, L2_BASE_TOKEN_ADDRESS, NONCE_HOLDER_ADDRESS,
};
use itertools::Itertools;
use revm::{
db::{CacheDB, DatabaseRef},
Expand Down Expand Up @@ -1944,8 +1946,6 @@ fn merge_db_account_data<ExtDB: DatabaseRef>(
active: &CacheDB<ExtDB>,
fork_db: &mut ForkDB,
) {
trace!(?addr, "merging database data");

let mut acc = if let Some(acc) = active.accounts.get(&addr).cloned() {
acc
} else {
Expand Down Expand Up @@ -1973,33 +1973,43 @@ fn merge_zk_account_data<ExtDB: DatabaseRef>(
active: &CacheDB<ExtDB>,
fork_db: &mut ForkDB,
) {
trace!(?addr, "merging zk database data");
let mut merge_system_contract_entry = |system_contract: Address, slot: U256| {
let mut acc = if let Some(acc) = active.accounts.get(&system_contract).cloned() {
acc
} else {
// Account does not exist
return;
};

// TODO: do the same for nonce and codes
let balance_addr = L2_BASE_TOKEN_ADDRESS.to_address();
let mut acc = if let Some(acc) = active.accounts.get(&balance_addr).cloned() {
acc
} else {
// Account does not exist
return;
};
let mut storage = Map::<U256, U256>::default();
if let Some(value) = acc.storage.get(&slot) {
storage.insert(slot, *value);
}

let mut balances = Map::<U256, U256>::default();
let slot = foundry_zksync_core::get_balance_key(addr);
if let Some(value) = acc.storage.get(&slot) {
balances.insert(slot, *value);
}
if let Some(fork_account) = fork_db.accounts.get_mut(&system_contract) {
// This will merge the fork's tracked storage with active storage and update values
fork_account.storage.extend(storage);
// swap them so we can insert the account as whole in the next step
std::mem::swap(&mut fork_account.storage, &mut acc.storage);
} else {
std::mem::swap(&mut storage, &mut acc.storage)
}

if let Some(fork_account) = fork_db.accounts.get_mut(&balance_addr) {
// This will merge the fork's tracked storage with active storage and update values
fork_account.storage.extend(balances);
// swap them so we can insert the account as whole in the next step
std::mem::swap(&mut fork_account.storage, &mut acc.storage);
} else {
std::mem::swap(&mut balances, &mut acc.storage)
}
fork_db.accounts.insert(system_contract, acc);
};

fork_db.accounts.insert(balance_addr, acc);
merge_system_contract_entry(
L2_BASE_TOKEN_ADDRESS.to_address(),
foundry_zksync_core::get_balance_key(addr),
);
merge_system_contract_entry(
ACCOUNT_CODE_STORAGE_ADDRESS.to_address(),
foundry_zksync_core::get_account_code_key(addr),
);
merge_system_contract_entry(
NONCE_HOLDER_ADDRESS.to_address(),
foundry_zksync_core::get_nonce_key(addr),
);
}

/// Returns true of the address is a contract
Expand Down
15 changes: 15 additions & 0 deletions crates/forge/tests/it/zk/issues.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//! Forge tests for zkysnc issues, to avoid regressions.
//!
//! Issue list: https://github.com/matter-labs/foundry-zksync/issues
use crate::{config::*, test_helpers::TEST_DATA_DEFAULT};
use forge::revm::primitives::SpecId;
use foundry_test_utils::Filter;

#[tokio::test(flavor = "multi_thread")]
async fn test_zk_issue497() {
let runner = TEST_DATA_DEFAULT.runner_zksync();
let filter = Filter::new(".*", "Issue497", ".*");

TestConfig::with_filter(runner, filter).evm_spec(SpecId::SHANGHAI).run().await;
}
1 change: 1 addition & 0 deletions crates/forge/tests/it/zk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
mod basic;
mod cheats;
mod contracts;
mod issues;
mod logs;
10 changes: 10 additions & 0 deletions crates/zksync/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ pub fn get_balance_key(address: Address) -> rU256 {
storage_key_for_eth_balance(&address.to_h160()).key().to_ru256()
}

/// Returns the account code storage key for a provided account address.
pub fn get_account_code_key(address: Address) -> rU256 {
zksync_types::get_code_key(&address.to_h160()).key().to_ru256()
}

/// Returns the account nonce key for a provided account address.
pub fn get_nonce_key(address: Address) -> rU256 {
zksync_types::get_nonce_key(&address.to_h160()).key().to_ru256()
}

/// Represents additional data for ZK transactions.
#[derive(Clone, Debug, Default)]
pub struct ZkTransactionMetadata {
Expand Down
Loading

0 comments on commit c3bcf49

Please sign in to comment.