Skip to content

Commit

Permalink
feat: implement basic L1 support and anvil_zks_commitBatch (#575)
Browse files Browse the repository at this point in the history
* add l1 setup guide with pre-generated artifacts

* use contracts with disabled commit verification

* implement l1 support through foundry anvil

* use copied version of IExecutor.sol

* make L1 anvil optional

* redirect L1 stdout to a file

* add some sanity check tests

* vastly simplify l1-setup process

* proper CLI option for L1 support + printing L1 config for user

* adapt e2e tests to new cli options

* migrate to anvil dependency

* revert alloy-zksync bump

* rename anvil ext namespace to `anvil_zks`

* update Cargo.lock
  • Loading branch information
itegulov authored Feb 12, 2025
1 parent 0489105 commit ee49bb9
Show file tree
Hide file tree
Showing 42 changed files with 6,068 additions and 257 deletions.
3,996 changes: 3,823 additions & 173 deletions Cargo.lock

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"crates/cli",
"crates/config",
"crates/core",
"crates/l1_sidecar",
"crates/types",
]
resolver = "2"
Expand Down Expand Up @@ -41,6 +42,8 @@ zksync_web3_decl = "=26.2.1-non-semver-compat"
#########################
anyhow = "1.0"
alloy = { version = "0.9.2", default-features = false }
foundry-anvil = { package = "anvil", git = "https://github.com/foundry-rs/foundry", rev = "v1.0.0" }
foundry-common = { git = "https://github.com/foundry-rs/foundry", rev = "v1.0.0" }
async-trait = "0.1.85"
chrono = { version = "0.4.31", default-features = false }
clap = { version = "4.2.4", features = ["derive", "env"] }
Expand All @@ -59,7 +62,10 @@ rand = "0.8"
reqwest = { version = "0.11", features = ["blocking"] }
rustc-hash = "1.1.0"
serde = { version = "1.0", features = ["derive"] }
serde_with = "1.14.0"
serde_json = "1.0"
serde_yaml = "0.9.33"
tempdir = "0.3.7"
thiserror = "1"
time = "0.3.36"
tokio = { version = "1", features = ["full", "tracing"] }
Expand All @@ -79,7 +85,6 @@ url = "2.5.4"
# Test dependencies #
#########################
httptest = "0.15.4"
tempdir = "0.3.7"
maplit = "1.0.2"
test-case = "3.3.1"
backon = "1.3.0"
Expand All @@ -91,4 +96,5 @@ anvil_zksync_api_decl = { path = "crates/api_decl" }
anvil_zksync_api_server = { path = "crates/api_server" }
anvil_zksync_config = { path = "crates/config" }
anvil_zksync_core = { path = "crates/core" }
anvil_zksync_l1_sidecar = { path = "crates/l1_sidecar" }
anvil_zksync_types = { path = "crates/types" }
2 changes: 1 addition & 1 deletion contracts
3 changes: 2 additions & 1 deletion crates/api_decl/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
mod namespaces;

pub use namespaces::{
AnvilNamespaceServer, ConfigNamespaceServer, EthTestNamespaceServer, EvmNamespaceServer,
AnvilNamespaceServer, AnvilZksNamespaceServer, ConfigNamespaceServer, EthTestNamespaceServer,
EvmNamespaceServer,
};

// Re-export available namespaces from zksync-era
Expand Down
10 changes: 10 additions & 0 deletions crates/api_decl/src/namespaces/anvil_zks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use jsonrpsee::core::RpcResult;
use jsonrpsee::proc_macros::rpc;
use zksync_types::{L1BatchNumber, H256};

/// Custom namespace that contains anvil-zksync specific methods.
#[rpc(server, namespace = "anvil_zks")]
pub trait AnvilZksNamespace {
#[method(name = "commitBatch")]
async fn commit_batch(&self, batch_number: L1BatchNumber) -> RpcResult<H256>;
}
5 changes: 3 additions & 2 deletions crates/api_decl/src/namespaces/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
mod anvil;
mod anvil_zks;
mod config;
mod eth_test;
mod evm;

