From 2a766a75db8f14a6eadf0fdecac415865724cb5f Mon Sep 17 00:00:00 2001 From: Lyova Potyomkin Date: Tue, 20 Feb 2024 14:00:07 +0200 Subject: [PATCH] feat(shared bridge): preparation for shared bridge migration (server) (#1012) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ This PR aims to reduce the diff between main and #298 to simplify the future review. The changes do not affect current server behavior. Only changes related to the zkSync server went into this PR. What didn't go into this PR: - various variable renamings - integration tests - changes that depend on the future version of era-contracts Where applicable, stubs and dummy (for now) config variables were introduced, which can be replaced/populated later. ## Why ❔ Breaking up a PR into smaller chunks should be easier to review. ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zk fmt` and `zk lint`. - [ ] Spellcheck has been run via `zk spellcheck`. - [ ] Linkcheck has been run via `zk linkcheck`. --------- Co-authored-by: Bence Haromi <56651250+benceharomi@users.noreply.github.com> Co-authored-by: Bence Haromi --- checks-config/era.dic | 4 + core/bin/external_node/src/config/mod.rs | 5 + core/bin/zksync_server/src/main.rs | 5 + core/lib/config/src/configs/contracts.rs | 12 + core/lib/config/src/testonly.rs | 5 + core/lib/contracts/src/lib.rs | 354 +++--------------- ...de45f8f3f3b6cf2e4136f25a1c3ab85d92668.json | 15 + core/lib/dal/src/protocol_versions_dal.rs | 43 ++- core/lib/env_config/src/chain.rs | 2 + core/lib/env_config/src/contracts.rs | 10 + core/lib/protobuf_config/src/contracts.rs | 50 +++ .../protobuf_config/src/proto/contracts.proto | 5 + core/lib/types/rustfmt.toml | 1 + core/lib/types/src/l2/mod.rs | 3 +- core/lib/types/src/protocol_version.rs | 320 +++++++++------- core/lib/types/src/storage/mod.rs | 3 +- core/lib/types/src/tx/execute.rs | 2 +- core/lib/types/src/zk_evm_types.rs | 8 +- core/lib/web3_decl/src/namespaces/zks.rs | 3 + .../web3/backend_jsonrpsee/namespaces/zks.rs | 4 + .../src/api_server/web3/namespaces/zks.rs | 5 + .../zksync_core/src/api_server/web3/state.rs | 2 + .../src/consistency_checker/mod.rs | 2 +- .../src/eth_sender/eth_tx_aggregator.rs | 126 +++---- core/lib/zksync_core/src/eth_sender/tests.rs | 7 +- .../src/eth_sender/zksync_functions.rs | 52 ++- .../event_processors/governance_upgrades.rs | 14 +- .../eth_watch/event_processors/upgrades.rs | 14 +- core/lib/zksync_core/src/genesis.rs | 42 ++- core/lib/zksync_core/src/lib.rs | 28 +- .../src/state_keeper/io/mempool.rs | 4 +- .../zksync_core/src/state_keeper/keeper.rs | 95 +++-- .../seal_criteria/conditional_sealer.rs | 2 +- .../zksync_core/src/state_keeper/tests/mod.rs | 47 ++- .../src/state_keeper/tests/tester.rs | 28 +- etc/env/base/contracts.toml | 7 + 36 files changed, 722 insertions(+), 607 deletions(-) create mode 100644 core/lib/dal/.sqlx/query-a8dad09a54cc991706141da557dde45f8f3f3b6cf2e4136f25a1c3ab85d92668.json create mode 100644 core/lib/types/rustfmt.toml 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 e5e61d3f6c10..69b2f6082f99 100644 --- a/core/bin/external_node/src/config/mod.rs +++ b/core/bin/external_node/src/config/mod.rs @@ -29,6 +29,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, @@ -49,6 +50,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") @@ -60,6 +63,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, @@ -558,6 +562,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 a72acdad81d5..fe75a47e7ae2 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