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

refactor: split off Fork/ForkSource from ForkStorage #556

Merged
merged 10 commits into from
Jan 23, 2025
Merged
2 changes: 2 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ tracing-subscriber = { version = "0.3", features = [
"json",
"local-time",
] }
url = "2.5.4"

#########################
# Test dependencies #
Expand Down
1 change: 1 addition & 0 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ tracing-subscriber.workspace = true
tower.workspace = true
tower-http.workspace = true
flate2.workspace = true
url.workspace = true

[dev-dependencies]
tempdir.workspace = true
49 changes: 47 additions & 2 deletions crates/cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use anvil_zksync_config::types::{
AccountGenerator, CacheConfig, CacheType, Genesis, SystemContractsOptions,
};
use anvil_zksync_config::TestNodeConfig;
use anvil_zksync_core::node::fork::ForkConfig;
use anvil_zksync_core::{
node::{InMemoryNode, VersionedState},
utils::write_json_file,
Expand All @@ -23,13 +24,15 @@ use std::env;
use std::io::Read;
use std::net::IpAddr;
use std::path::PathBuf;
use std::str::FromStr;
use std::time::Duration;
use std::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
use tokio::time::{Instant, Interval};
use url::Url;
use zksync_types::{H256, U256};

#[derive(Debug, Parser, Clone)]
Expand Down Expand Up @@ -340,7 +343,7 @@ pub struct ForkArgs {
alias = "network",
help = "Network to fork from (e.g., http://XXX:YY, mainnet, sepolia-testnet)."
)]
pub fork_url: String,
pub fork_url: ForkUrl,
// Fork at a given L2 miniblock height.
// If not set - will use the current finalized block from the network.
#[arg(
Expand All @@ -363,6 +366,48 @@ pub struct ForkArgs {
pub fork_transaction_hash: Option<H256>,
}

#[derive(Clone, Debug)]
pub enum ForkUrl {
Mainnet,
SepoliaTestnet,
Other(Url),
}

impl ForkUrl {
const MAINNET_URL: &'static str = "https://mainnet.era.zksync.io:443";
const SEPOLIA_TESTNET_URL: &'static str = "https://sepolia.era.zksync.dev:443";

pub fn to_config(&self) -> ForkConfig {
match self {
ForkUrl::Mainnet => ForkConfig {
url: Self::MAINNET_URL.parse().unwrap(),
estimate_gas_price_scale_factor: 1.5,
estimate_gas_scale_factor: 1.4,
},
ForkUrl::SepoliaTestnet => ForkConfig {
url: Self::SEPOLIA_TESTNET_URL.parse().unwrap(),
estimate_gas_price_scale_factor: 2.0,
estimate_gas_scale_factor: 1.3,
},
ForkUrl::Other(url) => ForkConfig::unknown(url.clone()),
}
}
}

impl FromStr for ForkUrl {
type Err = anyhow::Error;

fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
if s == "mainnet" {
Ok(ForkUrl::Mainnet)
} else if s == "sepolia-testnet" {
Ok(ForkUrl::SepoliaTestnet)
} else {
Ok(Url::from_str(s).map(ForkUrl::Other)?)
}
}
}

