diff --git a/Cargo.lock b/Cargo.lock index 442802faea3a..c77599884c34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12041,6 +12041,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", @@ -12578,6 +12579,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", @@ -13101,11 +13103,13 @@ version = "0.1.0" dependencies = [ "anyhow", "assert_matches", + "async-trait", "bigdecimal", "bincode", "blake2 0.10.6", "chrono", "derive_more 1.0.0", + "ethabi", "hex", "itertools 0.10.5", "num", diff --git a/core/bin/zksync_server/src/node_builder.rs b/core/bin/zksync_server/src/node_builder.rs index 940e368b3c3b..aeb5955475b0 100644 --- a/core/bin/zksync_server/src/node_builder.rs +++ b/core/bin/zksync_server/src/node_builder.rs @@ -72,9 +72,7 @@ use zksync_node_framework::{ service::{ZkStackService, ZkStackServiceBuilder}, }; use zksync_types::{ - commitment::{L1BatchCommitmentMode, PubdataType}, - pubdata_da::PubdataSendingMode, - settlement::SettlementMode, + commitment::L1BatchCommitmentMode, pubdata_da::PubdataSendingMode, settlement::SettlementMode, SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, }; use zksync_vlog::prometheus::PrometheusExporterConfig; @@ -121,24 +119,6 @@ impl MainNodeBuilder { self.node.runtime_handle() } - pub fn get_pubdata_type(&self) -> PubdataType { - if self.genesis_config.l1_batch_commit_data_generator_mode == L1BatchCommitmentMode::Rollup - { - return PubdataType::Rollup; - } - - let Some(da_client_config) = self.configs.da_client_config.clone() else { - return PubdataType::NoDA; - }; - - match da_client_config { - DAClientConfig::Avail(_) => PubdataType::Avail, - DAClientConfig::Celestia(_) => PubdataType::Celestia, - DAClientConfig::Eigen(_) => PubdataType::Eigen, - DAClientConfig::ObjectStore(_) => PubdataType::ObjectStore, - } - } - fn add_sigint_handler_layer(mut self) -> anyhow::Result { self.node.add_layer(SigintHandlerLayer); Ok(self) @@ -273,7 +253,7 @@ impl MainNodeBuilder { try_load_config!(self.configs.mempool_config), try_load_config!(wallets.state_keeper), self.contracts_config.l2_da_validator_addr, - self.get_pubdata_type(), + self.genesis_config.l1_batch_commit_data_generator_mode, ); let db_config = try_load_config!(self.configs.db_config); let experimental_vm_config = self diff --git a/core/lib/basic_types/src/commitment.rs b/core/lib/basic_types/src/commitment.rs index c43a55bab4a9..0eed46aad782 100644 --- a/core/lib/basic_types/src/commitment.rs +++ b/core/lib/basic_types/src/commitment.rs @@ -58,35 +58,8 @@ impl FromStr for L1BatchCommitmentMode { } } -#[derive(Default, Copy, Debug, Clone, PartialEq, Serialize, Deserialize, Display)] -pub enum PubdataType { - #[default] - Rollup, - NoDA, - Avail, - Celestia, - Eigen, - ObjectStore, -} - -impl FromStr for PubdataType { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - match s { - "Rollup" => Ok(Self::Rollup), - "NoDA" => Ok(Self::NoDA), - "Avail" => Ok(Self::Avail), - "Celestia" => Ok(Self::Celestia), - "Eigen" => Ok(Self::Eigen), - "ObjectStore" => Ok(Self::ObjectStore), - _ => Err("Incorrect DA client type; expected one of `Rollup`, `NoDA`, `Avail`, `Celestia`, `Eigen`, `ObjectStore`"), - } - } -} - #[derive(Default, Copy, Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct PubdataParams { pub l2_da_validator_address: Address, - pub pubdata_type: PubdataType, + pub pubdata_type: L1BatchCommitmentMode, } diff --git a/core/lib/config/src/configs/genesis.rs b/core/lib/config/src/configs/genesis.rs index 5d5e39309bb8..b9d66f6a44ca 100644 --- a/core/lib/config/src/configs/genesis.rs +++ b/core/lib/config/src/configs/genesis.rs @@ -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/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..feebd2dd3c51 100644 --- a/core/lib/env_config/src/contracts.rs +++ b/core/lib/env_config/src/contracts.rs @@ -1,22 +1,43 @@ use zksync_config::{configs::EcosystemContracts, ContractsConfig}; -use crate::{envy_load, FromEnv}; +use crate::{envy_load, FromEnv, FromEnvVariant}; impl FromEnv for EcosystemContracts { fn from_env() -> anyhow::Result { + Self::from_env_variant("".to_string()) + } +} +impl FromEnvVariant for EcosystemContracts { + fn from_env_variant(variant: String) -> anyhow::Result { Ok(Self { - bridgehub_proxy_addr: std::env::var("CONTRACTS_BRIDGEHUB_PROXY_ADDR")?.parse()?, - state_transition_proxy_addr: std::env::var("CONTRACTS_STATE_TRANSITION_PROXY_ADDR")? - .parse()?, - transparent_proxy_admin_addr: std::env::var("CONTRACTS_TRANSPARENT_PROXY_ADMIN_ADDR")? - .parse()?, + bridgehub_proxy_addr: std::env::var(format!( + "{variant}CONTRACTS_BRIDGEHUB_PROXY_ADDR" + ))? + .parse()?, + state_transition_proxy_addr: std::env::var(format!( + "{variant}CONTRACTS_STATE_TRANSITION_PROXY_ADDR" + ))? + .parse()?, + transparent_proxy_admin_addr: std::env::var(format!( + "{variant}CONTRACTS_TRANSPARENT_PROXY_ADMIN_ADDR" + ))? + .parse()?, + // Not supported yet + l1_bytecodes_supplier_addr: None, }) } } impl FromEnv for ContractsConfig { fn from_env() -> anyhow::Result { - let mut contracts: ContractsConfig = envy_load("contracts", "CONTRACTS_")?; + Self::from_env_variant("".to_string()) + } +} + +impl FromEnvVariant for ContractsConfig { + fn from_env_variant(variant: String) -> anyhow::Result { + let mut contracts: ContractsConfig = + envy_load("contracts", &format!("{variant}CONTRACTS_"))?; // Note: we are renaming the bridge, the address remains the same // These two config variables should always have the same value. // TODO(EVM-578): double check and potentially forbid both of them being `None`. @@ -35,7 +56,7 @@ impl FromEnv for ContractsConfig { panic!("L2 erc20 bridge address and L2 shared bridge address are different."); } } - contracts.ecosystem_contracts = EcosystemContracts::from_env().ok(); + contracts.ecosystem_contracts = EcosystemContracts::from_env_variant(variant).ok(); Ok(contracts) } } @@ -70,8 +91,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), + base_token_asset_id: None, + predeployed_l2_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/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/env_config/src/lib.rs b/core/lib/env_config/src/lib.rs index 325288056b35..eca003fd59cd 100644 --- a/core/lib/env_config/src/lib.rs +++ b/core/lib/env_config/src/lib.rs @@ -39,6 +39,10 @@ pub trait FromEnv: Sized { fn from_env() -> anyhow::Result; } +pub trait FromEnvVariant: Sized { + fn from_env_variant(variant_prefix: String) -> anyhow::Result; +} + /// Convenience function that loads the structure from the environment variable given the prefix. /// Panics if the config cannot be loaded from the environment variables. pub fn envy_load(name: &str, prefix: &str) -> anyhow::Result { diff --git a/core/lib/env_config/src/wallets.rs b/core/lib/env_config/src/wallets.rs index 3518d56f7b45..4cc0b049fc43 100644 --- a/core/lib/env_config/src/wallets.rs +++ b/core/lib/env_config/src/wallets.rs @@ -36,6 +36,7 @@ impl FromEnv for Wallets { Some(EthSender { operator, blob_operator, + gateway: None, }) } else { None 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 4332c0327ff1..21d83cca8468 100644 --- a/core/lib/multivm/src/utils/mod.rs +++ b/core/lib/multivm/src/utils/mod.rs @@ -517,6 +517,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 + | version @ VmVersion::Vm1_5_0IncreasedBootloaderMemory + | version @ 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/shadow/tests.rs b/core/lib/multivm/src/versions/shadow/tests.rs index 354459853f11..eef124ccd303 100644 --- a/core/lib/multivm/src/versions/shadow/tests.rs +++ b/core/lib/multivm/src/versions/shadow/tests.rs @@ -121,10 +121,13 @@ impl TestedVm for ShadowedFastVm { } fn push_transaction_with_refund(&mut self, tx: Transaction, refund: u64) { - self.get_mut("push_transaction_with_refund", |r| match r { - ShadowMut::Main(vm) => vm.push_transaction_with_refund(tx.clone(), refund), - ShadowMut::Shadow(vm) => vm.push_transaction_with_refund(tx.clone(), refund), - }); + self.get_mut( + "push_transaction_with_refund_and_compression", + |r| match r { + ShadowMut::Main(vm) => vm.push_transaction_with_refund(tx.clone(), refund), + ShadowMut::Shadow(vm) => vm.push_transaction_with_refund(tx.clone(), refund), + }, + ); } fn pubdata_input(&self) -> PubdataInput { @@ -308,7 +311,6 @@ mod l1_messenger { use crate::versions::testonly::l1_messenger::*; #[test] - #[ignore] // Requires post-gateway system contracts fn rollup_da_output_hash_match() { test_rollup_da_output_hash_match::(); } 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..bd2154ba3cf6 100644 --- a/core/lib/multivm/src/versions/testonly/l2_blocks.rs +++ b/core/lib/multivm/src/versions/testonly/l2_blocks.rs @@ -23,6 +23,7 @@ use crate::{ vm_latest::{ constants::{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/testonly/refunds.rs b/core/lib/multivm/src/versions/testonly/refunds.rs index 384a3edb7dbd..4d549f5a9be0 100644 --- a/core/lib/multivm/src/versions/testonly/refunds.rs +++ b/core/lib/multivm/src/versions/testonly/refunds.rs @@ -140,7 +140,7 @@ pub(crate) fn test_predetermined_refunded_gas() { current_state_without_predefined_refunds.user_l2_to_l1_logs ); - assert_ne!( + assert_eq!( current_state_with_changed_predefined_refunds.system_logs, current_state_without_predefined_refunds.system_logs ); 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 ca9ba097d472..0a84d02a96f9 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 79f20660c149..65abb389eee6 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..5fb8a9eb4d7c 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) } } @@ -242,7 +250,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..9fad4ddce45a 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 @@ -1,4 +1,4 @@ -use zksync_types::{ethabi, h256_to_u256, ProtocolVersionId, U256}; +use zksync_types::{ethabi, h256_to_u256, vm::VmVersion, ProtocolVersionId, U256}; use super::{l2_block::BootloaderL2Block, tx::BootloaderTx}; use crate::{ @@ -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()), @@ -139,34 +153,46 @@ pub(crate) fn apply_pubdata_to_memory( pubdata_information: &PubdataInput, protocol_version: ProtocolVersionId, ) { - 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 subversion = MultiVmSubversion::try_from(VmVersion::from(protocol_version)).unwrap(); + 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 c935b1c0e7f5..e2439c4c8060 100644 --- a/core/lib/multivm/src/versions/vm_fast/vm.rs +++ b/core/lib/multivm/src/versions/vm_fast/vm.rs @@ -52,8 +52,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, }; @@ -286,7 +287,7 @@ impl Vm { 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 @@ -319,7 +320,7 @@ impl Vm { 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 => { 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..2719853e0024 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) } } @@ -247,7 +257,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..a1d45334e9bf 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 @@ -1,4 +1,4 @@ -use zksync_types::{ethabi, h256_to_u256, ProtocolVersionId, U256}; +use zksync_types::{ethabi, h256_to_u256, vm::VmVersion, ProtocolVersionId, U256}; use super::tx::BootloaderTx; use crate::{ @@ -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()), @@ -144,40 +156,46 @@ pub(crate) fn apply_pubdata_to_memory( pubdata_information: &PubdataInput, protocol_version: ProtocolVersionId, ) { - 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 subversion = MultiVmSubversion::try_from(VmVersion::from(protocol_version)).unwrap(); + 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 c95771f9e849..a91090f21a69 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/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 0a7df48df80f..87a6b7ee0797 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 ff83abc45fcf..de0a28a00a5d 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..3ef1bd871dad 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")?, + base_token_asset_id: l1 + .base_token_asset_id + .as_ref() + .map(|x| parse_h256(x)) + .transpose() + .context("base_token_asset_id")?, + predeployed_l2_wrapped_base_token_address: l2 + .predeployed_l2_wrapped_base_token_address + .as_ref() + .map(|x| parse_h160(x)) + .transpose() + .context("predeployed_l2_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.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_l2_wrapped_base_token_address: this + .predeployed_l2_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..a1d6310572ce 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_l2_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/protobuf_config/src/wallets.rs b/core/lib/protobuf_config/src/wallets.rs index 3769dac443d0..e095de83b511 100644 --- a/core/lib/protobuf_config/src/wallets.rs +++ b/core/lib/protobuf_config/src/wallets.rs @@ -37,6 +37,7 @@ impl ProtoRepr for proto::Wallets { Some(EthSender { operator, blob_operator, + gateway: None, }) } else { None diff --git a/core/lib/types/Cargo.toml b/core/lib/types/Cargo.toml index 325fe22209a7..a5b87289ca84 100644 --- a/core/lib/types/Cargo.toml +++ b/core/lib/types/Cargo.toml @@ -19,6 +19,7 @@ zksync_mini_merkle_tree.workspace = true zksync_protobuf.workspace = true zksync_crypto_primitives.workspace = true +async-trait.workspace = true anyhow.workspace = true chrono = { workspace = true, features = ["serde"] } derive_more = { workspace = true, features = ["debug"] } @@ -35,6 +36,7 @@ num_enum.workspace = true hex.workspace = true prost.workspace = true itertools.workspace = true +ethabi.workspace = true tracing.workspace = true # Crypto stuff 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/lib/zksync_core_leftovers/src/temp_config_store/mod.rs b/core/lib/zksync_core_leftovers/src/temp_config_store/mod.rs index 5faef68507fa..38748c463979 100644 --- a/core/lib/zksync_core_leftovers/src/temp_config_store/mod.rs +++ b/core/lib/zksync_core_leftovers/src/temp_config_store/mod.rs @@ -139,6 +139,7 @@ impl TempConfigStore { Some(EthSender { operator, blob_operator, + gateway: None, }) }); let state_keeper = self 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/metrics.rs b/core/node/api_server/src/web3/metrics.rs index 9d8cbf813b03..2b78f58a16d2 100644 --- a/core/node/api_server/src/web3/metrics.rs +++ b/core/node/api_server/src/web3/metrics.rs @@ -394,7 +394,9 @@ impl ApiMetrics { Web3Error::ProxyError(err) => { tracing::warn!("Error proxying call to main node in method `{method}`: {err}"); } - _ => { /* do nothing */ } + _ => { + tracing::debug!("Error in method `{method}`: {err:#}"); + } } let labels = Web3ErrorLabels { 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..cefcfa535f55 100644 --- a/core/node/eth_sender/Cargo.toml +++ b/core/node/eth_sender/Cargo.toml @@ -24,6 +24,7 @@ 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 tokio = { workspace = true, features = ["time"] } anyhow.workspace = true diff --git a/core/node/eth_sender/src/aggregator.rs b/core/node/eth_sender/src/aggregator.rs index 8b0d8dfecea0..d67fe4df5c78 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, Core, CoreDal, DalError}; 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, @@ -39,17 +42,29 @@ pub struct Aggregator { operate_4844_mode: bool, pubdata_da: PubdataSendingMode, commitment_mode: L1BatchCommitmentMode, + priority_merkle_tree: MiniMerkleTree, } impl Aggregator { - pub fn new( + pub async fn new( config: SenderConfig, blob_store: Arc, custom_commit_sender_addr: Option
, commitment_mode: L1BatchCommitmentMode, + connection: &mut Connection<'_, Core>, settlement_mode: SettlementMode, - ) -> Self { + ) -> anyhow::Result { let pubdata_da = config.pubdata_sending_mode; + + let priority_tree_start_index = config.priority_tree_start_index.unwrap_or(0); + let priority_op_hashes = connection + .transactions_dal() + .get_l1_transactions_hashes(priority_tree_start_index) + .await + .map_err(DalError::generalize)?; + let priority_merkle_tree = + MiniMerkleTree::::from_hashes(KeccakHasher, priority_op_hashes.into_iter(), None); + let operate_4844_mode = custom_commit_sender_addr.is_some() && !settlement_mode.is_gateway(); @@ -121,7 +136,7 @@ impl Aggregator { })] }; - Self { + Ok(Self { commit_criteria, proof_criteria: vec![Box::from(NumberCriterion { op: AggregatedActionType::PublishProofOnchain, @@ -133,7 +148,8 @@ impl Aggregator { operate_4844_mode, pubdata_da, commitment_mode, - } + priority_merkle_tree, + }) } pub async fn get_next_ready_operation( @@ -199,11 +215,60 @@ impl Aggregator { ready_for_execute_batches, last_sealed_l1_batch, ) - .await; + .await?; + + let priority_tree_start_index = self.config.priority_tree_start_index.unwrap_or(0); + let mut priority_ops_proofs = vec![]; + for batch in l1_batches.iter() { + let first_priority_op_id_option = match storage + .blocks_dal() + .get_batch_first_priority_op_id(batch.header.number) + .await + .unwrap() + { + // Batch has no priority ops, no proofs to send + None => None, + // We haven't started to use the priority tree in the contracts yet + Some(id) if id < priority_tree_start_index => None, + Some(id) => Some(id), + }; + + let count = batch.header.l1_tx_count as usize; + if let Some(first_priority_op_id_in_batch) = first_priority_op_id_option { + let priority_tree_start_index = self.config.priority_tree_start_index.unwrap_or(0); + let new_l1_tx_hashes = storage + .transactions_dal() + .get_l1_transactions_hashes( + priority_tree_start_index + self.priority_merkle_tree.length(), + ) + .await + .unwrap(); + for hash in new_l1_tx_hashes { + self.priority_merkle_tree.push_hash(hash); + } + + self.priority_merkle_tree.trim_start( + first_priority_op_id_in_batch // global index + - priority_tree_start_index // first index when tree is activated + - self.priority_merkle_tree.start_index(), // first index in the tree + ); + let (_, left, right) = self + .priority_merkle_tree + .merkle_root_and_paths_for_range(..count); + let hashes = self.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/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..ec7d627c6d95 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,7 @@ 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_db_connection::error::DalError; use zksync_eth_client::BoundEthInterface; use zksync_eth_sender::{Aggregator, EthTxAggregator}; use zksync_types::{commitment::L1BatchCommitmentMode, settlement::SettlementMode, L2ChainId}; @@ -8,7 +9,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 +45,7 @@ use crate::{ pub struct EthTxAggregatorLayer { eth_sender_config: EthConfig, contracts_config: ContractsConfig, + gateway_contracts_config: Option, zksync_network_id: L2ChainId, l1_batch_commit_data_generator_mode: L1BatchCommitmentMode, settlement_mode: SettlementMode, @@ -53,6 +58,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 +77,7 @@ impl EthTxAggregatorLayer { pub fn new( eth_sender_config: EthConfig, contracts_config: ContractsConfig, + gateway_contracts_config: Option, zksync_network_id: L2ChainId, l1_batch_commit_data_generator_mode: L1BatchCommitmentMode, settlement_mode: SettlementMode, @@ -78,6 +85,7 @@ impl EthTxAggregatorLayer { Self { eth_sender_config, contracts_config, + gateway_contracts_config, zksync_network_id, l1_batch_commit_data_generator_mode, settlement_mode, @@ -95,7 +103,44 @@ 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_contracts_config); // Get resources. + + let (validator_timelock_addr, multicall3_addr, diamond_proxy_addr) = + if self.settlement_mode.is_gateway() { + ( + self.gateway_contracts_config + .clone() + .unwrap() + .validator_timelock_addr, + self.gateway_contracts_config + .clone() + .unwrap() + .multicall3_addr, + self.gateway_contracts_config + .clone() + .unwrap() + .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.unwrap().0 + } else { + input.eth_client.unwrap().0 + }; let master_pool = input.master_pool.get().await.unwrap(); let replica_pool = input.replica_pool.get().await.unwrap(); @@ -108,22 +153,29 @@ impl WiringLayer for EthTxAggregatorLayer { .map(BoundEthInterface::sender_account); let config = self.eth_sender_config.sender.context("sender")?; + let mut connection = replica_pool + .connection_tagged("eth_sender") + .await + .map_err(DalError::generalize)?; let aggregator = Aggregator::new( config.clone(), object_store, eth_client_blobs_addr, self.l1_batch_commit_data_generator_mode, + &mut connection, self.settlement_mode, - ); + ) + .await?; + drop(connection); 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..9d5b230dba00 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,11 @@ -use zksync_config::{ContractsConfig, EthWatchConfig}; +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 +22,8 @@ use crate::{ pub struct EthWatchLayer { eth_watch_config: EthWatchConfig, contracts_config: ContractsConfig, + gateway_contracts_config: Option, + settlement_mode: SettlementMode, chain_id: L2ChainId, } @@ -30,6 +32,7 @@ pub struct EthWatchLayer { pub struct Input { pub master_pool: PoolResource, pub eth_client: EthInterfaceResource, + pub gateway_client: Option, } #[derive(Debug, IntoContext)] @@ -43,11 +46,15 @@ impl EthWatchLayer { pub fn new( eth_watch_config: EthWatchConfig, contracts_config: ContractsConfig, + gateway_contracts_config: Option, + settlement_mode: SettlementMode, chain_id: L2ChainId, ) -> Self { Self { eth_watch_config, contracts_config, + gateway_contracts_config, + settlement_mode, chain_id, } } @@ -66,21 +73,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_contracts_config + .clone() + .unwrap() + .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_contracts_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..c3788fa782bc 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_contracts_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_contracts_config: Option, wallets: wallets::EthSender, ) -> Self { Self { eth_sender_config, contracts_config, - sl_chain_id, + gateway_contracts_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,41 @@ 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 input.gateway_client.is_some() { + if self + .gateway_contracts_config + .clone() + .is_some_and(|v| v.gateway_chain_id.0 != 0_u64) + { + let private_key = self.wallets.operator.private_key(); + let GatewayEthInterfaceResource(gateway_client) = input.gateway_client.unwrap(); + let gateway_contracts = self.gateway_contracts_config.unwrap(); + 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, + ); + 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/prover/Cargo.lock b/prover/Cargo.lock index 453a768c0678..7b8d16c2be89 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -8939,10 +8939,12 @@ name = "zksync_types" version = "0.1.0" dependencies = [ "anyhow", + "async-trait", "bigdecimal", "blake2 0.10.6", "chrono", "derive_more", + "ethabi", "hex", "itertools 0.10.5", "num", diff --git a/zkstack_cli/Cargo.lock b/zkstack_cli/Cargo.lock index 900ac677fd61..4f4c9add4be0 100644 --- a/zkstack_cli/Cargo.lock +++ b/zkstack_cli/Cargo.lock @@ -6965,10 +6965,12 @@ name = "zksync_types" version = "0.1.0" dependencies = [ "anyhow", + "async-trait", "bigdecimal", "blake2", "chrono", "derive_more", + "ethabi", "hex", "itertools 0.10.5", "num", 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, };