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

feat: Adding (experimental) zkos support #534

Merged
merged 19 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from 17 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
1 change: 1 addition & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@ categories = ["cryptography"]
#########################
zksync_multivm = { git = "https://github.com/matter-labs/zksync-era.git", rev = "core-v25.4.0" }
zksync_contracts = { git = "https://github.com/matter-labs/zksync-era.git", rev = "core-v25.4.0" }
zksync_basic_types = { git = "https://github.com/matter-labs/zksync-era.git", rev = "core-v25.4.0" }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this actually needed? We purposefully got rid of it in favour of zksync_types

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am using a bunch of methods from there (mostly to move things between u256 and h256) -- but as we have 'todo' Ican remove it for now.

zksync_types = { git = "https://github.com/matter-labs/zksync-era.git", rev = "core-v25.4.0" }
zksync_vm_interface = { git = "https://github.com/matter-labs/zksync-era.git", rev = "core-v25.4.0" }
zksync_web3_decl = { git = "https://github.com/matter-labs/zksync-era.git", rev = "core-v25.4.0", features = [
"server",
] }


#########################
# External dependencies #
#########################
Expand Down
6 changes: 6 additions & 0 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,9 @@ flate2.workspace = true

[dev-dependencies]
tempdir.workspace = true

[features]

zkos = ["anvil_zksync_core/zkos"]

default = []
3 changes: 3 additions & 0 deletions crates/config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@ rand.workspace = true
serde.workspace = true
serde_json.workspace = true
tracing.workspace = true

[features]
zkos = []
3 changes: 3 additions & 0 deletions crates/config/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,9 @@ impl TestNodeConfig {
"Disabled".red()
}
);
#[cfg(feature = "zkos")]
tracing::info!("ZK OS: {}", "Enabled".green());

println!("\n");
tracing::info!("========================================");
for host in &self.host {
Expand Down
7 changes: 7 additions & 0 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ zksync_multivm.workspace = true
zksync_contracts.workspace = true
zksync_types.workspace = true
zksync_web3_decl.workspace = true
zksync_basic_types.workspace = true

anyhow.workspace = true
tokio.workspace = true
Expand All @@ -42,6 +43,7 @@ flate2.workspace = true
thiserror.workspace = true
async-trait.workspace = true


[dev-dependencies]
maplit.workspace = true
ethers.workspace = true
Expand All @@ -50,3 +52,8 @@ tempdir.workspace = true
zksync-web3-rs.workspace = true
test-case.workspace = true
backon.workspace = true


[features]
zkos = []
default = []
1 change: 1 addition & 0 deletions crates/core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![cfg_attr(feature = "zkos", feature(allocator_api))]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, we are using stable rust now, is this absolutely necessary for zkos?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes :-(

//! anvil-zksync
//!
//! The `anvil-zksync` crate provides an in-memory node designed primarily for local testing.
Expand Down
3 changes: 1 addition & 2 deletions crates/core/src/node/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@ impl InMemoryNode {
)));
}

let allow_no_target = system_contracts.evm_emulator.is_some();
let mut l2_tx = L2Tx::from_request(request.into(), MAX_TX_SIZE, allow_no_target)
let mut l2_tx = L2Tx::from_request(request.into(), MAX_TX_SIZE, self.allow_no_target())
.map_err(Web3Error::SerializationError)?;
let execution_mode = zksync_multivm::interface::TxExecutionMode::EthCall;

Expand Down
40 changes: 12 additions & 28 deletions crates/core/src/node/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,20 @@ use std::collections::HashSet;

