diff --git a/Cargo.lock b/Cargo.lock index e5e83db18937..0a98fb5ccd23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11902,6 +11902,7 @@ dependencies = [ "zksync_eth_client", "zksync_health_check", "zksync_l1_contract_interface", + "zksync_mini_merkle_tree", "zksync_node_fee_model", "zksync_node_test_utils", "zksync_object_store", @@ -12435,6 +12436,7 @@ dependencies = [ "zksync_house_keeper", "zksync_logs_bloom_backfill", "zksync_metadata_calculator", + "zksync_mini_merkle_tree", "zksync_node_api_server", "zksync_node_consensus", "zksync_node_db_pruner", diff --git a/core/bin/block_reverter/src/main.rs b/core/bin/block_reverter/src/main.rs index 64f3a4825e80..eb06357c1968 100644 --- a/core/bin/block_reverter/src/main.rs +++ b/core/bin/block_reverter/src/main.rs @@ -16,7 +16,8 @@ use zksync_block_reverter::{ use zksync_config::{ configs::{ chain::NetworkConfig, wallets::Wallets, BasicWitnessInputProducerConfig, DatabaseSecrets, - GeneralConfig, L1Secrets, ObservabilityConfig, ProtectiveReadsWriterConfig, + GatewayChainConfig, GeneralConfig, L1Secrets, ObservabilityConfig, + ProtectiveReadsWriterConfig, }, ContractsConfig, DBConfig, EthConfig, GenesisConfig, PostgresConfig, }; @@ -24,6 +25,7 @@ use zksync_core_leftovers::temp_config_store::read_yaml_repr; use zksync_dal::{ConnectionPool, Core}; use zksync_env_config::{object_store::SnapshotsObjectStoreConfig, FromEnv}; use zksync_object_store::ObjectStoreFactory; +use zksync_protobuf_config::proto; use zksync_types::{Address, L1BatchNumber}; #[derive(Debug, Parser)] @@ -46,6 +48,10 @@ struct Cli { /// Path to yaml genesis config. If set, it will be used instead of env vars #[arg(long, global = true)] genesis_path: Option, + /// Path to yaml config of the chain on top of gateway. + /// It should be skipped in case a chain is not settling on top of Gateway. + #[arg(long, global = true)] + gateway_chain_path: Option, } #[derive(Debug, Subcommand)] @@ -182,23 +188,22 @@ async fn main() -> anyhow::Result<()> { .context("BasicWitnessInputProducerConfig::from_env()")?, }; let contracts = match opts.contracts_config_path { - Some(path) => read_yaml_repr::(&path) + Some(path) => read_yaml_repr::(&path) .context("failed decoding contracts YAML config")?, None => ContractsConfig::from_env().context("ContractsConfig::from_env()")?, }; let secrets_config = if let Some(path) = opts.secrets_path { Some( - read_yaml_repr::(&path) + read_yaml_repr::(&path) .context("failed decoding secrets YAML config")?, ) } else { None }; - let default_priority_fee_per_gas = eth_sender - .gas_adjuster - .context("gas_adjuster")? - .default_priority_fee_per_gas; + let gas_adjuster = eth_sender.gas_adjuster.context("gas_adjuster")?; + let default_priority_fee_per_gas = gas_adjuster.default_priority_fee_per_gas; + let settlement_mode = gas_adjuster.settlement_mode; let database_secrets = match &secrets_config { Some(secrets_config) => secrets_config @@ -230,7 +235,40 @@ async fn main() -> anyhow::Result<()> { } }; - let config = BlockReverterEthConfig::new(ð_sender, &contracts, zksync_network_id)?; + let (sl_rpc_url, sl_diamond_proxy, sl_validator_timelock) = if settlement_mode.is_gateway() { + // Gateway config is required to be provided by file for now. + let gateway_chain_config: GatewayChainConfig = + read_yaml_repr::( + &opts + .gateway_chain_path + .context("Genesis config path not provided")?, + ) + .context("failed decoding genesis YAML config")?; + + let gateway_url = l1_secrets + .gateway_rpc_url + .context("Gateway URL not found")?; + + ( + gateway_url, + gateway_chain_config.diamond_proxy_addr, + gateway_chain_config.validator_timelock_addr, + ) + } else { + ( + l1_secrets.l1_rpc_url, + contracts.diamond_proxy_addr, + contracts.validator_timelock_addr, + ) + }; + + let config = BlockReverterEthConfig::new( + ð_sender, + sl_diamond_proxy, + sl_validator_timelock, + zksync_network_id, + settlement_mode, + )?; let connection_pool = ConnectionPool::::builder( database_secrets.master_url()?, @@ -246,12 +284,12 @@ async fn main() -> anyhow::Result<()> { json, operator_address, } => { - let eth_client = Client::::http(l1_secrets.l1_rpc_url.clone()) + let sl_client = Client::::http(sl_rpc_url) .context("Ethereum client")? .build(); let suggested_values = block_reverter - .suggested_values(ð_client, &config, operator_address) + .suggested_values(&sl_client, &config, operator_address) .await?; if json { println!("{}", serde_json::to_string(&suggested_values)?); @@ -264,9 +302,7 @@ async fn main() -> anyhow::Result<()> { priority_fee_per_gas, nonce, } => { - let eth_client = Client::http(l1_secrets.l1_rpc_url.clone()) - .context("Ethereum client")? - .build(); + let sl_client = Client::http(sl_rpc_url).context("Ethereum client")?.build(); let reverter_private_key = if let Some(wallets_config) = wallets_config { wallets_config .eth_sender @@ -285,21 +321,21 @@ async fn main() -> anyhow::Result<()> { }; let priority_fee_per_gas = priority_fee_per_gas.unwrap_or(default_priority_fee_per_gas); - let l1_chain_id = eth_client + let l1_chain_id = sl_client .fetch_chain_id() .await .context("cannot fetch Ethereum chain ID")?; - let eth_client = PKSigningClient::new_raw( + let sl_client = PKSigningClient::new_raw( reverter_private_key, - contracts.diamond_proxy_addr, + sl_diamond_proxy, priority_fee_per_gas, l1_chain_id, - Box::new(eth_client), + Box::new(sl_client), ); block_reverter .send_ethereum_revert_transaction( - ð_client, + &sl_client, &config, L1BatchNumber(l1_batch_number), nonce, diff --git a/core/bin/external_node/src/config/mod.rs b/core/bin/external_node/src/config/mod.rs index 49f37116de75..db2c6eac9cf6 100644 --- a/core/bin/external_node/src/config/mod.rs +++ b/core/bin/external_node/src/config/mod.rs @@ -102,6 +102,7 @@ impl ConfigurationSource for Environment { /// This part of the external node config is fetched directly from the main node. #[derive(Debug, Deserialize)] pub(crate) struct RemoteENConfig { + pub l1_bytecodes_supplier_addr: Option
, #[serde(alias = "bridgehub_proxy_addr")] pub l1_bridgehub_proxy_addr: Option
, #[serde(alias = "state_transition_proxy_addr")] @@ -191,6 +192,9 @@ impl RemoteENConfig { l1_transparent_proxy_admin_addr: ecosystem_contracts .as_ref() .map(|a| a.transparent_proxy_admin_addr), + l1_bytecodes_supplier_addr: ecosystem_contracts + .as_ref() + .and_then(|a| a.l1_bytecodes_supplier_addr), l1_diamond_proxy_addr, l2_testnet_paymaster_addr, l1_erc20_bridge_proxy_addr: bridges.l1_erc20_default_bridge, @@ -216,6 +220,7 @@ impl RemoteENConfig { #[cfg(test)] fn mock() -> Self { Self { + l1_bytecodes_supplier_addr: None, l1_bridgehub_proxy_addr: None, l1_state_transition_proxy_addr: None, l1_transparent_proxy_admin_addr: None, @@ -983,11 +988,11 @@ impl OptionalENConfig { /// This part of the external node config is required for its operation. #[derive(Debug, Deserialize)] pub(crate) struct RequiredENConfig { - /// The chain ID of the L1 network (e.g., 1 for Ethereum mainnet). In the future, it may be different from the settlement layer. + /// The chain ID of the L1 network (e.g., 1 for Ethereum mainnet). pub l1_chain_id: L1ChainId, - /// The chain ID of the settlement layer (e.g., 1 for Ethereum mainnet). This ID will be checked against the `eth_client_url` RPC provider on initialization - /// to ensure that there's no mismatch between the expected and actual settlement layer network. - pub sl_chain_id: Option, + /// The chain ID of the gateway. This ID will be checked against the `gateway_rpc_url` RPC provider on initialization + /// to ensure that there's no mismatch between the expected and actual gateway network. + pub gateway_chain_id: Option, /// L2 chain ID (e.g., 270 for ZKsync Era mainnet). This ID will be checked against the `main_node_url` RPC provider on initialization /// to ensure that there's no mismatch between the expected and actual L2 network. pub l2_chain_id: L2ChainId, @@ -1009,10 +1014,6 @@ pub(crate) struct RequiredENConfig { } impl RequiredENConfig { - pub fn settlement_layer_id(&self) -> SLChainId { - self.sl_chain_id.unwrap_or(self.l1_chain_id.into()) - } - fn from_env() -> anyhow::Result { envy::prefixed("EN_") .from_env() @@ -1034,7 +1035,7 @@ impl RequiredENConfig { .context("Database config is required")?; Ok(RequiredENConfig { l1_chain_id: en_config.l1_chain_id, - sl_chain_id: None, + gateway_chain_id: en_config.gateway_chain_id, l2_chain_id: en_config.l2_chain_id, http_port: api_config.web3_json_rpc.http_port, ws_port: api_config.web3_json_rpc.ws_port, @@ -1055,7 +1056,7 @@ impl RequiredENConfig { fn mock(temp_dir: &tempfile::TempDir) -> Self { Self { l1_chain_id: L1ChainId(9), - sl_chain_id: None, + gateway_chain_id: None, l2_chain_id: L2ChainId::default(), http_port: 0, ws_port: 0, @@ -1401,12 +1402,12 @@ impl ExternalNodeConfig<()> { if let Some(local_diamond_proxy_addr) = self.optional.contracts_diamond_proxy_addr { anyhow::ensure!( local_diamond_proxy_addr == remote_diamond_proxy_addr, - "Diamond proxy address {local_diamond_proxy_addr:?} specified in config doesn't match one returned \ + "L1 diamond proxy address {local_diamond_proxy_addr:?} specified in config doesn't match one returned \ by main node ({remote_diamond_proxy_addr:?})" ); } else { tracing::info!( - "Diamond proxy address is not specified in config; will use address \ + "L1 diamond proxy address is not specified in config; will use address \ returned by main node: {remote_diamond_proxy_addr:?}" ); } @@ -1475,10 +1476,11 @@ impl From<&ExternalNodeConfig> for InternalApiConfig { l1_weth_bridge: config.remote.l1_weth_bridge_addr, l2_weth_bridge: config.remote.l2_weth_bridge_addr, }, + l1_bytecodes_supplier_addr: config.remote.l1_bytecodes_supplier_addr, l1_bridgehub_proxy_addr: config.remote.l1_bridgehub_proxy_addr, l1_state_transition_proxy_addr: config.remote.l1_state_transition_proxy_addr, l1_transparent_proxy_admin_addr: config.remote.l1_transparent_proxy_admin_addr, - l1_diamond_proxy_addr: config.remote.l1_diamond_proxy_addr, + l1_diamond_proxy_addr: config.l1_diamond_proxy_address(), l2_testnet_paymaster_addr: config.remote.l2_testnet_paymaster_addr, req_entities_limit: config.optional.req_entities_limit, fee_history_limit: config.optional.fee_history_limit, diff --git a/core/bin/external_node/src/node_builder.rs b/core/bin/external_node/src/node_builder.rs index 5f9445d30fe8..350c2a0ff000 100644 --- a/core/bin/external_node/src/node_builder.rs +++ b/core/bin/external_node/src/node_builder.rs @@ -133,7 +133,11 @@ impl ExternalNodeBuilder { fn add_external_node_metrics_layer(mut self) -> anyhow::Result { self.node.add_layer(ExternalNodeMetricsLayer { l1_chain_id: self.config.required.l1_chain_id, - sl_chain_id: self.config.required.settlement_layer_id(), + sl_chain_id: self + .config + .required + .gateway_chain_id + .unwrap_or(self.config.required.l1_chain_id.into()), l2_chain_id: self.config.required.l2_chain_id, postgres_pool_size: self.config.postgres.max_connections, }); @@ -179,8 +183,9 @@ impl ExternalNodeBuilder { fn add_query_eth_client_layer(mut self) -> anyhow::Result { let query_eth_client_layer = QueryEthClientLayer::new( - self.config.required.settlement_layer_id(), + self.config.required.l1_chain_id, self.config.required.eth_client_url.clone(), + self.config.required.gateway_chain_id, self.config.optional.gateway_url.clone(), ); self.node.add_layer(query_eth_client_layer); @@ -285,8 +290,9 @@ impl ExternalNodeBuilder { fn add_validate_chain_ids_layer(mut self) -> anyhow::Result { let layer = ValidateChainIdsLayer::new( - self.config.required.settlement_layer_id(), + self.config.required.l1_chain_id, self.config.required.l2_chain_id, + self.config.required.gateway_chain_id, ); self.node.add_layer(layer); Ok(self) diff --git a/core/bin/external_node/src/tests/framework.rs b/core/bin/external_node/src/tests/framework.rs index e9667f2c05db..698c88072f0b 100644 --- a/core/bin/external_node/src/tests/framework.rs +++ b/core/bin/external_node/src/tests/framework.rs @@ -17,7 +17,7 @@ use zksync_node_framework::{ task::TaskKind, FromContext, IntoContext, StopReceiver, Task, TaskId, WiringError, WiringLayer, }; -use zksync_types::{L2ChainId, SLChainId}; +use zksync_types::{L1ChainId, L2ChainId}; use zksync_web3_decl::client::{MockClient, L1, L2}; use super::ExternalNodeBuilder; @@ -127,11 +127,11 @@ impl WiringLayer for MockL1ClientLayer { fn layer_name(&self) -> &'static str { // We don't care about values, we just want to hijack the layer name. - // TODO(EVM-676): configure the `settlement_mode` here QueryEthClientLayer::new( - SLChainId(1), + L1ChainId(1), "https://example.com".parse().unwrap(), - Default::default(), + None, + None, ) .layer_name() } diff --git a/core/bin/system-constants-generator/src/main.rs b/core/bin/system-constants-generator/src/main.rs index cc2e031106b8..cd795f9f5326 100644 --- a/core/bin/system-constants-generator/src/main.rs +++ b/core/bin/system-constants-generator/src/main.rs @@ -3,7 +3,9 @@ use std::fs; use codegen::{Block, Scope}; use serde::{Deserialize, Serialize}; use zksync_multivm::{ - utils::{get_bootloader_encoding_space, get_bootloader_max_txs_in_batch}, + utils::{ + get_bootloader_encoding_space, get_bootloader_max_txs_in_batch, get_max_new_factory_deps, + }, vm_latest::constants::MAX_VM_PUBDATA_PER_BATCH, zk_evm_latest::zkevm_opcode_defs::{ circuit_prices::{ @@ -15,7 +17,7 @@ use zksync_multivm::{ }; use zksync_types::{ IntrinsicSystemGasConstants, ProtocolVersionId, GUARANTEED_PUBDATA_IN_TX, - L1_GAS_PER_PUBDATA_BYTE, MAX_NEW_FACTORY_DEPS, REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_BYTE, + L1_GAS_PER_PUBDATA_BYTE, REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_BYTE, }; use zksync_utils::env::Workspace; @@ -73,7 +75,7 @@ pub fn generate_l1_contracts_system_config(gas_constants: &IntrinsicSystemGasCon l1_tx_delta_544_encoding_bytes: gas_constants.l1_tx_delta_544_encoding_bytes, l1_tx_delta_factory_deps_l2_gas: gas_constants.l1_tx_delta_factory_dep_gas, l1_tx_delta_factory_deps_pubdata: gas_constants.l1_tx_delta_factory_dep_pubdata, - max_new_factory_deps: MAX_NEW_FACTORY_DEPS as u32, + max_new_factory_deps: get_max_new_factory_deps(ProtocolVersionId::latest().into()) as u32, required_l2_gas_price_per_pubdata: REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_BYTE, }; diff --git a/core/bin/zksync_server/src/main.rs b/core/bin/zksync_server/src/main.rs index 51e7b409c9a0..de284a0a052c 100644 --- a/core/bin/zksync_server/src/main.rs +++ b/core/bin/zksync_server/src/main.rs @@ -56,6 +56,10 @@ struct Cli { /// Path to the yaml with contracts. If set, it will be used instead of env vars. #[arg(long)] contracts_config_path: Option, + /// Path to the yaml with gateway contracts. Note, that at this moment, + /// env-based config is not supported for gateway-related functionality. + #[arg(long)] + gateway_contracts_config_path: Option, /// Path to the wallets config. If set, it will be used instead of env vars. #[arg(long)] wallets_path: Option, @@ -126,6 +130,20 @@ fn main() -> anyhow::Result<()> { .context("failed decoding contracts YAML config")?, }; + // We support only file based config for gateway + let gateway_contracts_config = if let Some(gateway_config_path) = + opt.gateway_contracts_config_path + { + let result = read_yaml_repr::( + &gateway_config_path, + ) + .context("failed decoding contracts YAML config")?; + + Some(result) + } else { + None + }; + let genesis = match opt.genesis_path { None => GenesisConfig::from_env().context("Genesis config")?, Some(path) => read_yaml_repr::(&path) @@ -136,7 +154,14 @@ fn main() -> anyhow::Result<()> { .clone() .context("observability config")?; - let node = MainNodeBuilder::new(configs, wallets, genesis, contracts_config, secrets)?; + let node = MainNodeBuilder::new( + configs, + wallets, + genesis, + contracts_config, + gateway_contracts_config, + secrets, + )?; let observability_guard = { // Observability initialization should be performed within tokio context. diff --git a/core/bin/zksync_server/src/node_builder.rs b/core/bin/zksync_server/src/node_builder.rs index d74928e8fbc7..f1db73164eb1 100644 --- a/core/bin/zksync_server/src/node_builder.rs +++ b/core/bin/zksync_server/src/node_builder.rs @@ -6,8 +6,8 @@ use std::time::Duration; use anyhow::{bail, Context}; use zksync_config::{ configs::{ - da_client::DAClientConfig, secrets::DataAvailabilitySecrets, wallets::Wallets, - GeneralConfig, Secrets, + da_client::DAClientConfig, gateway::GatewayChainConfig, secrets::DataAvailabilitySecrets, + wallets::Wallets, GeneralConfig, Secrets, }, ContractsConfig, GenesisConfig, }; @@ -90,6 +90,7 @@ pub struct MainNodeBuilder { wallets: Wallets, genesis_config: GenesisConfig, contracts_config: ContractsConfig, + gateway_chain_config: Option, secrets: Secrets, } @@ -99,6 +100,7 @@ impl MainNodeBuilder { wallets: Wallets, genesis_config: GenesisConfig, contracts_config: ContractsConfig, + gateway_chain_config: Option, secrets: Secrets, ) -> anyhow::Result { Ok(Self { @@ -107,6 +109,7 @@ impl MainNodeBuilder { wallets, genesis_config, contracts_config, + gateway_chain_config, secrets, }) } @@ -149,7 +152,7 @@ impl MainNodeBuilder { self.node.add_layer(PKSigningEthClientLayer::new( eth_config, self.contracts_config.clone(), - self.genesis_config.settlement_layer_id(), + self.gateway_chain_config.clone(), wallets, )); Ok(self) @@ -159,8 +162,11 @@ impl MainNodeBuilder { let genesis = self.genesis_config.clone(); let eth_config = try_load_config!(self.secrets.l1); let query_eth_client_layer = QueryEthClientLayer::new( - genesis.settlement_layer_id(), + genesis.l1_chain_id, eth_config.l1_rpc_url, + self.gateway_chain_config + .as_ref() + .map(|c| c.gateway_chain_id), eth_config.gateway_rpc_url, ); self.node.add_layer(query_eth_client_layer); @@ -279,6 +285,12 @@ impl MainNodeBuilder { self.node.add_layer(EthWatchLayer::new( try_load_config!(eth_config.watcher), self.contracts_config.clone(), + self.gateway_chain_config.clone(), + self.configs + .eth + .as_ref() + .and_then(|x| Some(x.gas_adjuster?.settlement_mode)) + .unwrap_or(SettlementMode::SettlesToL1), self.genesis_config.l2_chain_id, )); Ok(self) @@ -453,10 +465,10 @@ impl MainNodeBuilder { fn add_eth_tx_aggregator_layer(mut self) -> anyhow::Result { let eth_sender_config = try_load_config!(self.configs.eth); - self.node.add_layer(EthTxAggregatorLayer::new( eth_sender_config, self.contracts_config.clone(), + self.gateway_chain_config.clone(), self.genesis_config.l2_chain_id, self.genesis_config.l1_batch_commit_data_generator_mode, self.configs diff --git a/core/lib/basic_types/src/protocol_version.rs b/core/lib/basic_types/src/protocol_version.rs index ebecfaa1b872..39b99f2d04a2 100644 --- a/core/lib/basic_types/src/protocol_version.rs +++ b/core/lib/basic_types/src/protocol_version.rs @@ -142,7 +142,11 @@ impl ProtocolVersionId { } pub fn is_pre_gateway(&self) -> bool { - self <= &Self::Version26 + self < &Self::gateway_upgrade() + } + + pub fn is_post_gateway(&self) -> bool { + self >= &Self::gateway_upgrade() } pub fn is_1_4_0(&self) -> bool { @@ -180,6 +184,10 @@ impl ProtocolVersionId { pub fn is_post_1_5_0(&self) -> bool { self >= &ProtocolVersionId::Version23 } + + pub const fn gateway_upgrade() -> Self { + ProtocolVersionId::Version27 + } } impl Default for ProtocolVersionId { diff --git a/core/lib/config/src/configs/contracts.rs b/core/lib/config/src/configs/contracts.rs index 38576833fa3e..f6bd02f2dfae 100644 --- a/core/lib/config/src/configs/contracts.rs +++ b/core/lib/config/src/configs/contracts.rs @@ -1,13 +1,14 @@ // External uses use serde::{Deserialize, Serialize}; // Workspace uses -use zksync_basic_types::Address; +use zksync_basic_types::{Address, H256}; #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct EcosystemContracts { pub bridgehub_proxy_addr: Address, pub state_transition_proxy_addr: Address, pub transparent_proxy_admin_addr: Address, + pub l1_bytecodes_supplier_addr: Option
, } impl EcosystemContracts { @@ -16,6 +17,7 @@ impl EcosystemContracts { bridgehub_proxy_addr: Address::repeat_byte(0x14), state_transition_proxy_addr: Address::repeat_byte(0x15), transparent_proxy_admin_addr: Address::repeat_byte(0x15), + l1_bytecodes_supplier_addr: Some(Address::repeat_byte(0x16)), } } } @@ -46,6 +48,10 @@ pub struct ContractsConfig { pub ecosystem_contracts: Option, // Used by the RPC API and by the node builder in wiring the BaseTokenRatioProvider layer. pub base_token_addr: Option
, + pub l1_base_token_asset_id: Option, + + pub l2_predeployed_wrapped_base_token_address: Option
, + pub chain_admin_addr: Option
, pub l2_da_validator_addr: Option
, } @@ -69,6 +75,8 @@ impl ContractsConfig { l2_timestamp_asserter_addr: Some(Address::repeat_byte(0x19)), governance_addr: Address::repeat_byte(0x13), base_token_addr: Some(Address::repeat_byte(0x14)), + l1_base_token_asset_id: Some(H256::repeat_byte(0x15)), + l2_predeployed_wrapped_base_token_address: Some(Address::repeat_byte(0x1b)), ecosystem_contracts: Some(EcosystemContracts::for_tests()), chain_admin_addr: Some(Address::repeat_byte(0x18)), l2_da_validator_addr: Some(Address::repeat_byte(0x1a)), diff --git a/core/lib/config/src/configs/da_client/avail.rs b/core/lib/config/src/configs/da_client/avail.rs index 3993656d667a..48aaf5b0e61e 100644 --- a/core/lib/config/src/configs/da_client/avail.rs +++ b/core/lib/config/src/configs/da_client/avail.rs @@ -1,17 +1,17 @@ -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use zksync_basic_types::secrets::{APIKey, SeedPhrase}; pub const AVAIL_GAS_RELAY_CLIENT_NAME: &str = "GasRelay"; pub const AVAIL_FULL_CLIENT_NAME: &str = "FullClient"; -#[derive(Clone, Debug, PartialEq, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(tag = "avail_client")] pub enum AvailClientConfig { FullClient(AvailDefaultConfig), GasRelay(AvailGasRelayConfig), } -#[derive(Clone, Debug, PartialEq, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct AvailConfig { pub bridge_api_url: String, pub timeout_ms: usize, @@ -19,13 +19,13 @@ pub struct AvailConfig { pub config: AvailClientConfig, } -#[derive(Clone, Debug, PartialEq, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct AvailDefaultConfig { pub api_node_url: String, pub app_id: u32, } -#[derive(Clone, Debug, PartialEq, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct AvailGasRelayConfig { pub gas_relay_api_url: String, pub max_retries: usize, diff --git a/core/lib/config/src/configs/da_client/mod.rs b/core/lib/config/src/configs/da_client/mod.rs index 322c4a20aac8..f82fd134edb5 100644 --- a/core/lib/config/src/configs/da_client/mod.rs +++ b/core/lib/config/src/configs/da_client/mod.rs @@ -16,3 +16,9 @@ pub enum DAClientConfig { Eigen(EigenConfig), ObjectStore(ObjectStoreConfig), } + +impl From for DAClientConfig { + fn from(config: AvailConfig) -> Self { + Self::Avail(config) + } +} diff --git a/core/lib/config/src/configs/en_config.rs b/core/lib/config/src/configs/en_config.rs index 13a0e1f2c99d..5c8875eb4b4a 100644 --- a/core/lib/config/src/configs/en_config.rs +++ b/core/lib/config/src/configs/en_config.rs @@ -10,7 +10,6 @@ use zksync_basic_types::{ pub struct ENConfig { // Genesis pub l2_chain_id: L2ChainId, - pub sl_chain_id: Option, pub l1_chain_id: L1ChainId, pub l1_batch_commit_data_generator_mode: L1BatchCommitmentMode, @@ -19,4 +18,6 @@ pub struct ENConfig { pub main_node_rate_limit_rps: Option, pub bridge_addresses_refresh_interval_sec: Option, + + pub gateway_chain_id: Option, } diff --git a/core/lib/config/src/configs/eth_sender.rs b/core/lib/config/src/configs/eth_sender.rs index be2f5b532a3e..fb3dee3c3dd2 100644 --- a/core/lib/config/src/configs/eth_sender.rs +++ b/core/lib/config/src/configs/eth_sender.rs @@ -41,6 +41,7 @@ impl EthConfig { pubdata_sending_mode: PubdataSendingMode::Calldata, tx_aggregation_paused: false, tx_aggregation_only_prove_and_execute: false, + priority_tree_start_index: Some(0), time_in_mempool_in_l1_blocks_cap: 1800, }), gas_adjuster: Some(GasAdjusterConfig { @@ -92,7 +93,7 @@ pub struct SenderConfig { pub max_txs_in_flight: u64, /// The mode in which proofs are sent. pub proof_sending_mode: ProofSendingMode, - + /// Note, that it is used only for L1 transactions pub max_aggregated_tx_gas: u32, pub max_eth_tx_data_size: usize, pub max_aggregated_blocks_to_commit: u32, @@ -117,7 +118,8 @@ pub struct SenderConfig { /// special mode specifically for gateway migration to decrease number of non-executed batches #[serde(default = "SenderConfig::default_tx_aggregation_only_prove_and_execute")] pub tx_aggregation_only_prove_and_execute: bool, - + /// Index of the priority operation to start building the `PriorityMerkleTree` from. + pub priority_tree_start_index: Option, /// Cap of time in mempool for price calculations #[serde(default = "SenderConfig::default_time_in_mempool_in_l1_blocks_cap")] pub time_in_mempool_in_l1_blocks_cap: u32, @@ -183,9 +185,13 @@ pub struct GasAdjusterConfig { /// Parameter of the transaction base_fee_per_gas pricing formula #[serde(default = "GasAdjusterConfig::default_pricing_formula_parameter_b")] pub pricing_formula_parameter_b: f64, - /// Parameter by which the base fee will be multiplied for internal purposes + /// Parameter by which the base fee will be multiplied for internal purposes. + /// TODO(EVM-920): Note, that while the name says "L1", this same parameter is actually used for + /// any settlement layer. pub internal_l1_pricing_multiplier: f64, - /// If equal to Some(x), then it will always provide `x` as the L1 gas price + /// If equal to Some(x), then it will always provide `x` as the L1 gas price. + /// TODO(EVM-920): Note, that while the name says "L1", this same parameter is actually used for + /// any settlement layer. pub internal_enforced_l1_gas_price: Option, /// If equal to Some(x), then it will always provide `x` as the pubdata price pub internal_enforced_pubdata_price: Option, diff --git a/core/lib/config/src/configs/gateway.rs b/core/lib/config/src/configs/gateway.rs new file mode 100644 index 000000000000..3afa69de7cd8 --- /dev/null +++ b/core/lib/config/src/configs/gateway.rs @@ -0,0 +1,73 @@ +use zksync_basic_types::{web3::Bytes, Address, SLChainId}; + +use super::ContractsConfig; + +/// Config that is only stored for the gateway chain. +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)] +pub struct GatewayConfig { + pub state_transition_proxy_addr: Address, + pub state_transition_implementation_addr: Address, + pub verifier_addr: Address, + pub validator_timelock_addr: Address, + pub admin_facet_addr: Address, + pub mailbox_facet_addr: Address, + pub executor_facet_addr: Address, + pub getters_facet_addr: Address, + pub diamond_init_addr: Address, + pub genesis_upgrade_addr: Address, + pub default_upgrade_addr: Address, + pub multicall3_addr: Address, + pub relayed_sl_da_validator: Address, + pub validium_da_validator: Address, + pub diamond_cut_data: Bytes, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)] +pub struct GatewayChainConfig { + pub state_transition_proxy_addr: Address, + pub validator_timelock_addr: Address, + pub multicall3_addr: Address, + pub diamond_proxy_addr: Address, + // TODO(EVM-921): there is no "governace" for a chain, only an admin, we + // need to figure out what we mean here + pub chain_admin_addr: Option
, + pub governance_addr: Address, + pub gateway_chain_id: SLChainId, +} + +impl GatewayChainConfig { + pub fn from_gateway_and_chain_data( + gateway_config: &GatewayConfig, + diamond_proxy_addr: Address, + l2_chain_admin_addr: Address, + gateway_chain_id: SLChainId, + ) -> Self { + Self { + state_transition_proxy_addr: gateway_config.state_transition_proxy_addr, + validator_timelock_addr: gateway_config.validator_timelock_addr, + multicall3_addr: gateway_config.multicall3_addr, + diamond_proxy_addr, + chain_admin_addr: Some(l2_chain_admin_addr), + governance_addr: l2_chain_admin_addr, + gateway_chain_id, + } + } + + pub fn from_contracts_and_chain_id( + contracts: ContractsConfig, + gateway_chain_id: SLChainId, + ) -> Self { + Self { + state_transition_proxy_addr: contracts + .ecosystem_contracts + .unwrap() + .state_transition_proxy_addr, + validator_timelock_addr: contracts.validator_timelock_addr, + multicall3_addr: contracts.l1_multicall3_addr, + diamond_proxy_addr: contracts.diamond_proxy_addr, + chain_admin_addr: contracts.chain_admin_addr, + governance_addr: contracts.governance_addr, + gateway_chain_id, + } + } +} diff --git a/core/lib/config/src/configs/genesis.rs b/core/lib/config/src/configs/genesis.rs index 5d5e39309bb8..6fc117f8b679 100644 --- a/core/lib/config/src/configs/genesis.rs +++ b/core/lib/config/src/configs/genesis.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use zksync_basic_types::{ commitment::L1BatchCommitmentMode, protocol_version::{ProtocolSemanticVersion, ProtocolVersionId}, - Address, L1ChainId, L2ChainId, SLChainId, H256, + Address, L1ChainId, L2ChainId, H256, }; /// This config represents the genesis state of the chain. @@ -19,7 +19,6 @@ pub struct GenesisConfig { pub default_aa_hash: Option, pub evm_emulator_hash: Option, pub l1_chain_id: L1ChainId, - pub sl_chain_id: Option, pub l2_chain_id: L2ChainId, // Note: `serde` isn't used with protobuf config. The same alias is implemented in // `zksync_protobuf_config` manually. @@ -35,12 +34,6 @@ pub struct GenesisConfig { pub custom_genesis_state_path: Option, } -impl GenesisConfig { - pub fn settlement_layer_id(&self) -> SLChainId { - self.sl_chain_id.unwrap_or(self.l1_chain_id.into()) - } -} - impl GenesisConfig { pub fn for_tests() -> Self { GenesisConfig { @@ -53,7 +46,6 @@ impl GenesisConfig { default_aa_hash: Default::default(), evm_emulator_hash: Default::default(), l1_chain_id: L1ChainId(9), - sl_chain_id: None, protocol_version: Some(ProtocolSemanticVersion { minor: ProtocolVersionId::latest(), patch: 0.into(), diff --git a/core/lib/config/src/configs/mod.rs b/core/lib/config/src/configs/mod.rs index 5bf9a49e0acf..f4af71de38ad 100644 --- a/core/lib/config/src/configs/mod.rs +++ b/core/lib/config/src/configs/mod.rs @@ -18,6 +18,7 @@ pub use self::{ fri_prover_gateway::FriProverGatewayConfig, fri_witness_generator::FriWitnessGeneratorConfig, fri_witness_vector_generator::FriWitnessVectorGeneratorConfig, + gateway::{GatewayChainConfig, GatewayConfig}, general::GeneralConfig, genesis::GenesisConfig, object_store::ObjectStoreConfig, @@ -54,6 +55,7 @@ pub mod fri_prover_gateway; pub mod fri_prover_group; pub mod fri_witness_generator; pub mod fri_witness_vector_generator; +pub mod gateway; mod general; pub mod genesis; pub mod house_keeper; diff --git a/core/lib/config/src/testonly.rs b/core/lib/config/src/testonly.rs index 749dba1f9548..273015b760db 100644 --- a/core/lib/config/src/testonly.rs +++ b/core/lib/config/src/testonly.rs @@ -10,7 +10,7 @@ use zksync_basic_types::{ pubdata_da::PubdataSendingMode, secrets::{APIKey, SeedPhrase}, vm::FastVmMode, - L1BatchNumber, L1ChainId, L2ChainId, + L1BatchNumber, L1ChainId, L2ChainId, SLChainId, }; use zksync_consensus_utils::EncodeDist; use zksync_crypto_primitives::K256PrivateKey; @@ -267,6 +267,8 @@ impl Distribution for EncodeDist { l1_multicall3_addr: rng.gen(), ecosystem_contracts: self.sample(rng), base_token_addr: self.sample_opt(|| rng.gen()), + l1_base_token_asset_id: self.sample_opt(|| rng.gen()), + l2_predeployed_wrapped_base_token_address: self.sample_opt(|| rng.gen()), chain_admin_addr: self.sample_opt(|| rng.gen()), l2_da_validator_addr: self.sample_opt(|| rng.gen()), } @@ -417,6 +419,7 @@ impl Distribution for EncodeDist { pubdata_sending_mode: PubdataSendingMode::Calldata, tx_aggregation_paused: false, tx_aggregation_only_prove_and_execute: false, + priority_tree_start_index: self.sample(rng), time_in_mempool_in_l1_blocks_cap: self.sample(rng), } } @@ -738,7 +741,6 @@ impl Distribution for EncodeDist { evm_emulator_hash: Some(rng.gen()), fee_account: rng.gen(), l1_chain_id: L1ChainId(self.sample(rng)), - sl_chain_id: None, l2_chain_id: L2ChainId::default(), snark_wrapper_vk_hash: rng.gen(), dummy_verifier: rng.gen(), @@ -757,6 +759,7 @@ impl Distribution for EncodeDist { bridgehub_proxy_addr: rng.gen(), state_transition_proxy_addr: rng.gen(), transparent_proxy_admin_addr: rng.gen(), + l1_bytecodes_supplier_addr: rng.gen(), } } } @@ -933,7 +936,6 @@ impl Distribution for EncodeDist { configs::en_config::ENConfig { l2_chain_id: L2ChainId::default(), l1_chain_id: L1ChainId(rng.gen()), - sl_chain_id: None, main_node_url: format!("localhost:{}", rng.gen::()).parse().unwrap(), l1_batch_commit_data_generator_mode: match rng.gen_range(0..2) { 0 => L1BatchCommitmentMode::Rollup, @@ -941,6 +943,7 @@ impl Distribution for EncodeDist { }, main_node_rate_limit_rps: self.sample_opt(|| rng.gen()), bridge_addresses_refresh_interval_sec: self.sample_opt(|| rng.gen()), + gateway_chain_id: self.sample_opt(|| SLChainId(rng.gen())), } } } diff --git a/core/lib/constants/src/contracts.rs b/core/lib/constants/src/contracts.rs index 6e402c117bfe..09c1095b7721 100644 --- a/core/lib/constants/src/contracts.rs +++ b/core/lib/constants/src/contracts.rs @@ -166,6 +166,14 @@ pub const L2_MESSAGE_ROOT_ADDRESS: Address = H160([ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, ]); +pub const SLOAD_CONTRACT_ADDRESS: Address = H160([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x06, +]); +pub const L2_WRAPPED_BASE_TOKEN_IMPL: Address = H160([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x07, +]); pub const ERC20_TRANSFER_TOPIC: H256 = H256([ 221, 242, 82, 173, 27, 226, 200, 155, 105, 194, 176, 104, 252, 55, 141, 170, 149, 43, 167, 241, diff --git a/core/lib/constants/src/crypto.rs b/core/lib/constants/src/crypto.rs index ead2cc318370..0260ee578c88 100644 --- a/core/lib/constants/src/crypto.rs +++ b/core/lib/constants/src/crypto.rs @@ -3,8 +3,6 @@ pub const ZKPORTER_IS_AVAILABLE: bool = false; /// Depth of the account tree. pub const ROOT_TREE_DEPTH: usize = 256; -pub const MAX_NEW_FACTORY_DEPS: usize = 32; - /// To avoid DDoS we limit the size of the transactions size. /// TODO(X): remove this as a constant and introduce a config. pub const MAX_ENCODED_TX_SIZE: usize = 1 << 24; diff --git a/core/lib/constants/src/message_root.rs b/core/lib/constants/src/message_root.rs index 9bb8764cd667..adabd90c5874 100644 --- a/core/lib/constants/src/message_root.rs +++ b/core/lib/constants/src/message_root.rs @@ -1,14 +1,14 @@ /// Position of `chainCount` in `MessageRoot`'s storage layout. -pub const CHAIN_COUNT_KEY: usize = 0; +pub const CHAIN_COUNT_KEY: usize = 1; /// Position of `chainIndexToId` in `MessageRoot`'s storage layout. -pub const CHAIN_INDEX_TO_ID_KEY: usize = 2; +pub const CHAIN_INDEX_TO_ID_KEY: usize = 3; /// Position of `FullTree::_height` in `MessageRoot`'s storage layout. -pub const AGG_TREE_HEIGHT_KEY: usize = 3; +pub const AGG_TREE_HEIGHT_KEY: usize = 4; /// Position of `FullTree::nodes` in `MessageRoot`'s storage layout. -pub const AGG_TREE_NODES_KEY: usize = 5; +pub const AGG_TREE_NODES_KEY: usize = 6; /// Position of `chainTree` in `MessageRoot`'s storage layout. -pub const CHAIN_TREE_KEY: usize = 7; +pub const CHAIN_TREE_KEY: usize = 8; diff --git a/core/lib/dal/.sqlx/query-8b0cc0da34f13544e00ab9b18f54df64b3d50d310800efcc6449cb0e387d6ea5.json b/core/lib/dal/.sqlx/query-8b0cc0da34f13544e00ab9b18f54df64b3d50d310800efcc6449cb0e387d6ea5.json new file mode 100644 index 000000000000..e8ccd163849c --- /dev/null +++ b/core/lib/dal/.sqlx/query-8b0cc0da34f13544e00ab9b18f54df64b3d50d310800efcc6449cb0e387d6ea5.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n hash\n FROM\n transactions\n WHERE\n priority_op_id >= $1\n AND is_priority = TRUE\n ORDER BY\n priority_op_id\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "hash", + "type_info": "Bytea" + } + ], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [ + false + ] + }, + "hash": "8b0cc0da34f13544e00ab9b18f54df64b3d50d310800efcc6449cb0e387d6ea5" +} diff --git a/core/lib/dal/.sqlx/query-a0dce1ac6117680fe64a848c4f3efa4658926efb4444ff1b13c186d17847c5ad.json b/core/lib/dal/.sqlx/query-a0dce1ac6117680fe64a848c4f3efa4658926efb4444ff1b13c186d17847c5ad.json new file mode 100644 index 000000000000..ebbcd7a1a53b --- /dev/null +++ b/core/lib/dal/.sqlx/query-a0dce1ac6117680fe64a848c4f3efa4658926efb4444ff1b13c186d17847c5ad.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT eth_txs.chain_id\n FROM l1_batches\n JOIN eth_txs ON eth_txs.id = l1_batches.eth_execute_tx_id\n WHERE\n number = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "chain_id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [ + true + ] + }, + "hash": "a0dce1ac6117680fe64a848c4f3efa4658926efb4444ff1b13c186d17847c5ad" +} diff --git a/core/lib/dal/.sqlx/query-ecea1bc19022616ef1c2a69b9da38d8add1bf70064561a9631e46041d8ac7724.json b/core/lib/dal/.sqlx/query-ecea1bc19022616ef1c2a69b9da38d8add1bf70064561a9631e46041d8ac7724.json new file mode 100644 index 000000000000..08cb51eb7c89 --- /dev/null +++ b/core/lib/dal/.sqlx/query-ecea1bc19022616ef1c2a69b9da38d8add1bf70064561a9631e46041d8ac7724.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n MIN(priority_op_id) AS \"id?\"\n FROM\n transactions\n WHERE\n l1_batch_number = $1\n AND is_priority = TRUE\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id?", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [ + null + ] + }, + "hash": "ecea1bc19022616ef1c2a69b9da38d8add1bf70064561a9631e46041d8ac7724" +} diff --git a/core/lib/dal/src/blocks_dal.rs b/core/lib/dal/src/blocks_dal.rs index 5002c5a8afbf..9404f4d14332 100644 --- a/core/lib/dal/src/blocks_dal.rs +++ b/core/lib/dal/src/blocks_dal.rs @@ -1615,6 +1615,30 @@ impl BlocksDal<'_, '_> { .context("map_l1_batches()") } + pub async fn get_batch_first_priority_op_id( + &mut self, + batch_number: L1BatchNumber, + ) -> DalResult> { + let row = sqlx::query!( + r#" + SELECT + MIN(priority_op_id) AS "id?" + FROM + transactions + WHERE + l1_batch_number = $1 + AND is_priority = TRUE + "#, + i64::from(batch_number.0), + ) + .instrument("get_batch_first_priority_op_id") + .with_arg("batch_number", &batch_number) + .fetch_one(self.storage) + .await?; + + Ok(row.id.map(|id| id as usize)) + } + async fn raw_ready_for_execute_l1_batches( &mut self, max_l1_batch_timestamp_seconds: f64, diff --git a/core/lib/dal/src/eth_sender_dal.rs b/core/lib/dal/src/eth_sender_dal.rs index cc7c694a96fe..10f77718ba38 100644 --- a/core/lib/dal/src/eth_sender_dal.rs +++ b/core/lib/dal/src/eth_sender_dal.rs @@ -445,6 +445,27 @@ impl EthSenderDal<'_, '_> { Ok(row.and_then(|r| r.chain_id).map(|id| SLChainId(id as u64))) } + pub async fn get_batch_execute_chain_id( + &mut self, + batch_number: L1BatchNumber, + ) -> DalResult> { + let row = sqlx::query!( + r#" + SELECT eth_txs.chain_id + FROM l1_batches + JOIN eth_txs ON eth_txs.id = l1_batches.eth_execute_tx_id + WHERE + number = $1 + "#, + i64::from(batch_number.0), + ) + .instrument("get_batch_execute_chain_id") + .with_arg("batch_number", &batch_number) + .fetch_optional(self.storage) + .await?; + Ok(row.and_then(|r| r.chain_id).map(|id| SLChainId(id as u64))) + } + pub async fn get_confirmed_tx_hash_by_eth_tx_id( &mut self, eth_tx_id: u32, diff --git a/core/lib/dal/src/transactions_dal.rs b/core/lib/dal/src/transactions_dal.rs index a5dfb8932ddb..20ecf8736d7c 100644 --- a/core/lib/dal/src/transactions_dal.rs +++ b/core/lib/dal/src/transactions_dal.rs @@ -160,11 +160,37 @@ impl TransactionsDal<'_, '_> { ) .instrument("insert_transaction_l1") .with_arg("tx_hash", &tx_hash) - .fetch_optional(self.storage) + .execute(self.storage) .await?; + Ok(()) } + pub async fn get_l1_transactions_hashes(&mut self, start_id: usize) -> DalResult> { + let hashes = sqlx::query!( + r#" + SELECT + hash + FROM + transactions + WHERE + priority_op_id >= $1 + AND is_priority = TRUE + ORDER BY + priority_op_id + "#, + start_id as i64 + ) + .instrument("get_l1_transactions_hashes") + .with_arg("start_id", &start_id) + .fetch_all(self.storage) + .await?; + Ok(hashes + .into_iter() + .map(|row| H256::from_slice(&row.hash)) + .collect()) + } + pub async fn insert_system_transaction(&mut self, tx: &ProtocolUpgradeTx) -> DalResult<()> { let contract_address = tx.execute.contract_address; let contract_address_as_bytes = contract_address.map(|addr| addr.as_bytes().to_vec()); diff --git a/core/lib/env_config/src/contracts.rs b/core/lib/env_config/src/contracts.rs index ae4e1d1d5b4f..457a946d9831 100644 --- a/core/lib/env_config/src/contracts.rs +++ b/core/lib/env_config/src/contracts.rs @@ -10,6 +10,8 @@ impl FromEnv for EcosystemContracts { .parse()?, transparent_proxy_admin_addr: std::env::var("CONTRACTS_TRANSPARENT_PROXY_ADMIN_ADDR")? .parse()?, + // Not supported yet + l1_bytecodes_supplier_addr: None, }) } } @@ -70,8 +72,11 @@ mod tests { bridgehub_proxy_addr: addr("0x35ea7f92f4c5f433efe15284e99c040110cf6297"), state_transition_proxy_addr: addr("0xd90f1c081c6117241624e97cb6147257c3cb2097"), transparent_proxy_admin_addr: addr("0xdd6fa5c14e7550b4caf2aa2818d24c69cbc347e5"), + l1_bytecodes_supplier_addr: None, }), base_token_addr: Some(SHARED_BRIDGE_ETHER_TOKEN_ADDRESS), + l1_base_token_asset_id: None, + l2_predeployed_wrapped_base_token_address: None, chain_admin_addr: Some(addr("0xdd6fa5c14e7550b4caf2aa2818d24c69cbc347ff")), l2_da_validator_addr: Some(addr("0xed6fa5c14e7550b4caf2aa2818d24c69cbc347ff")), l2_timestamp_asserter_addr: Some(addr("0x0000000000000000000000000000000000000002")), diff --git a/core/lib/env_config/src/eth_sender.rs b/core/lib/env_config/src/eth_sender.rs index f822109ed935..5c0f042984e7 100644 --- a/core/lib/env_config/src/eth_sender.rs +++ b/core/lib/env_config/src/eth_sender.rs @@ -76,6 +76,7 @@ mod tests { tx_aggregation_only_prove_and_execute: false, tx_aggregation_paused: false, time_in_mempool_in_l1_blocks_cap: 2000, + priority_tree_start_index: None, }), gas_adjuster: Some(GasAdjusterConfig { default_priority_fee_per_gas: 20000000000, diff --git a/core/lib/env_config/src/genesis.rs b/core/lib/env_config/src/genesis.rs index db0228d91c13..234fbc85f6b9 100644 --- a/core/lib/env_config/src/genesis.rs +++ b/core/lib/env_config/src/genesis.rs @@ -85,7 +85,6 @@ impl FromEnv for GenesisConfig { evm_emulator_hash: state_keeper.evm_emulator_hash, // TODO(EVM-676): for now, the settlement layer is always the same as the L1 network l1_chain_id: L1ChainId(network_config.network.chain_id().0), - sl_chain_id: Some(network_config.network.chain_id()), l2_chain_id: network_config.zksync_network_id, snark_wrapper_vk_hash: contracts_config.snark_wrapper_vk_hash, fee_account: state_keeper diff --git a/core/lib/mini_merkle_tree/src/lib.rs b/core/lib/mini_merkle_tree/src/lib.rs index fed28edb10c0..56ffb2baf59a 100644 --- a/core/lib/mini_merkle_tree/src/lib.rs +++ b/core/lib/mini_merkle_tree/src/lib.rs @@ -5,9 +5,7 @@ #![warn(clippy::all, clippy::pedantic)] #![allow(clippy::must_use_candidate, clippy::similar_names)] -use std::{collections::VecDeque, iter, marker::PhantomData}; - -use once_cell::sync::OnceCell; +use std::{collections::VecDeque, iter, marker::PhantomData, ops::RangeTo}; #[cfg(test)] mod tests; @@ -95,6 +93,19 @@ where Self::from_hashes(hasher, hashes.into_iter(), min_tree_size) } + /// Adds a new leaf to the tree (replaces leftmost empty leaf). + /// If the tree is full, its size is doubled. + /// Note: empty leaves != zero leaves. + pub fn push(&mut self, leaf: L) { + let leaf_hash = self.hasher.hash_bytes(leaf.as_ref()); + self.push_hash(leaf_hash); + } +} + +impl MiniMerkleTree +where + H: HashEmptySubtree, +{ /// Creates a new Merkle tree from the supplied raw hashes. If `min_tree_size` is supplied and is larger than the /// number of the supplied leaves, the leaves are padded to `min_tree_size` with zero-hash entries, /// but are deemed empty. @@ -182,16 +193,19 @@ where /// Returns the root hash and the Merkle proofs for a range of leafs. /// The range is 0..length, where `0` is the leftmost untrimmed leaf (i.e. leaf under `self.start_index`). /// # Panics - /// Panics if `length` is 0 or greater than the number of leaves in the tree. + /// Panics if `range.end` is 0 or greater than the number of leaves in the tree. pub fn merkle_root_and_paths_for_range( &self, - length: usize, + range: RangeTo, ) -> (H256, Vec>, Vec>) { - assert!(length > 0, "range must not be empty"); - assert!(length <= self.hashes.len(), "not enough leaves in the tree"); + assert!(range.end > 0, "empty range"); + assert!(range.end <= self.hashes.len(), "range index out of bounds"); let mut right_path = vec![]; - let root_hash = - self.compute_merkle_root_and_path(length - 1, Some(&mut right_path), Some(Side::Right)); + let root_hash = self.compute_merkle_root_and_path( + range.end - 1, + Some(&mut right_path), + Some(Side::Right), + ); (root_hash, self.cache.clone(), right_path) } @@ -208,12 +222,9 @@ where } } - /// Adds a new leaf to the tree (replaces leftmost empty leaf). - /// If the tree is full, its size is doubled. - /// Note: empty leaves != zero leaves. - pub fn push(&mut self, leaf: L) { - let leaf_hash = self.hasher.hash_bytes(leaf.as_ref()); - self.push_hash(leaf_hash); + /// Returns the leftmost `length` untrimmed leaf hashes. + pub fn hashes_prefix(&self, length: usize) -> Vec { + self.hashes.iter().take(length).copied().collect() } /// Trims and caches the leftmost `count` leaves. @@ -319,8 +330,9 @@ pub trait HashEmptySubtree: 'static + Send + Sync + Hasher { /// Returns the hash of an empty subtree with the given depth. /// Implementations are encouraged to cache the returned values. fn empty_subtree_hash(&self, depth: usize) -> H256 { - static EMPTY_TREE_HASHES: OnceCell> = OnceCell::new(); - EMPTY_TREE_HASHES.get_or_init(|| compute_empty_tree_hashes(self.empty_leaf_hash()))[depth] + // We do not cache by default since then the cached values would be preserved + // for all implementations which is not correct for different leaves. + compute_empty_tree_hashes(self.empty_leaf_hash())[depth] } /// Returns an empty hash diff --git a/core/lib/mini_merkle_tree/src/tests.rs b/core/lib/mini_merkle_tree/src/tests.rs index 51a684d945fd..34444756f74d 100644 --- a/core/lib/mini_merkle_tree/src/tests.rs +++ b/core/lib/mini_merkle_tree/src/tests.rs @@ -222,7 +222,7 @@ fn merkle_proofs_are_valid_for_ranges() { let tree_len = tree.hashes.len(); for i in 1..=tree_len { - let (merkle_root, start_path, end_path) = tree.merkle_root_and_paths_for_range(i); + let (merkle_root, start_path, end_path) = tree.merkle_root_and_paths_for_range(..i); verify_range_merkle_proof( &leaves[..i], start_index, @@ -361,13 +361,13 @@ fn pushing_new_leaves() { tree.push([number; 88]); tree.push([number; 88]); - let (root, start_path, end_path) = tree.merkle_root_and_paths_for_range(1); + let (root, start_path, end_path) = tree.merkle_root_and_paths_for_range(..1); assert_eq!(root, *expected_root); assert_eq!(start_path.len(), end_path.len()); tree.trim_start(2); - let (root, start_path, end_path) = tree.merkle_root_and_paths_for_range(1); + let (root, start_path, end_path) = tree.merkle_root_and_paths_for_range(..1); assert_eq!(root, *expected_root); assert_eq!(start_path.len(), end_path.len()); } diff --git a/core/lib/multivm/src/utils/mod.rs b/core/lib/multivm/src/utils/mod.rs index 6f350084d22a..7bb907efc9b8 100644 --- a/core/lib/multivm/src/utils/mod.rs +++ b/core/lib/multivm/src/utils/mod.rs @@ -532,6 +532,32 @@ pub fn get_max_batch_base_layer_circuits(version: VmVersion) -> usize { } } +pub fn get_max_new_factory_deps(version: VmVersion) -> usize { + match version { + VmVersion::M5WithRefunds | VmVersion::M5WithoutRefunds => { + crate::vm_m5::vm_with_bootloader::MAX_NEW_FACTORY_DEPS + } + VmVersion::M6Initial | VmVersion::M6BugWithCompressionFixed => { + crate::vm_m6::vm_with_bootloader::MAX_NEW_FACTORY_DEPS + } + VmVersion::Vm1_3_2 => crate::vm_1_3_2::vm_with_bootloader::MAX_NEW_FACTORY_DEPS, + VmVersion::VmVirtualBlocks => crate::vm_virtual_blocks::constants::MAX_NEW_FACTORY_DEPS, + VmVersion::VmVirtualBlocksRefundsEnhancement => { + crate::vm_refunds_enhancement::constants::MAX_NEW_FACTORY_DEPS + } + VmVersion::VmBoojumIntegration => { + crate::vm_boojum_integration::constants::MAX_NEW_FACTORY_DEPS + } + VmVersion::Vm1_4_1 => crate::vm_1_4_1::constants::MAX_NEW_FACTORY_DEPS, + VmVersion::Vm1_4_2 => crate::vm_1_4_2::constants::MAX_NEW_FACTORY_DEPS, + version @ (VmVersion::Vm1_5_0SmallBootloaderMemory + | VmVersion::Vm1_5_0IncreasedBootloaderMemory + | VmVersion::VmGateway) => { + crate::vm_latest::constants::get_max_new_factory_deps(version.try_into().unwrap()) + } + } +} + /// Holds information about number of cycles used per circuit type. #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub(crate) struct CircuitCycleStatistic { diff --git a/core/lib/multivm/src/versions/testonly/gas_limit.rs b/core/lib/multivm/src/versions/testonly/gas_limit.rs index 789bfb97b217..08f143205e07 100644 --- a/core/lib/multivm/src/versions/testonly/gas_limit.rs +++ b/core/lib/multivm/src/versions/testonly/gas_limit.rs @@ -4,7 +4,10 @@ use zksync_types::{fee::Fee, Execute}; use super::{tester::VmTesterBuilder, TestedVm}; use crate::{ interface::TxExecutionMode, - vm_latest::constants::{TX_DESCRIPTION_OFFSET, TX_GAS_LIMIT_OFFSET}, + vm_latest::{ + constants::{get_tx_description_offset, TX_GAS_LIMIT_OFFSET}, + MultiVmSubversion, + }, }; /// Checks that `TX_GAS_LIMIT_OFFSET` constant is correct. @@ -29,6 +32,7 @@ pub(crate) fn test_tx_gas_limit_offset() { vm.vm.push_transaction(tx); - let slot = (TX_DESCRIPTION_OFFSET + TX_GAS_LIMIT_OFFSET) as u32; + let slot = + (get_tx_description_offset(MultiVmSubversion::latest()) + TX_GAS_LIMIT_OFFSET) as u32; vm.vm.verify_required_bootloader_heap(&[(slot, gas_limit)]); } diff --git a/core/lib/multivm/src/versions/testonly/l2_blocks.rs b/core/lib/multivm/src/versions/testonly/l2_blocks.rs index 0dfe600b73be..f8813231c9e1 100644 --- a/core/lib/multivm/src/versions/testonly/l2_blocks.rs +++ b/core/lib/multivm/src/versions/testonly/l2_blocks.rs @@ -21,8 +21,9 @@ use crate::{ TxExecutionMode, VmInterfaceExt, }, vm_latest::{ - constants::{TX_OPERATOR_L2_BLOCK_INFO_OFFSET, TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO}, + constants::{get_tx_operator_l2_block_info_offset, TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO}, utils::l2_blocks::get_l2_block_hash_key, + MultiVmSubversion, }, }; @@ -400,7 +401,8 @@ pub(crate) fn test_l2_block_first_in_batch() { fn set_manual_l2_block_info(vm: &mut impl TestedVm, tx_number: usize, block_info: L2BlockEnv) { let fictive_miniblock_position = - TX_OPERATOR_L2_BLOCK_INFO_OFFSET + TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO * tx_number; + get_tx_operator_l2_block_info_offset(MultiVmSubversion::latest()) + + TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO * tx_number; vm.write_to_bootloader_heap(&[ (fictive_miniblock_position, block_info.number.into()), (fictive_miniblock_position + 1, block_info.timestamp.into()), diff --git a/core/lib/multivm/src/versions/vm_1_3_2/vm_with_bootloader.rs b/core/lib/multivm/src/versions/vm_1_3_2/vm_with_bootloader.rs index 6515228903f6..eb2cbd058a7f 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/vm_with_bootloader.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/vm_with_bootloader.rs @@ -16,8 +16,7 @@ use zksync_contracts::BaseSystemContracts; use zksync_system_constants::MAX_L2_TX_GAS_LIMIT; use zksync_types::{ address_to_u256, bytecode::BytecodeHash, fee_model::L1PeggedBatchFeeModelInput, h256_to_u256, - l1::is_l1_tx_type, Address, Transaction, BOOTLOADER_ADDRESS, L1_GAS_PER_PUBDATA_BYTE, - MAX_NEW_FACTORY_DEPS, U256, + l1::is_l1_tx_type, Address, Transaction, BOOTLOADER_ADDRESS, L1_GAS_PER_PUBDATA_BYTE, U256, }; use crate::{ @@ -133,6 +132,8 @@ impl From for DerivedBlockContext { } } +pub(crate) const MAX_NEW_FACTORY_DEPS: usize = 32; + /// The size of the bootloader memory in bytes which is used by the protocol. /// While the maximal possible size is a lot higher, we restrict ourselves to a certain limit to reduce /// the requirements on RAM. diff --git a/core/lib/multivm/src/versions/vm_1_4_1/constants.rs b/core/lib/multivm/src/versions/vm_1_4_1/constants.rs index 9cf0a0b220ae..ec75f809ec48 100644 --- a/core/lib/multivm/src/versions/vm_1_4_1/constants.rs +++ b/core/lib/multivm/src/versions/vm_1_4_1/constants.rs @@ -2,7 +2,9 @@ use zk_evm_1_4_1::aux_structures::MemoryPage; pub use zk_evm_1_4_1::zkevm_opcode_defs::system_params::{ ERGS_PER_CIRCUIT, INITIAL_STORAGE_WRITE_PUBDATA_BYTES, MAX_PUBDATA_PER_BLOCK, }; -use zksync_system_constants::{MAX_L2_TX_GAS_LIMIT, MAX_NEW_FACTORY_DEPS}; +use zksync_system_constants::MAX_L2_TX_GAS_LIMIT; + +pub(crate) const MAX_NEW_FACTORY_DEPS: usize = 32; use crate::vm_1_4_1::old_vm::utils::heap_page_from_base; diff --git a/core/lib/multivm/src/versions/vm_1_4_2/constants.rs b/core/lib/multivm/src/versions/vm_1_4_2/constants.rs index 35266358a742..fad67596bd5a 100644 --- a/core/lib/multivm/src/versions/vm_1_4_2/constants.rs +++ b/core/lib/multivm/src/versions/vm_1_4_2/constants.rs @@ -3,7 +3,9 @@ use zk_evm_1_4_1::aux_structures::MemoryPage; pub use zk_evm_1_4_1::zkevm_opcode_defs::system_params::{ ERGS_PER_CIRCUIT, INITIAL_STORAGE_WRITE_PUBDATA_BYTES, }; -use zksync_system_constants::{MAX_L2_TX_GAS_LIMIT, MAX_NEW_FACTORY_DEPS}; +use zksync_system_constants::MAX_L2_TX_GAS_LIMIT; + +pub(crate) const MAX_NEW_FACTORY_DEPS: usize = 32; use crate::vm_1_4_2::old_vm::utils::heap_page_from_base; diff --git a/core/lib/multivm/src/versions/vm_boojum_integration/constants.rs b/core/lib/multivm/src/versions/vm_boojum_integration/constants.rs index 61749acd5e48..7b0442b7f972 100644 --- a/core/lib/multivm/src/versions/vm_boojum_integration/constants.rs +++ b/core/lib/multivm/src/versions/vm_boojum_integration/constants.rs @@ -2,10 +2,12 @@ use zk_evm_1_4_0::aux_structures::MemoryPage; pub use zk_evm_1_4_0::zkevm_opcode_defs::system_params::{ ERGS_PER_CIRCUIT, INITIAL_STORAGE_WRITE_PUBDATA_BYTES, MAX_PUBDATA_PER_BLOCK, }; -use zksync_system_constants::{L1_GAS_PER_PUBDATA_BYTE, MAX_L2_TX_GAS_LIMIT, MAX_NEW_FACTORY_DEPS}; +use zksync_system_constants::{L1_GAS_PER_PUBDATA_BYTE, MAX_L2_TX_GAS_LIMIT}; use crate::vm_boojum_integration::old_vm::utils::heap_page_from_base; +pub(crate) const MAX_NEW_FACTORY_DEPS: usize = 32; + /// The amount of ergs to be reserved at the end of the batch to ensure that it has enough ergs to verify compression, etc. pub(crate) const BOOTLOADER_BATCH_TIP_OVERHEAD: u32 = 80_000_000; diff --git a/core/lib/multivm/src/versions/vm_fast/bootloader_state/state.rs b/core/lib/multivm/src/versions/vm_fast/bootloader_state/state.rs index e104eba6ef4f..be4f2881297d 100644 --- a/core/lib/multivm/src/versions/vm_fast/bootloader_state/state.rs +++ b/core/lib/multivm/src/versions/vm_fast/bootloader_state/state.rs @@ -1,7 +1,7 @@ use std::cmp::Ordering; use once_cell::sync::OnceCell; -use zksync_types::{L2ChainId, ProtocolVersionId, U256}; +use zksync_types::{vm::VmVersion, L2ChainId, ProtocolVersionId, U256}; use super::{ l2_block::BootloaderL2Block, @@ -15,7 +15,10 @@ use crate::{ BootloaderMemory, CompressedBytecodeInfo, L2BlockEnv, TxExecutionMode, }, versions::vm_fast::transaction_data::TransactionData, - vm_latest::{constants::TX_DESCRIPTION_OFFSET, utils::l2_blocks::assert_next_block}, + vm_latest::{ + constants::get_tx_description_offset, utils::l2_blocks::assert_next_block, + MultiVmSubversion, + }, }; /// Intermediate bootloader-related VM state. @@ -47,6 +50,8 @@ pub struct BootloaderState { pubdata_information: OnceCell, /// Protocol version. protocol_version: ProtocolVersionId, + /// Protocol subversion + subversion: MultiVmSubversion, } impl BootloaderState { @@ -66,6 +71,7 @@ impl BootloaderState { free_tx_offset: 0, pubdata_information: Default::default(), protocol_version, + subversion: MultiVmSubversion::try_from(VmVersion::from(protocol_version)).unwrap(), } } @@ -129,6 +135,7 @@ impl BootloaderState { self.compressed_bytecodes_encoding, self.execution_mode, self.last_l2_block().txs.is_empty(), + self.subversion, ); self.compressed_bytecodes_encoding += compressed_bytecode_size; self.free_tx_offset = tx_offset + bootloader_tx.encoded_len(); @@ -178,13 +185,14 @@ impl BootloaderState { compressed_bytecodes_offset, self.execution_mode, num == 0, + self.subversion, ); offset += tx.encoded_len(); compressed_bytecodes_offset += compressed_bytecodes_size; tx_index += 1; } if l2_block.txs.is_empty() { - apply_l2_block(&mut initial_memory, l2_block, tx_index) + apply_l2_block(&mut initial_memory, l2_block, tx_index, self.subversion) } } @@ -198,6 +206,7 @@ impl BootloaderState { pubdata_builder, pubdata_information, self.protocol_version, + self.subversion, ); initial_memory } @@ -242,7 +251,7 @@ impl BootloaderState { /// Get offset of tx description pub(crate) fn get_tx_description_offset(&self, tx_index: usize) -> usize { - TX_DESCRIPTION_OFFSET + self.find_tx(tx_index).offset + get_tx_description_offset(self.subversion) + self.find_tx(tx_index).offset } pub(crate) fn insert_fictive_l2_block(&mut self) -> &BootloaderL2Block { diff --git a/core/lib/multivm/src/versions/vm_fast/bootloader_state/utils.rs b/core/lib/multivm/src/versions/vm_fast/bootloader_state/utils.rs index 9eb55d794235..8883fd33904b 100644 --- a/core/lib/multivm/src/versions/vm_fast/bootloader_state/utils.rs +++ b/core/lib/multivm/src/versions/vm_fast/bootloader_state/utils.rs @@ -7,12 +7,16 @@ use crate::{ BootloaderMemory, CompressedBytecodeInfo, TxExecutionMode, }, utils::bytecode, - vm_latest::constants::{ - BOOTLOADER_TX_DESCRIPTION_OFFSET, BOOTLOADER_TX_DESCRIPTION_SIZE, - COMPRESSED_BYTECODES_OFFSET, OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_OFFSET, - OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS, OPERATOR_REFUNDS_OFFSET, - TX_DESCRIPTION_OFFSET, TX_OPERATOR_L2_BLOCK_INFO_OFFSET, - TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO, TX_OVERHEAD_OFFSET, TX_TRUSTED_GAS_LIMIT_OFFSET, + vm_latest::{ + constants::{ + get_bootloader_tx_description_offset, get_compressed_bytecodes_offset, + get_operator_provided_l1_messenger_pubdata_offset, get_operator_refunds_offset, + get_tx_description_offset, get_tx_operator_l2_block_info_offset, + get_tx_overhead_offset, get_tx_trusted_gas_limit_offset, + BOOTLOADER_TX_DESCRIPTION_SIZE, OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS, + TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO, + }, + MultiVmSubversion, }, }; @@ -36,10 +40,11 @@ pub(super) fn apply_tx_to_memory( compressed_bytecodes_size: usize, execution_mode: TxExecutionMode, start_new_l2_block: bool, + subversion: MultiVmSubversion, ) -> usize { - let bootloader_description_offset = - BOOTLOADER_TX_DESCRIPTION_OFFSET + BOOTLOADER_TX_DESCRIPTION_SIZE * tx_index; - let tx_description_offset = TX_DESCRIPTION_OFFSET + tx_offset; + let bootloader_description_offset = get_bootloader_tx_description_offset(subversion) + + BOOTLOADER_TX_DESCRIPTION_SIZE * tx_index; + let tx_description_offset = get_tx_description_offset(subversion) + tx_offset; memory.push(( bootloader_description_offset, @@ -51,23 +56,30 @@ pub(super) fn apply_tx_to_memory( U256::from_big_endian(&(32 * tx_description_offset).to_be_bytes()), )); - let refund_offset = OPERATOR_REFUNDS_OFFSET + tx_index; + let refund_offset = get_operator_refunds_offset(subversion) + tx_index; memory.push((refund_offset, bootloader_tx.refund.into())); - let overhead_offset = TX_OVERHEAD_OFFSET + tx_index; + let overhead_offset = get_tx_overhead_offset(subversion) + tx_index; memory.push((overhead_offset, bootloader_tx.gas_overhead.into())); - let trusted_gas_limit_offset = TX_TRUSTED_GAS_LIMIT_OFFSET + tx_index; + let trusted_gas_limit_offset = get_tx_trusted_gas_limit_offset(subversion) + tx_index; memory.push((trusted_gas_limit_offset, bootloader_tx.trusted_gas_limit)); memory.extend( (tx_description_offset..tx_description_offset + bootloader_tx.encoded_len()) .zip(bootloader_tx.encoded.clone()), ); - apply_l2_block_inner(memory, bootloader_l2_block, tx_index, start_new_l2_block); + apply_l2_block_inner( + memory, + bootloader_l2_block, + tx_index, + start_new_l2_block, + subversion, + ); // Note, +1 is moving for pointer - let compressed_bytecodes_offset = COMPRESSED_BYTECODES_OFFSET + 1 + compressed_bytecodes_size; + let compressed_bytecodes_offset = + get_compressed_bytecodes_offset(subversion) + 1 + compressed_bytecodes_size; let encoded_compressed_bytecodes = get_memory_for_compressed_bytecodes(&bootloader_tx.compressed_bytecodes); @@ -85,8 +97,9 @@ pub(crate) fn apply_l2_block( memory: &mut BootloaderMemory, bootloader_l2_block: &BootloaderL2Block, txs_index: usize, + subversion: MultiVmSubversion, ) { - apply_l2_block_inner(memory, bootloader_l2_block, txs_index, true) + apply_l2_block_inner(memory, bootloader_l2_block, txs_index, true, subversion) } fn apply_l2_block_inner( @@ -94,13 +107,14 @@ fn apply_l2_block_inner( bootloader_l2_block: &BootloaderL2Block, txs_index: usize, start_new_l2_block: bool, + subversion: MultiVmSubversion, ) { // Since L2 block information start from the `TX_OPERATOR_L2_BLOCK_INFO_OFFSET` and each // L2 block info takes `TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO` slots, the position where the L2 block info // for this transaction needs to be written is: - let block_position = - TX_OPERATOR_L2_BLOCK_INFO_OFFSET + txs_index * TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO; + let block_position = get_tx_operator_l2_block_info_offset(subversion) + + txs_index * TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO; memory.extend(vec![ (block_position, bootloader_l2_block.number.into()), @@ -138,35 +152,47 @@ pub(crate) fn apply_pubdata_to_memory( pubdata_builder: &dyn PubdataBuilder, pubdata_information: &PubdataInput, protocol_version: ProtocolVersionId, + subversion: MultiVmSubversion, ) { - let (l1_messenger_pubdata_start_slot, pubdata) = if protocol_version.is_pre_gateway() { - // Skipping two slots as they will be filled by the bootloader itself: - // - One slot is for the selector of the call to the L1Messenger. - // - The other slot is for the 0x20 offset for the calldata. - let l1_messenger_pubdata_start_slot = OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_OFFSET + 2; - // Need to skip first word as it represents array offset - // while bootloader expects only [len || data] - let pubdata = ethabi::encode(&[ethabi::Token::Bytes( - pubdata_builder.l1_messenger_operator_input(pubdata_information, protocol_version), - )])[32..] - .to_vec(); - assert!( - pubdata.len() / 32 <= OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS - 2, - "The encoded pubdata is too big" - ); - (l1_messenger_pubdata_start_slot, pubdata) - } else { - // Skipping the first slot as it will be filled by the bootloader itself: - // It is for the selector of the call to the L1Messenger. - let l1_messenger_pubdata_start_slot = OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_OFFSET + 1; - let pubdata = - bootloader_memory_input(pubdata_builder, pubdata_information, protocol_version); - assert!( - // Note that unlike the previous version, the difference is `1`, since now it also includes the offset - pubdata.len() / 32 < OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS, - "The encoded pubdata is too big" - ); - (l1_messenger_pubdata_start_slot, pubdata) + let (l1_messenger_pubdata_start_slot, pubdata) = match subversion { + MultiVmSubversion::SmallBootloaderMemory | MultiVmSubversion::IncreasedBootloaderMemory => { + // Skipping two slots as they will be filled by the bootloader itself: + // - One slot is for the selector of the call to the L1Messenger. + // - The other slot is for the 0x20 offset for the calldata. + let l1_messenger_pubdata_start_slot = + get_operator_provided_l1_messenger_pubdata_offset(subversion) + 2; + + // Need to skip first word as it represents array offset + // while bootloader expects only [len || data] + let pubdata = ethabi::encode(&[ethabi::Token::Bytes( + pubdata_builder.l1_messenger_operator_input(pubdata_information, protocol_version), + )])[32..] + .to_vec(); + + assert!( + pubdata.len() / 32 <= OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS - 2, + "The encoded pubdata is too big" + ); + + (l1_messenger_pubdata_start_slot, pubdata) + } + MultiVmSubversion::Gateway => { + // Skipping the first slot as it will be filled by the bootloader itself: + // It is for the selector of the call to the L1Messenger. + let l1_messenger_pubdata_start_slot = + get_operator_provided_l1_messenger_pubdata_offset(subversion) + 1; + + let pubdata = + bootloader_memory_input(pubdata_builder, pubdata_information, protocol_version); + + assert!( + // Note that unlike the previous version, the difference is `1`, since now it also includes the offset + pubdata.len() / 32 < OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS, + "The encoded pubdata is too big" + ); + + (l1_messenger_pubdata_start_slot, pubdata) + } }; pubdata diff --git a/core/lib/multivm/src/versions/vm_fast/vm.rs b/core/lib/multivm/src/versions/vm_fast/vm.rs index e2310c254e96..6b14409a2e08 100644 --- a/core/lib/multivm/src/versions/vm_fast/vm.rs +++ b/core/lib/multivm/src/versions/vm_fast/vm.rs @@ -51,8 +51,9 @@ use crate::{ version::FastVmVersion, }, vm_latest::constants::{ - get_result_success_first_slot, get_vm_hook_params_start_position, get_vm_hook_position, - OPERATOR_REFUNDS_OFFSET, TX_GAS_LIMIT_OFFSET, VM_HOOK_PARAMS_COUNT, + get_operator_refunds_offset, get_result_success_first_slot, + get_vm_hook_params_start_position, get_vm_hook_position, TX_GAS_LIMIT_OFFSET, + VM_HOOK_PARAMS_COUNT, }, VmVersion, }; @@ -527,7 +528,7 @@ where pubdata_before = pubdata_after; let refund_value = refunds.operator_suggested_refund; self.write_to_bootloader_heap([( - OPERATOR_REFUNDS_OFFSET + current_tx_index, + get_operator_refunds_offset(self.vm_version.into()) + current_tx_index, refund_value.into(), )]); self.bootloader_state @@ -560,7 +561,7 @@ where let txs_index = self.bootloader_state.free_tx_index(); let l2_block = self.bootloader_state.insert_fictive_l2_block(); let mut memory = vec![]; - apply_l2_block(&mut memory, l2_block, txs_index); + apply_l2_block(&mut memory, l2_block, txs_index, self.vm_version.into()); self.write_to_bootloader_heap(memory); } Hook::PubdataRequested => { @@ -608,6 +609,7 @@ where pubdata_builder.expect("`pubdata_builder` is required to finish batch"), &pubdata_input, self.system_env.version, + self.vm_version.into(), ); self.write_to_bootloader_heap(memory_to_apply); } diff --git a/core/lib/multivm/src/versions/vm_latest/bootloader_state/state.rs b/core/lib/multivm/src/versions/vm_latest/bootloader_state/state.rs index 2085bbaba31f..8897ed2dc6e0 100644 --- a/core/lib/multivm/src/versions/vm_latest/bootloader_state/state.rs +++ b/core/lib/multivm/src/versions/vm_latest/bootloader_state/state.rs @@ -1,7 +1,7 @@ use std::cmp::Ordering; use once_cell::sync::OnceCell; -use zksync_types::{L2ChainId, ProtocolVersionId, U256}; +use zksync_types::{vm::VmVersion, L2ChainId, ProtocolVersionId, U256}; use zksync_vm_interface::pubdata::PubdataBuilder; use super::{tx::BootloaderTx, utils::apply_pubdata_to_memory}; @@ -16,9 +16,10 @@ use crate::{ snapshot::BootloaderStateSnapshot, utils::{apply_l2_block, apply_tx_to_memory}, }, - constants::TX_DESCRIPTION_OFFSET, + constants::get_tx_description_offset, types::internals::TransactionData, utils::l2_blocks::assert_next_block, + MultiVmSubversion, }, }; @@ -51,6 +52,8 @@ pub struct BootloaderState { pubdata_information: OnceCell, /// Protocol version. protocol_version: ProtocolVersionId, + /// Protocol subversion + subversion: MultiVmSubversion, } impl BootloaderState { @@ -70,6 +73,7 @@ impl BootloaderState { free_tx_offset: 0, pubdata_information: Default::default(), protocol_version, + subversion: MultiVmSubversion::try_from(VmVersion::from(protocol_version)).unwrap(), } } @@ -103,6 +107,10 @@ impl BootloaderState { .push(BootloaderL2Block::new(l2_block, self.free_tx_index())) } + pub(crate) fn get_vm_subversion(&self) -> MultiVmSubversion { + self.subversion + } + pub(crate) fn push_tx( &mut self, tx: TransactionData, @@ -133,6 +141,7 @@ impl BootloaderState { self.compressed_bytecodes_encoding, self.execution_mode, self.last_l2_block().txs.is_empty(), + self.subversion, ); self.compressed_bytecodes_encoding += compressed_bytecode_size; self.free_tx_offset = tx_offset + bootloader_tx.encoded_len(); @@ -183,13 +192,14 @@ impl BootloaderState { compressed_bytecodes_offset, self.execution_mode, num == 0, + self.subversion, ); offset += tx.encoded_len(); compressed_bytecodes_offset += compressed_bytecodes_size; tx_index += 1; } if l2_block.txs.is_empty() { - apply_l2_block(&mut initial_memory, l2_block, tx_index) + apply_l2_block(&mut initial_memory, l2_block, tx_index, self.subversion) } } @@ -203,6 +213,7 @@ impl BootloaderState { pubdata_builder, pubdata_information, self.protocol_version, + self.subversion, ); initial_memory } @@ -247,7 +258,7 @@ impl BootloaderState { /// Get offset of tx description pub(crate) fn get_tx_description_offset(&self, tx_index: usize) -> usize { - TX_DESCRIPTION_OFFSET + self.find_tx(tx_index).offset + get_tx_description_offset(self.subversion) + self.find_tx(tx_index).offset } pub(crate) fn insert_fictive_l2_block(&mut self) -> &BootloaderL2Block { diff --git a/core/lib/multivm/src/versions/vm_latest/bootloader_state/utils.rs b/core/lib/multivm/src/versions/vm_latest/bootloader_state/utils.rs index 58dc20346a6f..77a8ef3a1a71 100644 --- a/core/lib/multivm/src/versions/vm_latest/bootloader_state/utils.rs +++ b/core/lib/multivm/src/versions/vm_latest/bootloader_state/utils.rs @@ -10,12 +10,14 @@ use crate::{ vm_latest::{ bootloader_state::l2_block::BootloaderL2Block, constants::{ - BOOTLOADER_TX_DESCRIPTION_OFFSET, BOOTLOADER_TX_DESCRIPTION_SIZE, - COMPRESSED_BYTECODES_OFFSET, OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_OFFSET, - OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS, OPERATOR_REFUNDS_OFFSET, - TX_DESCRIPTION_OFFSET, TX_OPERATOR_L2_BLOCK_INFO_OFFSET, - TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO, TX_OVERHEAD_OFFSET, TX_TRUSTED_GAS_LIMIT_OFFSET, + get_bootloader_tx_description_offset, get_compressed_bytecodes_offset, + get_operator_provided_l1_messenger_pubdata_offset, get_operator_refunds_offset, + get_tx_description_offset, get_tx_operator_l2_block_info_offset, + get_tx_overhead_offset, get_tx_trusted_gas_limit_offset, + BOOTLOADER_TX_DESCRIPTION_SIZE, OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS, + TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO, }, + MultiVmSubversion, }, }; @@ -39,10 +41,11 @@ pub(super) fn apply_tx_to_memory( compressed_bytecodes_size: usize, execution_mode: TxExecutionMode, start_new_l2_block: bool, + subversion: MultiVmSubversion, ) -> usize { - let bootloader_description_offset = - BOOTLOADER_TX_DESCRIPTION_OFFSET + BOOTLOADER_TX_DESCRIPTION_SIZE * tx_index; - let tx_description_offset = TX_DESCRIPTION_OFFSET + tx_offset; + let bootloader_description_offset = get_bootloader_tx_description_offset(subversion) + + BOOTLOADER_TX_DESCRIPTION_SIZE * tx_index; + let tx_description_offset = get_tx_description_offset(subversion) + tx_offset; memory.push(( bootloader_description_offset, @@ -54,13 +57,13 @@ pub(super) fn apply_tx_to_memory( U256::from_big_endian(&(32 * tx_description_offset).to_be_bytes()), )); - let refund_offset = OPERATOR_REFUNDS_OFFSET + tx_index; + let refund_offset = get_operator_refunds_offset(subversion) + tx_index; memory.push((refund_offset, bootloader_tx.refund.into())); - let overhead_offset = TX_OVERHEAD_OFFSET + tx_index; + let overhead_offset = get_tx_overhead_offset(subversion) + tx_index; memory.push((overhead_offset, bootloader_tx.gas_overhead.into())); - let trusted_gas_limit_offset = TX_TRUSTED_GAS_LIMIT_OFFSET + tx_index; + let trusted_gas_limit_offset = get_tx_trusted_gas_limit_offset(subversion) + tx_index; memory.push((trusted_gas_limit_offset, bootloader_tx.trusted_gas_limit)); memory.extend( @@ -68,10 +71,17 @@ pub(super) fn apply_tx_to_memory( .zip(bootloader_tx.encoded.clone()), ); - apply_l2_block_inner(memory, bootloader_l2_block, tx_index, start_new_l2_block); + apply_l2_block_inner( + memory, + bootloader_l2_block, + tx_index, + start_new_l2_block, + subversion, + ); // Note, +1 is moving for pointer - let compressed_bytecodes_offset = COMPRESSED_BYTECODES_OFFSET + 1 + compressed_bytecodes_size; + let compressed_bytecodes_offset = + get_compressed_bytecodes_offset(subversion) + 1 + compressed_bytecodes_size; let encoded_compressed_bytecodes = get_memory_for_compressed_bytecodes(&bootloader_tx.compressed_bytecodes); @@ -89,8 +99,9 @@ pub(crate) fn apply_l2_block( memory: &mut BootloaderMemory, bootloader_l2_block: &BootloaderL2Block, txs_index: usize, + subversion: MultiVmSubversion, ) { - apply_l2_block_inner(memory, bootloader_l2_block, txs_index, true) + apply_l2_block_inner(memory, bootloader_l2_block, txs_index, true, subversion) } fn apply_l2_block_inner( @@ -98,13 +109,14 @@ fn apply_l2_block_inner( bootloader_l2_block: &BootloaderL2Block, txs_index: usize, start_new_l2_block: bool, + subversion: MultiVmSubversion, ) { // Since L2 block information start from the `TX_OPERATOR_L2_BLOCK_INFO_OFFSET` and each // L2 block info takes `TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO` slots, the position where the L2 block info // for this transaction needs to be written is: - let block_position = - TX_OPERATOR_L2_BLOCK_INFO_OFFSET + txs_index * TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO; + let block_position = get_tx_operator_l2_block_info_offset(subversion) + + txs_index * TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO; memory.extend(vec![ (block_position, bootloader_l2_block.number.into()), @@ -143,41 +155,47 @@ pub(crate) fn apply_pubdata_to_memory( pubdata_builder: &dyn PubdataBuilder, pubdata_information: &PubdataInput, protocol_version: ProtocolVersionId, + subversion: MultiVmSubversion, ) { - let (l1_messenger_pubdata_start_slot, pubdata) = if protocol_version.is_pre_gateway() { - // Skipping two slots as they will be filled by the bootloader itself: - // - One slot is for the selector of the call to the L1Messenger. - // - The other slot is for the 0x20 offset for the calldata. - let l1_messenger_pubdata_start_slot = OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_OFFSET + 2; - - // Need to skip first word as it represents array offset - // while bootloader expects only [len || data] - let pubdata = ethabi::encode(&[ethabi::Token::Bytes( - pubdata_builder.l1_messenger_operator_input(pubdata_information, protocol_version), - )])[32..] - .to_vec(); - - assert!( - pubdata.len() / 32 <= OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS - 2, - "The encoded pubdata is too big" - ); - - (l1_messenger_pubdata_start_slot, pubdata) - } else { - // Skipping the first slot as it will be filled by the bootloader itself: - // It is for the selector of the call to the L1Messenger. - let l1_messenger_pubdata_start_slot = OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_OFFSET + 1; - - let pubdata = - bootloader_memory_input(pubdata_builder, pubdata_information, protocol_version); - - assert!( - // Note that unlike the previous version, the difference is `1`, since now it also includes the offset - pubdata.len() / 32 < OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS, - "The encoded pubdata is too big" - ); - - (l1_messenger_pubdata_start_slot, pubdata) + let (l1_messenger_pubdata_start_slot, pubdata) = match subversion { + MultiVmSubversion::SmallBootloaderMemory | MultiVmSubversion::IncreasedBootloaderMemory => { + // Skipping two slots as they will be filled by the bootloader itself: + // - One slot is for the selector of the call to the L1Messenger. + // - The other slot is for the 0x20 offset for the calldata. + let l1_messenger_pubdata_start_slot = + get_operator_provided_l1_messenger_pubdata_offset(subversion) + 2; + + // Need to skip first word as it represents array offset + // while bootloader expects only [len || data] + let pubdata = ethabi::encode(&[ethabi::Token::Bytes( + pubdata_builder.l1_messenger_operator_input(pubdata_information, protocol_version), + )])[32..] + .to_vec(); + + assert!( + pubdata.len() / 32 <= OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS - 2, + "The encoded pubdata is too big" + ); + + (l1_messenger_pubdata_start_slot, pubdata) + } + MultiVmSubversion::Gateway => { + // Skipping the first slot as it will be filled by the bootloader itself: + // It is for the selector of the call to the L1Messenger. + let l1_messenger_pubdata_start_slot = + get_operator_provided_l1_messenger_pubdata_offset(subversion) + 1; + + let pubdata = + bootloader_memory_input(pubdata_builder, pubdata_information, protocol_version); + + assert!( + // Note that unlike the previous version, the difference is `1`, since now it also includes the offset + pubdata.len() / 32 < OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS, + "The encoded pubdata is too big" + ); + + (l1_messenger_pubdata_start_slot, pubdata) + } }; pubdata diff --git a/core/lib/multivm/src/versions/vm_latest/constants.rs b/core/lib/multivm/src/versions/vm_latest/constants.rs index 1578965d7a66..f454ced06227 100644 --- a/core/lib/multivm/src/versions/vm_latest/constants.rs +++ b/core/lib/multivm/src/versions/vm_latest/constants.rs @@ -3,10 +3,8 @@ use zk_evm_1_5_0::aux_structures::MemoryPage; pub use zk_evm_1_5_0::zkevm_opcode_defs::system_params::{ ERGS_PER_CIRCUIT, INITIAL_STORAGE_WRITE_PUBDATA_BYTES, }; -use zksync_system_constants::MAX_NEW_FACTORY_DEPS; -use super::vm::MultiVmSubversion; -use crate::vm_latest::old_vm::utils::heap_page_from_base; +use crate::vm_latest::{old_vm::utils::heap_page_from_base, MultiVmSubversion}; /// The amount of ergs to be reserved at the end of the batch to ensure that it has enough ergs to verify compression, etc. pub(crate) const BOOTLOADER_BATCH_TIP_OVERHEAD: u32 = 400_000_000; @@ -61,35 +59,72 @@ pub(crate) const MAX_POSTOP_SLOTS: usize = PAYMASTER_CONTEXT_SLOTS + 7; /// to be used for signing the transaction's content. const CURRENT_L2_TX_HASHES_SLOTS: usize = 2; +pub(crate) const fn get_max_new_factory_deps(subversion: MultiVmSubversion) -> usize { + match subversion { + MultiVmSubversion::SmallBootloaderMemory | MultiVmSubversion::IncreasedBootloaderMemory => { + 32 + } + // With gateway upgrade we increased max number of factory dependencies + MultiVmSubversion::Gateway => 64, + } +} + /// Slots used to store the calldata for the KnownCodesStorage to mark new factory /// dependencies as known ones. Besides the slots for the new factory dependencies themselves /// another 4 slots are needed for: selector, marker of whether the user should pay for the pubdata, /// the offset for the encoding of the array as well as the length of the array. -const NEW_FACTORY_DEPS_RESERVED_SLOTS: usize = MAX_NEW_FACTORY_DEPS + 4; +pub(crate) const fn get_new_factory_deps_reserved_slots(subversion: MultiVmSubversion) -> usize { + get_max_new_factory_deps(subversion) + 4 +} /// The operator can provide for each transaction the proposed minimal refund pub(crate) const OPERATOR_REFUNDS_SLOTS: usize = MAX_TXS_IN_BATCH; -pub(crate) const OPERATOR_REFUNDS_OFFSET: usize = DEBUG_SLOTS_OFFSET - + DEBUG_FIRST_SLOTS - + PAYMASTER_CONTEXT_SLOTS - + CURRENT_L2_TX_HASHES_SLOTS - + NEW_FACTORY_DEPS_RESERVED_SLOTS; +pub(crate) const fn get_operator_refunds_offset(subversion: MultiVmSubversion) -> usize { + DEBUG_SLOTS_OFFSET + + DEBUG_FIRST_SLOTS + + PAYMASTER_CONTEXT_SLOTS + + CURRENT_L2_TX_HASHES_SLOTS + + get_new_factory_deps_reserved_slots(subversion) +} + +pub(crate) const fn get_tx_overhead_offset(subversion: MultiVmSubversion) -> usize { + get_operator_refunds_offset(subversion) + OPERATOR_REFUNDS_SLOTS +} -pub(crate) const TX_OVERHEAD_OFFSET: usize = OPERATOR_REFUNDS_OFFSET + OPERATOR_REFUNDS_SLOTS; pub(crate) const TX_OVERHEAD_SLOTS: usize = MAX_TXS_IN_BATCH; -pub(crate) const TX_TRUSTED_GAS_LIMIT_OFFSET: usize = TX_OVERHEAD_OFFSET + TX_OVERHEAD_SLOTS; +pub(crate) const fn get_tx_trusted_gas_limit_offset(subversion: MultiVmSubversion) -> usize { + get_tx_overhead_offset(subversion) + TX_OVERHEAD_SLOTS +} + pub(crate) const TX_TRUSTED_GAS_LIMIT_SLOTS: usize = MAX_TXS_IN_BATCH; +pub(crate) const fn get_tx_operator_l2_block_info_offset(subversion: MultiVmSubversion) -> usize { + get_tx_trusted_gas_limit_offset(subversion) + TX_TRUSTED_GAS_LIMIT_SLOTS +} + +pub(crate) const TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO: usize = 4; +pub(crate) const TX_OPERATOR_L2_BLOCK_INFO_SLOTS: usize = + (MAX_TXS_IN_BATCH + 1) * TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO; + +pub(crate) const fn get_compressed_bytecodes_offset(subversion: MultiVmSubversion) -> usize { + get_tx_operator_l2_block_info_offset(subversion) + TX_OPERATOR_L2_BLOCK_INFO_SLOTS +} + pub(crate) const COMPRESSED_BYTECODES_SLOTS: usize = 196608; -pub(crate) const PRIORITY_TXS_L1_DATA_OFFSET: usize = - COMPRESSED_BYTECODES_OFFSET + COMPRESSED_BYTECODES_SLOTS; +pub(crate) const fn get_priority_txs_l1_data_offset(subversion: MultiVmSubversion) -> usize { + get_compressed_bytecodes_offset(subversion) + COMPRESSED_BYTECODES_SLOTS +} + pub(crate) const PRIORITY_TXS_L1_DATA_SLOTS: usize = 2; -pub const OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_OFFSET: usize = - PRIORITY_TXS_L1_DATA_OFFSET + PRIORITY_TXS_L1_DATA_SLOTS; +pub(crate) const fn get_operator_provided_l1_messenger_pubdata_offset( + subversion: MultiVmSubversion, +) -> usize { + get_priority_txs_l1_data_offset(subversion) + PRIORITY_TXS_L1_DATA_SLOTS +} /// One of "worst case" scenarios for the number of state diffs in a batch is when 780kb of pubdata is spent /// on repeated writes, that are all zeroed out. In this case, the number of diffs is `780kb / 5 = 156k`. This means that they will have @@ -101,12 +136,16 @@ pub const OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_OFFSET: usize = /// operator to ensure that it can form the correct calldata for the L1Messenger. pub(crate) const OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS: usize = 1360000; -pub(crate) const BOOTLOADER_TX_DESCRIPTION_OFFSET: usize = - OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_OFFSET + OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS; +pub(crate) const fn get_bootloader_tx_description_offset(subversion: MultiVmSubversion) -> usize { + get_operator_provided_l1_messenger_pubdata_offset(subversion) + + OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS +} /// The size of the bootloader memory dedicated to the encodings of transactions pub(crate) const fn get_bootloader_tx_encoding_space(subversion: MultiVmSubversion) -> u32 { - (get_used_bootloader_memory_words(subversion) - TX_DESCRIPTION_OFFSET - MAX_TXS_IN_BATCH) as u32 + (get_used_bootloader_memory_words(subversion) + - get_tx_description_offset(subversion) + - MAX_TXS_IN_BATCH) as u32 } // Size of the bootloader tx description in words @@ -114,9 +153,11 @@ pub(crate) const BOOTLOADER_TX_DESCRIPTION_SIZE: usize = 2; /// The actual descriptions of transactions should start after the minor descriptions and a MAX_POSTOP_SLOTS /// free slots to allow postOp encoding. -pub(crate) const TX_DESCRIPTION_OFFSET: usize = BOOTLOADER_TX_DESCRIPTION_OFFSET - + BOOTLOADER_TX_DESCRIPTION_SIZE * MAX_TXS_IN_BATCH - + MAX_POSTOP_SLOTS; +pub(crate) const fn get_tx_description_offset(subversion: MultiVmSubversion) -> usize { + get_bootloader_tx_description_offset(subversion) + + BOOTLOADER_TX_DESCRIPTION_SIZE * MAX_TXS_IN_BATCH + + MAX_POSTOP_SLOTS +} pub(crate) const TX_GAS_LIMIT_OFFSET: usize = 4; @@ -166,16 +207,6 @@ pub const ETH_CALL_GAS_LIMIT: u64 = BATCH_GAS_LIMIT; /// ID of the transaction from L1 pub const L1_TX_TYPE: u8 = 255; -pub(crate) const TX_OPERATOR_L2_BLOCK_INFO_OFFSET: usize = - TX_TRUSTED_GAS_LIMIT_OFFSET + TX_TRUSTED_GAS_LIMIT_SLOTS; - -pub(crate) const TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO: usize = 4; -pub(crate) const TX_OPERATOR_L2_BLOCK_INFO_SLOTS: usize = - (MAX_TXS_IN_BATCH + 1) * TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO; - -pub(crate) const COMPRESSED_BYTECODES_OFFSET: usize = - TX_OPERATOR_L2_BLOCK_INFO_OFFSET + TX_OPERATOR_L2_BLOCK_INFO_SLOTS; - /// The maximal gas limit that gets passed as compute for an L2 transaction. This is also the maximal limit allowed /// for L1->L2 transactions. /// diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs b/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs index 8755b98ddb8c..08ff79524b87 100755 --- a/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs @@ -116,10 +116,11 @@ impl DefaultExecutionTracer { bootloader_state: &mut BootloaderState, ) { let current_timestamp = Timestamp(state.local_state.timestamp); + let subversion = bootloader_state.get_vm_subversion(); let txs_index = bootloader_state.free_tx_index(); let l2_block = bootloader_state.insert_fictive_l2_block(); let mut memory = vec![]; - apply_l2_block(&mut memory, l2_block, txs_index); + apply_l2_block(&mut memory, l2_block, txs_index, subversion); state.memory.populate_page( BOOTLOADER_HEAP_PAGE as usize, memory, diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/pubdata_tracer.rs b/core/lib/multivm/src/versions/vm_latest/tracers/pubdata_tracer.rs index 7c55dd38561d..31309e6ff062 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/pubdata_tracer.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/pubdata_tracer.rs @@ -242,6 +242,7 @@ impl VmTracer for PubdataTracer { .as_ref(), &pubdata_input, bootloader_state.protocol_version(), + self.subversion, ); // Save the pubdata for the future initial bootloader memory building diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/refunds.rs b/core/lib/multivm/src/versions/vm_latest/tracers/refunds.rs index 6ef251c2db98..2bd08e094327 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/refunds.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/refunds.rs @@ -16,7 +16,7 @@ use crate::{ tracers::dynamic::vm_1_5_0::DynTracer, vm_latest::{ bootloader_state::BootloaderState, - constants::{BOOTLOADER_HEAP_PAGE, OPERATOR_REFUNDS_OFFSET, TX_GAS_LIMIT_OFFSET}, + constants::{get_operator_refunds_offset, BOOTLOADER_HEAP_PAGE, TX_GAS_LIMIT_OFFSET}, old_vm::{history_recorder::HistoryMode, memory::SimpleMemory}, tracers::{ traits::VmTracer, @@ -272,7 +272,8 @@ impl VmTracer for RefundsTracer { let refund_to_propose = tx_body_refund + self.block_overhead_refund(); - let refund_slot = OPERATOR_REFUNDS_OFFSET + current_tx_index; + let refund_slot = get_operator_refunds_offset(bootloader_state.get_vm_subversion()) + + current_tx_index; // Writing the refund into memory state.memory.populate_page( diff --git a/core/lib/multivm/src/versions/vm_m5/vm_with_bootloader.rs b/core/lib/multivm/src/versions/vm_m5/vm_with_bootloader.rs index c137dcac0bd1..c74ddcb25e5f 100644 --- a/core/lib/multivm/src/versions/vm_m5/vm_with_bootloader.rs +++ b/core/lib/multivm/src/versions/vm_m5/vm_with_bootloader.rs @@ -15,7 +15,7 @@ use zksync_contracts::BaseSystemContracts; use zksync_system_constants::MAX_L2_TX_GAS_LIMIT; use zksync_types::{ address_to_u256, bytecode::BytecodeHash, fee_model::L1PeggedBatchFeeModelInput, h256_to_u256, - Address, Transaction, BOOTLOADER_ADDRESS, L1_GAS_PER_PUBDATA_BYTE, MAX_NEW_FACTORY_DEPS, U256, + Address, Transaction, BOOTLOADER_ADDRESS, L1_GAS_PER_PUBDATA_BYTE, U256, }; use crate::{ @@ -155,6 +155,8 @@ pub const MAX_POSTOP_SLOTS: usize = PAYMASTER_CONTEXT_SLOTS + 7; // to be used for signing the transaction's content. const CURRENT_L2_TX_HASHES_SLOTS: usize = 2; +pub(crate) const MAX_NEW_FACTORY_DEPS: usize = 32; + // Slots used to store the calldata for the KnownCodesStorage to mark new factory // dependencies as known ones. Besides the slots for the new factory dependencies themselves // another 4 slots are needed for: selector, marker of whether the user should pay for the pubdata, diff --git a/core/lib/multivm/src/versions/vm_m6/vm_with_bootloader.rs b/core/lib/multivm/src/versions/vm_m6/vm_with_bootloader.rs index 4b2eb8212e61..248e0fcf0401 100644 --- a/core/lib/multivm/src/versions/vm_m6/vm_with_bootloader.rs +++ b/core/lib/multivm/src/versions/vm_m6/vm_with_bootloader.rs @@ -15,7 +15,7 @@ use zksync_contracts::BaseSystemContracts; use zksync_system_constants::MAX_L2_TX_GAS_LIMIT; use zksync_types::{ address_to_u256, bytecode::BytecodeHash, fee_model::L1PeggedBatchFeeModelInput, h256_to_u256, - Address, Transaction, BOOTLOADER_ADDRESS, L1_GAS_PER_PUBDATA_BYTE, MAX_NEW_FACTORY_DEPS, U256, + Address, Transaction, BOOTLOADER_ADDRESS, L1_GAS_PER_PUBDATA_BYTE, U256, }; use crate::{ @@ -34,6 +34,8 @@ use crate::{ }, }; +pub(crate) const MAX_NEW_FACTORY_DEPS: usize = 32; + // TODO (SMA-1703): move these to config and make them programmatically generable. // fill these values in the similar fashion as other overhead-related constants pub const BLOCK_OVERHEAD_GAS: u32 = 1200000; diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/constants.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/constants.rs index 27ce59f5709b..90faf03a2d14 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/constants.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/constants.rs @@ -2,10 +2,12 @@ use zk_evm_1_3_3::aux_structures::MemoryPage; pub use zk_evm_1_3_3::zkevm_opcode_defs::system_params::{ ERGS_PER_CIRCUIT, INITIAL_STORAGE_WRITE_PUBDATA_BYTES, MAX_PUBDATA_PER_BLOCK, }; -use zksync_system_constants::{L1_GAS_PER_PUBDATA_BYTE, MAX_L2_TX_GAS_LIMIT, MAX_NEW_FACTORY_DEPS}; +use zksync_system_constants::{L1_GAS_PER_PUBDATA_BYTE, MAX_L2_TX_GAS_LIMIT}; use crate::vm_refunds_enhancement::old_vm::utils::heap_page_from_base; +pub(crate) const MAX_NEW_FACTORY_DEPS: usize = 32; + /// The size of the bootloader memory in bytes which is used by the protocol. /// While the maximal possible size is a lot higher, we restrict ourselves to a certain limit to reduce /// the requirements on RAM. diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/constants.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/constants.rs index 69b713e2e9f1..c695f67ea300 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/constants.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/constants.rs @@ -2,7 +2,7 @@ use zk_evm_1_3_3::aux_structures::MemoryPage; pub use zk_evm_1_3_3::zkevm_opcode_defs::system_params::{ ERGS_PER_CIRCUIT, INITIAL_STORAGE_WRITE_PUBDATA_BYTES, MAX_PUBDATA_PER_BLOCK, }; -use zksync_system_constants::{L1_GAS_PER_PUBDATA_BYTE, MAX_L2_TX_GAS_LIMIT, MAX_NEW_FACTORY_DEPS}; +use zksync_system_constants::{L1_GAS_PER_PUBDATA_BYTE, MAX_L2_TX_GAS_LIMIT}; use crate::vm_virtual_blocks::old_vm::utils::heap_page_from_base; @@ -42,6 +42,8 @@ pub(crate) const MAX_POSTOP_SLOTS: usize = PAYMASTER_CONTEXT_SLOTS + 7; /// to be used for signing the transaction's content. const CURRENT_L2_TX_HASHES_SLOTS: usize = 2; +pub(crate) const MAX_NEW_FACTORY_DEPS: usize = 32; + /// Slots used to store the calldata for the KnownCodesStorage to mark new factory /// dependencies as known ones. Besides the slots for the new factory dependencies themselves /// another 4 slots are needed for: selector, marker of whether the user should pay for the pubdata, diff --git a/core/lib/protobuf_config/src/contracts.rs b/core/lib/protobuf_config/src/contracts.rs index 660246928edb..12cbf996697b 100644 --- a/core/lib/protobuf_config/src/contracts.rs +++ b/core/lib/protobuf_config/src/contracts.rs @@ -2,7 +2,7 @@ use anyhow::Context as _; use zksync_config::configs::{ContractsConfig, EcosystemContracts}; use zksync_protobuf::{repr::ProtoRepr, required}; -use crate::{parse_h160, proto::contracts as proto}; +use crate::{parse_h160, parse_h256, proto::contracts as proto}; impl ProtoRepr for proto::Contracts { type Type = ContractsConfig; @@ -30,6 +30,10 @@ impl ProtoRepr for proto::Contracts { ) .and_then(|x| parse_h160(x)) .context("transparent_proxy_admin_addr")?, + l1_bytecodes_supplier_addr: ecosystem_contracts + .l1_bytecodes_supplier_addr + .as_ref() + .map(|x| parse_h160(x).expect("Invalid address")), }) } else { None @@ -113,6 +117,18 @@ impl ProtoRepr for proto::Contracts { .map(|x| parse_h160(x)) .transpose() .context("base_token_addr")?, + l1_base_token_asset_id: l1 + .base_token_asset_id + .as_ref() + .map(|x| parse_h256(x)) + .transpose() + .context("base_token_asset_id")?, + l2_predeployed_wrapped_base_token_address: l2 + .predeployed_wrapped_base_token_address + .as_ref() + .map(|x| parse_h160(x)) + .transpose() + .context("l2 predeployed_wrapped_base_token_address")?, chain_admin_addr: l1 .chain_admin_addr .as_ref() @@ -145,6 +161,9 @@ impl ProtoRepr for proto::Contracts { "{:?}", ecosystem_contracts.transparent_proxy_admin_addr, )), + l1_bytecodes_supplier_addr: ecosystem_contracts + .l1_bytecodes_supplier_addr + .map(|x| format!("{:?}", x)), }); Self { ecosystem_contracts, @@ -156,6 +175,7 @@ impl ProtoRepr for proto::Contracts { default_upgrade_addr: Some(format!("{:?}", this.default_upgrade_addr)), multicall3_addr: Some(format!("{:?}", this.l1_multicall3_addr)), base_token_addr: this.base_token_addr.map(|a| format!("{:?}", a)), + base_token_asset_id: this.l1_base_token_asset_id.map(|x| format!("{:?}", x)), chain_admin_addr: this.chain_admin_addr.map(|a| format!("{:?}", a)), }), l2: Some(proto::L2 { @@ -164,6 +184,9 @@ impl ProtoRepr for proto::Contracts { legacy_shared_bridge_addr: this .l2_legacy_shared_bridge_addr .map(|a| format!("{:?}", a)), + predeployed_wrapped_base_token_address: this + .l2_predeployed_wrapped_base_token_address + .map(|x| format!("{:?}", x)), timestamp_asserter_addr: this .l2_timestamp_asserter_addr .map(|a| format!("{:?}", a)), diff --git a/core/lib/protobuf_config/src/en.rs b/core/lib/protobuf_config/src/en.rs index 700a1f0a8104..6c46222a080d 100644 --- a/core/lib/protobuf_config/src/en.rs +++ b/core/lib/protobuf_config/src/en.rs @@ -4,7 +4,7 @@ use std::{ }; use anyhow::Context; -use zksync_basic_types::{url::SensitiveUrl, L1ChainId, L2ChainId}; +use zksync_basic_types::{url::SensitiveUrl, L1ChainId, L2ChainId, SLChainId}; use zksync_config::configs::en_config::ENConfig; use zksync_protobuf::{required, ProtoRepr}; @@ -21,7 +21,6 @@ impl ProtoRepr for proto::ExternalNode { l1_chain_id: required(&self.l1_chain_id) .map(|x| L1ChainId(*x)) .context("l1_chain_id")?, - sl_chain_id: None, l2_chain_id: required(&self.l2_chain_id) .and_then(|x| L2ChainId::try_from(*x).map_err(|a| anyhow::anyhow!(a))) .context("l2_chain_id")?, @@ -37,6 +36,7 @@ impl ProtoRepr for proto::ExternalNode { bridge_addresses_refresh_interval_sec: self .bridge_addresses_refresh_interval_sec .and_then(NonZeroU64::new), + gateway_chain_id: self.gateway_chain_id.map(SLChainId), }) } @@ -55,6 +55,7 @@ impl ProtoRepr for proto::ExternalNode { bridge_addresses_refresh_interval_sec: this .bridge_addresses_refresh_interval_sec .map(|a| a.get()), + gateway_chain_id: this.gateway_chain_id.map(|c| c.0), } } } diff --git a/core/lib/protobuf_config/src/eth.rs b/core/lib/protobuf_config/src/eth.rs index 10e80810be57..a50b452ccd16 100644 --- a/core/lib/protobuf_config/src/eth.rs +++ b/core/lib/protobuf_config/src/eth.rs @@ -1,7 +1,7 @@ use anyhow::Context as _; use zksync_config::configs::{self}; use zksync_protobuf::{required, ProtoRepr}; -use zksync_types::pubdata_da::PubdataSendingMode; +use zksync_types::{pubdata_da::PubdataSendingMode, settlement::SettlementMode}; use crate::{proto::eth as proto, read_optional_repr}; @@ -45,6 +45,24 @@ impl proto::PubdataSendingMode { } } +impl proto::SettlementMode { + fn new(x: &SettlementMode) -> Self { + use SettlementMode as From; + match x { + From::SettlesToL1 => Self::SettlesToL1, + From::Gateway => Self::Gateway, + } + } + + fn parse(&self) -> SettlementMode { + use SettlementMode as To; + match self { + Self::SettlesToL1 => To::SettlesToL1, + Self::Gateway => To::Gateway, + } + } +} + impl ProtoRepr for proto::Eth { type Type = configs::eth_sender::EthConfig; @@ -107,6 +125,7 @@ impl ProtoRepr for proto::Sender { .parse(), tx_aggregation_only_prove_and_execute: self.tx_aggregation_paused.unwrap_or(false), tx_aggregation_paused: self.tx_aggregation_only_prove_and_execute.unwrap_or(false), + priority_tree_start_index: self.priority_op_start_index.map(|x| x as usize), time_in_mempool_in_l1_blocks_cap: self .time_in_mempool_in_l1_blocks_cap .unwrap_or(Self::Type::default_time_in_mempool_in_l1_blocks_cap()), @@ -137,6 +156,7 @@ impl ProtoRepr for proto::Sender { ), tx_aggregation_only_prove_and_execute: Some(this.tx_aggregation_only_prove_and_execute), tx_aggregation_paused: Some(this.tx_aggregation_paused), + priority_op_start_index: this.priority_tree_start_index.map(|x| x as u64), time_in_mempool_in_l1_blocks_cap: Some(this.time_in_mempool_in_l1_blocks_cap), } } @@ -171,8 +191,12 @@ impl ProtoRepr for proto::GasAdjuster { ) .context("internal_pubdata_pricing_multiplier")?, max_blob_base_fee: self.max_blob_base_fee, - // TODO(EVM-676): support this field - settlement_mode: Default::default(), + settlement_mode: self + .settlement_mode + .map(proto::SettlementMode::try_from) + .transpose()? + .map(|x| x.parse()) + .unwrap_or_default(), }) } @@ -194,6 +218,7 @@ impl ProtoRepr for proto::GasAdjuster { ), internal_pubdata_pricing_multiplier: Some(this.internal_pubdata_pricing_multiplier), max_blob_base_fee: this.max_blob_base_fee, + settlement_mode: Some(proto::SettlementMode::new(&this.settlement_mode).into()), } } } diff --git a/core/lib/protobuf_config/src/gateway.rs b/core/lib/protobuf_config/src/gateway.rs new file mode 100644 index 000000000000..099baf251ac5 --- /dev/null +++ b/core/lib/protobuf_config/src/gateway.rs @@ -0,0 +1,55 @@ +use anyhow::Context as _; +use zksync_basic_types::SLChainId; +use zksync_config::configs::gateway::GatewayChainConfig; +use zksync_protobuf::{repr::ProtoRepr, required}; + +use crate::{parse_h160, proto::gateway as proto}; + +impl ProtoRepr for proto::GatewayChainConfig { + type Type = GatewayChainConfig; + + fn read(&self) -> anyhow::Result { + Ok(Self::Type { + state_transition_proxy_addr: required(&self.state_transition_proxy_addr) + .and_then(|x| parse_h160(x)) + .context("state_transition_proxy_addr")?, + + validator_timelock_addr: required(&self.validator_timelock_addr) + .and_then(|x| parse_h160(x)) + .context("validator_timelock_addr")?, + + multicall3_addr: required(&self.multicall3_addr) + .and_then(|x| parse_h160(x)) + .context("multicall3_addr")?, + + diamond_proxy_addr: required(&self.diamond_proxy_addr) + .and_then(|x| parse_h160(x)) + .context("diamond_proxy_addr")?, + + chain_admin_addr: self + .chain_admin_addr + .as_ref() + .map(|x| parse_h160(x)) + .transpose()?, + + governance_addr: required(&self.governance_addr) + .and_then(|x| parse_h160(x)) + .context("governance_addr")?, + gateway_chain_id: required(&self.gateway_chain_id) + .map(|x| SLChainId(*x)) + .context("gateway_chain_id")?, + }) + } + + fn build(this: &Self::Type) -> Self { + Self { + state_transition_proxy_addr: Some(format!("{:?}", this.state_transition_proxy_addr)), + validator_timelock_addr: Some(format!("{:?}", this.validator_timelock_addr)), + multicall3_addr: Some(format!("{:?}", this.multicall3_addr)), + diamond_proxy_addr: Some(format!("{:?}", this.diamond_proxy_addr)), + chain_admin_addr: this.chain_admin_addr.map(|x| format!("{:?}", x)), + governance_addr: Some(format!("{:?}", this.governance_addr)), + gateway_chain_id: Some(this.gateway_chain_id.0), + } + } +} diff --git a/core/lib/protobuf_config/src/genesis.rs b/core/lib/protobuf_config/src/genesis.rs index 469140f7b0c2..1d0ddf952c90 100644 --- a/core/lib/protobuf_config/src/genesis.rs +++ b/core/lib/protobuf_config/src/genesis.rs @@ -84,7 +84,6 @@ impl ProtoRepr for proto::Genesis { l1_chain_id: required(&self.l1_chain_id) .map(|x| L1ChainId(*x)) .context("l1_chain_id")?, - sl_chain_id: None, l2_chain_id: required(&self.l2_chain_id) .and_then(|x| L2ChainId::try_from(*x).map_err(|a| anyhow::anyhow!(a))) .context("l2_chain_id")?, diff --git a/core/lib/protobuf_config/src/lib.rs b/core/lib/protobuf_config/src/lib.rs index 90c0ba071c0b..c4b5e944e901 100644 --- a/core/lib/protobuf_config/src/lib.rs +++ b/core/lib/protobuf_config/src/lib.rs @@ -20,6 +20,7 @@ mod eth; mod experimental; mod external_price_api_client; mod external_proof_integration_api; +mod gateway; mod general; mod genesis; mod house_keeper; diff --git a/core/lib/protobuf_config/src/proto/config/contracts.proto b/core/lib/protobuf_config/src/proto/config/contracts.proto index 4ae0ee1614f8..febbc981478b 100644 --- a/core/lib/protobuf_config/src/proto/config/contracts.proto +++ b/core/lib/protobuf_config/src/proto/config/contracts.proto @@ -6,6 +6,7 @@ message EcosystemContracts { optional string bridgehub_proxy_addr = 1; // optional; h160 optional string state_transition_proxy_addr = 2; // optional; h160 optional string transparent_proxy_admin_addr = 3; // optional; h160 + optional string l1_bytecodes_supplier_addr = 4; // optional; h160 } message L1 { @@ -17,6 +18,7 @@ message L1 { optional string multicall3_addr = 6; // required; H160 optional string base_token_addr = 7; // required; H160 optional string chain_admin_addr = 8; // required; H160 + optional string base_token_asset_id = 9; // required; H256 } message L2 { @@ -24,6 +26,7 @@ message L2 { optional string da_validator_addr = 2; // optional; H160 optional string legacy_shared_bridge_addr = 3; // optional; H160 optional string timestamp_asserter_addr = 4; // optional; H160 + optional string predeployed_wrapped_base_token_address = 5; // optional; H160 } message Bridge { diff --git a/core/lib/protobuf_config/src/proto/config/en.proto b/core/lib/protobuf_config/src/proto/config/en.proto index a8d304c8289e..3d3334e9eead 100644 --- a/core/lib/protobuf_config/src/proto/config/en.proto +++ b/core/lib/protobuf_config/src/proto/config/en.proto @@ -11,4 +11,5 @@ message ExternalNode { optional config.genesis.L1BatchCommitDataGeneratorMode l1_batch_commit_data_generator_mode = 7; // optional, default to rollup reserved 8; reserved "gateway_url"; optional uint64 bridge_addresses_refresh_interval_sec = 9; // optional + optional uint64 gateway_chain_id = 10; // optional } diff --git a/core/lib/protobuf_config/src/proto/config/eth_sender.proto b/core/lib/protobuf_config/src/proto/config/eth_sender.proto index 1176afd7c442..4a9bad0acc10 100644 --- a/core/lib/protobuf_config/src/proto/config/eth_sender.proto +++ b/core/lib/protobuf_config/src/proto/config/eth_sender.proto @@ -27,6 +27,11 @@ enum PubdataSendingMode { RELAYED_L2_CALLDATA = 3; } +enum SettlementMode { + SettlesToL1 = 0; + Gateway = 1; +} + message Sender { reserved 1; reserved "aggregated_proof_sizes"; optional uint64 wait_confirmations = 2; // optional @@ -49,6 +54,7 @@ message Sender { optional bool tx_aggregation_paused = 20; // required optional bool tx_aggregation_only_prove_and_execute = 21; // required optional uint32 time_in_mempool_in_l1_blocks_cap = 22; // optional + optional uint64 priority_op_start_index = 23; // optional } message GasAdjuster { @@ -64,6 +70,7 @@ message GasAdjuster { optional uint64 num_samples_for_blob_base_fee_estimate = 9; // required; optional double internal_pubdata_pricing_multiplier = 10; // required; optional uint64 max_blob_base_fee = 11; // optional; wei + optional SettlementMode settlement_mode = 13; // optional } message ETHWatch { diff --git a/core/lib/protobuf_config/src/proto/config/gateway.proto b/core/lib/protobuf_config/src/proto/config/gateway.proto new file mode 100644 index 000000000000..06330f530a1a --- /dev/null +++ b/core/lib/protobuf_config/src/proto/config/gateway.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +package zksync.config.gateway; + +message GatewayChainConfig { + optional string state_transition_proxy_addr = 1; + optional string validator_timelock_addr = 2; + optional string multicall3_addr = 3; + optional string diamond_proxy_addr = 4; + optional string chain_admin_addr = 5; + optional string governance_addr = 6; + optional uint64 gateway_chain_id = 7; +} diff --git a/core/lib/protobuf_config/src/proto/config/genesis.proto b/core/lib/protobuf_config/src/proto/config/genesis.proto index 2e9ebc82f25e..808bcee4799b 100644 --- a/core/lib/protobuf_config/src/proto/config/genesis.proto +++ b/core/lib/protobuf_config/src/proto/config/genesis.proto @@ -31,4 +31,5 @@ message Genesis { optional string evm_emulator_hash = 13; // optional; h256 optional string custom_genesis_state_path = 14; // optional; reserved 11; reserved "shared_bridge"; + reserved 15; reserved "sl_chain_id"; } diff --git a/core/lib/types/src/l1/mod.rs b/core/lib/types/src/l1/mod.rs index 33225dd6b0c9..c48305d2ab3d 100644 --- a/core/lib/types/src/l1/mod.rs +++ b/core/lib/types/src/l1/mod.rs @@ -1,6 +1,9 @@ //! Definition of ZKsync network priority operations: operations initiated from the L1. use serde::{Deserialize, Serialize}; +use zksync_basic_types::{web3::Log, Address, L1BlockNumber, PriorityOpId, H256, U256}; +use zksync_crypto_primitives::hasher::{keccak::KeccakHasher, Hasher}; +use zksync_mini_merkle_tree::HashEmptySubtree; use super::Transaction; use crate::{ @@ -12,10 +15,8 @@ use crate::{ l2::TransactionType, priority_op_onchain_data::{PriorityOpOnchainData, PriorityOpOnchainMetadata}, tx::Execute, - u256_to_address, - web3::Log, - Address, ExecuteTransactionCommon, L1BlockNumber, PriorityOpId, H256, - PRIORITY_OPERATION_L2_TX_TYPE, PROTOCOL_UPGRADE_TX_TYPE, U256, + u256_to_address, ExecuteTransactionCommon, PRIORITY_OPERATION_L2_TX_TYPE, + PROTOCOL_UPGRADE_TX_TYPE, }; pub mod error; @@ -209,6 +210,12 @@ pub struct L1Tx { pub received_timestamp_ms: u64, } +impl HashEmptySubtree for KeccakHasher { + fn empty_leaf_hash(&self) -> H256 { + self.hash_bytes(&[]) + } +} + impl From for Transaction { fn from(tx: L1Tx) -> Self { let L1Tx { diff --git a/core/node/api_server/src/tx_sender/mod.rs b/core/node/api_server/src/tx_sender/mod.rs index 4c98fc7c455c..e19cc6c1d706 100644 --- a/core/node/api_server/src/tx_sender/mod.rs +++ b/core/node/api_server/src/tx_sender/mod.rs @@ -13,7 +13,9 @@ use zksync_multivm::{ tracer::TimestampAsserterParams as TracerTimestampAsserterParams, OneshotTracingParams, TransactionExecutionMetrics, VmExecutionResultAndLogs, }, - utils::{derive_base_fee_and_gas_per_pubdata, get_max_batch_gas_limit}, + utils::{ + derive_base_fee_and_gas_per_pubdata, get_max_batch_gas_limit, get_max_new_factory_deps, + }, }; use zksync_node_fee_model::{ApiFeeInputProvider, BatchFeeModelInputProvider}; use zksync_state::PostgresStorageCaches; @@ -29,8 +31,7 @@ use zksync_types::{ transaction_request::CallOverrides, utils::storage_key_for_eth_balance, vm::FastVmMode, - AccountTreeId, Address, L2ChainId, Nonce, ProtocolVersionId, Transaction, H160, H256, - MAX_NEW_FACTORY_DEPS, U256, + AccountTreeId, Address, L2ChainId, Nonce, ProtocolVersionId, Transaction, H160, H256, U256, }; use zksync_vm_executor::oneshot::{ CallOrExecute, EstimateGas, MultiVmBaseSystemContracts, OneshotEnvParameters, @@ -475,10 +476,11 @@ impl TxSender { ); return Err(SubmitTxError::MaxPriorityFeeGreaterThanMaxFee); } - if tx.execute.factory_deps.len() > MAX_NEW_FACTORY_DEPS { + let max_new_factory_deps = get_max_new_factory_deps(protocol_version.into()); + if tx.execute.factory_deps.len() > max_new_factory_deps { return Err(SubmitTxError::TooManyFactoryDependencies( tx.execute.factory_deps.len(), - MAX_NEW_FACTORY_DEPS, + max_new_factory_deps, )); } diff --git a/core/node/api_server/src/web3/mod.rs b/core/node/api_server/src/web3/mod.rs index 620e9185078e..62f76b9d35f9 100644 --- a/core/node/api_server/src/web3/mod.rs +++ b/core/node/api_server/src/web3/mod.rs @@ -16,6 +16,7 @@ use zksync_metadata_calculator::api_server::TreeApiClient; use zksync_node_sync::SyncState; use zksync_types::L2BlockNumber; use zksync_web3_decl::{ + client::{DynClient, L2}, jsonrpsee::{ server::{ middleware::rpc::either::Either, BatchRequestConfig, RpcServiceBuilder, ServerBuilder, @@ -137,6 +138,7 @@ struct OptionalApiParams { mempool_cache: Option, extended_tracing: bool, pub_sub_events_sender: Option>, + l2_l1_log_proof_handler: Option>>, } /// Structure capable of spawning a configured Web3 API server along with all the required @@ -296,6 +298,14 @@ impl ApiBuilder { self } + pub fn with_l2_l1_log_proof_handler( + mut self, + l2_l1_log_proof_handler: Box>, + ) -> Self { + self.optional.l2_l1_log_proof_handler = Some(l2_l1_log_proof_handler); + self + } + // Intended for tests only. #[doc(hidden)] fn with_pub_sub_events(mut self, sender: mpsc::UnboundedSender) -> Self { @@ -379,6 +389,7 @@ impl ApiServer { last_sealed_l2_block: self.sealed_l2_block_handle, bridge_addresses_handle: self.bridge_addresses_handle, tree_api: self.optional.tree_api, + l2_l1_log_proof_handler: self.optional.l2_l1_log_proof_handler, }) } diff --git a/core/node/api_server/src/web3/namespaces/en.rs b/core/node/api_server/src/web3/namespaces/en.rs index f990144a4acd..e93f918ad446 100644 --- a/core/node/api_server/src/web3/namespaces/en.rs +++ b/core/node/api_server/src/web3/namespaces/en.rs @@ -164,6 +164,7 @@ impl EnNamespace { .api_config .l1_transparent_proxy_admin_addr .unwrap(), + l1_bytecodes_supplier_addr: self.state.api_config.l1_bytecodes_supplier_addr, }) .context("Shared bridge doesn't supported")?) } @@ -213,7 +214,6 @@ impl EnNamespace { .base_system_contracts_hashes .evm_emulator, l1_chain_id: self.state.api_config.l1_chain_id, - sl_chain_id: Some(self.state.api_config.l1_chain_id.into()), l2_chain_id: self.state.api_config.l2_chain_id, snark_wrapper_vk_hash: verifier_config.snark_wrapper_vk_hash, fee_account, diff --git a/core/node/api_server/src/web3/namespaces/zks.rs b/core/node/api_server/src/web3/namespaces/zks.rs index c692494d5091..b272f7c443e9 100644 --- a/core/node/api_server/src/web3/namespaces/zks.rs +++ b/core/node/api_server/src/web3/namespaces/zks.rs @@ -27,7 +27,8 @@ use zksync_types::{ L1_MESSENGER_ADDRESS, L2_BASE_TOKEN_ADDRESS, REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_BYTE, U256, U64, }; use zksync_web3_decl::{ - error::Web3Error, + error::{ClientRpcContext, Web3Error}, + namespaces::ZksNamespaceClient, types::{Address, Token, H256}, }; @@ -237,6 +238,14 @@ impl ZksNamespace { msg: H256, l2_log_position: Option, ) -> Result, Web3Error> { + if let Some(handler) = &self.state.l2_l1_log_proof_handler { + return handler + .get_l2_to_l1_msg_proof(block_number, sender, msg, l2_log_position) + .rpc_context("get_l2_to_l1_msg_proof") + .await + .map_err(Into::into); + } + let mut storage = self.state.acquire_connection().await?; self.state .start_info @@ -361,14 +370,14 @@ impl ZksNamespace { let Some(sl_chain_id) = storage .eth_sender_dal() - .get_batch_commit_chain_id(l1_batch_number) + .get_batch_execute_chain_id(l1_batch_number) .await .map_err(DalError::generalize)? else { return Ok(None); }; - let (batch_proof_len, batch_chain_proof) = + let (batch_proof_len, batch_chain_proof, is_final_node) = if sl_chain_id.0 != self.state.api_config.l1_chain_id.0 { let Some(batch_chain_proof) = storage .blocks_dal() @@ -379,9 +388,13 @@ impl ZksNamespace { return Ok(None); }; - (batch_chain_proof.batch_proof_len, batch_chain_proof.proof) + ( + batch_chain_proof.batch_proof_len, + batch_chain_proof.proof, + false, + ) } else { - (0, Vec::new()) + (0, Vec::new(), true) }; let proof = { @@ -389,6 +402,7 @@ impl ZksNamespace { metadata[0] = LOG_PROOF_SUPPORTED_METADATA_VERSION; metadata[1] = log_leaf_proof.len() as u8; metadata[2] = batch_proof_len as u8; + metadata[3] = if is_final_node { 1 } else { 0 }; let mut result = vec![H256(metadata)]; @@ -410,6 +424,14 @@ impl ZksNamespace { tx_hash: H256, index: Option, ) -> Result, Web3Error> { + if let Some(handler) = &self.state.l2_l1_log_proof_handler { + return handler + .get_l2_to_l1_log_proof(tx_hash, index) + .rpc_context("get_l2_to_l1_log_proof") + .await + .map_err(Into::into); + } + let mut storage = self.state.acquire_connection().await?; let Some((l1_batch_number, l1_batch_tx_index)) = storage .blocks_web3_dal() diff --git a/core/node/api_server/src/web3/state.rs b/core/node/api_server/src/web3/state.rs index 900cd165c045..bdefd79b6dd6 100644 --- a/core/node/api_server/src/web3/state.rs +++ b/core/node/api_server/src/web3/state.rs @@ -23,7 +23,11 @@ use zksync_types::{ api, commitment::L1BatchCommitmentMode, l2::L2Tx, transaction_request::CallRequest, Address, L1BatchNumber, L1ChainId, L2BlockNumber, L2ChainId, H256, U256, U64, }; -use zksync_web3_decl::{error::Web3Error, types::Filter}; +use zksync_web3_decl::{ + client::{DynClient, L2}, + error::Web3Error, + types::Filter, +}; use super::{ backend_jsonrpsee::MethodTracer, @@ -103,6 +107,7 @@ pub struct InternalApiConfig { pub estimate_gas_acceptable_overestimation: u32, pub estimate_gas_optimize_search: bool, pub bridge_addresses: api::BridgeAddresses, + pub l1_bytecodes_supplier_addr: Option
, pub l1_bridgehub_proxy_addr: Option
, pub l1_state_transition_proxy_addr: Option
, pub l1_transparent_proxy_admin_addr: Option
, @@ -160,6 +165,10 @@ impl InternalApiConfig { .ecosystem_contracts .as_ref() .map(|a| a.transparent_proxy_admin_addr), + l1_bytecodes_supplier_addr: contracts_config + .ecosystem_contracts + .as_ref() + .and_then(|a| a.l1_bytecodes_supplier_addr), l1_diamond_proxy_addr: contracts_config.diamond_proxy_addr, l2_testnet_paymaster_addr: contracts_config.l2_testnet_paymaster_addr, req_entities_limit: web3_config.req_entities_limit(), @@ -244,6 +253,7 @@ pub(crate) struct RpcState { pub(super) mempool_cache: Option, pub(super) last_sealed_l2_block: SealedL2BlockNumber, pub(super) bridge_addresses_handle: BridgeAddressesHandle, + pub(super) l2_l1_log_proof_handler: Option>>, } impl RpcState { diff --git a/core/node/block_reverter/src/lib.rs b/core/node/block_reverter/src/lib.rs index c7397ee475a7..53f67e8c9ae5 100644 --- a/core/node/block_reverter/src/lib.rs +++ b/core/node/block_reverter/src/lib.rs @@ -3,7 +3,7 @@ use std::{path::Path, sync::Arc, time::Duration}; use anyhow::Context as _; use serde::Serialize; use tokio::{fs, sync::Semaphore}; -use zksync_config::{ContractsConfig, EthConfig}; +use zksync_config::EthConfig; use zksync_contracts::hyperchain_contract; use zksync_dal::{ConnectionPool, Core, CoreDal}; // Public re-export to simplify the API use. @@ -16,6 +16,7 @@ use zksync_storage::RocksDB; use zksync_types::{ aggregated_operations::AggregatedActionType, ethabi::Token, + settlement::SettlementMode, snapshots::{ SnapshotFactoryDependencies, SnapshotMetadata, SnapshotStorageLogsChunk, SnapshotStorageLogsStorageKey, @@ -27,29 +28,40 @@ use zksync_types::{ #[cfg(test)] mod tests; +/// The amount of gas to be used for transactions on top of Gateway. +/// It is larger than the L1 one, since the gas required is typically +/// higher on gateway, due to pubdata price fluctuations. +const GATEWAY_DEFAULT_GAS: usize = 50_000_000; +/// The amount of gas to be used for transactions on top of L1 chains. +const L1_DEFAULT_GAS: usize = 5_000_000; + #[derive(Debug)] pub struct BlockReverterEthConfig { - diamond_proxy_addr: H160, - validator_timelock_addr: H160, + sl_diamond_proxy_addr: H160, + sl_validator_timelock_addr: H160, default_priority_fee_per_gas: u64, hyperchain_id: L2ChainId, + settlement_mode: SettlementMode, } impl BlockReverterEthConfig { pub fn new( eth_config: &EthConfig, - contract: &ContractsConfig, + sl_diamond_proxy_addr: Address, + sl_validator_timelock_addr: Address, hyperchain_id: L2ChainId, + settlement_mode: SettlementMode, ) -> anyhow::Result { Ok(Self { - diamond_proxy_addr: contract.diamond_proxy_addr, - validator_timelock_addr: contract.validator_timelock_addr, + sl_diamond_proxy_addr, + sl_validator_timelock_addr, default_priority_fee_per_gas: eth_config .gas_adjuster .as_ref() .context("gas adjuster")? .default_priority_fee_per_gas, hyperchain_id, + settlement_mode, }) } } @@ -473,7 +485,7 @@ impl BlockReverter { /// Sends a revert transaction to L1. pub async fn send_ethereum_revert_transaction( &self, - eth_client: &dyn BoundEthInterface, + sl_client: &dyn BoundEthInterface, eth_config: &BlockReverterEthConfig, last_l1_batch_to_keep: L1BatchNumber, nonce: u64, @@ -495,17 +507,23 @@ impl BlockReverter { ]) .context("failed encoding `revertBatchesSharedBridge` input")?; + let gas = if eth_config.settlement_mode.is_gateway() { + GATEWAY_DEFAULT_GAS + } else { + L1_DEFAULT_GAS + }; + let options = Options { nonce: Some(nonce.into()), - gas: Some(5_000_000.into()), + gas: Some(gas.into()), ..Default::default() }; - let signed_tx = eth_client - .sign_prepared_tx_for_addr(data, eth_config.validator_timelock_addr, options) + let signed_tx = sl_client + .sign_prepared_tx_for_addr(data, eth_config.sl_validator_timelock_addr, options) .await .context("cannot sign revert transaction")?; - let hash = eth_client + let hash = sl_client .as_ref() .send_raw_tx(signed_tx.raw_tx) .await @@ -513,7 +531,7 @@ impl BlockReverter { tracing::info!("Sent revert transaction to L1 with hash {hash:?}"); loop { - let maybe_receipt = eth_client + let maybe_receipt = sl_client .as_ref() .tx_receipt(hash) .await @@ -563,7 +581,7 @@ impl BlockReverter { ) -> anyhow::Result { tracing::info!("Computing suggested revert values for config {eth_config:?}"); - let contract_address = eth_config.diamond_proxy_addr; + let contract_address = eth_config.sl_diamond_proxy_addr; let last_committed_l1_batch_number = Self::get_l1_batch_number_from_contract( eth_client, diff --git a/core/node/eth_sender/Cargo.toml b/core/node/eth_sender/Cargo.toml index 90b5727d9500..f578743dcea9 100644 --- a/core/node/eth_sender/Cargo.toml +++ b/core/node/eth_sender/Cargo.toml @@ -24,6 +24,8 @@ zksync_object_store.workspace = true zksync_prover_interface.workspace = true zksync_shared_metrics.workspace = true zksync_node_fee_model.workspace = true +zksync_mini_merkle_tree.workspace = true +once_cell.workspace = true tokio = { workspace = true, features = ["time"] } anyhow.workspace = true @@ -35,6 +37,5 @@ tracing.workspace = true [dev-dependencies] test-casing.workspace = true zksync_node_test_utils.workspace = true -once_cell.workspace = true assert_matches.workspace = true test-log.workspace = true diff --git a/core/node/eth_sender/src/aggregator.rs b/core/node/eth_sender/src/aggregator.rs index 8b0d8dfecea0..c5e415061409 100644 --- a/core/node/eth_sender/src/aggregator.rs +++ b/core/node/eth_sender/src/aggregator.rs @@ -2,14 +2,17 @@ use std::sync::Arc; use zksync_config::configs::eth_sender::{ProofSendingMode, SenderConfig}; use zksync_contracts::BaseSystemContractsHashes; -use zksync_dal::{Connection, Core, CoreDal}; +use zksync_dal::{Connection, ConnectionPool, Core, CoreDal}; use zksync_l1_contract_interface::i_executor::methods::{ExecuteBatches, ProveBatches}; +use zksync_mini_merkle_tree::MiniMerkleTree; use zksync_object_store::{ObjectStore, ObjectStoreError}; use zksync_prover_interface::outputs::L1BatchProofForL1; use zksync_types::{ aggregated_operations::AggregatedActionType, - commitment::{L1BatchCommitmentMode, L1BatchWithMetadata}, + commitment::{L1BatchCommitmentMode, L1BatchWithMetadata, PriorityOpsMerkleProof}, + hasher::keccak::KeccakHasher, helpers::unix_timestamp_ms, + l1::L1Tx, protocol_version::{L1VerifierConfig, ProtocolSemanticVersion}, pubdata_da::PubdataSendingMode, settlement::SettlementMode, @@ -31,6 +34,7 @@ pub struct Aggregator { execute_criteria: Vec>, config: SenderConfig, blob_store: Arc, + pool: ConnectionPool, /// If we are operating in 4844 mode we need to wait for commit transaction /// to get included before sending the respective prove and execute transactions. /// In non-4844 mode of operation we operate with the single address and this @@ -39,18 +43,21 @@ pub struct Aggregator { operate_4844_mode: bool, pubdata_da: PubdataSendingMode, commitment_mode: L1BatchCommitmentMode, + priority_merkle_tree: Option>, } impl Aggregator { - pub fn new( + pub async fn new( config: SenderConfig, blob_store: Arc, custom_commit_sender_addr: Option
, commitment_mode: L1BatchCommitmentMode, + pool: ConnectionPool, settlement_mode: SettlementMode, - ) -> Self { + ) -> anyhow::Result { let pubdata_da = config.pubdata_sending_mode; - let operate_4844_mode = + + let operate_4844_mode: bool = custom_commit_sender_addr.is_some() && !settlement_mode.is_gateway(); // We do not have a reliable lower bound for gas needed to execute batches on gateway so we do not aggregate. @@ -121,7 +128,7 @@ impl Aggregator { })] }; - Self { + Ok(Self { commit_criteria, proof_criteria: vec![Box::from(NumberCriterion { op: AggregatedActionType::PublishProofOnchain, @@ -133,7 +140,9 @@ impl Aggregator { operate_4844_mode, pubdata_da, commitment_mode, - } + priority_merkle_tree: None, + pool, + }) } pub async fn get_next_ready_operation( @@ -178,6 +187,35 @@ impl Aggregator { } } + async fn get_or_init_tree(&mut self) -> &mut MiniMerkleTree { + if self.priority_merkle_tree.is_none() { + // We unwrap here since it is only invoked during initialization + let mut connection = self.pool.connection_tagged("eth_sender").await.unwrap(); + + let priority_op_hashes = + if let Some(priority_tree_start_index) = self.config.priority_tree_start_index { + // We unwrap here since it is only invoked during initialization + connection + .transactions_dal() + .get_l1_transactions_hashes(priority_tree_start_index) + .await + .unwrap() + } else { + vec![] + }; + let priority_merkle_tree = MiniMerkleTree::::from_hashes( + KeccakHasher, + priority_op_hashes.into_iter(), + None, + ); + + self.priority_merkle_tree = Some(priority_merkle_tree); + }; + + // It is known that the `self.priority_merkle_tree` is initialized, so it is safe to unwrap here + self.priority_merkle_tree.as_mut().unwrap() + } + async fn get_execute_operations( &mut self, storage: &mut Connection<'_, Core>, @@ -199,11 +237,65 @@ impl Aggregator { ready_for_execute_batches, last_sealed_l1_batch, ) - .await; + .await?; + + let priority_tree_start_index = + if let Some(start_index) = self.config.priority_tree_start_index { + start_index + } else { + let length = l1_batches.len(); + return Some(ExecuteBatches { + l1_batches, + priority_ops_proofs: vec![Default::default(); length], + }); + }; + let priority_merkle_tree = self.get_or_init_tree().await; + + let mut priority_ops_proofs = vec![]; + for batch in &l1_batches { + let first_priority_op_id_option = storage + .blocks_dal() + .get_batch_first_priority_op_id(batch.header.number) + .await + .unwrap() + .filter(|id| *id >= priority_tree_start_index); + + let count = batch.header.l1_tx_count as usize; + if let Some(first_priority_op_id_in_batch) = first_priority_op_id_option { + let new_l1_tx_hashes = storage + .transactions_dal() + .get_l1_transactions_hashes( + priority_tree_start_index + priority_merkle_tree.length(), + ) + .await + .unwrap(); + for hash in new_l1_tx_hashes { + priority_merkle_tree.push_hash(hash); + } + + // We cache paths for priority transactions that happened in the previous batches. + // For this we absorb all the elements up to `first_priority_op_id_in_batch`.` + priority_merkle_tree.trim_start( + first_priority_op_id_in_batch // global index + - priority_tree_start_index // first index when tree is activated + - priority_merkle_tree.start_index(), // first index in the tree + ); + let (_, left, right) = + priority_merkle_tree.merkle_root_and_paths_for_range(..count); + let hashes = priority_merkle_tree.hashes_prefix(count); + priority_ops_proofs.push(PriorityOpsMerkleProof { + left_path: left.into_iter().map(Option::unwrap_or_default).collect(), + right_path: right.into_iter().map(Option::unwrap_or_default).collect(), + hashes, + }); + } else { + priority_ops_proofs.push(Default::default()); + } + } - l1_batches.map(|l1_batches| ExecuteBatches { + Some(ExecuteBatches { l1_batches, - priority_ops_proofs: Vec::new(), + priority_ops_proofs, }) } diff --git a/core/node/eth_sender/src/tester.rs b/core/node/eth_sender/src/tester.rs index 3caf0cae5f87..37fcff19c56e 100644 --- a/core/node/eth_sender/src/tester.rs +++ b/core/node/eth_sender/src/tester.rs @@ -244,6 +244,17 @@ impl EthSenderTester { None }; + let aggregator = Aggregator::new( + aggregator_config.clone(), + MockObjectStore::arc(), + custom_commit_sender_addr, + commitment_mode, + connection_pool.clone(), + SettlementMode::SettlesToL1, + ) + .await + .unwrap(); + let aggregator = EthTxAggregator::new( connection_pool.clone(), SenderConfig { @@ -252,13 +263,7 @@ impl EthSenderTester { ..eth_sender.clone() }, // Aggregator - unused - Aggregator::new( - aggregator_config.clone(), - MockObjectStore::arc(), - custom_commit_sender_addr, - commitment_mode, - SettlementMode::SettlesToL1, - ), + aggregator, gateway.clone(), // ZKsync contract address Address::random(), @@ -406,15 +411,16 @@ impl EthSenderTester { pub async fn save_execute_tx(&mut self, l1_batch_number: L1BatchNumber) -> EthTx { assert_eq!(l1_batch_number, self.next_l1_batch_number_to_execute); + let l1_batch_headers = vec![ + self.get_l1_batch_header_from_db(self.next_l1_batch_number_to_execute) + .await, + ]; let operation = AggregatedOperation::Execute(ExecuteBatches { - l1_batches: vec![ - self.get_l1_batch_header_from_db(self.next_l1_batch_number_to_execute) - .await, - ] - .into_iter() - .map(l1_batch_with_metadata) - .collect(), - priority_ops_proofs: Vec::new(), + priority_ops_proofs: vec![Default::default(); l1_batch_headers.len()], + l1_batches: l1_batch_headers + .into_iter() + .map(l1_batch_with_metadata) + .collect(), }); self.next_l1_batch_number_to_execute += 1; self.save_operation(operation).await diff --git a/core/node/genesis/src/lib.rs b/core/node/genesis/src/lib.rs index d49c25bff03c..b3aa8de344d6 100644 --- a/core/node/genesis/src/lib.rs +++ b/core/node/genesis/src/lib.rs @@ -179,7 +179,6 @@ pub fn mock_genesis_config() -> GenesisConfig { default_aa_hash: Some(base_system_contracts_hashes.default_aa), evm_emulator_hash: base_system_contracts_hashes.evm_emulator, l1_chain_id: L1ChainId(9), - sl_chain_id: None, l2_chain_id: L2ChainId::default(), snark_wrapper_vk_hash: first_l1_verifier_config.snark_wrapper_vk_hash, fee_account: Default::default(), diff --git a/core/node/node_framework/Cargo.toml b/core/node/node_framework/Cargo.toml index eec9b8ef4b7a..583be91ef54f 100644 --- a/core/node/node_framework/Cargo.toml +++ b/core/node/node_framework/Cargo.toml @@ -48,6 +48,7 @@ zksync_contract_verification_server.workspace = true zksync_queued_job_processor.workspace = true zksync_reorg_detector.workspace = true zksync_vm_runner.workspace = true +zksync_mini_merkle_tree.workspace = true zksync_node_db_pruner.workspace = true zksync_base_token_adjuster.workspace = true zksync_node_storage_init.workspace = true diff --git a/core/node/node_framework/src/implementations/layers/eth_sender/aggregator.rs b/core/node/node_framework/src/implementations/layers/eth_sender/aggregator.rs index d2be0b383393..d20e1a4ecfb5 100644 --- a/core/node/node_framework/src/implementations/layers/eth_sender/aggregator.rs +++ b/core/node/node_framework/src/implementations/layers/eth_sender/aggregator.rs @@ -1,6 +1,6 @@ use anyhow::Context; use zksync_circuit_breaker::l1_txs::FailedL1TransactionChecker; -use zksync_config::configs::{eth_sender::EthConfig, ContractsConfig}; +use zksync_config::configs::{eth_sender::EthConfig, gateway::GatewayChainConfig, ContractsConfig}; use zksync_eth_client::BoundEthInterface; use zksync_eth_sender::{Aggregator, EthTxAggregator}; use zksync_types::{commitment::L1BatchCommitmentMode, settlement::SettlementMode, L2ChainId}; @@ -8,7 +8,10 @@ use zksync_types::{commitment::L1BatchCommitmentMode, settlement::SettlementMode use crate::{ implementations::resources::{ circuit_breakers::CircuitBreakersResource, - eth_interface::{BoundEthInterfaceForBlobsResource, BoundEthInterfaceResource}, + eth_interface::{ + BoundEthInterfaceForBlobsResource, BoundEthInterfaceForL2Resource, + BoundEthInterfaceResource, + }, healthcheck::AppHealthCheckResource, object_store::ObjectStoreResource, pools::{MasterPool, PoolResource, ReplicaPool}, @@ -41,6 +44,7 @@ use crate::{ pub struct EthTxAggregatorLayer { eth_sender_config: EthConfig, contracts_config: ContractsConfig, + gateway_chain_config: Option, zksync_network_id: L2ChainId, l1_batch_commit_data_generator_mode: L1BatchCommitmentMode, settlement_mode: SettlementMode, @@ -53,6 +57,7 @@ pub struct Input { pub replica_pool: PoolResource, pub eth_client: Option, pub eth_client_blobs: Option, + pub eth_client_gateway: Option, pub object_store: ObjectStoreResource, #[context(default)] pub circuit_breakers: CircuitBreakersResource, @@ -71,6 +76,7 @@ impl EthTxAggregatorLayer { pub fn new( eth_sender_config: EthConfig, contracts_config: ContractsConfig, + gateway_chain_config: Option, zksync_network_id: L2ChainId, l1_batch_commit_data_generator_mode: L1BatchCommitmentMode, settlement_mode: SettlementMode, @@ -78,6 +84,7 @@ impl EthTxAggregatorLayer { Self { eth_sender_config, contracts_config, + gateway_chain_config, zksync_network_id, l1_batch_commit_data_generator_mode, settlement_mode, @@ -95,7 +102,42 @@ impl WiringLayer for EthTxAggregatorLayer { } async fn wire(self, input: Self::Input) -> Result { + tracing::info!( + "Wiring tx_aggregator in {:?} mode which is {}", + self.settlement_mode, + self.settlement_mode.is_gateway() + ); + tracing::info!("Contracts: {:?}", self.contracts_config); + tracing::info!("Gateway contracts: {:?}", self.gateway_chain_config); // Get resources. + + let (validator_timelock_addr, multicall3_addr, diamond_proxy_addr) = + if self.settlement_mode.is_gateway() { + let gateway_chain_config = self + .gateway_chain_config + .as_ref() + .context("gateway_chain_config")?; + ( + gateway_chain_config.validator_timelock_addr, + gateway_chain_config.multicall3_addr, + gateway_chain_config.diamond_proxy_addr, + ) + } else { + ( + self.contracts_config.validator_timelock_addr, + self.contracts_config.l1_multicall3_addr, + self.contracts_config.diamond_proxy_addr, + ) + }; + + let eth_client = if self.settlement_mode.is_gateway() { + input + .eth_client_gateway + .context("eth_client_gateway missing")? + .0 + } else { + input.eth_client.context("eth_client missing")?.0 + }; let master_pool = input.master_pool.get().await.unwrap(); let replica_pool = input.replica_pool.get().await.unwrap(); @@ -113,17 +155,19 @@ impl WiringLayer for EthTxAggregatorLayer { object_store, eth_client_blobs_addr, self.l1_batch_commit_data_generator_mode, + replica_pool.clone(), self.settlement_mode, - ); + ) + .await?; let eth_tx_aggregator = EthTxAggregator::new( master_pool.clone(), config.clone(), aggregator, - input.eth_client.unwrap().0, - self.contracts_config.validator_timelock_addr, - self.contracts_config.l1_multicall3_addr, - self.contracts_config.diamond_proxy_addr, + eth_client, + validator_timelock_addr, + multicall3_addr, + diamond_proxy_addr, self.zksync_network_id, eth_client_blobs_addr, self.settlement_mode, diff --git a/core/node/node_framework/src/implementations/layers/eth_watch.rs b/core/node/node_framework/src/implementations/layers/eth_watch.rs index e871f5661d22..eeffae4ae6d9 100644 --- a/core/node/node_framework/src/implementations/layers/eth_watch.rs +++ b/core/node/node_framework/src/implementations/layers/eth_watch.rs @@ -1,11 +1,12 @@ -use zksync_config::{ContractsConfig, EthWatchConfig}; +use anyhow::Context; +use zksync_config::{configs::gateway::GatewayChainConfig, ContractsConfig, EthWatchConfig}; use zksync_contracts::chain_admin_contract; -use zksync_eth_watch::{EthHttpQueryClient, EthWatch}; -use zksync_types::L2ChainId; +use zksync_eth_watch::{EthHttpQueryClient, EthWatch, L2EthClient}; +use zksync_types::{settlement::SettlementMode, L2ChainId}; use crate::{ implementations::resources::{ - eth_interface::EthInterfaceResource, + eth_interface::{EthInterfaceResource, L2InterfaceResource}, pools::{MasterPool, PoolResource}, }, service::StopReceiver, @@ -22,6 +23,8 @@ use crate::{ pub struct EthWatchLayer { eth_watch_config: EthWatchConfig, contracts_config: ContractsConfig, + gateway_chain_config: Option, + settlement_mode: SettlementMode, chain_id: L2ChainId, } @@ -30,6 +33,7 @@ pub struct EthWatchLayer { pub struct Input { pub master_pool: PoolResource, pub eth_client: EthInterfaceResource, + pub gateway_client: Option, } #[derive(Debug, IntoContext)] @@ -43,11 +47,15 @@ impl EthWatchLayer { pub fn new( eth_watch_config: EthWatchConfig, contracts_config: ContractsConfig, + gateway_chain_config: Option, + settlement_mode: SettlementMode, chain_id: L2ChainId, ) -> Self { Self { eth_watch_config, contracts_config, + gateway_chain_config, + settlement_mode, chain_id, } } @@ -66,21 +74,54 @@ impl WiringLayer for EthWatchLayer { let main_pool = input.master_pool.get().await?; let client = input.eth_client.0; - let eth_client = EthHttpQueryClient::new( + let sl_diamond_proxy_addr = if self.settlement_mode.is_gateway() { + self.gateway_chain_config + .clone() + .context("Lacking `gateway_contracts_config`")? + .diamond_proxy_addr + } else { + self.contracts_config.diamond_proxy_addr + }; + tracing::info!( + "Diamond proxy address ethereum: {:#?}", + self.contracts_config.diamond_proxy_addr + ); + tracing::info!( + "Diamond proxy address settlement_layer: {:#?}", + sl_diamond_proxy_addr + ); + + let l1_client = EthHttpQueryClient::new( client, self.contracts_config.diamond_proxy_addr, self.contracts_config .ecosystem_contracts + .as_ref() .map(|a| a.state_transition_proxy_addr), self.contracts_config.chain_admin_addr, self.contracts_config.governance_addr, self.eth_watch_config.confirmations_for_eth_event, ); + let sl_l2_client: Option> = + if let Some(gateway_client) = input.gateway_client { + let contracts_config = self.gateway_chain_config.unwrap(); + Some(Box::new(EthHttpQueryClient::new( + gateway_client.0, + contracts_config.diamond_proxy_addr, + Some(contracts_config.state_transition_proxy_addr), + contracts_config.chain_admin_addr, + contracts_config.governance_addr, + self.eth_watch_config.confirmations_for_eth_event, + ))) + } else { + None + }; + let eth_watch = EthWatch::new( &chain_admin_contract(), - Box::new(eth_client), - None, + Box::new(l1_client), + sl_l2_client, main_pool, self.eth_watch_config.poll_interval(), self.chain_id, diff --git a/core/node/node_framework/src/implementations/layers/pk_signing_eth_client.rs b/core/node/node_framework/src/implementations/layers/pk_signing_eth_client.rs index fdef23a40692..bcc01c20cc42 100644 --- a/core/node/node_framework/src/implementations/layers/pk_signing_eth_client.rs +++ b/core/node/node_framework/src/implementations/layers/pk_signing_eth_client.rs @@ -1,14 +1,14 @@ use anyhow::Context as _; use zksync_config::{ - configs::{wallets, ContractsConfig}, + configs::{gateway::GatewayChainConfig, wallets, ContractsConfig}, EthConfig, }; -use zksync_eth_client::clients::PKSigningClient; -use zksync_types::SLChainId; +use zksync_eth_client::{clients::PKSigningClient, EthInterface}; use crate::{ implementations::resources::eth_interface::{ - BoundEthInterfaceForBlobsResource, BoundEthInterfaceResource, EthInterfaceResource, + BoundEthInterfaceForBlobsResource, BoundEthInterfaceForL2Resource, + BoundEthInterfaceResource, EthInterfaceResource, GatewayEthInterfaceResource, }, wiring_layer::{WiringError, WiringLayer}, FromContext, IntoContext, @@ -19,7 +19,7 @@ use crate::{ pub struct PKSigningEthClientLayer { eth_sender_config: EthConfig, contracts_config: ContractsConfig, - sl_chain_id: SLChainId, + gateway_chain_config: Option, wallets: wallets::EthSender, } @@ -27,6 +27,7 @@ pub struct PKSigningEthClientLayer { #[context(crate = crate)] pub struct Input { pub eth_client: EthInterfaceResource, + pub gateway_client: Option, } #[derive(Debug, IntoContext)] @@ -35,19 +36,20 @@ pub struct Output { pub signing_client: BoundEthInterfaceResource, /// Only provided if the blob operator key is provided to the layer. pub signing_client_for_blobs: Option, + pub signing_client_for_gateway: Option, } impl PKSigningEthClientLayer { pub fn new( eth_sender_config: EthConfig, contracts_config: ContractsConfig, - sl_chain_id: SLChainId, + gateway_chain_config: Option, wallets: wallets::EthSender, ) -> Self { Self { eth_sender_config, contracts_config, - sl_chain_id, + gateway_chain_config, wallets, } } @@ -71,11 +73,15 @@ impl WiringLayer for PKSigningEthClientLayer { .context("gas_adjuster config is missing")?; let EthInterfaceResource(query_client) = input.eth_client; + let l1_chain_id = query_client + .fetch_chain_id() + .await + .map_err(WiringError::internal)?; let signing_client = PKSigningClient::new_raw( private_key.clone(), self.contracts_config.diamond_proxy_addr, gas_adjuster_config.default_priority_fee_per_gas, - self.sl_chain_id, + l1_chain_id, query_client.clone(), ); let signing_client = BoundEthInterfaceResource(Box::new(signing_client)); @@ -86,15 +92,39 @@ impl WiringLayer for PKSigningEthClientLayer { private_key.clone(), self.contracts_config.diamond_proxy_addr, gas_adjuster_config.default_priority_fee_per_gas, - self.sl_chain_id, + l1_chain_id, query_client, ); BoundEthInterfaceForBlobsResource(Box::new(signing_client_for_blobs)) }); + let signing_client_for_gateway = if let (Some(client), Some(gateway_contracts)) = + (&input.gateway_client, self.gateway_chain_config.as_ref()) + { + if gateway_contracts.gateway_chain_id.0 != 0u64 { + let private_key = self.wallets.operator.private_key(); + let GatewayEthInterfaceResource(gateway_client) = client; + let signing_client_for_blobs = PKSigningClient::new_raw( + private_key.clone(), + gateway_contracts.diamond_proxy_addr, + gas_adjuster_config.default_priority_fee_per_gas, + gateway_contracts.gateway_chain_id, + gateway_client.clone(), + ); + Some(BoundEthInterfaceForL2Resource(Box::new( + signing_client_for_blobs, + ))) + } else { + None + } + } else { + None + }; + Ok(Output { signing_client, signing_client_for_blobs, + signing_client_for_gateway, }) } } diff --git a/core/node/node_framework/src/implementations/layers/query_eth_client.rs b/core/node/node_framework/src/implementations/layers/query_eth_client.rs index 73d28f6a2aae..62bbccd41e24 100644 --- a/core/node/node_framework/src/implementations/layers/query_eth_client.rs +++ b/core/node/node_framework/src/implementations/layers/query_eth_client.rs @@ -1,5 +1,5 @@ use anyhow::Context; -use zksync_types::{url::SensitiveUrl, L2ChainId, SLChainId}; +use zksync_types::{url::SensitiveUrl, L1ChainId, L2ChainId, SLChainId}; use zksync_web3_decl::client::Client; use crate::{ @@ -13,21 +13,24 @@ use crate::{ /// Wiring layer for Ethereum client. #[derive(Debug)] pub struct QueryEthClientLayer { - chain_id: SLChainId, - web3_url: SensitiveUrl, - gateway_web3_url: Option, + l1_chain_id: L1ChainId, + l1_rpc_url: SensitiveUrl, + gateway_chain_id: Option, + gateway_rpc_url: Option, } impl QueryEthClientLayer { pub fn new( - chain_id: SLChainId, - web3_url: SensitiveUrl, - gateway_web3_url: Option, + l1_chain_id: L1ChainId, + l1_rpc_url: SensitiveUrl, + gateway_chain_id: Option, + gateway_rpc_url: Option, ) -> Self { Self { - chain_id, - web3_url, - gateway_web3_url, + l1_chain_id, + l1_rpc_url, + gateway_chain_id, + gateway_rpc_url, } } } @@ -53,27 +56,29 @@ impl WiringLayer for QueryEthClientLayer { // Both `query_client_gateway` and `query_client_l2` use the same URL, but provide different type guarantees. Ok(Output { query_client_l1: EthInterfaceResource(Box::new( - Client::http(self.web3_url.clone()) + Client::http(self.l1_rpc_url.clone()) .context("Client::new()")? - .for_network(self.chain_id.into()) + .for_network(self.l1_chain_id.into()) .build(), )), - query_client_l2: if let Some(gateway_web3_url) = self.gateway_web3_url.clone() { - Some(L2InterfaceResource(Box::new( - Client::http(gateway_web3_url) - .context("Client::new()")? - .for_network(L2ChainId::try_from(self.chain_id.0).unwrap().into()) - .build(), - ))) + query_client_l2: if let Some(gateway_rpc_url) = self.gateway_rpc_url.clone() { + let mut builder = Client::http(gateway_rpc_url).context("Client::new()")?; + if let Some(gateway_chain_id) = self.gateway_chain_id { + builder = + builder.for_network(L2ChainId::try_from(gateway_chain_id.0).unwrap().into()) + } + + Some(L2InterfaceResource(Box::new(builder.build()))) } else { None }, - query_client_gateway: if let Some(gateway_web3_url) = self.gateway_web3_url { - Some(GatewayEthInterfaceResource(Box::new( - Client::http(gateway_web3_url) - .context("Client::new()")? - .build(), - ))) + query_client_gateway: if let Some(gateway_rpc_url) = self.gateway_rpc_url { + let mut builder = Client::http(gateway_rpc_url).context("Client::new()")?; + if let Some(gateway_chain_id) = self.gateway_chain_id { + builder = builder.for_network(gateway_chain_id.into()) + } + + Some(GatewayEthInterfaceResource(Box::new(builder.build()))) } else { None }, diff --git a/core/node/node_framework/src/implementations/layers/validate_chain_ids.rs b/core/node/node_framework/src/implementations/layers/validate_chain_ids.rs index 37bf37c27bed..926da1fcd5eb 100644 --- a/core/node/node_framework/src/implementations/layers/validate_chain_ids.rs +++ b/core/node/node_framework/src/implementations/layers/validate_chain_ids.rs @@ -1,9 +1,10 @@ use zksync_node_sync::validate_chain_ids_task::ValidateChainIdsTask; -use zksync_types::{L2ChainId, SLChainId}; +use zksync_types::{L1ChainId, L2ChainId, SLChainId}; use crate::{ implementations::resources::{ - eth_interface::EthInterfaceResource, main_node_client::MainNodeClientResource, + eth_interface::{EthInterfaceResource, GatewayEthInterfaceResource}, + main_node_client::MainNodeClientResource, }, service::StopReceiver, task::{Task, TaskId, TaskKind}, @@ -17,21 +18,24 @@ use crate::{ /// ## Requests resources /// /// - `EthInterfaceResource` -/// - `MainNodeClientResource +/// - `GatewayEthInterfaceResource` +/// - `MainNodeClientResource` /// /// ## Adds preconditions /// /// - `ValidateChainIdsTask` #[derive(Debug)] pub struct ValidateChainIdsLayer { - sl_chain_id: SLChainId, + l1_chain_id: L1ChainId, l2_chain_id: L2ChainId, + gateway_chain_id: Option, } #[derive(Debug, FromContext)] #[context(crate = crate)] pub struct Input { - pub eth_client: EthInterfaceResource, + pub l1_client: EthInterfaceResource, + pub gateway_client: Option, pub main_node_client: MainNodeClientResource, } @@ -43,10 +47,15 @@ pub struct Output { } impl ValidateChainIdsLayer { - pub fn new(sl_chain_id: SLChainId, l2_chain_id: L2ChainId) -> Self { + pub fn new( + l1_chain_id: L1ChainId, + l2_chain_id: L2ChainId, + gateway_chain_id: Option, + ) -> Self { Self { - sl_chain_id, + l1_chain_id, l2_chain_id, + gateway_chain_id, } } } @@ -61,14 +70,16 @@ impl WiringLayer for ValidateChainIdsLayer { } async fn wire(self, input: Self::Input) -> Result { - let EthInterfaceResource(query_client) = input.eth_client; + let EthInterfaceResource(l1_query_client) = input.l1_client; let MainNodeClientResource(main_node_client) = input.main_node_client; let task = ValidateChainIdsTask::new( - self.sl_chain_id, + self.l1_chain_id, self.l2_chain_id, - query_client, + self.gateway_chain_id, + l1_query_client, main_node_client, + input.gateway_client.map(|c| c.0), ); Ok(Output { task }) diff --git a/core/node/node_sync/src/validate_chain_ids_task.rs b/core/node/node_sync/src/validate_chain_ids_task.rs index 3507cbb2b2f9..9dcdba9134c8 100644 --- a/core/node/node_sync/src/validate_chain_ids_task.rs +++ b/core/node/node_sync/src/validate_chain_ids_task.rs @@ -5,7 +5,7 @@ use std::time::Duration; use futures::FutureExt; use tokio::sync::watch; use zksync_eth_client::EthInterface; -use zksync_types::{L2ChainId, SLChainId}; +use zksync_types::{L1ChainId, L2ChainId, SLChainId}; use zksync_web3_decl::{ client::{DynClient, L1, L2}, error::ClientRpcContext, @@ -15,49 +15,57 @@ use zksync_web3_decl::{ /// Task that validates chain IDs using main node and Ethereum clients. #[derive(Debug)] pub struct ValidateChainIdsTask { - sl_chain_id: SLChainId, + l1_chain_id: L1ChainId, l2_chain_id: L2ChainId, - eth_client: Box>, + gateway_chain_id: Option, + l1_client: Box>, main_node_client: Box>, + gateway_client: Option>>, } impl ValidateChainIdsTask { const BACKOFF_INTERVAL: Duration = Duration::from_secs(5); pub fn new( - sl_chain_id: SLChainId, + l1_chain_id: L1ChainId, l2_chain_id: L2ChainId, - eth_client: Box>, + gateway_chain_id: Option, + l1_client: Box>, main_node_client: Box>, + gateway_client: Option>>, ) -> Self { Self { - sl_chain_id, + l1_chain_id, l2_chain_id, - eth_client: eth_client.for_component("chain_ids_validation"), + gateway_chain_id, + l1_client: l1_client.for_component("chain_ids_validation"), main_node_client: main_node_client.for_component("chain_ids_validation"), + gateway_client: gateway_client.map(|c| c.for_component("chain_ids_validation")), } } - async fn check_eth_client( - eth_client: Box>, - expected: SLChainId, + async fn check_client( + l1_client: Option>>, + expected: Option, ) -> anyhow::Result<()> { + let (Some(l1_client), Some(expected)) = (l1_client, expected) else { + return Ok(()); + }; + loop { - match eth_client.fetch_chain_id().await { + match l1_client.fetch_chain_id().await { Ok(chain_id) => { anyhow::ensure!( expected == chain_id, - "Configured L1 chain ID doesn't match the one from Ethereum node. \ - Make sure your configuration is correct and you are corrected to the right Ethereum node. \ - Eth node chain ID: {chain_id}. Local config value: {expected}" - ); - tracing::info!( - "Checked that L1 chain ID {chain_id} is returned by Ethereum client" + "Configured chain ID doesn't match the one from node. \ + Make sure your configuration is correct and you are corrected to the right node. \ + node chain ID: {chain_id}. Local config value: {expected}" ); + tracing::info!("Checked that chain ID {chain_id} is returned by client"); return Ok(()); } Err(err) => { - tracing::warn!("Error getting L1 chain ID from Ethereum client: {err}"); + tracing::warn!("Error getting chain ID from client: {err}"); tokio::time::sleep(Self::BACKOFF_INTERVAL).await; } } @@ -66,7 +74,7 @@ impl ValidateChainIdsTask { async fn check_l1_chain_using_main_node( main_node_client: Box>, - expected: SLChainId, + expected: L1ChainId, ) -> anyhow::Result<()> { loop { match main_node_client @@ -75,9 +83,9 @@ impl ValidateChainIdsTask { .await { Ok(chain_id) => { - let chain_id = SLChainId(chain_id.as_u64()); + let chain_id = chain_id.as_u64(); anyhow::ensure!( - expected == chain_id, + expected.0 == chain_id, "Configured L1 chain ID doesn't match the one from main node. \ Make sure your configuration is correct and you are corrected to the right main node. \ Main node L1 chain ID: {chain_id}. Local config value: {expected}" @@ -140,14 +148,21 @@ impl ValidateChainIdsTask { /// Runs the task once, exiting either when all the checks are performed or when the stop signal is received. pub async fn run_once(self, mut stop_receiver: watch::Receiver) -> anyhow::Result<()> { - let eth_client_check = Self::check_eth_client(self.eth_client, self.sl_chain_id); + let l1_client_check = + Self::check_client(Some(self.l1_client), Some(self.l1_chain_id.0.into())); let main_node_l1_check = - Self::check_l1_chain_using_main_node(self.main_node_client.clone(), self.sl_chain_id); + Self::check_l1_chain_using_main_node(self.main_node_client.clone(), self.l1_chain_id); let main_node_l2_check = Self::check_l2_chain_using_main_node(self.main_node_client, self.l2_chain_id); - let joined_futures = - futures::future::try_join3(eth_client_check, main_node_l1_check, main_node_l2_check) - .fuse(); + let gateway_check = Self::check_client(self.gateway_client, self.gateway_chain_id); + + let joined_futures = futures::future::try_join4( + l1_client_check, + main_node_l1_check, + main_node_l2_check, + gateway_check, + ) + .fuse(); tokio::select! { res = joined_futures => res.map(drop), _ = stop_receiver.changed() => Ok(()), @@ -158,16 +173,19 @@ impl ValidateChainIdsTask { pub async fn run(self, mut stop_receiver: watch::Receiver) -> anyhow::Result<()> { // Since check futures are fused, they are safe to poll after getting resolved; they will never resolve again, // so we'll just wait for another check or a stop signal. - let eth_client_check = Self::check_eth_client(self.eth_client, self.sl_chain_id).fuse(); + let l1_client_check = + Self::check_client(Some(self.l1_client), Some(self.l1_chain_id.0.into())).fuse(); let main_node_l1_check = - Self::check_l1_chain_using_main_node(self.main_node_client.clone(), self.sl_chain_id) + Self::check_l1_chain_using_main_node(self.main_node_client.clone(), self.l1_chain_id) .fuse(); let main_node_l2_check = Self::check_l2_chain_using_main_node(self.main_node_client, self.l2_chain_id).fuse(); + let gateway_check = Self::check_client(self.gateway_client, self.gateway_chain_id).fuse(); tokio::select! { - Err(err) = eth_client_check => Err(err), + Err(err) = l1_client_check => Err(err), Err(err) = main_node_l1_check => Err(err), Err(err) = main_node_l2_check => Err(err), + Err(err) = gateway_check => Err(err), _ = stop_receiver.changed() => Ok(()), } } @@ -191,10 +209,12 @@ mod tests { .build(); let validation_task = ValidateChainIdsTask::new( - SLChainId(3), // << mismatch with the Ethereum client + L1ChainId(3), // << mismatch with the Ethereum client L2ChainId::default(), + None, Box::new(eth_client.clone()), Box::new(main_node_client.clone()), + None, ); let (_stop_sender, stop_receiver) = watch::channel(false); let err = validation_task @@ -203,15 +223,17 @@ mod tests { .unwrap_err() .to_string(); assert!( - err.contains("L1 chain ID") && err.contains("Ethereum node"), + err.contains("chain ID") && err.contains("node") && !err.contains("main node"), "{err}" ); let validation_task = ValidateChainIdsTask::new( - SLChainId(9), // << mismatch with the main node client + L1ChainId(9), // << mismatch with the main node client L2ChainId::from(270), + None, Box::new(eth_client.clone()), Box::new(main_node_client), + None, ); let err = validation_task .run(stop_receiver.clone()) @@ -229,10 +251,12 @@ mod tests { .build(); let validation_task = ValidateChainIdsTask::new( - SLChainId(9), + L1ChainId(9), L2ChainId::from(271), // << mismatch with the main node client + None, Box::new(eth_client), Box::new(main_node_client), + None, ); let err = validation_task .run(stop_receiver) @@ -256,10 +280,12 @@ mod tests { .build(); let validation_task = ValidateChainIdsTask::new( - SLChainId(9), + L1ChainId(9), L2ChainId::default(), + None, Box::new(eth_client), Box::new(main_node_client), + None, ); let (stop_sender, stop_receiver) = watch::channel(false); let task = tokio::spawn(validation_task.run(stop_receiver)); diff --git a/zkstack_cli/crates/zkstack/completion/_zkstack.zsh b/zkstack_cli/crates/zkstack/completion/_zkstack.zsh index fc6f29851e66..48734234cb29 100644 --- a/zkstack_cli/crates/zkstack/completion/_zkstack.zsh +++ b/zkstack_cli/crates/zkstack/completion/_zkstack.zsh @@ -2124,6 +2124,7 @@ _arguments "${_arguments_options[@]}" : \ '--db-url=[]:DB_URL:_default' \ '--db-name=[]:DB_NAME:_default' \ '--l1-rpc-url=[]:L1_RPC_URL:_default' \ +'--gateway-rpc-url=[]:GATEWAY_RPC_URL:_default' \ '--chain=[Chain to use]:CHAIN:_default' \ '-u[Use default database urls and names]' \ '--use-default[Use default database urls and names]' \ diff --git a/zkstack_cli/crates/zkstack/completion/zkstack.fish b/zkstack_cli/crates/zkstack/completion/zkstack.fish index 8a5b338fcda2..f470cb03b074 100644 --- a/zkstack_cli/crates/zkstack/completion/zkstack.fish +++ b/zkstack_cli/crates/zkstack/completion/zkstack.fish @@ -572,6 +572,7 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand external-node; and not _ complete -c zkstack -n "__fish_zkstack_using_subcommand external-node; and __fish_seen_subcommand_from configs" -l db-url -r complete -c zkstack -n "__fish_zkstack_using_subcommand external-node; and __fish_seen_subcommand_from configs" -l db-name -r complete -c zkstack -n "__fish_zkstack_using_subcommand external-node; and __fish_seen_subcommand_from configs" -l l1-rpc-url -r +complete -c zkstack -n "__fish_zkstack_using_subcommand external-node; and __fish_seen_subcommand_from configs" -l gateway-rpc-url -r complete -c zkstack -n "__fish_zkstack_using_subcommand external-node; and __fish_seen_subcommand_from configs" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand external-node; and __fish_seen_subcommand_from configs" -s u -l use-default -d 'Use default database urls and names' complete -c zkstack -n "__fish_zkstack_using_subcommand external-node; and __fish_seen_subcommand_from configs" -s v -l verbose -d 'Verbose mode' diff --git a/zkstack_cli/crates/zkstack/completion/zkstack.sh b/zkstack_cli/crates/zkstack/completion/zkstack.sh index bb373c3f63eb..56f2acd124bb 100644 --- a/zkstack_cli/crates/zkstack/completion/zkstack.sh +++ b/zkstack_cli/crates/zkstack/completion/zkstack.sh @@ -5370,7 +5370,7 @@ _zkstack() { return 0 ;; zkstack__external__node__configs) - opts="-u -v -h --db-url --db-name --l1-rpc-url --use-default --verbose --chain --ignore-prerequisites --help" + opts="-u -v -h --db-url --db-name --l1-rpc-url --gateway-rpc-url --use-default --verbose --chain --ignore-prerequisites --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -5388,6 +5388,10 @@ _zkstack() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --gateway-rpc-url) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; --chain) COMPREPLY=($(compgen -f "${cur}")) return 0 diff --git a/zkstack_cli/crates/zkstack/src/commands/external_node/args/prepare_configs.rs b/zkstack_cli/crates/zkstack/src/commands/external_node/args/prepare_configs.rs index 3f91380b7bdf..b1759702c461 100644 --- a/zkstack_cli/crates/zkstack/src/commands/external_node/args/prepare_configs.rs +++ b/zkstack_cli/crates/zkstack/src/commands/external_node/args/prepare_configs.rs @@ -21,6 +21,8 @@ pub struct PrepareConfigArgs { pub db_name: Option, #[clap(long)] pub l1_rpc_url: Option, + #[clap(long)] + pub gateway_rpc_url: Option, #[clap(long, short, help = MSG_USE_DEFAULT_DATABASES_HELP)] pub use_default: bool, } @@ -33,6 +35,7 @@ impl PrepareConfigArgs { PrepareConfigFinal { db: DatabaseConfig::new(DATABASE_SERVER_URL.clone(), db_name), l1_rpc_url: LOCAL_RPC_URL.to_string(), + gateway_rpc_url: None, } } else { let db_url = self.db_url.unwrap_or_else(|| { @@ -57,6 +60,7 @@ impl PrepareConfigArgs { PrepareConfigFinal { db: DatabaseConfig::new(db_url, db_name), l1_rpc_url, + gateway_rpc_url: self.gateway_rpc_url, } } } @@ -66,4 +70,5 @@ impl PrepareConfigArgs { pub struct PrepareConfigFinal { pub db: DatabaseConfig, pub l1_rpc_url: String, + pub gateway_rpc_url: Option, } diff --git a/zkstack_cli/crates/zkstack/src/commands/external_node/prepare_configs.rs b/zkstack_cli/crates/zkstack/src/commands/external_node/prepare_configs.rs index 03a586a0652a..8e937e3903d4 100644 --- a/zkstack_cli/crates/zkstack/src/commands/external_node/prepare_configs.rs +++ b/zkstack_cli/crates/zkstack/src/commands/external_node/prepare_configs.rs @@ -63,7 +63,6 @@ fn prepare_configs( let en_config = ENConfig { l2_chain_id: genesis.l2_chain_id, l1_chain_id: genesis.l1_chain_id, - sl_chain_id: genesis.sl_chain_id, l1_batch_commit_data_generator_mode: genesis.l1_batch_commit_data_generator_mode, main_node_url: SensitiveUrl::from_str( &general @@ -75,6 +74,7 @@ fn prepare_configs( )?, main_node_rate_limit_rps: None, bridge_addresses_refresh_interval_sec: None, + gateway_chain_id: None, }; let mut general_en = general.clone(); general_en.consensus_config = None; @@ -102,6 +102,12 @@ fn prepare_configs( attester_key: None, node_key: Some(NodeSecretKey(Secret::new(node_key))), }; + + let gateway_rpc_url = if let Some(url) = args.gateway_rpc_url { + Some(SensitiveUrl::from_str(&url).context("gateway_url")?) + } else { + None + }; let secrets = SecretsConfig { consensus: Some(consensus_secrets), database: Some(DatabaseSecrets { @@ -111,7 +117,7 @@ fn prepare_configs( }), l1: Some(L1Secrets { l1_rpc_url: SensitiveUrl::from_str(&args.l1_rpc_url).context("l1_rpc_url")?, - gateway_rpc_url: None, + gateway_rpc_url, }), data_availability: None, };