From 1d815957534a2da449fdaf914150ff641d2f1296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cperekopskiy=E2=80=9D?= Date: Tue, 17 Oct 2023 16:45:04 +0300 Subject: [PATCH 1/9] eth_watch changes for governor upgrades --- core/lib/config/src/configs/contracts.rs | 2 + core/lib/contracts/src/lib.rs | 18 ++- core/lib/types/src/protocol_version.rs | 107 ++++++++++++++++ core/lib/zksync_core/src/eth_watch/client.rs | 46 ++++++- .../event_processors/governance_upgrades.rs | 117 ++++++++++++++++++ .../src/eth_watch/event_processors/mod.rs | 1 + core/lib/zksync_core/src/eth_watch/mod.rs | 27 +++- core/lib/zksync_core/src/lib.rs | 1 + 8 files changed, 308 insertions(+), 11 deletions(-) create mode 100644 core/lib/zksync_core/src/eth_watch/event_processors/governance_upgrades.rs diff --git a/core/lib/config/src/configs/contracts.rs b/core/lib/config/src/configs/contracts.rs index ddc26f7bf35..4c3230f7c26 100644 --- a/core/lib/config/src/configs/contracts.rs +++ b/core/lib/config/src/configs/contracts.rs @@ -34,6 +34,7 @@ pub struct ContractsConfig { pub fri_recursion_scheduler_level_vk_hash: H256, pub fri_recursion_node_level_vk_hash: H256, pub fri_recursion_leaf_level_vk_hash: H256, + pub governance_addr: Option
, } impl ContractsConfig { @@ -93,6 +94,7 @@ mod tests { fri_recursion_leaf_level_vk_hash: hash( "0x72167c43a46cf38875b267d67716edc4563861364a3c03ab7aee73498421e828", ), + governance_addr: None, } } diff --git a/core/lib/contracts/src/lib.rs b/core/lib/contracts/src/lib.rs index 1fb12da5531..d56c9b21314 100644 --- a/core/lib/contracts/src/lib.rs +++ b/core/lib/contracts/src/lib.rs @@ -24,6 +24,8 @@ pub enum ContractLanguage { Yul, } +const GOVERNANCE_CONTRACT_FILE: &str = + "contracts/ethereum/artifacts/cache/solpp-generated-contracts/governance/IGovernance.sol/IGovernance.json"; const ZKSYNC_CONTRACT_FILE: &str = "contracts/ethereum/artifacts/cache/solpp-generated-contracts/zksync/interfaces/IZkSync.sol/IZkSync.json"; const MULTICALL3_CONTRACT_FILE: &str = @@ -50,9 +52,17 @@ fn read_file_to_json_value(path: impl AsRef) -> serde_json::Value { .unwrap_or_else(|e| panic!("Failed to parse file {:?}: {}", path, e)) } +pub fn load_contract_if_present + std::fmt::Debug>(path: P) -> Option { + let zksync_home = std::env::var("ZKSYNC_HOME").unwrap_or_else(|_| ".".into()); + let path = Path::new(&zksync_home).join(path); + path.exists().then(|| { + serde_json::from_value(read_file_to_json_value(&path)["abi"].take()) + .unwrap_or_else(|e| panic!("Failed to parse contract abi from file {:?}: {}", path, e)) + }) +} + pub fn load_contract + std::fmt::Debug>(path: P) -> Contract { - serde_json::from_value(read_file_to_json_value(&path)["abi"].take()) - .unwrap_or_else(|e| panic!("Failed to parse contract abi from file {:?}: {}", path, e)) + load_contract_if_present(path).unwrap() } pub fn load_sys_contract(contract_name: &str) -> Contract { @@ -69,6 +79,10 @@ pub fn read_contract_abi(path: impl AsRef) -> String { .to_string() } +pub fn governance_contract() -> Option { + load_contract_if_present(GOVERNANCE_CONTRACT_FILE) +} + pub fn zksync_contract() -> Contract { load_contract(ZKSYNC_CONTRACT_FILE) } diff --git a/core/lib/types/src/protocol_version.rs b/core/lib/types/src/protocol_version.rs index afc4868785a..9c87a9cdd86 100644 --- a/core/lib/types/src/protocol_version.rs +++ b/core/lib/types/src/protocol_version.rs @@ -156,6 +156,32 @@ pub struct L1VerifierConfig { pub recursion_scheduler_level_vk_hash: H256, } +/// Represents a call to be made during governance operation. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Call { + /// The address to which the call will be made. + pub target: Address, + /// The amount of Ether (in wei) to be sent along with the call. + pub value: U256, + /// The calldata to be executed on the `target` address. + pub data: Vec, + /// Hash of the corresponding Ethereum transaction. Size should be 32 bytes. + pub eth_hash: H256, + /// Block in which Ethereum transaction was included. + pub eth_block: u64, +} + +/// Defines the structure of an operation that Governance contract executes. +#[derive(Debug, Clone, Default)] +pub struct GovernanceOperation { + /// An array of `Call` structs, each representing a call to be made during the operation. + pub calls: Vec, + /// The hash of the predecessor operation, that should be executed before this operation. + pub predecessor: H256, + /// The value used for creating unique operation hashes. + pub salt: H256, +} + /// Protocol upgrade proposal from L1. /// Most of the fields are optional meaning if value is none /// then this field is not changed within an upgrade. @@ -408,6 +434,87 @@ impl TryFrom for ProtocolUpgrade { } } +impl TryFrom for GovernanceOperation { + type Error = crate::ethabi::Error; + + fn try_from(event: Log) -> Result { + let call_param_type = ParamType::Tuple(vec![ + ParamType::Address, + ParamType::Uint(256), + ParamType::Bytes, + ]); + + let operation_param_type = ParamType::Tuple(vec![ + ParamType::Array(Box::new(call_param_type)), + ParamType::FixedBytes(32), + ParamType::FixedBytes(32), + ]); + let mut decoded = decode(&[ParamType::Uint(256), operation_param_type], &event.data.0)?; + decoded = decoded.remove(1).into_tuple().unwrap(); + + 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 calls = decoded.remove(0).into_array().unwrap(); + let predecessor = H256::from_slice(&decoded.remove(0).into_fixed_bytes().unwrap()); + let salt = H256::from_slice(&decoded.remove(0).into_fixed_bytes().unwrap()); + + let calls = calls + .into_iter() + .map(|call| { + let mut decoded = call.into_tuple().unwrap(); + + Call { + target: decoded.remove(0).into_address().unwrap(), + value: decoded.remove(0).into_uint().unwrap(), + data: decoded.remove(0).into_bytes().unwrap(), + eth_hash, + eth_block, + } + }) + .collect(); + + Ok(Self { + calls, + predecessor, + salt, + }) + } +} + +impl TryFrom for ProtocolUpgrade { + type Error = crate::ethabi::Error; + + fn try_from(call: Call) -> Result { + let data = call + .data + .into_iter() + .skip(4) + .chain(encode(&[Token::FixedBytes(H256::zero().0.to_vec())])) + .collect::>() + .into(); + let log = Log { + address: Default::default(), + topics: Default::default(), + data, + block_hash: Default::default(), + block_number: Some(call.eth_block.into()), + transaction_hash: Some(call.eth_hash), + transaction_index: Default::default(), + log_index: Default::default(), + transaction_log_index: Default::default(), + log_type: Default::default(), + removed: Default::default(), + }; + ProtocolUpgrade::try_from(log) + } +} + #[derive(Debug, Clone, Default)] pub struct ProtocolVersion { /// Protocol version ID diff --git a/core/lib/zksync_core/src/eth_watch/client.rs b/core/lib/zksync_core/src/eth_watch/client.rs index f8154f14dbc..5074a170fb1 100644 --- a/core/lib/zksync_core/src/eth_watch/client.rs +++ b/core/lib/zksync_core/src/eth_watch/client.rs @@ -48,6 +48,7 @@ pub struct EthHttpQueryClient { client: E, topics: Vec, zksync_contract_addr: Address, + governance_address: Option
, verifier_contract_abi: Contract, confirmations_for_eth_event: Option, } @@ -56,13 +57,19 @@ impl EthHttpQueryClient { pub fn new( client: E, zksync_contract_addr: Address, + governance_address: Option
, confirmations_for_eth_event: Option, ) -> Self { - tracing::debug!("New eth client, contract addr: {:x}", zksync_contract_addr); + tracing::debug!( + "New eth client, zkSync addr: {:x}, governance addr: {:?}", + zksync_contract_addr, + governance_address + ); Self { client, topics: Vec::new(), zksync_contract_addr, + governance_address, verifier_contract_abi: verifier_contract(), confirmations_for_eth_event, } @@ -75,7 +82,13 @@ impl EthHttpQueryClient { topics: Vec, ) -> Result, Error> { let filter = FilterBuilder::default() - .address(vec![self.zksync_contract_addr]) + .address( + [Some(self.zksync_contract_addr), self.governance_address] + .iter() + .flatten() + .copied() + .collect(), + ) .from_block(from) .to_block(to) .topics(Some(topics), None, None, None) @@ -88,10 +101,14 @@ impl EthHttpQueryClient { #[async_trait::async_trait] impl EthClient for EthHttpQueryClient { async fn scheduler_vk_hash(&self, verifier_address: Address) -> Result { - let vk_token: Token = self + // This is here for backward compatibility with the old verifier: + // Legacy verifier returns the full verification key; + // New verifier returns the hash of the verification key. + + let vk_hash = self .client .call_contract_function( - "get_verification_key", + "verificationKeyHash", (), None, Default::default(), @@ -99,8 +116,25 @@ impl EthClient for EthHttpQueryClient Option { + let Some(governance_contract) = governance_contract() else { + return None; + }; + + Some(Self { + diamond_proxy_address, + last_seen_version_id, + upgrade_proposal_signature: governance_contract + .event("TransparentOperationScheduled") + .expect("TransparentOperationScheduled event is missing in abi") + .signature(), + }) + } +} + +#[async_trait::async_trait] +impl EventProcessor for GovernanceUpgradesEventProcessor { + async fn process_events( + &mut self, + storage: &mut StorageProcessor<'_>, + client: &W, + events: Vec, + ) -> Result<(), Error> { + let mut upgrades = Vec::new(); + for event in events + .into_iter() + .filter(|event| event.topics[0] == self.upgrade_proposal_signature) + { + let governance_operation = GovernanceOperation::try_from(event) + .map_err(|err| Error::LogParse(format!("{:?}", err)))?; + // Some calls can target other contracts than Diamond proxy, skip them. + for call in governance_operation + .calls + .into_iter() + .filter(|call| call.target == self.diamond_proxy_address) + { + let upgrade = ProtocolUpgrade::try_from(call) + .map_err(|err| Error::LogParse(format!("{:?}", err)))?; + // Scheduler VK is not present in proposal event. It is hardcoded in verifier contract. + let scheduler_vk_hash = if let Some(address) = upgrade.verifier_address { + Some(client.scheduler_vk_hash(address).await?) + } else { + None + }; + 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 last_id = new_upgrades.last().unwrap().0.id; + let stage_start = Instant::now(); + for (upgrade, scheduler_vk_hash) in new_upgrades { + let previous_version = storage + .protocol_versions_dal() + .load_previous_version(upgrade.id) + .await + .expect("Expected previous version to be present in DB"); + let new_version = previous_version.apply_upgrade(upgrade, scheduler_vk_hash); + storage + .protocol_versions_dal() + .save_protocol_version_with_tx(new_version) + .await; + } + metrics::histogram!("eth_watcher.poll_eth_node", stage_start.elapsed(), "stage" => "persist_upgrades"); + + self.last_seen_version_id = last_id; + + Ok(()) + } + + fn relevant_topic(&self) -> H256 { + self.upgrade_proposal_signature + } +} diff --git a/core/lib/zksync_core/src/eth_watch/event_processors/mod.rs b/core/lib/zksync_core/src/eth_watch/event_processors/mod.rs index 70e1db9a3f1..84ea1eeb04c 100644 --- a/core/lib/zksync_core/src/eth_watch/event_processors/mod.rs +++ b/core/lib/zksync_core/src/eth_watch/event_processors/mod.rs @@ -2,6 +2,7 @@ use crate::eth_watch::client::{Error, EthClient}; use zksync_dal::StorageProcessor; use zksync_types::{web3::types::Log, H256}; +pub mod governance_upgrades; pub mod priority_ops; pub mod upgrades; diff --git a/core/lib/zksync_core/src/eth_watch/mod.rs b/core/lib/zksync_core/src/eth_watch/mod.rs index 1cd2eb95947..abd76633979 100644 --- a/core/lib/zksync_core/src/eth_watch/mod.rs +++ b/core/lib/zksync_core/src/eth_watch/mod.rs @@ -26,6 +26,7 @@ mod tests; use self::{ client::{Error, EthClient, EthHttpQueryClient, RETRY_LIMIT}, event_processors::{ + governance_upgrades::GovernanceUpgradesEventProcessor, priority_ops::PriorityOpsEventProcessor, upgrades::UpgradesEventProcessor, EventProcessor, }, metrics::{PollStage, METRICS}, @@ -48,7 +49,12 @@ pub struct EthWatch { } impl EthWatch { - pub async fn new(mut client: W, pool: &ConnectionPool, poll_interval: Duration) -> Self { + pub async fn new( + diamond_proxy_address: Address, + mut client: W, + pool: &ConnectionPool, + poll_interval: Duration, + ) -> Self { let mut storage = pool.access_storage_tagged("eth_watch").await.unwrap(); let state = Self::initialize_state(&client, &mut storage).await; @@ -58,10 +64,17 @@ impl EthWatch { let priority_ops_processor = PriorityOpsEventProcessor::new(state.next_expected_priority_id); let upgrades_processor = UpgradesEventProcessor::new(state.last_seen_version_id); - let event_processors: Vec>> = vec![ + let mut event_processors: Vec>> = vec![ Box::new(priority_ops_processor), Box::new(upgrades_processor), ]; + let governance_upgrades_processor = GovernanceUpgradesEventProcessor::new( + diamond_proxy_address, + state.last_seen_version_id, + ); + if let Some(governance_upgrades_processor) = governance_upgrades_processor { + event_processors.push(Box::new(governance_upgrades_processor)) + } let topics = event_processors .iter() @@ -174,16 +187,24 @@ pub async fn start_eth_watch( pool: ConnectionPool, eth_gateway: E, diamond_proxy_addr: Address, + governance_addr: Option
, stop_receiver: watch::Receiver, ) -> anyhow::Result>> { let eth_watch = ETHWatchConfig::from_env().context("ETHWatchConfig::from_env()")?; let eth_client = EthHttpQueryClient::new( eth_gateway, diamond_proxy_addr, + governance_addr, eth_watch.confirmations_for_eth_event, ); - let mut eth_watch = EthWatch::new(eth_client, &pool, eth_watch.poll_interval()).await; + let mut eth_watch = EthWatch::new( + diamond_proxy_addr, + eth_client, + &pool, + eth_watch.poll_interval(), + ) + .await; Ok(tokio::spawn(async move { eth_watch.run(pool, stop_receiver).await diff --git a/core/lib/zksync_core/src/lib.rs b/core/lib/zksync_core/src/lib.rs index f64eaffefc2..6fd49f95dbe 100644 --- a/core/lib/zksync_core/src/lib.rs +++ b/core/lib/zksync_core/src/lib.rs @@ -501,6 +501,7 @@ pub async fn initialize_components( eth_watch_pool, query_client.clone(), main_zksync_contract_address, + contracts_config.governance_addr, stop_receiver.clone(), ) .await From ed5e99fc446e13142499364a290a2f7d29dd375f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cperekopskiy=E2=80=9D?= Date: Tue, 17 Oct 2023 16:49:32 +0300 Subject: [PATCH 2/9] Add comment --- core/lib/types/src/protocol_version.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/lib/types/src/protocol_version.rs b/core/lib/types/src/protocol_version.rs index 9c87a9cdd86..f292fa4a195 100644 --- a/core/lib/types/src/protocol_version.rs +++ b/core/lib/types/src/protocol_version.rs @@ -491,6 +491,10 @@ impl TryFrom for ProtocolUpgrade { type Error = crate::ethabi::Error; fn try_from(call: Call) -> Result { + // Reuses `ProtocolUpgrade::try_from`. + // `ProtocolUpgrade::try_from` only uses 3 log fields: `data`, `block_number`, `transaction_hash`. Others can be filled with dummy values. + // We build data as `call.data` without first 4 bytes which are for selector + // and append it with `bytes32(0)` for compatibility with old event data. let data = call .data .into_iter() From 676c8bb0cac54c88414974c8d3f147bb6acf2cc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cperekopskiy=E2=80=9D?= Date: Tue, 17 Oct 2023 17:13:01 +0300 Subject: [PATCH 3/9] Fix tests --- core/lib/zksync_core/src/eth_watch/tests.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/lib/zksync_core/src/eth_watch/tests.rs b/core/lib/zksync_core/src/eth_watch/tests.rs index d9448efbfab..123a9ead25e 100644 --- a/core/lib/zksync_core/src/eth_watch/tests.rs +++ b/core/lib/zksync_core/src/eth_watch/tests.rs @@ -190,6 +190,7 @@ async fn test_normal_operation_l1_txs(connection_pool: ConnectionPool) { let mut client = FakeEthClient::new(); let mut watcher = EthWatch::new( + Address::default(), client.clone(), &connection_pool, std::time::Duration::from_nanos(1), @@ -235,6 +236,7 @@ async fn test_normal_operation_upgrades(connection_pool: ConnectionPool) { let mut client = FakeEthClient::new(); let mut watcher = EthWatch::new( + Address::default(), client.clone(), &connection_pool, std::time::Duration::from_nanos(1), @@ -293,6 +295,7 @@ async fn test_gap_in_upgrades(connection_pool: ConnectionPool) { let mut client = FakeEthClient::new(); let mut watcher = EthWatch::new( + Address::default(), client.clone(), &connection_pool, std::time::Duration::from_nanos(1), @@ -330,6 +333,7 @@ async fn test_gap_in_single_batch(connection_pool: ConnectionPool) { let mut client = FakeEthClient::new(); let mut watcher = EthWatch::new( + Address::default(), client.clone(), &connection_pool, std::time::Duration::from_nanos(1), @@ -357,6 +361,7 @@ async fn test_gap_between_batches(connection_pool: ConnectionPool) { let mut client = FakeEthClient::new(); let mut watcher = EthWatch::new( + Address::default(), client.clone(), &connection_pool, std::time::Duration::from_nanos(1), @@ -389,6 +394,7 @@ async fn test_overlapping_batches(connection_pool: ConnectionPool) { let mut client = FakeEthClient::new(); let mut watcher = EthWatch::new( + Address::default(), client.clone(), &connection_pool, std::time::Duration::from_nanos(1), From e9c3822e49050a9c53680f51132cab3b749a1d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cperekopskiy=E2=80=9D?= Date: Wed, 18 Oct 2023 11:30:19 +0300 Subject: [PATCH 4/9] nits --- core/lib/contracts/src/lib.rs | 4 +++- core/lib/types/src/protocol_version.rs | 6 +++--- .../eth_watch/event_processors/governance_upgrades.rs | 10 ++++++++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/core/lib/contracts/src/lib.rs b/core/lib/contracts/src/lib.rs index d56c9b21314..cdc5b8b0e6a 100644 --- a/core/lib/contracts/src/lib.rs +++ b/core/lib/contracts/src/lib.rs @@ -62,7 +62,9 @@ pub fn load_contract_if_present + std::fmt::Debug>(path: P) -> Op } pub fn load_contract + std::fmt::Debug>(path: P) -> Contract { - load_contract_if_present(path).unwrap() + load_contract_if_present(&path).unwrap_or_else(|| { + panic!("Failed to load contract from {:?}", path); + }) } pub fn load_sys_contract(contract_name: &str) -> Contract { diff --git a/core/lib/types/src/protocol_version.rs b/core/lib/types/src/protocol_version.rs index f292fa4a195..b4fbe6edeac 100644 --- a/core/lib/types/src/protocol_version.rs +++ b/core/lib/types/src/protocol_version.rs @@ -156,7 +156,7 @@ pub struct L1VerifierConfig { pub recursion_scheduler_level_vk_hash: H256, } -/// Represents a call to be made during governance operation. +/// Represents a call that was made during governance operation. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Call { /// The address to which the call will be made. @@ -165,13 +165,13 @@ pub struct Call { pub value: U256, /// The calldata to be executed on the `target` address. pub data: Vec, - /// Hash of the corresponding Ethereum transaction. Size should be 32 bytes. + /// Hash of the corresponding Ethereum transaction. pub eth_hash: H256, /// Block in which Ethereum transaction was included. pub eth_block: u64, } -/// Defines the structure of an operation that Governance contract executes. +/// Defines the structure of an operation that Governance contract executed. #[derive(Debug, Clone, Default)] pub struct GovernanceOperation { /// An array of `Call` structs, each representing a call to be made during the operation. 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 81f3e903b9a..77543d5458a 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 @@ -11,10 +11,11 @@ use zksync_types::{ ProtocolVersionId, H256, }; -/// Responsible for saving new protocol upgrade proposals to the database. +/// Listens to operation events coming from the governance contract and saves new protocol upgrade proposals to the database. #[derive(Debug)] pub struct GovernanceUpgradesEventProcessor { diamond_proxy_address: Address, + /// Last protocol version seen. Used to skip events for already known upgrade proposals. last_seen_version_id: ProtocolVersionId, upgrade_proposal_signature: H256, } @@ -97,7 +98,12 @@ impl EventProcessor for GovernanceUpgradesEventProcessor .protocol_versions_dal() .load_previous_version(upgrade.id) .await - .expect("Expected previous version to be present in DB"); + .unwrap_or_else(|| { + panic!( + "Expected some version preceding {:?} be present in DB", + upgrade.id + ) + }); let new_version = previous_version.apply_upgrade(upgrade, scheduler_vk_hash); storage .protocol_versions_dal() From 96626bfdb6afd2b154e4ebb3c3fcbc6fdbc320db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cperekopskiy=E2=80=9D?= Date: Wed, 18 Oct 2023 11:43:24 +0300 Subject: [PATCH 5/9] Handle missing governance contract on caller side --- .../event_processors/governance_upgrades.rs | 16 ++++++---------- core/lib/zksync_core/src/eth_watch/mod.rs | 13 ++++++++----- 2 files changed, 14 insertions(+), 15 deletions(-) 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 77543d5458a..2867f63cd40 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 @@ -4,11 +4,10 @@ use crate::eth_watch::{ }; use std::convert::TryFrom; use std::time::Instant; -use zksync_contracts::governance_contract; use zksync_dal::StorageProcessor; use zksync_types::{ - protocol_version::GovernanceOperation, web3::types::Log, Address, ProtocolUpgrade, - ProtocolVersionId, H256, + ethabi::Contract, protocol_version::GovernanceOperation, web3::types::Log, Address, + ProtocolUpgrade, ProtocolVersionId, H256, }; /// Listens to operation events coming from the governance contract and saves new protocol upgrade proposals to the database. @@ -24,19 +23,16 @@ impl GovernanceUpgradesEventProcessor { pub fn new( diamond_proxy_address: Address, last_seen_version_id: ProtocolVersionId, - ) -> Option { - let Some(governance_contract) = governance_contract() else { - return None; - }; - - Some(Self { + governance_contract: &Contract, + ) -> Self { + Self { diamond_proxy_address, last_seen_version_id, upgrade_proposal_signature: governance_contract .event("TransparentOperationScheduled") .expect("TransparentOperationScheduled event is missing in abi") .signature(), - }) + } } } diff --git a/core/lib/zksync_core/src/eth_watch/mod.rs b/core/lib/zksync_core/src/eth_watch/mod.rs index abd76633979..6a129b4610d 100644 --- a/core/lib/zksync_core/src/eth_watch/mod.rs +++ b/core/lib/zksync_core/src/eth_watch/mod.rs @@ -11,6 +11,7 @@ use std::time::Duration; use zksync_config::constants::PRIORITY_EXPIRATION; use zksync_config::ETHWatchConfig; +use zksync_contracts::governance_contract; use zksync_dal::{ConnectionPool, StorageProcessor}; use zksync_eth_client::EthInterface; use zksync_types::{ @@ -68,11 +69,13 @@ impl EthWatch { Box::new(priority_ops_processor), Box::new(upgrades_processor), ]; - let governance_upgrades_processor = GovernanceUpgradesEventProcessor::new( - diamond_proxy_address, - state.last_seen_version_id, - ); - if let Some(governance_upgrades_processor) = governance_upgrades_processor { + + if let Some(governance_contract) = governance_contract() { + let governance_upgrades_processor = GovernanceUpgradesEventProcessor::new( + diamond_proxy_address, + state.last_seen_version_id, + &governance_contract, + ); event_processors.push(Box::new(governance_upgrades_processor)) } From f346609f86fcd8d23d52deae8611f8541d26de93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cperekopskiy=E2=80=9D?= Date: Wed, 18 Oct 2023 11:46:56 +0300 Subject: [PATCH 6/9] Add comment --- core/lib/zksync_core/src/eth_watch/client.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/lib/zksync_core/src/eth_watch/client.rs b/core/lib/zksync_core/src/eth_watch/client.rs index 5074a170fb1..af38ac79ae7 100644 --- a/core/lib/zksync_core/src/eth_watch/client.rs +++ b/core/lib/zksync_core/src/eth_watch/client.rs @@ -48,6 +48,8 @@ pub struct EthHttpQueryClient { client: E, topics: Vec, zksync_contract_addr: Address, + /// Address of the `Governance` contract. It's optional because it is present only for post-boojum chains. + /// If address is some then client will listen to events coming from it. governance_address: Option
, verifier_contract_abi: Contract, confirmations_for_eth_event: Option, From fd82ec3fbfbccaf3c384fc0a83b74962ea319ed0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cperekopskiy=E2=80=9D?= Date: Wed, 18 Oct 2023 13:01:06 +0300 Subject: [PATCH 7/9] Add test --- core/lib/zksync_core/src/eth_watch/mod.rs | 12 +- core/lib/zksync_core/src/eth_watch/tests.rs | 260 +++++++++++++++++--- core/lib/zksync_core/src/lib.rs | 8 +- 3 files changed, 241 insertions(+), 39 deletions(-) diff --git a/core/lib/zksync_core/src/eth_watch/mod.rs b/core/lib/zksync_core/src/eth_watch/mod.rs index 6a129b4610d..150b6556ab4 100644 --- a/core/lib/zksync_core/src/eth_watch/mod.rs +++ b/core/lib/zksync_core/src/eth_watch/mod.rs @@ -11,11 +11,11 @@ use std::time::Duration; use zksync_config::constants::PRIORITY_EXPIRATION; use zksync_config::ETHWatchConfig; -use zksync_contracts::governance_contract; use zksync_dal::{ConnectionPool, StorageProcessor}; use zksync_eth_client::EthInterface; use zksync_types::{ - web3::types::BlockNumber as Web3BlockNumber, Address, PriorityOpId, ProtocolVersionId, + ethabi::Contract, web3::types::BlockNumber as Web3BlockNumber, Address, PriorityOpId, + ProtocolVersionId, }; mod client; @@ -52,6 +52,7 @@ pub struct EthWatch { impl EthWatch { pub async fn new( diamond_proxy_address: Address, + governance_contract: Option, mut client: W, pool: &ConnectionPool, poll_interval: Duration, @@ -70,7 +71,7 @@ impl EthWatch { Box::new(upgrades_processor), ]; - if let Some(governance_contract) = governance_contract() { + if let Some(governance_contract) = governance_contract { let governance_upgrades_processor = GovernanceUpgradesEventProcessor::new( diamond_proxy_address, state.last_seen_version_id, @@ -190,19 +191,20 @@ pub async fn start_eth_watch( pool: ConnectionPool, eth_gateway: E, diamond_proxy_addr: Address, - governance_addr: Option
, + governance: Option<(Contract, Address)>, stop_receiver: watch::Receiver, ) -> anyhow::Result>> { let eth_watch = ETHWatchConfig::from_env().context("ETHWatchConfig::from_env()")?; let eth_client = EthHttpQueryClient::new( eth_gateway, diamond_proxy_addr, - governance_addr, + governance.as_ref().map(|(_, address)| *address), eth_watch.confirmations_for_eth_event, ); let mut eth_watch = EthWatch::new( diamond_proxy_addr, + governance.map(|(contract, _)| contract), eth_client, &pool, eth_watch.poll_interval(), diff --git a/core/lib/zksync_core/src/eth_watch/tests.rs b/core/lib/zksync_core/src/eth_watch/tests.rs index 123a9ead25e..01fb83b98c0 100644 --- a/core/lib/zksync_core/src/eth_watch/tests.rs +++ b/core/lib/zksync_core/src/eth_watch/tests.rs @@ -10,7 +10,7 @@ use zksync_dal::{ConnectionPool, StorageProcessor}; use zksync_types::protocol_version::{ProtocolUpgradeTx, ProtocolUpgradeTxCommonData}; use zksync_types::web3::types::{Address, BlockNumber}; use zksync_types::{ - ethabi::{encode, Hash, Token}, + ethabi::{encode, Contract, Hash, Token}, l1::{L1Tx, OpProcessingType, PriorityQueueType}, web3::types::Log, Execute, L1TxCommonData, PriorityOpId, ProtocolUpgrade, ProtocolVersion, ProtocolVersionId, @@ -22,7 +22,8 @@ use crate::eth_watch::{client::EthClient, EthWatch}; struct FakeEthClientData { transactions: HashMap>, - upgrades: HashMap>, + diamond_upgrades: HashMap>, + governance_upgrades: HashMap>, last_finalized_block_number: u64, } @@ -30,7 +31,8 @@ impl FakeEthClientData { fn new() -> Self { Self { transactions: Default::default(), - upgrades: Default::default(), + diamond_upgrades: Default::default(), + governance_upgrades: Default::default(), last_finalized_block_number: 0, } } @@ -45,12 +47,21 @@ impl FakeEthClientData { } } - fn add_upgrades(&mut self, upgrades: &[(ProtocolUpgrade, u64)]) { + fn add_diamond_upgrades(&mut self, upgrades: &[(ProtocolUpgrade, u64)]) { for (upgrade, eth_block) in upgrades { - self.upgrades + self.diamond_upgrades .entry(*eth_block) .or_default() - .push(upgrade_into_log(upgrade.clone(), *eth_block)); + .push(upgrade_into_diamond_proxy_log(upgrade.clone(), *eth_block)); + } + } + + fn add_governance_upgrades(&mut self, upgrades: &[(ProtocolUpgrade, u64)]) { + for (upgrade, eth_block) in upgrades { + self.governance_upgrades + .entry(*eth_block) + .or_default() + .push(upgrade_into_governor_log(upgrade.clone(), *eth_block)); } } @@ -75,8 +86,12 @@ impl FakeEthClient { self.inner.write().await.add_transactions(transactions); } - async fn add_upgrades(&mut self, upgrades: &[(ProtocolUpgrade, u64)]) { - self.inner.write().await.add_upgrades(upgrades); + async fn add_diamond_upgrades(&mut self, upgrades: &[(ProtocolUpgrade, u64)]) { + self.inner.write().await.add_diamond_upgrades(upgrades); + } + + async fn add_governance_upgrades(&mut self, upgrades: &[(ProtocolUpgrade, u64)]) { + self.inner.write().await.add_governance_upgrades(upgrades); } async fn set_last_finalized_block_number(&mut self, number: u64) { @@ -113,7 +128,10 @@ impl EthClient for FakeEthClient { if let Some(ops) = self.inner.read().await.transactions.get(&number) { logs.extend_from_slice(ops); } - if let Some(ops) = self.inner.read().await.upgrades.get(&number) { + if let Some(ops) = self.inner.read().await.diamond_upgrades.get(&number) { + logs.extend_from_slice(ops); + } + if let Some(ops) = self.inner.read().await.governance_upgrades.get(&number) { logs.extend_from_slice(ops); } } @@ -191,6 +209,7 @@ async fn test_normal_operation_l1_txs(connection_pool: ConnectionPool) { let mut client = FakeEthClient::new(); let mut watcher = EthWatch::new( Address::default(), + None, client.clone(), &connection_pool, std::time::Duration::from_nanos(1), @@ -237,6 +256,7 @@ async fn test_normal_operation_upgrades(connection_pool: ConnectionPool) { let mut client = FakeEthClient::new(); let mut watcher = EthWatch::new( Address::default(), + None, client.clone(), &connection_pool, std::time::Duration::from_nanos(1), @@ -245,7 +265,7 @@ async fn test_normal_operation_upgrades(connection_pool: ConnectionPool) { let mut storage = connection_pool.access_test_storage().await; client - .add_upgrades(&[ + .add_diamond_upgrades(&[ ( ProtocolUpgrade { id: ProtocolVersionId::latest(), @@ -296,6 +316,7 @@ async fn test_gap_in_upgrades(connection_pool: ConnectionPool) { let mut client = FakeEthClient::new(); let mut watcher = EthWatch::new( Address::default(), + None, client.clone(), &connection_pool, std::time::Duration::from_nanos(1), @@ -304,7 +325,7 @@ async fn test_gap_in_upgrades(connection_pool: ConnectionPool) { let mut storage = connection_pool.access_test_storage().await; client - .add_upgrades(&[( + .add_diamond_upgrades(&[( ProtocolUpgrade { id: ProtocolVersionId::next(), tx: None, @@ -326,6 +347,66 @@ async fn test_gap_in_upgrades(connection_pool: ConnectionPool) { assert_eq!(db_ids[1], next_version); } +#[db_test] +async fn test_normal_operation_governance_upgrades(connection_pool: ConnectionPool) { + setup_db(&connection_pool).await; + + let mut client = FakeEthClient::new(); + let mut watcher = EthWatch::new( + Address::default(), + Some(governance_contract()), + client.clone(), + &connection_pool, + std::time::Duration::from_nanos(1), + ) + .await; + + let mut storage = connection_pool.access_test_storage().await; + client + .add_governance_upgrades(&[ + ( + ProtocolUpgrade { + id: ProtocolVersionId::latest(), + tx: None, + ..Default::default() + }, + 10, + ), + ( + ProtocolUpgrade { + id: ProtocolVersionId::next(), + tx: Some(build_upgrade_tx(ProtocolVersionId::next(), 18)), + ..Default::default() + }, + 18, + ), + ]) + .await; + client.set_last_finalized_block_number(15).await; + // second upgrade will not be processed, as it has less than 5 confirmations + watcher.loop_iteration(&mut storage).await.unwrap(); + + let db_ids = storage.protocol_versions_dal().all_version_ids().await; + // there should be genesis version and just added version + assert_eq!(db_ids.len(), 2); + assert_eq!(db_ids[1], ProtocolVersionId::latest()); + + client.set_last_finalized_block_number(20).await; + // now the second upgrade will be processed + watcher.loop_iteration(&mut storage).await.unwrap(); + let db_ids = storage.protocol_versions_dal().all_version_ids().await; + assert_eq!(db_ids.len(), 3); + assert_eq!(db_ids[2], ProtocolVersionId::next()); + + // check that tx was saved with the last upgrade + let tx = storage + .protocol_versions_dal() + .get_protocol_upgrade_tx(ProtocolVersionId::next()) + .await + .unwrap(); + assert_eq!(tx.common_data.upgrade_id, ProtocolVersionId::next()); +} + #[db_test] #[should_panic] async fn test_gap_in_single_batch(connection_pool: ConnectionPool) { @@ -334,6 +415,7 @@ async fn test_gap_in_single_batch(connection_pool: ConnectionPool) { let mut client = FakeEthClient::new(); let mut watcher = EthWatch::new( Address::default(), + None, client.clone(), &connection_pool, std::time::Duration::from_nanos(1), @@ -362,6 +444,7 @@ async fn test_gap_between_batches(connection_pool: ConnectionPool) { let mut client = FakeEthClient::new(); let mut watcher = EthWatch::new( Address::default(), + None, client.clone(), &connection_pool, std::time::Duration::from_nanos(1), @@ -395,6 +478,7 @@ async fn test_overlapping_batches(connection_pool: ConnectionPool) { let mut client = FakeEthClient::new(); let mut watcher = EthWatch::new( Address::default(), + None, client.clone(), &connection_pool, std::time::Duration::from_nanos(1), @@ -496,7 +580,72 @@ fn tx_into_log(tx: L1Tx) -> Log { } } -fn upgrade_into_log(upgrade: ProtocolUpgrade, eth_block: u64) -> Log { +fn upgrade_into_diamond_proxy_log(upgrade: ProtocolUpgrade, eth_block: u64) -> Log { + let diamond_cut = upgrade_into_diamond_cut(upgrade); + let data = encode(&[diamond_cut, Token::FixedBytes(vec![0u8; 32])]); + Log { + address: Address::repeat_byte(0x1), + topics: vec![zksync_contract() + .event("ProposeTransparentUpgrade") + .expect("ProposeTransparentUpgrade event is missing in abi") + .signature()], + data: data.into(), + block_hash: Some(H256::repeat_byte(0x11)), + block_number: Some(eth_block.into()), + transaction_hash: Some(H256::random()), + transaction_index: Some(0u64.into()), + log_index: Some(0u64.into()), + transaction_log_index: Some(0u64.into()), + log_type: None, + removed: None, + } +} + +fn upgrade_into_governor_log(upgrade: ProtocolUpgrade, eth_block: u64) -> Log { + let diamond_cut = upgrade_into_diamond_cut(upgrade); + let execute_upgrade_selector = zksync_contract() + .function("executeUpgrade") + .unwrap() + .short_signature(); + let diamond_upgrade_calldata = execute_upgrade_selector + .iter() + .copied() + .chain(encode(&[diamond_cut])) + .collect(); + let governance_call = Token::Tuple(vec![ + Token::Address(Default::default()), + Token::Uint(U256::default()), + Token::Bytes(diamond_upgrade_calldata), + ]); + let governance_operation = Token::Tuple(vec![ + Token::Array(vec![governance_call]), + Token::FixedBytes(vec![0u8; 32]), + Token::FixedBytes(vec![0u8; 32]), + ]); + let final_data = encode(&[Token::FixedBytes(vec![0u8; 32]), governance_operation]); + + Log { + address: Address::repeat_byte(0x1), + topics: vec![ + governance_contract() + .event("TransparentOperationScheduled") + .expect("TransparentOperationScheduled event is missing in abi") + .signature(), + Default::default(), + ], + data: final_data.into(), + block_hash: Some(H256::repeat_byte(0x11)), + block_number: Some(eth_block.into()), + transaction_hash: Some(H256::random()), + transaction_index: Some(0u64.into()), + log_index: Some(0u64.into()), + transaction_log_index: Some(0u64.into()), + log_type: None, + removed: None, + } +} + +fn upgrade_into_diamond_cut(upgrade: ProtocolUpgrade) -> Token { let tx_data_token = if let Some(tx) = upgrade.tx { Token::Tuple(vec![ Token::Uint(0xfe.into()), @@ -598,7 +747,7 @@ fn upgrade_into_log(upgrade: ProtocolUpgrade, eth_block: u64) -> Log { Token::Address(Default::default()), ]); - let final_token = Token::Tuple(vec![ + Token::Tuple(vec![ Token::Array(vec![]), Token::Address(Default::default()), Token::Bytes( @@ -607,25 +756,7 @@ fn upgrade_into_log(upgrade: ProtocolUpgrade, eth_block: u64) -> Log { .chain(encode(&[upgrade_token])) .collect(), ), - ]); - - let data = encode(&[final_token, Token::FixedBytes(vec![0u8; 32])]); - Log { - address: Address::repeat_byte(0x1), - topics: vec![zksync_contract() - .event("ProposeTransparentUpgrade") - .expect("ProposeTransparentUpgrade event is missing in abi") - .signature()], - data: data.into(), - block_hash: Some(H256::repeat_byte(0x11)), - block_number: Some(eth_block.into()), - transaction_hash: Some(H256::random()), - transaction_index: Some(0u64.into()), - log_index: Some(0u64.into()), - transaction_log_index: Some(0u64.into()), - log_type: None, - removed: None, - } + ]) } async fn setup_db(connection_pool: &ConnectionPool) { @@ -639,3 +770,68 @@ async fn setup_db(connection_pool: &ConnectionPool) { }) .await; } + +fn governance_contract() -> Contract { + let json = r#"[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "_id", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "delay", + "type": "uint256" + }, + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct IGovernance.Call[]", + "name": "calls", + "type": "tuple[]" + }, + { + "internalType": "bytes32", + "name": "predecessor", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + } + ], + "indexed": false, + "internalType": "struct IGovernance.Operation", + "name": "_operation", + "type": "tuple" + } + ], + "name": "TransparentOperationScheduled", + "type": "event" + } + ]"#; + serde_json::from_str(json).unwrap() +} diff --git a/core/lib/zksync_core/src/lib.rs b/core/lib/zksync_core/src/lib.rs index 6fd49f95dbe..19a02ad3018 100644 --- a/core/lib/zksync_core/src/lib.rs +++ b/core/lib/zksync_core/src/lib.rs @@ -27,7 +27,7 @@ use zksync_config::{ ApiConfig, ContractsConfig, DBConfig, ETHClientConfig, ETHSenderConfig, FetcherConfig, ProverConfigs, }; -use zksync_contracts::BaseSystemContracts; +use zksync_contracts::{governance_contract, BaseSystemContracts}; use zksync_dal::{ connection::DbVariant, healthcheck::ConnectionPoolHealthCheck, ConnectionPool, StorageProcessor, }; @@ -496,12 +496,16 @@ pub async fn initialize_components( .build() .await .context("failed to build eth_watch_pool")?; + let governance = match (governance_contract(), contracts_config.governance_addr) { + (Some(contract), Some(address)) => Some((contract, address)), + _ => None, + }; task_futures.push( start_eth_watch( eth_watch_pool, query_client.clone(), main_zksync_contract_address, - contracts_config.governance_addr, + governance, stop_receiver.clone(), ) .await From fe9799ea6bfa0123cd05b1391a33407be8f99e0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cperekopskiy=E2=80=9D?= Date: Wed, 18 Oct 2023 13:08:21 +0300 Subject: [PATCH 8/9] Add comments + better names --- core/lib/types/src/protocol_version.rs | 31 +++++++++++++++++++------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/core/lib/types/src/protocol_version.rs b/core/lib/types/src/protocol_version.rs index b4fbe6edeac..3eeecb0db04 100644 --- a/core/lib/types/src/protocol_version.rs +++ b/core/lib/types/src/protocol_version.rs @@ -449,8 +449,10 @@ impl TryFrom for GovernanceOperation { ParamType::FixedBytes(32), ParamType::FixedBytes(32), ]); + // Decode data. let mut decoded = decode(&[ParamType::Uint(256), operation_param_type], &event.data.0)?; - decoded = decoded.remove(1).into_tuple().unwrap(); + // Extract `GovernanceOperation` data. + let mut decoded_governance_operation = decoded.remove(1).into_tuple().unwrap(); let eth_hash = event .transaction_hash @@ -460,19 +462,32 @@ impl TryFrom for GovernanceOperation { .expect("Event block number is missing") .as_u64(); - let calls = decoded.remove(0).into_array().unwrap(); - let predecessor = H256::from_slice(&decoded.remove(0).into_fixed_bytes().unwrap()); - let salt = H256::from_slice(&decoded.remove(0).into_fixed_bytes().unwrap()); + let calls = decoded_governance_operation.remove(0).into_array().unwrap(); + let predecessor = H256::from_slice( + &decoded_governance_operation + .remove(0) + .into_fixed_bytes() + .unwrap(), + ); + let salt = H256::from_slice( + &decoded_governance_operation + .remove(0) + .into_fixed_bytes() + .unwrap(), + ); let calls = calls .into_iter() .map(|call| { - let mut decoded = call.into_tuple().unwrap(); + let mut decoded_governance_operation = call.into_tuple().unwrap(); Call { - target: decoded.remove(0).into_address().unwrap(), - value: decoded.remove(0).into_uint().unwrap(), - data: decoded.remove(0).into_bytes().unwrap(), + target: decoded_governance_operation + .remove(0) + .into_address() + .unwrap(), + value: decoded_governance_operation.remove(0).into_uint().unwrap(), + data: decoded_governance_operation.remove(0).into_bytes().unwrap(), eth_hash, eth_block, } From e4153b331b5c57a5bb8168fbaf8584f74af932cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cperekopskiy=E2=80=9D?= Date: Wed, 18 Oct 2023 18:01:19 +0300 Subject: [PATCH 9/9] Add negative unit test --- core/lib/types/src/protocol_version.rs | 43 ++++++++++++++++++++++++++ core/lib/zksync_core/src/lib.rs | 9 +++--- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/core/lib/types/src/protocol_version.rs b/core/lib/types/src/protocol_version.rs index 3eeecb0db04..8faa998636c 100644 --- a/core/lib/types/src/protocol_version.rs +++ b/core/lib/types/src/protocol_version.rs @@ -685,3 +685,46 @@ impl From for VmVersion { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn governance_operation_from_log() { + let call_token = Token::Tuple(vec![ + Token::Address(Address::random()), + Token::Uint(U256::zero()), + Token::Bytes(vec![1, 2, 3]), + ]); + let operation_token = Token::Tuple(vec![ + Token::Array(vec![call_token]), + Token::FixedBytes(H256::random().0.to_vec()), + Token::FixedBytes(H256::random().0.to_vec()), + ]); + let event_data = encode(&[Token::Uint(U256::zero()), operation_token]); + + let correct_log = Log { + address: Default::default(), + topics: Default::default(), + data: event_data.into(), + block_hash: Default::default(), + block_number: Some(1u64.into()), + transaction_hash: Some(H256::random()), + transaction_index: Default::default(), + log_index: Default::default(), + transaction_log_index: Default::default(), + log_type: Default::default(), + removed: Default::default(), + }; + let decoded_op: GovernanceOperation = correct_log.clone().try_into().unwrap(); + assert_eq!(decoded_op.calls.len(), 1); + + let mut incorrect_log = correct_log; + incorrect_log + .data + .0 + .truncate(incorrect_log.data.0.len() - 32); + assert!(TryInto::::try_into(incorrect_log).is_err()); + } +} diff --git a/core/lib/zksync_core/src/lib.rs b/core/lib/zksync_core/src/lib.rs index 19a02ad3018..7fd7db9443a 100644 --- a/core/lib/zksync_core/src/lib.rs +++ b/core/lib/zksync_core/src/lib.rs @@ -496,10 +496,11 @@ pub async fn initialize_components( .build() .await .context("failed to build eth_watch_pool")?; - let governance = match (governance_contract(), contracts_config.governance_addr) { - (Some(contract), Some(address)) => Some((contract, address)), - _ => None, - }; + let governance = contracts_config.governance_addr.map(|addr| { + let contract = governance_contract() + .expect("Governance contract must be present if governance_addr is set in config"); + (contract, addr) + }); task_futures.push( start_eth_watch( eth_watch_pool,