use anyhow::Context as _;
use colored::Colorize;
use zksync_multivm::interface::{ExecutionResult, TxExecutionMode};
use zksync_multivm::interface::ExecutionResult;
use zksync_multivm::vm_latest::constants::ETH_CALL_GAS_LIMIT;
use zksync_types::h256_to_u256;
use zksync_types::{
api,
api::{Block, BlockIdVariant, BlockNumber, TransactionVariant},
get_code_key, get_nonce_key,
get_code_key,
l2::L2Tx,
transaction_request::TransactionRequest,
utils::storage_key_for_standard_token_balance,
PackedEthSignature, L2_BASE_TOKEN_ADDRESS, MAX_L1_TRANSACTION_GAS_LIMIT,
PackedEthSignature, MAX_L1_TRANSACTION_GAS_LIMIT,
};
use zksync_types::{
web3::{self, Bytes},
AccountTreeId, Address, H160, H256, U256, U64,
Address, H160, H256, U256, U64,
};
use zksync_web3_decl::{
error::Web3Error,
Expand All @@ -29,15 +28,15 @@ use crate::{
utils::{h256_to_u64, TransparentError},
};

use super::keys::StorageKeyLayout;

impl InMemoryNode {
pub async fn call_impl(
&self,
req: zksync_types::transaction_request::CallRequest,
) -> Result<Bytes, Web3Error> {
let system_contracts = self.system_contracts.contracts_for_l2_call().clone();
let allow_no_target = system_contracts.evm_emulator.is_some();

let mut tx = L2Tx::from_request(req.into(), MAX_TX_SIZE, allow_no_target)?;
let mut tx = L2Tx::from_request(req.into(), MAX_TX_SIZE, self.allow_no_target())?;
tx.common_data.fee.gas_limit = ETH_CALL_GAS_LIMIT.into();
let call_result = self
.run_l2_call(tx, system_contracts)
Expand Down Expand Up @@ -78,13 +77,7 @@ impl InMemoryNode {
let chain_id = self.inner.read().await.fork_storage.chain_id;

let (tx_req, hash) = TransactionRequest::from_bytes(&tx_bytes.0, chain_id)?;
// Impersonation does not matter in this context so we assume the tx is not impersonated:
// system contracts here are fetched solely to check for EVM emulator.
let system_contracts = self
.system_contracts
.contracts(TxExecutionMode::VerifyExecute, false);
let allow_no_target = system_contracts.evm_emulator.is_some();
let mut l2_tx = L2Tx::from_request(tx_req, MAX_TX_SIZE, allow_no_target)?;
let mut l2_tx = L2Tx::from_request(tx_req, MAX_TX_SIZE, self.allow_no_target())?;

l2_tx.set_input(tx_bytes.0, hash);
if hash != l2_tx.hash() {
Expand Down Expand Up @@ -144,13 +137,7 @@ impl InMemoryNode {
27,
))?;

// Impersonation does not matter in this context so we assume the tx is not impersonated:
// system contracts here are fetched solely to check for EVM emulator.
let system_contracts = self
.system_contracts
.contracts(TxExecutionMode::VerifyExecute, false);
let allow_no_target = system_contracts.evm_emulator.is_some();
let mut l2_tx: L2Tx = L2Tx::from_request(tx_req, MAX_TX_SIZE, allow_no_target)?;
let mut l2_tx: L2Tx = L2Tx::from_request(tx_req, MAX_TX_SIZE, self.allow_no_target())?;

// `v` was overwritten with 0 during converting into l2 tx
let mut signature = vec![0u8; 65];
Expand Down Expand Up @@ -183,10 +170,7 @@ impl InMemoryNode {
// TODO: Support
_block: Option<BlockIdVariant>,
) -> anyhow::Result<U256> {
let balance_key = storage_key_for_standard_token_balance(
AccountTreeId::new(L2_BASE_TOKEN_ADDRESS),
&address,
);
let balance_key = StorageKeyLayout::get_storage_key_for_base_token(&address);

let inner_guard = self.inner.read().await;
match inner_guard.fork_storage.read_value_internal(&balance_key) {
Expand Down Expand Up @@ -281,7 +265,7 @@ impl InMemoryNode {
_block: Option<BlockIdVariant>,
) -> anyhow::Result<U256> {
let inner = self.inner.read().await;
let nonce_key = get_nonce_key(&address);
let nonce_key = StorageKeyLayout::get_nonce_key(&address);

match inner.fork_storage.read_value_internal(&nonce_key) {
Ok(result) => Ok(h256_to_u64(result).into()),
Expand Down Expand Up @@ -619,7 +603,7 @@ mod tests {
utils::deployed_address_create,
Bloom, K256PrivateKey, L2BlockNumber, StorageKey, EMPTY_UNCLES_HASH,
};
use zksync_types::{u256_to_h256, web3, Nonce};
use zksync_types::{u256_to_h256, web3, AccountTreeId, Nonce};
use zksync_web3_decl::types::{SyncState, ValueOrArray};

async fn test_node(url: &str) -> InMemoryNode {
Expand Down
32 changes: 29 additions & 3 deletions crates/core/src/node/in_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,17 @@ use std::sync::Arc;
use tokio::sync::RwLock;
use zksync_contracts::BaseSystemContracts;
use zksync_multivm::interface::storage::{ReadStorage, StoragePtr};
#[cfg(not(feature = "zkos"))]
use zksync_multivm::interface::VmFactory;
use zksync_multivm::interface::{
ExecutionResult, InspectExecutionMode, L1BatchEnv, L2BlockEnv, TxExecutionMode, VmFactory,
VmInterface,
ExecutionResult, InspectExecutionMode, L1BatchEnv, L2BlockEnv, TxExecutionMode, VmInterface,
};
use zksync_multivm::tracers::CallTracer;
use zksync_multivm::utils::{get_batch_base_fee, get_max_batch_gas_limit};
use zksync_multivm::vm_latest::{HistoryDisabled, ToTracerPointer, Vm};
#[cfg(not(feature = "zkos"))]
use zksync_multivm::vm_latest::Vm;

use zksync_multivm::vm_latest::{HistoryDisabled, ToTracerPointer};
use zksync_multivm::VmVersion;
use zksync_types::api::{Block, DebugCall, TransactionReceipt, TransactionVariant};
use zksync_types::block::unpack_block_info;
Expand Down Expand Up @@ -306,6 +310,18 @@ impl InMemoryNode {
Ok(())
}

/// Whether it accepts the transactions that have 'null' as target.
/// This is used only when EVM emulator is enabled, or we're running in zkos mode.
pub fn allow_no_target(&self) -> bool {
#[cfg(feature = "zkos")]
return true;
#[cfg(not(feature = "zkos"))]
self.system_contracts
.contracts_for_l2_call()
.evm_emulator
.is_some()
}

/// Applies multiple transactions across multiple blocks. All transactions are expected to be
/// executable. Note that on error this method may leave node in partially applied state (i.e.
/// some txs have been applied while others have not).
Expand Down Expand Up @@ -381,7 +397,17 @@ impl InMemoryNode {
let system_env = inner.create_system_env(base_contracts, execution_mode);

let storage = StorageView::new(&inner.fork_storage).into_rc_ptr();

#[cfg(not(feature = "zkos"))]
let mut vm: Vm<_, HistoryDisabled> = Vm::new(batch_env, system_env, storage);
#[cfg(feature = "zkos")]
let mut vm: super::zkos::ZKOsVM<_, HistoryDisabled> = super::zkos::ZKOsVM::new(
batch_env,
system_env,
storage,
// TODO: this might be causing a deadlock.. check..
&inner.fork_storage.inner.read().unwrap().raw_storage,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am in the process of getting rid of raw_storage (and hiding InMemoryStorage in general), what should be the appropriate interface here? I am guessing this is what you were talking about in the Slack thread, basically you need some sort of interface to be able to re-construct the tree (short-term, long-term we will figure something out)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, short term, I need a way to iterate over all elements from the storage.

);

// We must inject *some* signature (otherwise bootloader code fails to generate hash).
if l2_tx.common_data.signature.is_empty() {
Expand Down
23 changes: 5 additions & 18 deletions crates/core/src/node/in_memory_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,14 @@ use super::inner::fork::ForkDetails;
use super::pool::TxBatch;
use super::sealer::BlockSealerMode;
use super::InMemoryNode;
use crate::node::keys::StorageKeyLayout;
use crate::utils::bytecode_to_factory_dep;
use anvil_zksync_types::api::{DetailedTransaction, ResetRequest};
use anyhow::anyhow;
use std::time::Duration;
use zksync_types::api::{Block, TransactionVariant};
use zksync_types::u256_to_h256;
use zksync_types::{
get_code_key, get_nonce_key,
utils::{nonces_to_full_nonce, storage_key_for_eth_balance},
L2BlockNumber, StorageKey,
};
use zksync_types::{get_code_key, utils::nonces_to_full_nonce, L2BlockNumber, StorageKey};
use zksync_types::{AccountTreeId, Address, H256, U256, U64};

type Result<T> = anyhow::Result<T>;
Expand Down Expand Up @@ -170,7 +167,7 @@ impl InMemoryNode {

pub async fn set_balance(&self, address: Address, balance: U256) -> bool {
let writer = self.inner.write().await;
let balance_key = storage_key_for_eth_balance(&address);
let balance_key = StorageKeyLayout::get_storage_key_for_base_token(&address);
writer
.fork_storage
.set_value(balance_key, u256_to_h256(balance));
Expand All @@ -184,7 +181,7 @@ impl InMemoryNode {

pub async fn set_nonce(&self, address: Address, nonce: U256) -> bool {
let writer = self.inner.write().await;
let nonce_key = get_nonce_key(&address);
let nonce_key = StorageKeyLayout::get_nonce_key(&address);
let enforced_full_nonce = nonces_to_full_nonce(nonce, nonce);
tracing::info!(
"👷 Nonces for address {:?} have been set to {}",
Expand Down Expand Up @@ -323,17 +320,7 @@ impl InMemoryNode {
.strip_prefix("0x")
.ok_or_else(|| anyhow!("code must be 0x-prefixed"))?;
let code_bytes = hex::decode(code_slice)?;
let hashcode = bytecode_to_factory_dep(code_bytes)?;
let hash = u256_to_h256(hashcode.0);
let code = hashcode
.1
.iter()
.flat_map(|entry| {
let mut bytes = vec![0u8; 32];
entry.to_big_endian(&mut bytes);
bytes.to_vec()
})
.collect();
let (hash, code) = bytecode_to_factory_dep(code_bytes)?;
writer.fork_storage.store_factory_dep(hash, code);
writer.fork_storage.set_value(code_key, hash);
Ok(())
Expand Down
Loading
Loading