pub use self::{
anvil::AnvilNamespaceServer, config::ConfigNamespaceServer, eth_test::EthTestNamespaceServer,
evm::EvmNamespaceServer,
anvil::AnvilNamespaceServer, anvil_zks::AnvilZksNamespaceServer, config::ConfigNamespaceServer,
eth_test::EthTestNamespaceServer, evm::EvmNamespaceServer,
};
1 change: 1 addition & 0 deletions crates/api_server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ categories.workspace = true
[dependencies]
anvil_zksync_api_decl.workspace = true
anvil_zksync_core.workspace = true
anvil_zksync_l1_sidecar.workspace = true
anvil_zksync_types.workspace = true

zksync_types.workspace = true
Expand Down
26 changes: 26 additions & 0 deletions crates/api_server/src/impls/anvil_zks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use crate::error::RpcError;
use anvil_zksync_api_decl::AnvilZksNamespaceServer;
use anvil_zksync_l1_sidecar::L1Sidecar;
use jsonrpsee::core::{async_trait, RpcResult};
use zksync_types::{L1BatchNumber, H256};

pub struct AnvilZksNamespace {
l1_sidecar: L1Sidecar,
}

impl AnvilZksNamespace {
pub fn new(l1_sidecar: L1Sidecar) -> Self {
Self { l1_sidecar }
}
}

#[async_trait]
impl AnvilZksNamespaceServer for AnvilZksNamespace {
async fn commit_batch(&self, batch_number: L1BatchNumber) -> RpcResult<H256> {
Ok(self
.l1_sidecar
.commit_batch(batch_number)
.await
.map_err(RpcError::from)?)
}
}
7 changes: 4 additions & 3 deletions crates/api_server/src/impls/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod anvil;
mod anvil_zks;
mod config;
mod debug;
mod eth;
Expand All @@ -9,7 +10,7 @@ mod web3;
mod zks;

pub use self::{
anvil::AnvilNamespace, config::ConfigNamespace, debug::DebugNamespace, eth::EthNamespace,
eth_test::EthTestNamespace, evm::EvmNamespace, net::NetNamespace, web3::Web3Namespace,
zks::ZksNamespace,
anvil::AnvilNamespace, anvil_zks::AnvilZksNamespace, config::ConfigNamespace,
debug::DebugNamespace, eth::EthNamespace, eth_test::EthTestNamespace, evm::EvmNamespace,
net::NetNamespace, web3::Web3Namespace, zks::ZksNamespace,
};
4 changes: 2 additions & 2 deletions crates/api_server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod impls;
mod server;

pub use impls::{
AnvilNamespace, ConfigNamespace, DebugNamespace, EthNamespace, EthTestNamespace, EvmNamespace,
NetNamespace, Web3Namespace, ZksNamespace,
AnvilNamespace, AnvilZksNamespace, ConfigNamespace, DebugNamespace, EthNamespace,
EthTestNamespace, EvmNamespace, NetNamespace, Web3Namespace, ZksNamespace,
};
pub use server::NodeServerBuilder;
21 changes: 13 additions & 8 deletions crates/api_server/src/server.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use crate::{
AnvilNamespace, ConfigNamespace, DebugNamespace, EthNamespace, EthTestNamespace, EvmNamespace,
NetNamespace, Web3Namespace, ZksNamespace,
AnvilNamespace, AnvilZksNamespace, ConfigNamespace, DebugNamespace, EthNamespace,
EthTestNamespace, EvmNamespace, NetNamespace, Web3Namespace, ZksNamespace,
};
use anvil_zksync_api_decl::{
AnvilNamespaceServer, ConfigNamespaceServer, DebugNamespaceServer, EthNamespaceServer,
EthTestNamespaceServer, EvmNamespaceServer, NetNamespaceServer, Web3NamespaceServer,
ZksNamespaceServer,
AnvilNamespaceServer, AnvilZksNamespaceServer, ConfigNamespaceServer, DebugNamespaceServer,
EthNamespaceServer, EthTestNamespaceServer, EvmNamespaceServer, NetNamespaceServer,
Web3NamespaceServer, ZksNamespaceServer,
};
use anvil_zksync_core::node::InMemoryNode;
use anvil_zksync_l1_sidecar::L1Sidecar;
use http::Method;
use jsonrpsee::server::middleware::http::ProxyGetRequestLayer;
use jsonrpsee::server::{RpcServiceBuilder, ServerBuilder, ServerHandle};
Expand All @@ -18,15 +19,17 @@ use tower_http::cors::{AllowOrigin, CorsLayer};
#[derive(Clone)]
pub struct NodeServerBuilder {
node: InMemoryNode,
l1_sidecar: L1Sidecar,
health_api_enabled: bool,
cors_enabled: bool,
allow_origin: AllowOrigin,
}

