Skip to content

Commit

Permalink
fix: use tx_nonce for transactions, handle multiple deploys for same …
Browse files Browse the repository at this point in the history
…bytecode (#307)
  • Loading branch information
nbaztec authored Apr 5, 2024
1 parent 6926323 commit fa1a70e
Show file tree
Hide file tree
Showing 10 changed files with 74 additions and 19 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions crates/cheatcodes/src/inspector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,9 @@ impl Cheatcodes {
let account_code_account = ACCOUNT_CODE_STORAGE_ADDRESS.to_address();
journaled_account(data, account_code_account).expect("failed to load account");

// TODO we might need to store the deployment nonce under the contract storage
// to not lose it across VMs.

let block_info_key = CURRENT_VIRTUAL_BLOCK_INFO_POSITION.to_ru256();
let (block_info, _) =
data.journaled_state.sload(system_account, block_info_key, data.db).unwrap_or_default();
Expand Down Expand Up @@ -464,6 +467,7 @@ impl Cheatcodes {
let nonce_key = get_nonce_key(&zk_address).key().to_ru256();
l2_eth_storage.insert(balance_key, StorageSlot::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());
nonce_storage.insert(nonce_key, StorageSlot::new(full_nonce.to_ru256()));

Expand Down
9 changes: 7 additions & 2 deletions crates/evm/evm/src/executors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use revm::{
primitives::{
BlockEnv, Bytecode, Env, ExecutionResult, Output, ResultAndState, SpecId, TransactTo, TxEnv,
},
Database,
};
use std::collections::HashMap;

Expand Down Expand Up @@ -147,8 +148,12 @@ impl Executor {

if self.use_zk {
let (address, slot) = foundry_zksync_core::state::get_nonce_storage(address);
let full_nonce = foundry_zksync_core::state::new_full_nonce(nonce, nonce);
self.backend.insert_account_storage(address, slot, full_nonce)?;
// fetch the full nonce to preserve account's deployment nonce
let full_nonce = self.backend.storage(address, slot)?;
let full_nonce = foundry_zksync_core::state::parse_full_nonce(full_nonce);
let new_full_nonce =
foundry_zksync_core::state::new_full_nonce(nonce, full_nonce.deploy_nonce);
self.backend.insert_account_storage(address, slot, new_full_nonce)?;
}

Ok(self)
Expand Down
1 change: 0 additions & 1 deletion crates/zksync/compiler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ ansi_term = "0.12.1"
globset = "0.4"
eyre = "0.6"
semver = "1"
futures = "0.3"
url = "2"
anyhow = { version = "1.0.70" }
dirs = { version = "5.0.0" }
Expand Down
19 changes: 17 additions & 2 deletions crates/zksync/compiler/src/zksolc/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,8 +338,23 @@ impl ZkSolcConfigBuilder {
let compiler_path = if let Some(compiler_path) = self.compiler_path {
compiler_path
} else if let Some(compiler_version) = self.compiler_version {
futures::executor::block_on(setup_zksolc_manager(compiler_version))
.map_err(|err| format!("failed setting up zksolc: {err:?}"))?
// TODO: we are forcibly converting this method to sync since it can be called either
// within a sync (tests) or async (binary) context. We should fix that and stick to
// a single context
match tokio::runtime::Handle::try_current() {
Ok(handle) => std::thread::spawn(move || {
handle
.block_on(setup_zksolc_manager(compiler_version))
.map_err(|err| err.to_string())
})
.join()
.map_err(|err| format!("{err:?}"))?,
Err(_) => tokio::runtime::Runtime::new()
.expect("failed starting runtime")
.block_on(setup_zksolc_manager(compiler_version))
.map_err(|err| err.to_string()),
}
.map_err(|err| format!("failed setting up zksolc: {err:?}"))?
} else {
return Err("must specify either the compiler_version or compiler_path".to_string());
};
Expand Down
6 changes: 6 additions & 0 deletions crates/zksync/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,9 @@ pub async fn estimate_gas<M: Middleware>(

Ok(EstimatedGas { price: gas_price.to_ru256(), limit: fee.gas_limit.to_ru256() })
}

/// Returns true if the provided address is a reserved zkSync system address
/// All addresses less than 2^16 are considered reserved addresses.
pub fn is_system_address(address: Address) -> bool {
address.to_h256().to_ru256().lt(&rU256::from(2u128.pow(16)))
}
19 changes: 17 additions & 2 deletions crates/zksync/core/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ use revm::primitives::{Address as rAddress, U256 as rU256};

use zksync_types::{
get_nonce_key,
utils::{nonces_to_full_nonce, storage_key_for_eth_balance},
utils::{decompose_full_nonce, nonces_to_full_nonce, storage_key_for_eth_balance},
};

use crate::convert::{ConvertAddress, ConvertH160, ConvertH256, ConvertU256};
use crate::convert::{ConvertAddress, ConvertH160, ConvertH256, ConvertRU256, ConvertU256};

/// Returns balance storage slot
pub fn get_balance_storage(address: rAddress) -> (rAddress, rU256) {
Expand All @@ -27,3 +27,18 @@ pub fn get_nonce_storage(address: rAddress) -> (rAddress, rU256) {
pub fn new_full_nonce(tx_nonce: u64, deploy_nonce: u64) -> rU256 {
nonces_to_full_nonce(tx_nonce.into(), deploy_nonce.into()).to_ru256()
}

/// Represents a ZKSync account nonce with two 64-bit transaction and deployment nonces.
#[derive(Default, Debug, Clone, Copy)]
pub struct FullNonce {
/// Transaction nonce.
pub tx_nonce: u64,
/// Deployment nonce.
pub deploy_nonce: u64,
}

/// Decomposes a full nonce into transaction and deploy nonces.
pub fn parse_full_nonce(full_nonce: rU256) -> FullNonce {
let (tx, deploy) = decompose_full_nonce(full_nonce.to_u256());
FullNonce { tx_nonce: tx.as_u64(), deploy_nonce: deploy.as_u64() }
}
2 changes: 1 addition & 1 deletion crates/zksync/core/src/vm/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ where
Self { db, journaled_state, factory_deps, override_keys }
}

/// Returns the nonce for a given account from NonceHolder storage.
/// Returns the code hash for a given account from AccountCode storage.
pub fn get_code_hash(&mut self, address: Address) -> H256 {
let address = address.to_h160();
let code_key = get_code_key(&address);
Expand Down
30 changes: 21 additions & 9 deletions crates/zksync/core/src/vm/runner.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::{
convert::{ConvertAddress, ConvertH160, ConvertH256, ConvertRU256, ConvertU256},
is_system_address,
vm::tracer::CheatcodeTracer,
};
use alloy_primitives::Log;
Expand Down Expand Up @@ -80,15 +81,11 @@ where
);

let caller = env.tx.caller;
let (transact_to, nonce) = match env.tx.transact_to {
TransactTo::Call(to) => {
(to.to_h160(), ZKVMData::new(db, &mut journaled_state).get_tx_nonce(caller))
}
let nonce = ZKVMData::new(db, &mut journaled_state).get_tx_nonce(caller);
let transact_to = match env.tx.transact_to {
TransactTo::Call(to) => to.to_h160(),
TransactTo::Create(CreateScheme::Create) |
TransactTo::Create(CreateScheme::Create2 { .. }) => (
CONTRACT_DEPLOYER_ADDRESS,
ZKVMData::new(db, &mut journaled_state).get_deploy_nonce(caller),
),
TransactTo::Create(CreateScheme::Create2 { .. }) => CONTRACT_DEPLOYER_ADDRESS,
};

let (gas_limit, max_fee_per_gas) = gas_params(env, db, &mut journaled_state, caller);
Expand Down Expand Up @@ -174,7 +171,7 @@ where
let caller = call.caller;
let calldata = encode_create_params(&call.scheme, contract.zk_bytecode_hash, constructor_input);
let factory_deps = vec![contract.zk_deployed_bytecode.clone()];
let nonce = ZKVMData::new(db, journaled_state).get_deploy_nonce(caller);
let nonce = ZKVMData::new(db, journaled_state).get_tx_nonce(caller);

let (gas_limit, max_fee_per_gas) = gas_params(env, db, journaled_state, caller);
let tx = L2Tx::new(
Expand Down Expand Up @@ -373,6 +370,21 @@ where
let bytecode = Bytecode::new_raw(Bytes::from(bytecode));
let hash = B256::from_slice(v.as_bytes());
codes.insert(k.key().to_h160().to_address(), (hash, bytecode));
} else {
// We populate bytecodes for all non-system addresses
if !is_system_address(k.key().to_h160().to_address()) {
if let Some(bytecode) = (&mut era_db).load_factory_dep(*v) {
let hash = B256::from_slice(v.as_bytes());
let bytecode = Bytecode::new_raw(Bytes::from(bytecode));
codes.insert(k.key().to_h160().to_address(), (hash, bytecode));
} else {
tracing::warn!(
"no bytecode was found for {:?}, requested by account {:?}",
*v,
k.key().to_h160()
);
}
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion zk-tests/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ function success() {

function fail() {
echo "Displaying run.log..."
cat run.log
echo ''
echo '=================================='
printf "\e[31m> [FAILURE]\e[0m %s\n" "$1"
Expand Down Expand Up @@ -130,6 +129,7 @@ RUST_LOG=warn "${BINARY_PATH}" test --use "./${SOLC}" -vvv --zksync || fail "fo
echo "Running script..."
start_era_test_node
RUST_LOG=warn "${BINARY_PATH}" script ./script/Deploy.s.sol:DeployScript --broadcast --private-key "0x3d3cbc973389cb26f657686445bcc75662b415b656078503592ac8c1abb8810e" --chain 260 --gas-estimate-multiplier 310 --rpc-url http://localhost:8011 --use "./${SOLC}" --slow -vvv || fail "forge script failed"
RUST_LOG=warn "${BINARY_PATH}" script ./script/Deploy.s.sol:DeployScript --broadcast --private-key "0x3d3cbc973389cb26f657686445bcc75662b415b656078503592ac8c1abb8810e" --chain 260 --gas-estimate-multiplier 310 --rpc-url http://localhost:8011 --use "./${SOLC}" --slow -vvv || fail "forge script failed on 2nd deploy"
stop_era_test_node

success

0 comments on commit fa1a70e

Please sign in to comment.