diff --git a/checks-config/era.dic b/checks-config/era.dic index b5ef3712f51a..22d6189ca671 100644 --- a/checks-config/era.dic +++ b/checks-config/era.dic @@ -411,6 +411,10 @@ prode StorageBatchInfo CommitBatchInfo IExecutor +SetChainId +setChainId +SetChainIdUpgrade +state_transition_manager_contract // Names Vyper diff --git a/core/bin/external_node/src/config/mod.rs b/core/bin/external_node/src/config/mod.rs index e37a71d79c6f..a4a6bc39b48b 100644 --- a/core/bin/external_node/src/config/mod.rs +++ b/core/bin/external_node/src/config/mod.rs @@ -28,6 +28,7 @@ const BYTES_IN_MEGABYTE: usize = 1_024 * 1_024; /// This part of the external node config is fetched directly from the main node. #[derive(Debug, Deserialize, Clone, PartialEq)] pub struct RemoteENConfig { + pub bridgehub_proxy_addr: Option
, pub diamond_proxy_addr: Address, pub l1_erc20_bridge_proxy_addr: Address, pub l2_erc20_bridge_addr: Address, @@ -48,6 +49,8 @@ impl RemoteENConfig { .get_testnet_paymaster() .rpc_context("get_testnet_paymaster") .await?; + // In case EN is connected to the old server version without `get_bridgehub_contract` method. + let bridgehub_proxy_addr = client.get_bridgehub_contract().await.ok().flatten(); let diamond_proxy_addr = client .get_main_contract() .rpc_context("get_main_contract") @@ -59,6 +62,7 @@ impl RemoteENConfig { let l1_chain_id = L1ChainId(l1_chain_id.as_u64()); Ok(Self { + bridgehub_proxy_addr, diamond_proxy_addr, l2_testnet_paymaster_addr, l1_erc20_bridge_proxy_addr: bridges.l1_erc20_default_bridge, @@ -557,6 +561,7 @@ impl From for InternalApiConfig { l1_weth_bridge: config.remote.l1_weth_bridge_proxy_addr, l2_weth_bridge: config.remote.l2_weth_bridge_addr, }, + bridgehub_proxy_addr: config.remote.bridgehub_proxy_addr, diamond_proxy_addr: config.remote.diamond_proxy_addr, l2_testnet_paymaster_addr: config.remote.l2_testnet_paymaster_addr, req_entities_limit: config.optional.req_entities_limit, diff --git a/core/bin/zksync_server/src/main.rs b/core/bin/zksync_server/src/main.rs index b17913d30947..2deb64d45722 100644 --- a/core/bin/zksync_server/src/main.rs +++ b/core/bin/zksync_server/src/main.rs @@ -37,6 +37,10 @@ struct Cli { /// Generate genesis block for the first contract deployment using temporary DB. #[arg(long)] genesis: bool, + /// Wait for the `setChainId` event during genesis. + /// If `--genesis` is not set, this flag is ignored. + #[arg(long)] + set_chain_id: bool, /// Rebuild tree. #[arg(long)] rebuild_tree: bool, @@ -142,6 +146,7 @@ async fn main() -> anyhow::Result<()> { &network, &contracts, ð_client.web3_url, + opt.set_chain_id, ) .await .context("genesis_init")?; diff --git a/core/lib/config/src/configs/contracts.rs b/core/lib/config/src/configs/contracts.rs index a05027e0bf0d..70d4e685586f 100644 --- a/core/lib/config/src/configs/contracts.rs +++ b/core/lib/config/src/configs/contracts.rs @@ -40,6 +40,13 @@ pub struct ContractsConfig { pub fri_recursion_leaf_level_vk_hash: H256, pub prover_at_genesis: ProverAtGenesis, pub snark_wrapper_vk_hash: H256, + + // These contracts will be used after shared bridge integration. + pub bridgehub_proxy_addr: Option
, + pub bridgehub_impl_addr: Option
, + pub state_transition_proxy_addr: Option
, + pub state_transition_impl_addr: Option
, + pub transparent_proxy_admin_addr: Option
, } impl ContractsConfig { @@ -52,6 +59,7 @@ impl ContractsConfig { mailbox_facet_addr: Address::repeat_byte(0x01), executor_facet_addr: Address::repeat_byte(0x02), admin_facet_addr: Address::repeat_byte(0x03), + transparent_proxy_admin_addr: Some(Address::repeat_byte(0x04)), getters_facet_addr: Address::repeat_byte(0x05), verifier_addr: Address::repeat_byte(0x06), diamond_init_addr: Address::repeat_byte(0x07), @@ -77,6 +85,10 @@ impl ContractsConfig { governance_addr: Address::repeat_byte(0x13), prover_at_genesis: ProverAtGenesis::Fri, snark_wrapper_vk_hash: H256::repeat_byte(0x09), + bridgehub_proxy_addr: Some(Address::repeat_byte(0x14)), + bridgehub_impl_addr: Some(Address::repeat_byte(0x15)), + state_transition_proxy_addr: Some(Address::repeat_byte(0x16)), + state_transition_impl_addr: Some(Address::repeat_byte(0x17)), } } } diff --git a/core/lib/config/src/testonly.rs b/core/lib/config/src/testonly.rs index 3d9fa817bd2b..065e1b3e6054 100644 --- a/core/lib/config/src/testonly.rs +++ b/core/lib/config/src/testonly.rs @@ -363,6 +363,11 @@ impl RandomConfig for configs::ContractsConfig { fri_recursion_leaf_level_vk_hash: g.gen(), prover_at_genesis: g.gen(), snark_wrapper_vk_hash: g.gen(), + bridgehub_impl_addr: g.gen(), + bridgehub_proxy_addr: g.gen(), + state_transition_proxy_addr: g.gen(), + state_transition_impl_addr: g.gen(), + transparent_proxy_admin_addr: g.gen(), } } } diff --git a/core/lib/contracts/src/lib.rs b/core/lib/contracts/src/lib.rs index f6bfd9c1e4a3..065008d5dbce 100644 --- a/core/lib/contracts/src/lib.rs +++ b/core/lib/contracts/src/lib.rs @@ -11,7 +11,7 @@ use std::{ use ethabi::{ ethereum_types::{H256, U256}, - Contract, Function, + Contract, Event, Function, }; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; @@ -489,358 +489,94 @@ pub static PRE_BOOJUM_COMMIT_FUNCTION: Lazy = Lazy::new(|| { serde_json::from_str(abi).unwrap() }); -pub static PRE_BOOJUM_PROVE_FUNCTION: Lazy = Lazy::new(|| { - let abi = r#" - { +pub static SET_CHAIN_ID_EVENT: Lazy = Lazy::new(|| { + let abi = r#"{ + "anonymous": false, "inputs": [ { - "components": [ - { - "internalType": "uint64", - "name": "blockNumber", - "type": "uint64" - }, - { - "internalType": "bytes32", - "name": "blockHash", - "type": "bytes32" - }, - { - "internalType": "uint64", - "name": "indexRepeatedStorageChanges", - "type": "uint64" - }, - { - "internalType": "uint256", - "name": "numberOfLayer1Txs", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "priorityOperationsHash", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "l2LogsTreeRoot", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "timestamp", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "commitment", - "type": "bytes32" - } - ], - "internalType": "struct IExecutor.StoredBlockInfo", - "name": "_prevBlock", - "type": "tuple" + "indexed": true, + "name": "_stateTransitionChain", + "type": "address" }, { "components": [ { - "internalType": "uint64", - "name": "blockNumber", - "type": "uint64" - }, - { - "internalType": "bytes32", - "name": "blockHash", - "type": "bytes32" - }, - { - "internalType": "uint64", - "name": "indexRepeatedStorageChanges", - "type": "uint64" + "name": "txType", + "type": "uint256" }, { - "internalType": "uint256", - "name": "numberOfLayer1Txs", + "name": "from", "type": "uint256" }, { - "internalType": "bytes32", - "name": "priorityOperationsHash", - "type": "bytes32" + "name": "to", + "type": "uint256" }, { - "internalType": "bytes32", - "name": "l2LogsTreeRoot", - "type": "bytes32" + "name": "gasLimit", + "type": "uint256" }, { - "internalType": "uint256", - "name": "timestamp", + "name": "gasPerPubdataByteLimit", "type": "uint256" }, { - "internalType": "bytes32", - "name": "commitment", - "type": "bytes32" - } - ], - "internalType": "struct IExecutor.StoredBlockInfo[]", - "name": "_committedBlocks", - "type": "tuple[]" - }, - { - "components": [ - { - "internalType": "uint256[]", - "name": "recursiveAggregationInput", - "type": "uint256[]" + "name": "maxFeePerGas", + "type": "uint256" }, { - "internalType": "uint256[]", - "name": "serializedProof", - "type": "uint256[]" - } - ], - "internalType": "struct IExecutor.ProofInput", - "name": "_proof", - "type": "tuple" - } - ], - "name": "proveBlocks", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }"#; - serde_json::from_str(abi).unwrap() -}); - -pub static PRE_BOOJUM_EXECUTE_FUNCTION: Lazy = Lazy::new(|| { - let abi = r#" - { - "inputs": [ - { - "components": [ - { - "internalType": "uint64", - "name": "blockNumber", - "type": "uint64" + "name": "maxPriorityFeePerGas", + "type": "uint256" }, { - "internalType": "bytes32", - "name": "blockHash", - "type": "bytes32" + "name": "paymaster", + "type": "uint256" }, { - "internalType": "uint64", - "name": "indexRepeatedStorageChanges", - "type": "uint64" + "name": "nonce", + "type": "uint256" }, { - "internalType": "uint256", - "name": "numberOfLayer1Txs", + "name": "value", "type": "uint256" }, { - "internalType": "bytes32", - "name": "priorityOperationsHash", - "type": "bytes32" + "name": "reserved", + "type": "uint256[4]" }, { - "internalType": "bytes32", - "name": "l2LogsTreeRoot", - "type": "bytes32" + "name": "data", + "type": "bytes" }, { - "internalType": "uint256", - "name": "timestamp", - "type": "uint256" + "name": "signature", + "type": "bytes" }, { - "internalType": "bytes32", - "name": "commitment", - "type": "bytes32" - } - ], - "internalType": "struct IExecutor.StoredBlockInfo[]", - "name": "_blocksData", - "type": "tuple[]" - } - ], - "name": "executeBlocks", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }"#; - serde_json::from_str(abi).unwrap() -}); - -pub static PRE_BOOJUM_GET_VK_FUNCTION: Lazy = Lazy::new(|| { - let abi = r#"{ - "inputs": [], - "name": "get_verification_key", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "domain_size", - "type": "uint256" + "name": "factoryDeps", + "type": "uint256[]" }, { - "internalType": "uint256", - "name": "num_inputs", - "type": "uint256" + "name": "paymasterInput", + "type": "bytes" }, { - "components": [ - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "internalType": "struct PairingsBn254.Fr", - "name": "omega", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "X", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "Y", - "type": "uint256" - } - ], - "internalType": "struct PairingsBn254.G1Point[2]", - "name": "gate_selectors_commitments", - "type": "tuple[2]" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "X", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "Y", - "type": "uint256" - } - ], - "internalType": "struct PairingsBn254.G1Point[8]", - "name": "gate_setup_commitments", - "type": "tuple[8]" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "X", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "Y", - "type": "uint256" - } - ], - "internalType": "struct PairingsBn254.G1Point[4]", - "name": "permutation_commitments", - "type": "tuple[4]" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "X", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "Y", - "type": "uint256" - } - ], - "internalType": "struct PairingsBn254.G1Point", - "name": "lookup_selector_commitment", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "X", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "Y", - "type": "uint256" - } - ], - "internalType": "struct PairingsBn254.G1Point[4]", - "name": "lookup_tables_commitments", - "type": "tuple[4]" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "X", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "Y", - "type": "uint256" - } - ], - "internalType": "struct PairingsBn254.G1Point", - "name": "lookup_table_type_commitment", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "internalType": "struct PairingsBn254.Fr[3]", - "name": "non_residues", - "type": "tuple[3]" - }, - { - "components": [ - { - "internalType": "uint256[2]", - "name": "X", - "type": "uint256[2]" - }, - { - "internalType": "uint256[2]", - "name": "Y", - "type": "uint256[2]" - } - ], - "internalType": "struct PairingsBn254.G2Point[2]", - "name": "g2_elements", - "type": "tuple[2]" + "name": "reservedDynamic", + "type": "bytes" } ], - "internalType": "struct VerificationKey", - "name": "vk", + "indexed": false, + "name": "_l2Transaction", "type": "tuple" + }, + { + "indexed": true, + "name": "_protocolVersion", + "type": "uint256" } ], - "stateMutability": "pure", - "type": "function" + "name": "SetChainIdUpgrade", + "type": "event" }"#; serde_json::from_str(abi).unwrap() }); diff --git a/core/lib/dal/.sqlx/query-a8dad09a54cc991706141da557dde45f8f3f3b6cf2e4136f25a1c3ab85d92668.json b/core/lib/dal/.sqlx/query-a8dad09a54cc991706141da557dde45f8f3f3b6cf2e4136f25a1c3ab85d92668.json new file mode 100644 index 000000000000..1fd7b8fdf1b3 --- /dev/null +++ b/core/lib/dal/.sqlx/query-a8dad09a54cc991706141da557dde45f8f3f3b6cf2e4136f25a1c3ab85d92668.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE protocol_versions\n SET\n upgrade_tx_hash = $1\n WHERE\n id = $2\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Bytea", + "Int4" + ] + }, + "nullable": [] + }, + "hash": "a8dad09a54cc991706141da557dde45f8f3f3b6cf2e4136f25a1c3ab85d92668" +} diff --git a/core/lib/dal/src/protocol_versions_dal.rs b/core/lib/dal/src/protocol_versions_dal.rs index 63f09dde5286..7c2079ec4268 100644 --- a/core/lib/dal/src/protocol_versions_dal.rs +++ b/core/lib/dal/src/protocol_versions_dal.rs @@ -65,7 +65,7 @@ impl ProtocolVersionsDal<'_, '_> { base_system_contracts_hashes.bootloader.as_bytes(), base_system_contracts_hashes.default_aa.as_bytes(), verifier_address.as_bytes(), - tx_hash.map(|tx_hash| tx_hash.0.to_vec()), + tx_hash.as_ref().map(H256::as_bytes), ) .execute(self.storage.conn()) .await @@ -98,6 +98,47 @@ impl ProtocolVersionsDal<'_, '_> { db_transaction.commit().await.unwrap(); } + async fn save_genesis_upgrade_tx_hash(&mut self, id: ProtocolVersionId, tx_hash: Option) { + sqlx::query!( + r#" + UPDATE protocol_versions + SET + upgrade_tx_hash = $1 + WHERE + id = $2 + "#, + tx_hash.as_ref().map(H256::as_bytes), + id as i32, + ) + .execute(self.storage.conn()) + .await + .unwrap(); + } + + /// Attaches a transaction used to set ChainId to the genesis protocol version. + /// Also inserts that transaction into the database. + pub async fn save_genesis_upgrade_with_tx( + &mut self, + id: ProtocolVersionId, + tx: ProtocolUpgradeTx, + ) { + let tx_hash = Some(tx.common_data.hash()); + + let mut db_transaction = self.storage.start_transaction().await.unwrap(); + + db_transaction + .transactions_dal() + .insert_system_transaction(tx) + .await; + + db_transaction + .protocol_versions_dal() + .save_genesis_upgrade_tx_hash(id, tx_hash) + .await; + + db_transaction.commit().await.unwrap(); + } + pub async fn base_system_contracts_by_timestamp( &mut self, current_timestamp: u64, diff --git a/core/lib/env_config/src/chain.rs b/core/lib/env_config/src/chain.rs index c258c5092e51..b5028e991076 100644 --- a/core/lib/env_config/src/chain.rs +++ b/core/lib/env_config/src/chain.rs @@ -125,6 +125,8 @@ mod tests { CHAIN_STATE_KEEPER_SAVE_CALL_TRACES="false" CHAIN_STATE_KEEPER_UPLOAD_WITNESS_INPUTS_TO_GCS="false" CHAIN_STATE_KEEPER_ENUM_INDEX_MIGRATION_CHUNK_SIZE="2000" + CHAIN_STATE_KEEPER_VIRTUAL_BLOCKS_PER_MINIBLOCK="1" + CHAIN_STATE_KEEPER_VIRTUAL_BLOCKS_INTERVAL="1" "#; lock.set_env(config); diff --git a/core/lib/env_config/src/contracts.rs b/core/lib/env_config/src/contracts.rs index 537b68414c63..bf2dc31d7bb3 100644 --- a/core/lib/env_config/src/contracts.rs +++ b/core/lib/env_config/src/contracts.rs @@ -19,6 +19,10 @@ mod tests { fn expected_config() -> ContractsConfig { ContractsConfig { + bridgehub_proxy_addr: Some(addr("35ea7f92f4c5f433efe15284e99c040110cf6297")), + bridgehub_impl_addr: Some(addr("87d456da9ed212eb49d80d96afb44afddf36adf8")), + state_transition_proxy_addr: Some(addr("d90f1c081c6117241624e97cb6147257c3cb2097")), + state_transition_impl_addr: Some(addr("c957c0e82d3bafb5ad46ffbcc66900648784eb05")), governance_addr: addr("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"), mailbox_facet_addr: addr("0f6Fa881EF414Fc6E818180657c2d5CD7Ac6cCAd"), executor_facet_addr: addr("18B631537801963A964211C0E86645c1aBfbB2d3"), @@ -28,6 +32,7 @@ mod tests { diamond_init_addr: addr("FFC35A5e767BE36057c34586303498e3de7C62Ba"), diamond_upgrade_init_addr: addr("FFC35A5e767BE36057c34586303498e3de7C62Ba"), diamond_proxy_addr: addr("F00B988a98Ca742e7958DeF9F7823b5908715f4a"), + transparent_proxy_admin_addr: Some(addr("dd6fa5c14e7550b4caf2aa2818d24c69cbc347e5")), validator_timelock_addr: addr("F00B988a98Ca742e7958DeF9F7823b5908715f4a"), genesis_tx_hash: hash( "b99ebfea46cbe05a21cd80fe5597d97b204befc52a16303f579c607dc1ac2e2e", @@ -100,6 +105,11 @@ CONTRACTS_FRI_RECURSION_NODE_LEVEL_VK_HASH="0x5a3ef282b21e12fe1f4438e5bb158fc506 CONTRACTS_FRI_RECURSION_LEAF_LEVEL_VK_HASH="0x72167c43a46cf38875b267d67716edc4563861364a3c03ab7aee73498421e828" CONTRACTS_PROVER_AT_GENESIS="fri" CONTRACTS_SNARK_WRAPPER_VK_HASH="0x4be443afd605a782b6e56d199df2460a025c81b3dea144e135bece83612563f2" +CONTRACTS_BRIDGEHUB_PROXY_ADDR="0x35ea7f92f4c5f433efe15284e99c040110cf6297" +CONTRACTS_BRIDGEHUB_IMPL_ADDR="0x87d456da9ed212eb49d80d96afb44afddf36adf8" +CONTRACTS_STATE_TRANSITION_PROXY_ADDR="0xd90f1c081c6117241624e97cb6147257c3cb2097" +CONTRACTS_STATE_TRANSITION_IMPL_ADDR="0xc957c0e82d3bafb5ad46ffbcc66900648784eb05" +CONTRACTS_TRANSPARENT_PROXY_ADMIN_ADDR="0xdd6fa5c14e7550b4caf2aa2818d24c69cbc347e5" "#; lock.set_env(config); diff --git a/core/lib/protobuf_config/src/contracts.rs b/core/lib/protobuf_config/src/contracts.rs index 6521bd0bf810..0a55f9b1145f 100644 --- a/core/lib/protobuf_config/src/contracts.rs +++ b/core/lib/protobuf_config/src/contracts.rs @@ -122,6 +122,36 @@ impl ProtoRepr for proto::Contracts { snark_wrapper_vk_hash: required(&self.snark_wrapper_vk_hash) .and_then(|x| parse_h256(x)) .context("snark_wrapper_vk_hash")?, + bridgehub_proxy_addr: self + .bridgehub_proxy_addr + .as_ref() + .map(|x| parse_h160(x)) + .transpose() + .context("bridgehub_proxy_addr")?, + bridgehub_impl_addr: self + .bridgehub_impl_addr + .as_ref() + .map(|x| parse_h160(x)) + .transpose() + .context("bridgehub_impl_addr")?, + state_transition_proxy_addr: self + .state_transition_proxy_addr + .as_ref() + .map(|x| parse_h160(x)) + .transpose() + .context("state_transition_proxy_addr")?, + state_transition_impl_addr: self + .state_transition_impl_addr + .as_ref() + .map(|x| parse_h160(x)) + .transpose() + .context("state_transition_impl_addr")?, + transparent_proxy_admin_addr: self + .transparent_proxy_admin_addr + .as_ref() + .map(|x| parse_h160(x)) + .transpose() + .context("transparent_proxy_admin_addr")?, }) } @@ -174,6 +204,26 @@ impl ProtoRepr for proto::Contracts { ), prover_at_genesis: Some(proto::ProverAtGenesis::new(&this.prover_at_genesis).into()), snark_wrapper_vk_hash: Some(this.snark_wrapper_vk_hash.as_bytes().into()), + bridgehub_proxy_addr: this + .bridgehub_proxy_addr + .as_ref() + .map(|x| x.as_bytes().into()), + bridgehub_impl_addr: this + .bridgehub_impl_addr + .as_ref() + .map(|x| x.as_bytes().into()), + state_transition_proxy_addr: this + .state_transition_proxy_addr + .as_ref() + .map(|x| x.as_bytes().into()), + state_transition_impl_addr: this + .state_transition_impl_addr + .as_ref() + .map(|x| x.as_bytes().into()), + transparent_proxy_admin_addr: this + .transparent_proxy_admin_addr + .as_ref() + .map(|x| x.as_bytes().into()), } } } diff --git a/core/lib/protobuf_config/src/proto/contracts.proto b/core/lib/protobuf_config/src/proto/contracts.proto index 1acda022cd90..87fd02b615c2 100644 --- a/core/lib/protobuf_config/src/proto/contracts.proto +++ b/core/lib/protobuf_config/src/proto/contracts.proto @@ -36,4 +36,9 @@ message Contracts { optional bytes fri_recursion_leaf_level_vk_hash = 26; // required; H256 optional ProverAtGenesis prover_at_genesis = 27; // required optional bytes snark_wrapper_vk_hash = 28; // required; H256 + optional bytes bridgehub_proxy_addr = 29; // optional; H160 + optional bytes bridgehub_impl_addr = 30; // optional; H160 + optional bytes state_transition_proxy_addr = 31; // optional; H160 + optional bytes state_transition_impl_addr = 32; // optional; H160 + optional bytes transparent_proxy_admin_addr = 33; // optional; H160 } diff --git a/core/lib/types/rustfmt.toml b/core/lib/types/rustfmt.toml new file mode 100644 index 000000000000..d4fcd2a3e948 --- /dev/null +++ b/core/lib/types/rustfmt.toml @@ -0,0 +1 @@ +merge_derives = false diff --git a/core/lib/types/src/l2/mod.rs b/core/lib/types/src/l2/mod.rs index 2498f3490462..a14374728e33 100644 --- a/core/lib/types/src/l2/mod.rs +++ b/core/lib/types/src/l2/mod.rs @@ -22,7 +22,8 @@ use crate::{ pub mod error; -#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Serialize, Deserialize, TryFromPrimitive)] #[repr(u32)] pub enum TransactionType { // Native ECDSA Transaction diff --git a/core/lib/types/src/protocol_version.rs b/core/lib/types/src/protocol_version.rs index 034987d01c81..639ff7f6192b 100644 --- a/core/lib/types/src/protocol_version.rs +++ b/core/lib/types/src/protocol_version.rs @@ -17,9 +17,8 @@ use crate::{ }; #[repr(u16)] -#[derive( - Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, TryFromPrimitive, Serialize, Deserialize, -)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(TryFromPrimitive, Serialize, Deserialize)] pub enum ProtocolVersionId { Version0 = 0, Version1, @@ -93,6 +92,15 @@ impl ProtocolVersionId { self <= &Self::Version17 } + pub fn is_pre_shared_bridge(&self) -> bool { + // TODO: review this when we actually deploy shared bridge + true + } + + pub fn is_1_4_0(&self) -> bool { + self >= &ProtocolVersionId::Version18 && self < &ProtocolVersionId::Version20 + } + pub fn is_post_1_4_1(&self) -> bool { self >= &ProtocolVersionId::Version20 } @@ -119,9 +127,8 @@ impl TryFrom for ProtocolVersionId { } #[repr(u16)] -#[derive( - Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, TryFromPrimitive, Serialize, Deserialize, -)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[derive(TryFromPrimitive, Serialize, Deserialize)] pub enum FriProtocolVersionId { Version0 = 0, Version1, @@ -227,6 +234,27 @@ pub struct ProtocolUpgrade { pub tx: Option, } +fn get_transaction_param_type() -> ParamType { + ParamType::Tuple(vec![ + ParamType::Uint(256), // `txType` + ParamType::Uint(256), // sender + ParamType::Uint(256), // to + ParamType::Uint(256), // gasLimit + ParamType::Uint(256), // `gasPerPubdataLimit` + ParamType::Uint(256), // maxFeePerGas + ParamType::Uint(256), // maxPriorityFeePerGas + ParamType::Uint(256), // paymaster + ParamType::Uint(256), // nonce (serial ID) + ParamType::Uint(256), // value + ParamType::FixedArray(Box::new(ParamType::Uint(256)), 4), // reserved + ParamType::Bytes, // calldata + ParamType::Bytes, // signature + ParamType::Array(Box::new(ParamType::Uint(256))), // factory deps + ParamType::Bytes, // paymaster input + ParamType::Bytes, // `reservedDynamic` + ]) +} + impl TryFrom for ProtocolUpgrade { type Error = crate::ethabi::Error; @@ -252,24 +280,7 @@ impl TryFrom for ProtocolUpgrade { _ => unreachable!(), }; - let transaction_param_type = ParamType::Tuple(vec![ - ParamType::Uint(256), // `txType` - ParamType::Uint(256), // sender - ParamType::Uint(256), // to - ParamType::Uint(256), // gasLimit - ParamType::Uint(256), // `gasPerPubdataLimit` - ParamType::Uint(256), // maxFeePerGas - ParamType::Uint(256), // maxPriorityFeePerGas - ParamType::Uint(256), // paymaster - ParamType::Uint(256), // nonce (serial ID) - ParamType::Uint(256), // value - ParamType::FixedArray(Box::new(ParamType::Uint(256)), 4), // reserved - ParamType::Bytes, // calldata - ParamType::Bytes, // signature - ParamType::Array(Box::new(ParamType::Uint(256))), // factory deps - ParamType::Bytes, // paymaster input - ParamType::Bytes, // `reservedDynamic` - ]); + let transaction_param_type: ParamType = get_transaction_param_type(); let verifier_params_type = ParamType::Tuple(vec![ ParamType::FixedBytes(32), ParamType::FixedBytes(32), @@ -299,119 +310,21 @@ impl TryFrom for ProtocolUpgrade { unreachable!(); }; - let Token::Tuple(mut transaction) = decoded.remove(0) else { + let Token::Tuple(transaction) = decoded.remove(0) else { unreachable!() }; let factory_deps = decoded.remove(0).into_array().unwrap(); - let tx = { - let canonical_tx_hash = H256(keccak256(&encode(&[Token::Tuple(transaction.clone())]))); - - assert_eq!(transaction.len(), 16); - - let tx_type = transaction.remove(0).into_uint().unwrap(); - if tx_type == PROTOCOL_UPGRADE_TX_TYPE.into() { - // There is an upgrade tx. Decoding it. - let sender = transaction.remove(0).into_uint().unwrap(); - let sender = u256_to_account_address(&sender); - - let contract_address = transaction.remove(0).into_uint().unwrap(); - let contract_address = u256_to_account_address(&contract_address); - - let gas_limit = transaction.remove(0).into_uint().unwrap(); - - let gas_per_pubdata_limit = transaction.remove(0).into_uint().unwrap(); - - let max_fee_per_gas = transaction.remove(0).into_uint().unwrap(); - - let max_priority_fee_per_gas = transaction.remove(0).into_uint().unwrap(); - assert_eq!(max_priority_fee_per_gas, U256::zero()); - - let paymaster = transaction.remove(0).into_uint().unwrap(); - let paymaster = u256_to_account_address(&paymaster); - assert_eq!(paymaster, Address::zero()); - - let upgrade_id = transaction.remove(0).into_uint().unwrap(); - - let msg_value = transaction.remove(0).into_uint().unwrap(); - - let reserved = transaction - .remove(0) - .into_fixed_array() - .unwrap() - .into_iter() - .map(|token| token.into_uint().unwrap()) - .collect::>(); - assert_eq!(reserved.len(), 4); - - let to_mint = reserved[0]; - let refund_recipient = u256_to_account_address(&reserved[1]); - - // All other reserved fields should be zero - for item in reserved.iter().skip(2) { - assert_eq!(item, &U256::zero()); - } + let eth_hash = event + .transaction_hash + .expect("Event transaction hash is missing"); + let eth_block = event + .block_number + .expect("Event block number is missing") + .as_u64(); - let calldata = transaction.remove(0).into_bytes().unwrap(); - - let signature = transaction.remove(0).into_bytes().unwrap(); - assert_eq!(signature.len(), 0); - - let _factory_deps_hashes = transaction.remove(0).into_array().unwrap(); - - let paymaster_input = transaction.remove(0).into_bytes().unwrap(); - assert_eq!(paymaster_input.len(), 0); - - // TODO (SMA-1621): check that `reservedDynamic` are constructed correctly. - let reserved_dynamic = transaction.remove(0).into_bytes().unwrap(); - assert_eq!(reserved_dynamic.len(), 0); - - let eth_hash = event - .transaction_hash - .expect("Event transaction hash is missing"); - let eth_block = event - .block_number - .expect("Event block number is missing") - .as_u64(); - - let common_data = ProtocolUpgradeTxCommonData { - canonical_tx_hash, - sender, - upgrade_id: (upgrade_id.as_u32() as u16).try_into().unwrap(), - to_mint, - refund_recipient, - gas_limit, - max_fee_per_gas, - gas_per_pubdata_limit, - eth_hash, - eth_block, - }; - - let factory_deps = factory_deps - .into_iter() - .map(|t| t.into_bytes().unwrap()) - .collect(); - - let execute = Execute { - contract_address, - calldata: calldata.to_vec(), - factory_deps: Some(factory_deps), - value: msg_value, - }; - - Some(ProtocolUpgradeTx { - common_data, - execute, - received_timestamp_ms: unix_timestamp_ms(), - }) - } else if tx_type == U256::zero() { - // There is no upgrade tx. - None - } else { - panic!("Unexpected tx type {} when decoding upgrade", tx_type); - } - }; + let tx = ProtocolUpgradeTx::decode_tx(transaction, eth_hash, eth_block, factory_deps); let bootloader_code_hash = H256::from_slice(&decoded.remove(0).into_fixed_bytes().unwrap()); let default_account_code_hash = H256::from_slice(&decoded.remove(0).into_fixed_bytes().unwrap()); @@ -458,6 +371,147 @@ impl TryFrom for ProtocolUpgrade { } } +pub fn decode_set_chain_id_event( + event: Log, +) -> Result<(ProtocolVersionId, ProtocolUpgradeTx), crate::ethabi::Error> { + let transaction_param_type: ParamType = get_transaction_param_type(); + + let Token::Tuple(transaction) = decode(&[transaction_param_type], &event.data.0)?.remove(0) + else { + unreachable!() + }; + + let version_id = event.topics[2].to_low_u64_be(); + + let eth_hash = event + .transaction_hash + .expect("Event transaction hash is missing"); + let eth_block = event + .block_number + .expect("Event block number is missing") + .as_u64(); + + let factory_deps: Vec = Vec::new(); + + let upgrade_tx = ProtocolUpgradeTx::decode_tx(transaction, eth_hash, eth_block, factory_deps) + .expect("Upgrade tx is missing"); + let version_id = + ProtocolVersionId::try_from(version_id as u16).expect("Version is not supported"); + + Ok((version_id, upgrade_tx)) +} + +impl ProtocolUpgradeTx { + pub fn decode_tx( + mut transaction: Vec, + eth_hash: H256, + eth_block: u64, + factory_deps: Vec, + ) -> Option { + let canonical_tx_hash = H256(keccak256(&encode(&[Token::Tuple(transaction.clone())]))); + assert_eq!(transaction.len(), 16); + + let tx_type = transaction.remove(0).into_uint().unwrap(); + if tx_type == U256::zero() { + // There is no upgrade tx. + return None; + } + + assert_eq!( + tx_type, + PROTOCOL_UPGRADE_TX_TYPE.into(), + "Unexpected tx type {} when decoding upgrade", + tx_type + ); + + // There is an upgrade tx. Decoding it. + let sender = transaction.remove(0).into_uint().unwrap(); + let sender = u256_to_account_address(&sender); + + let contract_address = transaction.remove(0).into_uint().unwrap(); + let contract_address = u256_to_account_address(&contract_address); + + let gas_limit = transaction.remove(0).into_uint().unwrap(); + + let gas_per_pubdata_limit = transaction.remove(0).into_uint().unwrap(); + + let max_fee_per_gas = transaction.remove(0).into_uint().unwrap(); + + let max_priority_fee_per_gas = transaction.remove(0).into_uint().unwrap(); + assert_eq!(max_priority_fee_per_gas, U256::zero()); + + let paymaster = transaction.remove(0).into_uint().unwrap(); + let paymaster = u256_to_account_address(&paymaster); + assert_eq!(paymaster, Address::zero()); + + let upgrade_id = transaction.remove(0).into_uint().unwrap(); + + let msg_value = transaction.remove(0).into_uint().unwrap(); + + let reserved = transaction + .remove(0) + .into_fixed_array() + .unwrap() + .into_iter() + .map(|token| token.into_uint().unwrap()) + .collect::>(); + assert_eq!(reserved.len(), 4); + + let to_mint = reserved[0]; + let refund_recipient = u256_to_account_address(&reserved[1]); + + // All other reserved fields should be zero + for item in reserved.iter().skip(2) { + assert_eq!(item, &U256::zero()); + } + + let calldata = transaction.remove(0).into_bytes().unwrap(); + + let signature = transaction.remove(0).into_bytes().unwrap(); + assert_eq!(signature.len(), 0); + + let _factory_deps_hashes = transaction.remove(0).into_array().unwrap(); + + let paymaster_input = transaction.remove(0).into_bytes().unwrap(); + assert_eq!(paymaster_input.len(), 0); + + // TODO (SMA-1621): check that `reservedDynamic` are constructed correctly. + let reserved_dynamic = transaction.remove(0).into_bytes().unwrap(); + assert_eq!(reserved_dynamic.len(), 0); + + let common_data = ProtocolUpgradeTxCommonData { + canonical_tx_hash, + sender, + upgrade_id: (upgrade_id.as_u32() as u16).try_into().unwrap(), + to_mint, + refund_recipient, + gas_limit, + max_fee_per_gas, + gas_per_pubdata_limit, + eth_hash, + eth_block, + }; + + let factory_deps = factory_deps + .into_iter() + .map(|t| t.into_bytes().unwrap()) + .collect(); + + let execute = Execute { + contract_address, + calldata: calldata.to_vec(), + factory_deps: Some(factory_deps), + value: msg_value, + }; + + Some(ProtocolUpgradeTx { + common_data, + execute, + received_timestamp_ms: unix_timestamp_ms(), + }) + } +} + impl TryFrom for ProtocolUpgrade { type Error = crate::ethabi::Error; @@ -606,7 +660,7 @@ impl ProtocolVersion { } } -#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ProtocolUpgradeTxCommonData { /// Sender of the transaction. @@ -641,7 +695,7 @@ impl ProtocolUpgradeTxCommonData { } } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ProtocolUpgradeTx { pub execute: Execute, pub common_data: ProtocolUpgradeTxCommonData, diff --git a/core/lib/types/src/storage/mod.rs b/core/lib/types/src/storage/mod.rs index 54694f63c504..9d558d013fa0 100644 --- a/core/lib/types/src/storage/mod.rs +++ b/core/lib/types/src/storage/mod.rs @@ -15,7 +15,8 @@ pub use zksync_system_constants::*; use zksync_utils::address_to_h256; /// Typed fully qualified key of the storage slot in global state tree. -#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] +#[derive(Serialize, Deserialize)] pub struct StorageKey { account: AccountTreeId, key: H256, diff --git a/core/lib/types/src/tx/execute.rs b/core/lib/types/src/tx/execute.rs index 21f0b401cce2..889c276a3e7a 100644 --- a/core/lib/types/src/tx/execute.rs +++ b/core/lib/types/src/tx/execute.rs @@ -5,7 +5,7 @@ use zksync_utils::ZeroPrefixHexSerde; use crate::{web3::ethabi, Address, EIP712TypedStructure, StructBuilder, H256, U256}; /// `Execute` transaction executes a previously deployed smart contract in the L2 rollup. -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Execute { pub contract_address: Address, diff --git a/core/lib/types/src/zk_evm_types.rs b/core/lib/types/src/zk_evm_types.rs index a7973ab36fec..f165df0c57f5 100644 --- a/core/lib/types/src/zk_evm_types.rs +++ b/core/lib/types/src/zk_evm_types.rs @@ -1,3 +1,4 @@ +use serde::{Deserialize, Serialize}; use zksync_basic_types::{Address, U256}; #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -9,12 +10,11 @@ pub enum FarCallOpcode { } /// Struct representing the VM timestamp -#[derive( - Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, PartialOrd, Ord, -)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Serialize, Deserialize)] pub struct Timestamp(pub u32); -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct LogQuery { pub timestamp: Timestamp, pub tx_number_in_block: u16, diff --git a/core/lib/web3_decl/src/namespaces/zks.rs b/core/lib/web3_decl/src/namespaces/zks.rs index 66ca97cb03aa..d62827056232 100644 --- a/core/lib/web3_decl/src/namespaces/zks.rs +++ b/core/lib/web3_decl/src/namespaces/zks.rs @@ -33,6 +33,9 @@ pub trait ZksNamespace { #[method(name = "estimateGasL1ToL2")] async fn estimate_gas_l1_to_l2(&self, req: CallRequest) -> RpcResult; + #[method(name = "getBridgehubContract")] + async fn get_bridgehub_contract(&self) -> RpcResult>; + #[method(name = "getMainContract")] async fn get_main_contract(&self) -> RpcResult
; diff --git a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpsee/namespaces/zks.rs b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpsee/namespaces/zks.rs index e6ef5ddeb907..a3397b39d87a 100644 --- a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpsee/namespaces/zks.rs +++ b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpsee/namespaces/zks.rs @@ -30,6 +30,10 @@ impl ZksNamespaceServer for ZksNamespace { .map_err(into_jsrpc_error) } + async fn get_bridgehub_contract(&self) -> RpcResult> { + Ok(self.get_bridgehub_contract_impl()) + } + async fn get_main_contract(&self) -> RpcResult
{ Ok(self.get_main_contract_impl()) } diff --git a/core/lib/zksync_core/src/api_server/web3/namespaces/zks.rs b/core/lib/zksync_core/src/api_server/web3/namespaces/zks.rs index 61746a20b033..626f747072d5 100644 --- a/core/lib/zksync_core/src/api_server/web3/namespaces/zks.rs +++ b/core/lib/zksync_core/src/api_server/web3/namespaces/zks.rs @@ -123,6 +123,11 @@ impl ZksNamespace { .map_err(|err| err.into_web3_error(method_name)) } + #[tracing::instrument(skip(self))] + pub fn get_bridgehub_contract_impl(&self) -> Option
{ + self.state.api_config.bridgehub_proxy_addr + } + #[tracing::instrument(skip(self))] pub fn get_main_contract_impl(&self) -> Address { self.state.api_config.diamond_proxy_addr diff --git a/core/lib/zksync_core/src/api_server/web3/state.rs b/core/lib/zksync_core/src/api_server/web3/state.rs index d930880c4be7..6620d1d664bf 100644 --- a/core/lib/zksync_core/src/api_server/web3/state.rs +++ b/core/lib/zksync_core/src/api_server/web3/state.rs @@ -81,6 +81,7 @@ pub struct InternalApiConfig { pub estimate_gas_scale_factor: f64, pub estimate_gas_acceptable_overestimation: u32, pub bridge_addresses: api::BridgeAddresses, + pub bridgehub_proxy_addr: Option
, pub diamond_proxy_addr: Address, pub l2_testnet_paymaster_addr: Option
, pub req_entities_limit: usize, @@ -106,6 +107,7 @@ impl InternalApiConfig { l1_weth_bridge: contracts_config.l1_weth_bridge_proxy_addr, l2_weth_bridge: contracts_config.l2_weth_bridge_addr, }, + bridgehub_proxy_addr: contracts_config.bridgehub_proxy_addr, 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(), diff --git a/core/lib/zksync_core/src/consistency_checker/mod.rs b/core/lib/zksync_core/src/consistency_checker/mod.rs index fe49c42b9d3d..40b6eb0314d1 100644 --- a/core/lib/zksync_core/src/consistency_checker/mod.rs +++ b/core/lib/zksync_core/src/consistency_checker/mod.rs @@ -249,7 +249,7 @@ impl ConsistencyChecker { .with_context(|| format!("Commit for tx {commit_tx_hash:?} not found on L1"))? .input; // TODO (PLA-721): Check receiving contract and selector - + // TODO: Add support for post shared bridge commits let commit_function = if local.is_pre_boojum { &*PRE_BOOJUM_COMMIT_FUNCTION } else { diff --git a/core/lib/zksync_core/src/eth_sender/eth_tx_aggregator.rs b/core/lib/zksync_core/src/eth_sender/eth_tx_aggregator.rs index 3fca8dbcd283..13045119875a 100644 --- a/core/lib/zksync_core/src/eth_sender/eth_tx_aggregator.rs +++ b/core/lib/zksync_core/src/eth_sender/eth_tx_aggregator.rs @@ -7,17 +7,16 @@ use zksync_dal::{ConnectionPool, StorageProcessor}; use zksync_eth_client::{BoundEthInterface, CallFunctionArgs}; use zksync_l1_contract_interface::{ multicall3::{Multicall3Call, Multicall3Result}, - pre_boojum_verifier::old_l1_vk_commitment, Detokenize, Tokenizable, Tokenize, }; use zksync_types::{ commitment::SerializeCommitment, eth_sender::EthTx, - ethabi::{Contract, Token}, + ethabi::Token, l2_to_l1_log::UserL2ToL1Log, protocol_version::{L1VerifierConfig, VerifierParams}, web3::contract::Error as Web3ContractError, - Address, ProtocolVersionId, H256, U256, + Address, L2ChainId, ProtocolVersionId, H256, U256, }; use super::aggregated_operations::AggregatedOperation; @@ -53,19 +52,25 @@ pub struct EthTxAggregator { pub(super) main_zksync_contract_address: Address, functions: ZkSyncFunctions, base_nonce: u64, + rollup_chain_id: L2ChainId, } impl EthTxAggregator { - pub fn new( + pub async fn new( config: SenderConfig, aggregator: Aggregator, eth_client: Arc, timelock_contract_address: Address, l1_multicall3_address: Address, main_zksync_contract_address: Address, - base_nonce: u64, + rollup_chain_id: L2ChainId, ) -> Self { let functions = ZkSyncFunctions::default(); + let base_nonce = eth_client + .pending_nonce("eth_sender") + .await + .unwrap() + .as_u64(); Self { config, aggregator, @@ -75,6 +80,7 @@ impl EthTxAggregator { main_zksync_contract_address, functions, base_nonce, + rollup_chain_id, } } @@ -296,34 +302,12 @@ impl EthTxAggregator { async fn get_recursion_scheduler_level_vk_hash( &mut self, verifier_address: Address, - contracts_are_pre_boojum: bool, ) -> Result { - // This is here for backward compatibility with the old verifier: - // Pre-boojum verifier returns the full verification key; - // New verifier returns the hash of the verification key - tracing::debug!("Calling get_verification_key"); - if contracts_are_pre_boojum { - let abi = Contract { - functions: [( - self.functions.get_verification_key.name.clone(), - vec![self.functions.get_verification_key.clone()], - )] - .into(), - ..Default::default() - }; - let args = CallFunctionArgs::new(&self.functions.get_verification_key.name, ()) - .for_contract(verifier_address, abi); - - let vk = self.eth_client.call_contract_function(args).await?; - Ok(old_l1_vk_commitment(Token::from_tokens(vk)?)) - } else { - let get_vk_hash = self.functions.verification_key_hash.as_ref(); - tracing::debug!("Calling verificationKeyHash"); - let args = CallFunctionArgs::new(&get_vk_hash.unwrap().name, ()) - .for_contract(verifier_address, self.functions.verifier_contract.clone()); - let vk_hash = self.eth_client.call_contract_function(args).await?; - Ok(H256::from_tokens(vk_hash)?) - } + let get_vk_hash = &self.functions.verification_key_hash; + let args = CallFunctionArgs::new(&get_vk_hash.name, ()) + .for_contract(verifier_address, self.functions.verifier_contract.clone()); + let vk_hash = self.eth_client.call_contract_function(args).await?; + Ok(H256::from_tokens(vk_hash)?) } #[tracing::instrument(skip(self, storage))] @@ -340,10 +324,10 @@ impl EthTxAggregator { tracing::error!("Failed to get multicall data {err:?}"); err })?; - let contracts_are_pre_boojum = protocol_version_id.is_pre_boojum(); + let contracts_are_pre_shared_bridge = protocol_version_id.is_pre_shared_bridge(); let recursion_scheduler_level_vk_hash = self - .get_recursion_scheduler_level_vk_hash(verifier_address, contracts_are_pre_boojum) + .get_recursion_scheduler_level_vk_hash(verifier_address) .await .map_err(|err| { tracing::error!("Failed to get VK hash from the Verifier {err:?}"); @@ -364,7 +348,7 @@ impl EthTxAggregator { .await { let tx = self - .save_eth_tx(storage, &agg_op, contracts_are_pre_boojum) + .save_eth_tx(storage, &agg_op, contracts_are_pre_shared_bridge) .await?; Self::report_eth_tx_saving(storage, agg_op, &tx).await; } @@ -407,50 +391,64 @@ impl EthTxAggregator { fn encode_aggregated_op( &self, op: &AggregatedOperation, - contracts_are_pre_boojum: bool, + contracts_are_pre_shared_bridge: bool, ) -> Vec { - let operation_is_pre_boojum = op.protocol_version().is_pre_boojum(); + let operation_is_pre_shared_bridge = op.protocol_version().is_pre_shared_bridge(); + assert_eq!( + contracts_are_pre_shared_bridge, + operation_is_pre_shared_bridge + ); + + let mut args = vec![Token::Uint(self.rollup_chain_id.as_u64().into())]; - // For "commit" and "prove" operations it's necessary that the contracts are of the same version as L1 batches are. - // For "execute" it's not required, i.e. we can "execute" pre-boojum batches with post-boojum contracts. match op.clone() { AggregatedOperation::Commit(op) => { - assert_eq!(contracts_are_pre_boojum, operation_is_pre_boojum); - let f = if contracts_are_pre_boojum { - &self.functions.pre_boojum_commit + if contracts_are_pre_shared_bridge { + self.functions + .pre_shared_bridge_commit + .encode_input(&op.into_tokens()) + .expect("Failed to encode commit transaction data") } else { + args.extend(op.into_tokens()); self.functions - .post_boojum_commit + .post_shared_bridge_commit .as_ref() - .expect("Missing ABI for commitBatches") - }; - f.encode_input(&op.into_tokens()) - .expect("Failed to encode commit transaction data") + .expect("Missing ABI for commitBatchesSharedBridge") + .encode_input(&args) + .expect("Failed to encode commit transaction data") + } } AggregatedOperation::PublishProofOnchain(op) => { - assert_eq!(contracts_are_pre_boojum, operation_is_pre_boojum); - let f = if contracts_are_pre_boojum { - &self.functions.pre_boojum_prove + if contracts_are_pre_shared_bridge { + self.functions + .pre_shared_bridge_prove + .encode_input(&op.into_tokens()) + .expect("Failed to encode prove transaction data") } else { + args.extend(op.into_tokens()); self.functions - .post_boojum_prove + .post_shared_bridge_prove .as_ref() - .expect("Missing ABI for proveBatches") - }; - f.encode_input(&op.into_tokens()) - .expect("Failed to encode prove transaction data") + .expect("Missing ABI for proveBatchesSharedBridge") + .encode_input(&args) + .expect("Failed to encode prove transaction data") + } } AggregatedOperation::Execute(op) => { - let f = if contracts_are_pre_boojum { - &self.functions.pre_boojum_execute + if contracts_are_pre_shared_bridge { + self.functions + .pre_shared_bridge_execute + .encode_input(&op.into_tokens()) + .expect("Failed to encode execute transaction data") } else { + args.extend(op.into_tokens()); self.functions - .post_boojum_execute + .post_shared_bridge_execute .as_ref() - .expect("Missing ABI for executeBatches") - }; - f.encode_input(&op.into_tokens()) - .expect("Failed to encode execute transaction data") + .expect("Missing ABI for executeBatchesSharedBridge") + .encode_input(&args) + .expect("Failed to encode execute transaction data") + } } } } @@ -459,11 +457,11 @@ impl EthTxAggregator { &self, storage: &mut StorageProcessor<'_>, aggregated_op: &AggregatedOperation, - contracts_are_pre_boojum: bool, + contracts_are_pre_shared_bridge: bool, ) -> Result { let mut transaction = storage.start_transaction().await.unwrap(); let nonce = self.get_next_nonce(&mut transaction).await?; - let calldata = self.encode_aggregated_op(aggregated_op, contracts_are_pre_boojum); + let calldata = self.encode_aggregated_op(aggregated_op, contracts_are_pre_shared_bridge); let l1_batch_number_range = aggregated_op.l1_batch_range(); let op_type = aggregated_op.get_action_type(); diff --git a/core/lib/zksync_core/src/eth_sender/tests.rs b/core/lib/zksync_core/src/eth_sender/tests.rs index cd5741886add..858f65294401 100644 --- a/core/lib/zksync_core/src/eth_sender/tests.rs +++ b/core/lib/zksync_core/src/eth_sender/tests.rs @@ -111,8 +111,9 @@ impl EthSenderTester { Address::random(), contracts_config.l1_multicall3_addr, Address::random(), - 0, - ); + Default::default(), + ) + .await; let manager = EthTxManager::new( eth_sender_config.sender, @@ -964,7 +965,7 @@ async fn send_operation( .save_eth_tx( &mut tester.conn.access_storage().await.unwrap(), &aggregated_operation, - false, + true, ) .await .unwrap(); diff --git a/core/lib/zksync_core/src/eth_sender/zksync_functions.rs b/core/lib/zksync_core/src/eth_sender/zksync_functions.rs index 8e27af5b628d..00ca15707638 100644 --- a/core/lib/zksync_core/src/eth_sender/zksync_functions.rs +++ b/core/lib/zksync_core/src/eth_sender/zksync_functions.rs @@ -1,17 +1,14 @@ -use zksync_contracts::{ - multicall_contract, verifier_contract, zksync_contract, PRE_BOOJUM_COMMIT_FUNCTION, - PRE_BOOJUM_EXECUTE_FUNCTION, PRE_BOOJUM_GET_VK_FUNCTION, PRE_BOOJUM_PROVE_FUNCTION, -}; +use zksync_contracts::{multicall_contract, verifier_contract, zksync_contract}; use zksync_types::ethabi::{Contract, Function}; #[derive(Debug)] pub(super) struct ZkSyncFunctions { - pub(super) pre_boojum_commit: Function, - pub(super) post_boojum_commit: Option, - pub(super) pre_boojum_prove: Function, - pub(super) post_boojum_prove: Option, - pub(super) pre_boojum_execute: Function, - pub(super) post_boojum_execute: Option, + pub(super) pre_shared_bridge_commit: Function, + pub(super) post_shared_bridge_commit: Option, + pub(super) pre_shared_bridge_prove: Function, + pub(super) post_shared_bridge_prove: Option, + pub(super) pre_shared_bridge_execute: Function, + pub(super) post_shared_bridge_execute: Option, pub(super) get_l2_bootloader_bytecode_hash: Function, pub(super) get_l2_default_account_bytecode_hash: Function, pub(super) get_verifier: Function, @@ -19,8 +16,7 @@ pub(super) struct ZkSyncFunctions { pub(super) get_protocol_version: Function, pub(super) verifier_contract: Contract, - pub(super) get_verification_key: Function, - pub(super) verification_key_hash: Option, + pub(super) verification_key_hash: Function, pub(super) multicall_contract: Contract, pub(super) aggregate3: Function, @@ -50,12 +46,15 @@ impl Default for ZkSyncFunctions { let verifier_contract = verifier_contract(); let multicall_contract = multicall_contract(); - let pre_boojum_commit = PRE_BOOJUM_COMMIT_FUNCTION.clone(); - let post_boojum_commit = get_optional_function(&zksync_contract, "commitBatches"); - let pre_boojum_prove = PRE_BOOJUM_PROVE_FUNCTION.clone(); - let post_boojum_prove = get_optional_function(&zksync_contract, "proveBatches"); - let pre_boojum_execute = PRE_BOOJUM_EXECUTE_FUNCTION.clone(); - let post_boojum_execute = get_optional_function(&zksync_contract, "executeBatches"); + let pre_shared_bridge_commit = get_function(&zksync_contract, "commitBatches"); + let post_shared_bridge_commit = + get_optional_function(&zksync_contract, "commitBatchesSharedBridge"); + let pre_shared_bridge_prove = get_function(&zksync_contract, "proveBatches"); + let post_shared_bridge_prove = + get_optional_function(&zksync_contract, "proveBatchesSharedBridge"); + let pre_shared_bridge_execute = get_function(&zksync_contract, "executeBatches"); + let post_shared_bridge_execute = + get_optional_function(&zksync_contract, "executeBatchesSharedBridge"); let get_l2_bootloader_bytecode_hash = get_function(&zksync_contract, "getL2BootloaderBytecodeHash"); let get_l2_default_account_bytecode_hash = @@ -63,25 +62,22 @@ impl Default for ZkSyncFunctions { let get_verifier = get_function(&zksync_contract, "getVerifier"); let get_verifier_params = get_function(&zksync_contract, "getVerifierParams"); let get_protocol_version = get_function(&zksync_contract, "getProtocolVersion"); - let get_verification_key = PRE_BOOJUM_GET_VK_FUNCTION.clone(); let aggregate3 = get_function(&multicall_contract, "aggregate3"); - let verification_key_hash = - get_optional_function(&verifier_contract, "verificationKeyHash"); + let verification_key_hash = get_function(&verifier_contract, "verificationKeyHash"); ZkSyncFunctions { - pre_boojum_commit, - post_boojum_commit, - pre_boojum_prove, - post_boojum_prove, - pre_boojum_execute, - post_boojum_execute, + pre_shared_bridge_commit, + post_shared_bridge_commit, + pre_shared_bridge_prove, + post_shared_bridge_prove, + pre_shared_bridge_execute, + post_shared_bridge_execute, get_l2_bootloader_bytecode_hash, get_l2_default_account_bytecode_hash, get_verifier, get_verifier_params, get_protocol_version, verifier_contract, - get_verification_key, verification_key_hash, multicall_contract, aggregate3, diff --git a/core/lib/zksync_core/src/eth_watch/event_processors/governance_upgrades.rs b/core/lib/zksync_core/src/eth_watch/event_processors/governance_upgrades.rs index 6008f4a05e96..7066838fee88 100644 --- a/core/lib/zksync_core/src/eth_watch/event_processors/governance_upgrades.rs +++ b/core/lib/zksync_core/src/eth_watch/event_processors/governance_upgrades.rs @@ -76,24 +76,18 @@ impl EventProcessor for GovernanceUpgradesEventProcessor { } } - if upgrades.is_empty() { - return Ok(()); - } - - let ids_str: Vec<_> = upgrades - .iter() - .map(|(u, _)| format!("{}", u.id as u16)) - .collect(); - tracing::debug!("Received upgrades with ids: {}", ids_str.join(", ")); - let new_upgrades: Vec<_> = upgrades .into_iter() .skip_while(|(v, _)| v.id as u16 <= self.last_seen_version_id as u16) .collect(); + if new_upgrades.is_empty() { return Ok(()); } + let ids: Vec<_> = new_upgrades.iter().map(|(u, _)| u.id as u16).collect(); + tracing::debug!("Received upgrades with ids: {:?}", ids); + let last_id = new_upgrades.last().unwrap().0.id; let stage_start = Instant::now(); for (upgrade, scheduler_vk_hash) in new_upgrades { diff --git a/core/lib/zksync_core/src/eth_watch/event_processors/upgrades.rs b/core/lib/zksync_core/src/eth_watch/event_processors/upgrades.rs index e7f906cdf070..393dad5afcda 100644 --- a/core/lib/zksync_core/src/eth_watch/event_processors/upgrades.rs +++ b/core/lib/zksync_core/src/eth_watch/event_processors/upgrades.rs @@ -52,24 +52,18 @@ impl EventProcessor for UpgradesEventProcessor { upgrades.push((upgrade, scheduler_vk_hash)); } - if upgrades.is_empty() { - return Ok(()); - } - - let ids_str: Vec<_> = upgrades - .iter() - .map(|(u, _)| format!("{}", u.id as u16)) - .collect(); - tracing::debug!("Received upgrades with ids: {}", ids_str.join(", ")); - let new_upgrades: Vec<_> = upgrades .into_iter() .skip_while(|(v, _)| v.id as u16 <= self.last_seen_version_id as u16) .collect(); + if new_upgrades.is_empty() { return Ok(()); } + let ids: Vec<_> = new_upgrades.iter().map(|(u, _)| u.id as u16).collect(); + tracing::debug!("Received upgrades with ids: {:?}", ids); + let last_id = new_upgrades.last().unwrap().0.id; let stage_latency = METRICS.poll_eth_node[&PollStage::PersistUpgrades].start(); for (upgrade, scheduler_vk_hash) in new_upgrades { diff --git a/core/lib/zksync_core/src/genesis.rs b/core/lib/zksync_core/src/genesis.rs index 9dfd9b952cee..8b02cc13ba1a 100644 --- a/core/lib/zksync_core/src/genesis.rs +++ b/core/lib/zksync_core/src/genesis.rs @@ -8,9 +8,11 @@ use multivm::{ zk_evm_latest::aux_structures::{LogQuery as MultiVmLogQuery, Timestamp as MultiVMTimestamp}, zkevm_test_harness_latest::witness::sort_storage_access::sort_storage_access_queries, }; -use zksync_contracts::BaseSystemContracts; +use zksync_contracts::{BaseSystemContracts, SET_CHAIN_ID_EVENT}; use zksync_dal::StorageProcessor; +use zksync_eth_client::{clients::QueryClient, EthInterface}; use zksync_merkle_tree::domain::ZkSyncTree; +use zksync_system_constants::PRIORITY_EXPIRATION; use zksync_types::{ block::{ BlockGasCount, DeployedContract, L1BatchHeader, L1BatchTreeData, MiniblockHasher, @@ -19,8 +21,9 @@ use zksync_types::{ commitment::{CommitmentInput, L1BatchCommitment}, fee_model::BatchFeeInput, get_code_key, get_system_context_init_logs, - protocol_version::{L1VerifierConfig, ProtocolVersion}, + protocol_version::{decode_set_chain_id_event, L1VerifierConfig, ProtocolVersion}, tokens::{TokenInfo, TokenMetadata, ETHEREUM_ADDRESS}, + web3::types::{BlockNumber, FilterBuilder}, zk_evm_types::{LogQuery, Timestamp}, AccountTreeId, Address, L1BatchNumber, L2ChainId, MiniblockNumber, ProtocolVersionId, StorageKey, StorageLog, StorageLogKind, H256, @@ -421,6 +424,41 @@ async fn save_genesis_l1_batch_metadata( Ok(()) } +pub(crate) async fn save_set_chain_id_tx( + eth_client_url: &str, + diamond_proxy_address: Address, + state_transition_manager_address: Address, + storage: &mut StorageProcessor<'_>, +) -> anyhow::Result<()> { + let eth_client = QueryClient::new(eth_client_url)?; + let to = eth_client.block_number("fetch_chain_id_tx").await?.as_u64(); + let from = to - PRIORITY_EXPIRATION; + let filter = FilterBuilder::default() + .address(vec![state_transition_manager_address]) + .topics( + Some(vec![SET_CHAIN_ID_EVENT.signature()]), + Some(vec![diamond_proxy_address.into()]), + None, + None, + ) + .from_block(from.into()) + .to_block(BlockNumber::Latest) + .build(); + let mut logs = eth_client.logs(filter, "fetch_chain_id_tx").await?; + anyhow::ensure!( + logs.len() == 1, + "Expected a single set_chain_id event, got these {}: {:?}", + logs.len(), + logs + ); + let (version_id, upgrade_tx) = decode_set_chain_id_event(logs.remove(0))?; + storage + .protocol_versions_dal() + .save_genesis_upgrade_with_tx(version_id, upgrade_tx) + .await; + Ok(()) +} + #[cfg(test)] mod tests { use zksync_dal::ConnectionPool; diff --git a/core/lib/zksync_core/src/lib.rs b/core/lib/zksync_core/src/lib.rs index 5575712adda0..4bbbc7551b56 100644 --- a/core/lib/zksync_core/src/lib.rs +++ b/core/lib/zksync_core/src/lib.rs @@ -29,7 +29,7 @@ use zksync_contracts::{governance_contract, BaseSystemContracts}; use zksync_dal::{healthcheck::ConnectionPoolHealthCheck, ConnectionPool}; use zksync_eth_client::{ clients::{PKSigningClient, QueryClient}, - BoundEthInterface, CallFunctionArgs, EthInterface, + CallFunctionArgs, EthInterface, }; use zksync_health_check::{CheckHealth, HealthStatus, ReactiveHealthCheck}; use zksync_object_store::{ObjectStore, ObjectStoreFactory}; @@ -105,6 +105,7 @@ pub async fn genesis_init( network_config: &NetworkConfig, contracts_config: &ContractsConfig, eth_client_url: &str, + wait_for_set_chain_id: bool, ) -> anyhow::Result<()> { let db_url = postgres_config.master_url()?; let pool = ConnectionPool::singleton(db_url) @@ -176,6 +177,20 @@ pub async fn genesis_init( }, ) .await?; + + if wait_for_set_chain_id { + genesis::save_set_chain_id_tx( + eth_client_url, + contracts_config.diamond_proxy_addr, + contracts_config + .state_transition_proxy_addr + .context("state_transition_proxy_addr is not set, but needed for genesis")?, + &mut storage, + ) + .await + .context("Failed to save SetChainId upgrade transaction")?; + } + Ok(()) } @@ -569,6 +584,7 @@ pub async fn initialize_components( } let main_zksync_contract_address = contracts_config.diamond_proxy_addr; + if components.contains(&Component::EthWatcher) { let started_at = Instant::now(); tracing::info!("initializing ETH-Watcher"); @@ -612,7 +628,6 @@ pub async fn initialize_components( .context("eth_sender_config")?; let eth_client = PKSigningClient::from_config(ð_sender, &contracts_config, ð_client_config); - let nonce = eth_client.pending_nonce("eth_sender").await.unwrap(); let eth_tx_aggregator_actor = EthTxAggregator::new( eth_sender.sender.clone(), Aggregator::new( @@ -623,8 +638,13 @@ pub async fn initialize_components( contracts_config.validator_timelock_addr, contracts_config.l1_multicall3_addr, main_zksync_contract_address, - nonce.as_u64(), - ); + configs + .network_config + .as_ref() + .context("network_config")? + .zksync_network_id, + ) + .await; task_futures.push(tokio::spawn( eth_tx_aggregator_actor.run(eth_sender_pool, stop_receiver.clone()), )); diff --git a/core/lib/zksync_core/src/state_keeper/io/mempool.rs b/core/lib/zksync_core/src/state_keeper/io/mempool.rs index c7597a4aec33..c1ae3474d303 100644 --- a/core/lib/zksync_core/src/state_keeper/io/mempool.rs +++ b/core/lib/zksync_core/src/state_keeper/io/mempool.rs @@ -503,7 +503,7 @@ impl MempoolIO { } async fn wait_for_previous_l1_batch_hash(&self) -> H256 { - tracing::info!( + tracing::trace!( "Getting previous L1 batch hash for L1 batch #{}", self.current_l1_batch_number ); @@ -525,7 +525,7 @@ impl MempoolIO { .unwrap(); wait_latency.observe(); - tracing::info!( + tracing::trace!( "Got previous L1 batch hash: {batch_hash:?} for L1 batch #{}", self.current_l1_batch_number ); diff --git a/core/lib/zksync_core/src/state_keeper/keeper.rs b/core/lib/zksync_core/src/state_keeper/keeper.rs index 49d269b67f8b..e331aec9763a 100644 --- a/core/lib/zksync_core/src/state_keeper/keeper.rs +++ b/core/lib/zksync_core/src/state_keeper/keeper.rs @@ -10,8 +10,11 @@ use multivm::interface::{Halt, L1BatchEnv, SystemEnv}; use tokio::sync::watch; use zksync_dal::ConnectionPool; use zksync_types::{ - block::MiniblockExecutionData, l2::TransactionType, protocol_version::ProtocolUpgradeTx, - storage_writes_deduplicator::StorageWritesDeduplicator, Transaction, + block::MiniblockExecutionData, + l2::TransactionType, + protocol_version::{ProtocolUpgradeTx, ProtocolVersionId}, + storage_writes_deduplicator::StorageWritesDeduplicator, + L1BatchNumber, Transaction, }; use super::{ @@ -31,7 +34,7 @@ pub(super) const POLL_WAIT_DURATION: Duration = Duration::from_secs(1); /// Structure used to indicate that task cancellation was requested. #[derive(thiserror::Error, Debug)] -enum Error { +pub(super) enum Error { #[error("canceled")] Canceled, #[error(transparent)] @@ -148,27 +151,9 @@ impl ZkSyncStateKeeper { let protocol_version = system_env.version; let mut updates_manager = UpdatesManager::new(&l1_batch_env, &system_env); - let previous_batch_protocol_version = - self.io.load_previous_batch_version_id().await.unwrap(); - let version_changed = protocol_version != previous_batch_protocol_version; - - let mut protocol_upgrade_tx = if pending_miniblocks.is_empty() && version_changed { - self.io.load_upgrade_tx(protocol_version).await - } else if !pending_miniblocks.is_empty() && version_changed { - // Sanity check: if `txs_to_reexecute` is not empty and upgrade tx is present for this block - // then it must be the first one in `txs_to_reexecute`. - if self.io.load_upgrade_tx(protocol_version).await.is_some() { - let first_tx_to_reexecute = &pending_miniblocks[0].txs[0]; - assert_eq!( - first_tx_to_reexecute.tx_format(), - TransactionType::ProtocolUpgradeTransaction - ) - } - - None - } else { - None - }; + let mut protocol_upgrade_tx: Option = self + .load_protocol_upgrade_tx(&pending_miniblocks, protocol_version, l1_batch_env.number) + .await?; let mut batch_executor = self .batch_executor_base @@ -232,7 +217,6 @@ impl ZkSyncStateKeeper { .ok_or(Error::Canceled)?; let version_changed = system_env.version != sealed_batch_protocol_version; - protocol_upgrade_tx = if version_changed { self.io.load_upgrade_tx(system_env.version).await } else { @@ -242,6 +226,67 @@ impl ZkSyncStateKeeper { Err(Error::Canceled) } + /// This function is meant to be called only once during the state-keeper initialization. + /// It will check if we should load a protocol upgrade or a `setChainId` transaction, + /// perform some checks and return it. + pub(super) async fn load_protocol_upgrade_tx( + &mut self, + pending_miniblocks: &[MiniblockExecutionData], + protocol_version: ProtocolVersionId, + l1_batch_number: L1BatchNumber, + ) -> Result, Error> { + // After the Shared Bridge is integrated, + // there has to be a setChainId upgrade transaction after the chain genesis. + // It has to be the first transaction of the first batch. + // The setChainId upgrade does not bump the protocol version, but attaches an upgrade + // transaction to the genesis protocol version version. + let first_batch_in_shared_bridge = + l1_batch_number == L1BatchNumber(1) && !protocol_version.is_pre_shared_bridge(); + let previous_batch_protocol_version = + self.io.load_previous_batch_version_id().await.unwrap(); + + let version_changed = protocol_version != previous_batch_protocol_version; + let protocol_upgrade_tx = self.io.load_upgrade_tx(protocol_version).await; + + let protocol_upgrade_tx = if pending_miniblocks.is_empty() + && (version_changed || first_batch_in_shared_bridge) + { + // We have a new upgrade transaction - either a regular protocol upgrade or a `setChainId` upgrade. + // If we are running an EN, `protocol_upgrade_tx` will be `None` here since it is fetched from the main node. + tracing::info!( + "There is an new upgrade tx to be executed in batch #{}", + l1_batch_number + ); + protocol_upgrade_tx + } else if !pending_miniblocks.is_empty() + && (version_changed || first_batch_in_shared_bridge) + { + // We already processed the upgrade tx but did not seal the batch it was in. + // Sanity check: if `txs_to_reexecute` is not empty and upgrade tx is present for this block + // then it must be the first one in `txs_to_reexecute`. + if protocol_upgrade_tx.is_some() { + let first_tx_to_reexecute = &pending_miniblocks[0].txs[0]; + assert_eq!( + first_tx_to_reexecute.tx_format(), + TransactionType::ProtocolUpgradeTransaction, + "Expected an upgrade transaction to be the first one in pending_miniblocks, but found {:?}", + first_tx_to_reexecute.hash() + ); + } + tracing::info!( + "There is a protocol upgrade in batch #{}, upgrade tx already processed", + l1_batch_number + ); + None + } else { + // We do not have any upgrade transactions in this batch. + tracing::info!("There is no protocol upgrade in batch #{}", l1_batch_number); + None + }; + + Ok(protocol_upgrade_tx) + } + fn is_canceled(&self) -> bool { *self.stop_receiver.borrow() } diff --git a/core/lib/zksync_core/src/state_keeper/seal_criteria/conditional_sealer.rs b/core/lib/zksync_core/src/state_keeper/seal_criteria/conditional_sealer.rs index 4e9e797b5a0e..4effb257f027 100644 --- a/core/lib/zksync_core/src/state_keeper/seal_criteria/conditional_sealer.rs +++ b/core/lib/zksync_core/src/state_keeper/seal_criteria/conditional_sealer.rs @@ -39,7 +39,7 @@ pub trait ConditionalSealer: 'static + fmt::Debug + Send + Sync { /// /// The checks are deterministic, i.e., should depend solely on execution metrics and [`StateKeeperConfig`]. /// Non-deterministic seal criteria are expressed using [`IoSealCriteria`](super::IoSealCriteria). -#[derive(Debug)] +#[derive(Debug, Default)] pub struct SequencerSealer { config: StateKeeperConfig, sealers: Vec>, diff --git a/core/lib/zksync_core/src/state_keeper/tests/mod.rs b/core/lib/zksync_core/src/state_keeper/tests/mod.rs index 6e18e22ee82c..4de8c3d5ca7f 100644 --- a/core/lib/zksync_core/src/state_keeper/tests/mod.rs +++ b/core/lib/zksync_core/src/state_keeper/tests/mod.rs @@ -14,6 +14,7 @@ use multivm::{ vm_latest::{constants::BLOCK_GAS_LIMIT, VmExecutionLogs}, }; use once_cell::sync::Lazy; +use tokio::sync::watch; use zksync_config::configs::chain::StateKeeperConfig; use zksync_contracts::BaseSystemContracts; use zksync_system_constants::ZKPORTER_IS_AVAILABLE; @@ -30,8 +31,8 @@ use zksync_types::{ mod tester; use self::tester::{ - bootloader_tip_out_of_gas, pending_batch_data, random_tx, rejected_exec, successful_exec, - successful_exec_with_metrics, TestScenario, + bootloader_tip_out_of_gas, pending_batch_data, random_tx, random_upgrade_tx, rejected_exec, + successful_exec, successful_exec_with_metrics, TestIO, TestScenario, }; pub(crate) use self::tester::{MockBatchExecutor, TestBatchExecutorBuilder}; use crate::{ @@ -44,6 +45,7 @@ use crate::{ }, types::ExecutionMetricsForCriteria, updates::UpdatesManager, + ZkSyncStateKeeper, }, utils::testonly::create_l2_transaction, }; @@ -437,6 +439,47 @@ async fn pending_batch_is_applied() { .await; } +/// Load protocol upgrade transactions +#[tokio::test] +async fn load_upgrade_tx() { + let sealer = SequencerSealer::default(); + let scenario = TestScenario::new(); + let batch_executor_base = TestBatchExecutorBuilder::new(&scenario); + let (stop_sender, stop_receiver) = watch::channel(false); + + let mut io = TestIO::new(stop_sender, scenario); + io.add_upgrade_tx(ProtocolVersionId::latest(), random_upgrade_tx(1)); + io.add_upgrade_tx(ProtocolVersionId::next(), random_upgrade_tx(2)); + + let mut sk = ZkSyncStateKeeper::new( + stop_receiver, + Box::new(io), + Box::new(batch_executor_base), + Arc::new(sealer), + ); + + // Since the version hasn't changed, and we are not using shared bridge, we should not load any + // upgrade transactions. + assert_eq!( + sk.load_protocol_upgrade_tx(&[], ProtocolVersionId::latest(), L1BatchNumber(2)) + .await + .unwrap(), + None + ); + + // If the protocol version has changed, we should load the upgrade transaction. + assert_eq!( + sk.load_protocol_upgrade_tx(&[], ProtocolVersionId::next(), L1BatchNumber(2)) + .await + .unwrap(), + Some(random_upgrade_tx(2)) + ); + + // TODO: add one more test case for the shared bridge after it's integrated. + // If we are processing the 1st batch while using the shared bridge, + // we should load the upgrade transaction -- that's the `SetChainIdUpgrade`. +} + /// Unconditionally seal the batch without triggering specific criteria. #[tokio::test] async fn unconditional_sealing() { diff --git a/core/lib/zksync_core/src/state_keeper/tests/tester.rs b/core/lib/zksync_core/src/state_keeper/tests/tester.rs index 63b96e82dd0e..0f077eacf71b 100644 --- a/core/lib/zksync_core/src/state_keeper/tests/tester.rs +++ b/core/lib/zksync_core/src/state_keeper/tests/tester.rs @@ -234,6 +234,18 @@ pub(crate) fn random_tx(tx_number: u64) -> Transaction { tx.into() } +/// Creates a random protocol upgrade transaction. Provided tx number would be used as a transaction hash, +/// so it's easier to understand which transaction caused test to fail. +pub(crate) fn random_upgrade_tx(tx_number: u64) -> ProtocolUpgradeTx { + let mut tx = ProtocolUpgradeTx { + execute: Default::default(), + common_data: Default::default(), + received_timestamp_ms: 0, + }; + tx.common_data.canonical_tx_hash = H256::from_low_u64_be(tx_number); + tx +} + /// Creates a `TxExecutionResult` object denoting a successful tx execution. pub(crate) fn successful_exec() -> TxExecutionResult { TxExecutionResult::Success { @@ -385,7 +397,7 @@ pub(crate) struct TestBatchExecutorBuilder { } impl TestBatchExecutorBuilder { - fn new(scenario: &TestScenario) -> Self { + pub(super) fn new(scenario: &TestScenario) -> Self { let mut txs = VecDeque::new(); let mut batch_txs = HashMap::new(); let mut rollback_set = HashSet::new(); @@ -543,7 +555,7 @@ impl TestBatchExecutor { } #[derive(Debug)] -pub(crate) struct TestIO { +pub(super) struct TestIO { stop_sender: watch::Sender, batch_number: L1BatchNumber, timestamp: u64, @@ -556,10 +568,11 @@ pub(crate) struct TestIO { skipping_txs: bool, protocol_version: ProtocolVersionId, previous_batch_protocol_version: ProtocolVersionId, + protocol_upgrade_txs: HashMap, } impl TestIO { - fn new(stop_sender: watch::Sender, scenario: TestScenario) -> Self { + pub(super) fn new(stop_sender: watch::Sender, scenario: TestScenario) -> Self { Self { stop_sender, batch_number: L1BatchNumber(1), @@ -571,9 +584,14 @@ impl TestIO { skipping_txs: false, protocol_version: ProtocolVersionId::latest(), previous_batch_protocol_version: ProtocolVersionId::latest(), + protocol_upgrade_txs: HashMap::default(), } } + pub(super) fn add_upgrade_tx(&mut self, version: ProtocolVersionId, tx: ProtocolUpgradeTx) { + self.protocol_upgrade_txs.insert(version, tx); + } + fn pop_next_item(&mut self, request: &str) -> ScenarioItem { if self.scenario.actions.is_empty() { panic!( @@ -766,9 +784,9 @@ impl StateKeeperIO for TestIO { async fn load_upgrade_tx( &mut self, - _version_id: ProtocolVersionId, + version_id: ProtocolVersionId, ) -> Option { - None + self.protocol_upgrade_txs.get(&version_id).cloned() } } diff --git a/etc/env/base/contracts.toml b/etc/env/base/contracts.toml index bfc9b82aa715..0c8995023e98 100644 --- a/etc/env/base/contracts.toml +++ b/etc/env/base/contracts.toml @@ -48,6 +48,13 @@ PROVER_AT_GENESIS="fri" INITIAL_PROTOCOL_VERSION=18 +# These are currently not used, but will be used once the shared bridge is up +BRIDGEHUB_PROXY_ADDR = "0x0000000000000000000000000000000000000000" +BRIDGEHUB_IMPL_ADDR = "0x0000000000000000000000000000000000000000" +STATE_TRANSITION_PROXY_ADDR = "0x0000000000000000000000000000000000000000" +STATE_TRANSITION_IMPL_ADDR = "0x0000000000000000000000000000000000000000" +TRANSPARENT_PROXY_ADMIN_ADDR = "0x0000000000000000000000000000000000000000" + [contracts.test] dummy_verifier=true easy_priority_mode=false