impl NodeServerBuilder {
pub fn new(node: InMemoryNode, allow_origin: AllowOrigin) -> Self {
pub fn new(node: InMemoryNode, l1_sidecar: L1Sidecar, allow_origin: AllowOrigin) -> Self {
Self {
node,
l1_sidecar,
health_api_enabled: false,
cors_enabled: false,
allow_origin,
Expand All @@ -41,14 +44,16 @@ impl NodeServerBuilder {
self.cors_enabled = true;
}

fn default_rpc(node: InMemoryNode) -> RpcModule<()> {
fn default_rpc(node: InMemoryNode, l1_sidecar: L1Sidecar) -> RpcModule<()> {
let mut rpc = RpcModule::new(());
rpc.merge(EthNamespace::new(node.clone()).into_rpc())
.unwrap();
rpc.merge(EthTestNamespace::new(node.clone()).into_rpc())
.unwrap();
rpc.merge(AnvilNamespace::new(node.clone()).into_rpc())
.unwrap();
rpc.merge(AnvilZksNamespace::new(l1_sidecar).into_rpc())
.unwrap();
rpc.merge(EvmNamespace::new(node.clone()).into_rpc())
.unwrap();
rpc.merge(DebugNamespace::new(node.clone()).into_rpc())
Expand Down Expand Up @@ -89,7 +94,7 @@ impl NodeServerBuilder {
match server_builder.build(addr).await {
Ok(server) => {
let local_addr = server.local_addr().unwrap();
let rpc = Self::default_rpc(self.node);
let rpc = Self::default_rpc(self.node, self.l1_sidecar);
// `jsonrpsee` does `tokio::spawn` within `start` method, so we cannot invoke it here, as this method
// should only build the server. This way we delay the launch until the `NodeServer::run` is invoked.
Ok(NodeServer {
Expand Down
9 changes: 5 additions & 4 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ keywords.workspace = true
categories.workspace = true

[dependencies]
anvil_zksync_core.workspace = true
anvil_zksync_config.workspace = true
anvil_zksync_api_server.workspace = true
anvil_zksync_config.workspace = true
anvil_zksync_core.workspace = true
anvil_zksync_l1_sidecar.workspace = true
anvil_zksync_types.workspace = true

alloy = { workspace = true, default-features = false, features = ["signer-mnemonic"] }

zksync_types.workspace = true

alloy = { workspace = true, default-features = false, features = ["signer-mnemonic", "reqwest"] }
anyhow.workspace = true
clap.workspace = true
eyre.workspace = true
Expand Down
17 changes: 14 additions & 3 deletions crates/cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use anvil_zksync_config::constants::{
use anvil_zksync_config::types::{
AccountGenerator, CacheConfig, CacheType, Genesis, SystemContractsOptions,
};
use anvil_zksync_config::TestNodeConfig;
use anvil_zksync_config::{L1Config, TestNodeConfig};
use anvil_zksync_core::node::fork::ForkConfig;
use anvil_zksync_core::{
node::{InMemoryNode, VersionedState},
Expand Down Expand Up @@ -315,6 +315,14 @@ pub struct Cli {
/// Transaction ordering in the mempool.
#[arg(long, default_value = "fifo")]
pub order: TransactionOrder,

/// Enable L1 support.
#[arg(long, help_heading = "UNSTABLE - L1")]
pub with_l1: bool,

/// Port the spawned L1 anvil node will listen on.
#[arg(long, requires = "with_l1", help_heading = "UNSTABLE - L1")]
pub l1_port: Option<u16>,
}

#[derive(Debug, Subcommand, Clone)]
Expand Down Expand Up @@ -503,7 +511,10 @@ impl Cli {
.with_state_interval(self.state_interval)
.with_dump_state(self.dump_state)
.with_preserve_historical_states(self.preserve_historical_states)
.with_load_state(self.load_state);
.with_load_state(self.load_state)
.with_l1_config(self.with_l1.then(|| L1Config {
port: self.l1_port.unwrap_or(8012),
}));

if self.emulate_evm && self.dev_system_contracts != Some(SystemContractsOptions::Local) {
return Err(eyre::eyre!(
Expand Down Expand Up @@ -631,7 +642,7 @@ impl PeriodicStateDumper {
// An endless future that periodically dumps the state to disk if configured.
// Implementation adapted from: https://github.com/foundry-rs/foundry/blob/206dab285437bd6889463ab006b6a5fb984079d8/crates/anvil/src/cmd.rs#L658
impl Future for PeriodicStateDumper {
type Output = ();
type Output = anyhow::Result<()>;

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
Expand Down
26 changes: 20 additions & 6 deletions crates/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ use anvil_zksync_core::node::{
};
use anvil_zksync_core::observability::Observability;
use anvil_zksync_core::system_contracts::SystemContracts;
use anvil_zksync_l1_sidecar::L1Sidecar;
use anyhow::{anyhow, Context};
use clap::Parser;
use std::fs::File;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::time::Duration;
use std::{env, net::SocketAddr, str::FromStr};
Expand Down Expand Up @@ -186,6 +189,16 @@ async fn main() -> anyhow::Result<()> {
None
};

let mut node_service_tasks: Vec<Pin<Box<dyn Future<Output = anyhow::Result<()>>>>> = Vec::new();
let l1_sidecar = match config.l1_config.as_ref() {
Some(l1_config) => {
let (l1_sidecar, l1_sidecar_runner) = L1Sidecar::builtin(l1_config.port).await?;
node_service_tasks.push(Box::pin(l1_sidecar_runner.run()));
l1_sidecar
}
None => L1Sidecar::none(),
};

let impersonation = ImpersonationManager::default();
if config.enable_auto_impersonate {
// Enable auto impersonation if configured
Expand Down Expand Up @@ -231,6 +244,7 @@ async fn main() -> anyhow::Result<()> {
};
let (block_sealer, block_sealer_state) =
BlockSealer::new(sealing_mode, pool.clone(), node_handle.clone());
node_service_tasks.push(Box::pin(block_sealer.run()));

let node: InMemoryNode = InMemoryNode::new(
node_inner,
Expand Down Expand Up @@ -290,6 +304,7 @@ async fn main() -> anyhow::Result<()> {

let mut server_builder = NodeServerBuilder::new(
node.clone(),
l1_sidecar,
AllowOrigin::exact(
config
.allow_origin
Expand Down Expand Up @@ -369,8 +384,10 @@ async fn main() -> anyhow::Result<()> {
dump_interval,
preserve_historical_states,
);
node_service_tasks.push(Box::pin(state_dumper));

config.print(fork_print_info.as_ref());
let node_service_stopped = futures::future::select_all(node_service_tasks);

tokio::select! {
_ = tokio::signal::ctrl_c() => {
Expand All @@ -379,12 +396,9 @@ async fn main() -> anyhow::Result<()> {
_ = any_server_stopped => {
tracing::trace!("node server was stopped")
},
_ = block_sealer.run() => {
tracing::trace!("block sealer was stopped")
},
_ = state_dumper => {
tracing::trace!("state dumper was stopped")
},
_ = node_service_stopped => {
tracing::trace!("node service was stopped")
}
}

Ok(())
Expand Down
Loading

0 comments on commit ee49bb9

Please sign in to comment.