Skip to content

Commit

Permalink
refactor: split off Fork/ForkSource from ForkStorage (#556)
Browse files Browse the repository at this point in the history
* split off `Fork`/`ForkSource` from `ForkStorage`

* fix tests

* return old gas factor behaviour

* return to spawning an independent tokio runtime

* delete leftover docs

* return legacy fork replay behavior

* update e2e-tests-rust lockfile

* add todo for supported protocol versions
  • Loading branch information
itegulov authored Jan 23, 2025
1 parent 1061c30 commit 7273a59
Show file tree
Hide file tree
Showing 24 changed files with 2,138 additions and 2,811 deletions.
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

0 comments on commit 7273a59

Please sign in to comment.