#[derive(Debug, Parser, Clone)]
pub struct ReplayArgs {
/// Whether to fork from existing network.
Expand All @@ -377,7 +422,7 @@ pub struct ReplayArgs {
alias = "network",
help = "Network to fork from (e.g., http://XXX:YY, mainnet, sepolia-testnet)."
)]
pub fork_url: String,
pub fork_url: ForkUrl,
/// Transaction hash to replay.
#[arg(help = "Transaction hash to replay.")]
pub tx: H256,
Expand Down
113 changes: 47 additions & 66 deletions crates/cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::bytecode_override::override_bytecodes;
use crate::cli::{Cli, Command, PeriodicStateDumper};
use crate::cli::{Cli, Command, ForkUrl, PeriodicStateDumper};
use crate::utils::update_with_fork_details;
use anvil_zksync_api_server::NodeServerBuilder;
use anvil_zksync_config::constants::{
Expand All @@ -10,7 +10,7 @@ use anvil_zksync_config::constants::{
use anvil_zksync_config::types::SystemContractsOptions;
use anvil_zksync_config::ForkPrintInfo;
use anvil_zksync_core::filters::EthFilters;
use anvil_zksync_core::node::fork::ForkDetails;
use anvil_zksync_core::node::fork::ForkClient;
use anvil_zksync_core::node::{
BlockSealer, BlockSealerMode, ImpersonationManager, InMemoryNode, InMemoryNodeInner,
NodeExecutor, StorageKeyLayout, TestNodeFeeInputProvider, TxPool,
Expand All @@ -27,8 +27,7 @@ use tokio::sync::RwLock;
use tower_http::cors::AllowOrigin;
use tracing_subscriber::filter::LevelFilter;
use zksync_types::fee_model::{FeeModelConfigV2, FeeParams};
use zksync_types::H160;
use zksync_web3_decl::namespaces::ZksNamespaceClient;
use zksync_types::{L2BlockNumber, H160};

mod bytecode_override;
mod cli;
Expand Down Expand Up @@ -57,7 +56,7 @@ async fn main() -> anyhow::Result<()> {

// Use `Command::Run` as default.
let command = command.as_ref().unwrap_or(&Command::Run);
let fork_details = match command {
let (fork_client, transactions_to_replay) = match command {
Command::Run => {
if config.offline {
tracing::warn!("Running in offline mode: default fee parameters will be used.");
Expand All @@ -79,16 +78,12 @@ async fn main() -> anyhow::Result<()> {
config.l1_pubdata_price.or(Some(DEFAULT_FAIR_PUBDATA_PRICE)),
)
.with_chain_id(config.chain_id.or(Some(TEST_NODE_NETWORK_ID)));
None
(None, Vec::new())
} else {
// Initialize the client to get the fee params
let (_, client) = ForkDetails::fork_network_and_client("mainnet")
.map_err(|e| anyhow!("Failed to initialize client: {:?}", e))?;

let fee = client.get_fee_params().await.map_err(|e| {
tracing::error!("Failed to fetch fee params: {:?}", e);
anyhow!(e)
})?;
let client =
ForkClient::at_block_number(ForkUrl::Mainnet.to_config(), None).await?;
let fee = client.get_fee_params().await?;

match fee {
FeeParams::V2(fee_v2) => {
Expand Down Expand Up @@ -120,56 +115,37 @@ async fn main() -> anyhow::Result<()> {
}
}

None
(None, Vec::new())
}
}
Command::Fork(fork) => {
let fork_details_result = if let Some(tx_hash) = fork.fork_transaction_hash {
// If fork_transaction_hash is provided, use from_network_tx
ForkDetails::from_network_tx(&fork.fork_url, tx_hash, &config.cache_config).await
// TODO: For now, we do not replay earlier transactions when forking to keep compatibility
// with the legacy forking behavior.
let (fork_client, _) = if let Some(tx_hash) = fork.fork_transaction_hash {
// If transaction hash is provided, we fork at the parent of block containing tx
ForkClient::at_before_tx(fork.fork_url.to_config(), tx_hash).await?
} else {
// Otherwise, use from_network
ForkDetails::from_network(
&fork.fork_url,
fork.fork_block_number,
&config.cache_config,
// Otherwise, we fork at the provided block
(
ForkClient::at_block_number(
fork.fork_url.to_config(),
fork.fork_block_number.map(|bn| L2BlockNumber(bn as u32)),
)
.await?,
Vec::new(),
)
.await
};

update_with_fork_details(&mut config, fork_details_result).await?
update_with_fork_details(&mut config, &fork_client.details).await;
(Some(fork_client), Vec::new())
}
Command::ReplayTx(replay_tx) => {
let fork_details_result = ForkDetails::from_network_tx(
&replay_tx.fork_url,
replay_tx.tx,
&config.cache_config,
)
.await;

update_with_fork_details(&mut config, fork_details_result).await?
}
};
let (fork_client, earlier_txs) =
ForkClient::at_before_tx(replay_tx.fork_url.to_config(), replay_tx.tx).await?;

// If we're replaying the transaction, we need to sync to the previous block
// and then replay all the transactions that happened in
let transactions_to_replay = if let Command::ReplayTx(replay_tx) = command {
match fork_details
.as_ref()
.unwrap()
.get_earlier_transactions_in_same_block(replay_tx.tx)
{
Ok(txs) => txs,
Err(error) => {
tracing::error!(
"failed to get earlier transactions in the same block for replay tx: {:?}",
error
);
return Err(anyhow!(error));
}
update_with_fork_details(&mut config, &fork_client.details).await;
(Some(fork_client), earlier_txs)
}
} else {
vec![]
};

if matches!(
Expand All @@ -181,28 +157,31 @@ async fn main() -> anyhow::Result<()> {
}
}

let fork_print_info = if let Some(fd) = fork_details.as_ref() {
let fee_model_config_v2 = match fd.fee_params {
Some(FeeParams::V2(fee_params_v2)) => {
let fork_print_info = if let Some(fork_client) = &fork_client {
let fee_model_config_v2 = match &fork_client.details.fee_params {
FeeParams::V2(fee_params_v2) => {
let config = fee_params_v2.config();
Some(FeeModelConfigV2 {
FeeModelConfigV2 {
minimal_l2_gas_price: config.minimal_l2_gas_price,
compute_overhead_part: config.compute_overhead_part,
pubdata_overhead_part: config.pubdata_overhead_part,
batch_overhead_l1_gas: config.batch_overhead_l1_gas,
max_gas_per_batch: config.max_gas_per_batch,
max_pubdata_per_batch: config.max_pubdata_per_batch,
})
}
}
_ => None,
_ => anyhow::bail!(
"fork is using unsupported fee parameters: {:?}",
fork_client.details.fee_params
),
};

Some(ForkPrintInfo {
network_rpc: fd.fork_source.get_fork_url().unwrap_or_default(),
l1_block: fd.l1_block.to_string(),
l2_block: fd.l2_miniblock.to_string(),
block_timestamp: fd.block_timestamp.to_string(),
fork_block_hash: format!("{:#x}", fd.l2_block.hash),
network_rpc: fork_client.url.to_string(),
l1_block: fork_client.details.batch_number.to_string(),
l2_block: fork_client.details.block_number.to_string(),
block_timestamp: fork_client.details.block_timestamp.to_string(),
fork_block_hash: format!("{:#x}", fork_client.details.block_hash),
fee_model_config_v2,
})
} else {
Expand All @@ -216,7 +195,8 @@ async fn main() -> anyhow::Result<()> {
}
let pool = TxPool::new(impersonation.clone(), config.transaction_order);

let fee_input_provider = TestNodeFeeInputProvider::from_fork(fork_details.as_ref());
let fee_input_provider =
TestNodeFeeInputProvider::from_fork(fork_client.as_ref().map(|f| &f.details));
let filters = Arc::new(RwLock::new(EthFilters::default()));
let system_contracts = SystemContracts::from_options(
&config.system_contracts_options,
Expand All @@ -229,8 +209,8 @@ async fn main() -> anyhow::Result<()> {
StorageKeyLayout::ZkEra
};

let (node_inner, storage, blockchain, time) = InMemoryNodeInner::init(
fork_details,
let (node_inner, storage, blockchain, time, fork) = InMemoryNodeInner::init(
fork_client,
fee_input_provider.clone(),
filters,
config.clone(),
Expand Down Expand Up @@ -258,6 +238,7 @@ async fn main() -> anyhow::Result<()> {
node_inner,
blockchain,
storage,
fork,
node_handle,
Some(observability),
time,
Expand Down
49 changes: 18 additions & 31 deletions crates/cli/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,36 +11,23 @@ pub fn parse_genesis_file(path: &str) -> Result<Genesis, String> {
}

/// Updates the configuration from fork details.
pub async fn update_with_fork_details(
config: &mut TestNodeConfig,
fork_details_result: Result<ForkDetails, eyre::Report>,
) -> Result<Option<ForkDetails>, anyhow::Error> {
match fork_details_result {
Ok(fd) => {
let l1_gas_price = config.l1_gas_price.or(Some(fd.l1_gas_price));
let l2_gas_price = config.l2_gas_price.or(Some(fd.l2_fair_gas_price));
let l1_pubdata_price = config.l1_pubdata_price.or(Some(fd.fair_pubdata_price));
let price_scale = config
.price_scale_factor
.or(Some(fd.estimate_gas_price_scale_factor));
let gas_limit_scale = config
.limit_scale_factor
.or(Some(fd.estimate_gas_scale_factor));
let chain_id = config.chain_id.or(Some(fd.chain_id.as_u64() as u32));
pub async fn update_with_fork_details(config: &mut TestNodeConfig, fd: &ForkDetails) {
let l1_gas_price = config.l1_gas_price.or(Some(fd.l1_gas_price));
let l2_gas_price = config.l2_gas_price.or(Some(fd.l2_fair_gas_price));
let l1_pubdata_price = config.l1_pubdata_price.or(Some(fd.fair_pubdata_price));
let price_scale = config
.price_scale_factor
.or(Some(fd.estimate_gas_price_scale_factor));
let gas_limit_scale = config
.limit_scale_factor
.or(Some(fd.estimate_gas_scale_factor));
let chain_id = config.chain_id.or(Some(fd.chain_id.as_u64() as u32));

config
.update_l1_gas_price(l1_gas_price)
.update_l2_gas_price(l2_gas_price)
.update_l1_pubdata_price(l1_pubdata_price)
.update_price_scale(price_scale)
.update_gas_limit_scale(gas_limit_scale)
.update_chain_id(chain_id);

Ok(Some(fd))
}
Err(error) => {
tracing::error!("Error while attempting to fork: {:?}", error);
Err(anyhow::anyhow!(error))
}
}
config
.update_l1_gas_price(l1_gas_price)
.update_l2_gas_price(l2_gas_price)
.update_l1_pubdata_price(l1_pubdata_price)
.update_price_scale(price_scale)
.update_gas_limit_scale(gas_limit_scale)
.update_chain_id(chain_id);
}
Loading
Loading