diff --git a/.github/release-please/manifest.json b/.github/release-please/manifest.json index 5b7501b6573..44e10fb13fd 100644 --- a/.github/release-please/manifest.json +++ b/.github/release-please/manifest.json @@ -1,5 +1,5 @@ { - "core": "24.26.0", + "core": "24.27.0", "prover": "16.5.0", "zk_toolbox": "0.1.2" } diff --git a/Cargo.lock b/Cargo.lock index 64965ab621a..d43b3f3d991 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9840,6 +9840,7 @@ dependencies = [ "anyhow", "async-recursion", "async-trait", + "test-log", "thiserror", "tokio", "tracing", @@ -9855,7 +9856,7 @@ dependencies = [ [[package]] name = "zksync_external_node" -version = "24.26.0" +version = "24.27.0" dependencies = [ "anyhow", "assert_matches", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 9f4d65132ec..6cf2ff4419a 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [24.27.0](https://github.com/matter-labs/zksync-era/compare/core-v24.26.0...core-v24.27.0) (2024-09-25) + + +### Features + +* **vm:** Split old and new VM implementations ([#2915](https://github.com/matter-labs/zksync-era/issues/2915)) ([93bc66f](https://github.com/matter-labs/zksync-era/commit/93bc66f21f9f67a440f06f1c4402e0d687698741)) + + +### Bug Fixes + +* **api:** Return correct flat call tracer ([#2917](https://github.com/matter-labs/zksync-era/issues/2917)) ([218646a](https://github.com/matter-labs/zksync-era/commit/218646aa1c56200f4ffee99b7f83366e2689354f)) + ## [24.26.0](https://github.com/matter-labs/zksync-era/compare/core-v24.25.0...core-v24.26.0) (2024-09-23) diff --git a/core/bin/external_node/Cargo.toml b/core/bin/external_node/Cargo.toml index a0f12b24244..d841ee5b42e 100644 --- a/core/bin/external_node/Cargo.toml +++ b/core/bin/external_node/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "zksync_external_node" description = "Non-validator ZKsync node" -version = "24.26.0" # x-release-please-version +version = "24.27.0" # x-release-please-version edition.workspace = true authors.workspace = true homepage.workspace = true diff --git a/core/lib/contracts/src/lib.rs b/core/lib/contracts/src/lib.rs index a60d9fbf181..a72b5c95d1b 100644 --- a/core/lib/contracts/src/lib.rs +++ b/core/lib/contracts/src/lib.rs @@ -49,12 +49,13 @@ const DIAMOND_INIT_CONTRACT_FILE: (&str, &str) = ( const GOVERNANCE_CONTRACT_FILE: (&str, &str) = ("governance", "IGovernance.sol/IGovernance.json"); const CHAIN_ADMIN_CONTRACT_FILE: (&str, &str) = ("governance", "IChainAdmin.sol/IChainAdmin.json"); const GETTERS_FACET_CONTRACT_FILE: (&str, &str) = ( - "state-transition/chain-deps/facets", - "Getters.sol/GettersFacet.json", + "state-transition/chain-interfaces", + "IGetters.sol/IGetters.json", ); const MULTICALL3_CONTRACT_FILE: (&str, &str) = ("dev-contracts", "Multicall3.sol/Multicall3.json"); const VERIFIER_CONTRACT_FILE: (&str, &str) = ("state-transition", "Verifier.sol/Verifier.json"); + const _IERC20_CONTRACT_FILE: &str = "contracts/l1-contracts/artifacts/contracts/common/interfaces/IERC20.sol/IERC20.json"; const _FAIL_ON_RECEIVE_CONTRACT_FILE: &str = diff --git a/core/lib/dal/.sqlx/query-26c80e9bafcf7989e7d40c6e424240b34ce29aad3ac6571116e084d45574c448.json b/core/lib/dal/.sqlx/query-26c80e9bafcf7989e7d40c6e424240b34ce29aad3ac6571116e084d45574c448.json new file mode 100644 index 00000000000..ee5de53d6e6 --- /dev/null +++ b/core/lib/dal/.sqlx/query-26c80e9bafcf7989e7d40c6e424240b34ce29aad3ac6571116e084d45574c448.json @@ -0,0 +1,33 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n next_block_to_process\n FROM\n processed_events\n WHERE\n TYPE = $1\n AND chain_id = $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "next_block_to_process", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + { + "Custom": { + "name": "event_type", + "kind": { + "Enum": [ + "ProtocolUpgrades", + "PriorityTransactions" + ] + } + } + }, + "Int8" + ] + }, + "nullable": [ + false + ] + }, + "hash": "26c80e9bafcf7989e7d40c6e424240b34ce29aad3ac6571116e084d45574c448" +} diff --git a/core/lib/dal/.sqlx/query-afdeecb78e3af802c2b8ffb0f5ab9863082ccd1ce45b2d20e1119f1e78171d66.json b/core/lib/dal/.sqlx/query-afdeecb78e3af802c2b8ffb0f5ab9863082ccd1ce45b2d20e1119f1e78171d66.json new file mode 100644 index 00000000000..bb0d73ee6c8 --- /dev/null +++ b/core/lib/dal/.sqlx/query-afdeecb78e3af802c2b8ffb0f5ab9863082ccd1ce45b2d20e1119f1e78171d66.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO\n processed_events (\n TYPE,\n chain_id,\n next_block_to_process\n )\n VALUES\n ($1, $2, $3)\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + { + "Custom": { + "name": "event_type", + "kind": { + "Enum": [ + "ProtocolUpgrades", + "PriorityTransactions" + ] + } + } + }, + "Int8", + "Int8" + ] + }, + "nullable": [] + }, + "hash": "afdeecb78e3af802c2b8ffb0f5ab9863082ccd1ce45b2d20e1119f1e78171d66" +} diff --git a/core/lib/dal/.sqlx/query-c61682ed92c1a43855a991598d593639a63047f46dc8e605ff37b78f43f5cef5.json b/core/lib/dal/.sqlx/query-c61682ed92c1a43855a991598d593639a63047f46dc8e605ff37b78f43f5cef5.json new file mode 100644 index 00000000000..b797ccb4604 --- /dev/null +++ b/core/lib/dal/.sqlx/query-c61682ed92c1a43855a991598d593639a63047f46dc8e605ff37b78f43f5cef5.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE processed_events\n SET\n next_block_to_process = $3\n WHERE\n TYPE = $1\n AND chain_id = $2\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + { + "Custom": { + "name": "event_type", + "kind": { + "Enum": [ + "ProtocolUpgrades", + "PriorityTransactions" + ] + } + } + }, + "Int8", + "Int8" + ] + }, + "nullable": [] + }, + "hash": "c61682ed92c1a43855a991598d593639a63047f46dc8e605ff37b78f43f5cef5" +} diff --git a/core/lib/dal/migrations/20240912085550_add_eth_watcher_progress_table.down.sql b/core/lib/dal/migrations/20240912085550_add_eth_watcher_progress_table.down.sql new file mode 100644 index 00000000000..79331481f58 --- /dev/null +++ b/core/lib/dal/migrations/20240912085550_add_eth_watcher_progress_table.down.sql @@ -0,0 +1,4 @@ +DROP TABLE IF EXISTS processed_events; + +DROP TYPE IF EXISTS event_type; + diff --git a/core/lib/dal/migrations/20240912085550_add_eth_watcher_progress_table.up.sql b/core/lib/dal/migrations/20240912085550_add_eth_watcher_progress_table.up.sql new file mode 100644 index 00000000000..be8a2b89a02 --- /dev/null +++ b/core/lib/dal/migrations/20240912085550_add_eth_watcher_progress_table.up.sql @@ -0,0 +1,9 @@ +CREATE TYPE event_type AS ENUM ('ProtocolUpgrades', 'PriorityTransactions'); + +CREATE TABLE processed_events +( + type event_type NOT NULL, + chain_id BIGINT NOT NULL, + next_block_to_process BIGINT NOT NULL, + PRIMARY KEY (chain_id, type) +) diff --git a/core/lib/dal/src/eth_watcher_dal.rs b/core/lib/dal/src/eth_watcher_dal.rs new file mode 100644 index 00000000000..3220868decf --- /dev/null +++ b/core/lib/dal/src/eth_watcher_dal.rs @@ -0,0 +1,154 @@ +use zksync_db_connection::{connection::Connection, error::DalResult, instrument::InstrumentExt}; +use zksync_types::SLChainId; + +use crate::Core; + +pub struct EthWatcherDal<'a, 'c> { + pub(crate) storage: &'a mut Connection<'c, Core>, +} + +#[derive(Debug, Copy, Clone, sqlx::Type)] +#[sqlx(type_name = "event_type")] +pub enum EventType { + ProtocolUpgrades, + PriorityTransactions, +} + +impl EthWatcherDal<'_, '_> { + // Returns last set value of next_block_to_process for given event_type and chain_id. + // If the value was missing, initializes it with provided next_block_to_process value + pub async fn get_or_set_next_block_to_process( + &mut self, + event_type: EventType, + chain_id: SLChainId, + next_block_to_process: u64, + ) -> DalResult { + let result = sqlx::query!( + r#" + SELECT + next_block_to_process + FROM + processed_events + WHERE + TYPE = $1 + AND chain_id = $2 + "#, + event_type as EventType, + chain_id.0 as i64 + ) + .instrument("get_or_set_next_block_to_process") + .with_arg("event_type", &event_type) + .with_arg("chain_id", &chain_id) + .fetch_optional(self.storage) + .await?; + + if let Some(row) = result { + Ok(row.next_block_to_process as u64) + } else { + sqlx::query!( + r#" + INSERT INTO + processed_events ( + TYPE, + chain_id, + next_block_to_process + ) + VALUES + ($1, $2, $3) + "#, + event_type as EventType, + chain_id.0 as i64, + next_block_to_process as i64 + ) + .instrument("get_or_set_next_block_to_process - insert") + .with_arg("event_type", &event_type) + .with_arg("chain_id", &chain_id) + .execute(self.storage) + .await?; + + Ok(next_block_to_process) + } + } + + pub async fn update_next_block_to_process( + &mut self, + event_type: EventType, + chain_id: SLChainId, + next_block_to_process: u64, + ) -> DalResult<()> { + sqlx::query!( + r#" + UPDATE processed_events + SET + next_block_to_process = $3 + WHERE + TYPE = $1 + AND chain_id = $2 + "#, + event_type as EventType, + chain_id.0 as i64, + next_block_to_process as i64 + ) + .instrument("update_next_block_to_process") + .with_arg("event_type", &event_type) + .with_arg("chain_id", &chain_id) + .execute(self.storage) + .await?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ConnectionPool, Core, CoreDal}; + + #[tokio::test] + async fn test_get_or_set_next_block_to_process_with_different_event_types() { + let pool = ConnectionPool::::test_pool().await; + let mut conn = pool.connection().await.unwrap(); + let mut dal = conn.processed_events_dal(); + + // Test with ProtocolUpgrades + let next_block = dal + .get_or_set_next_block_to_process(EventType::ProtocolUpgrades, SLChainId(1), 100) + .await + .expect("Failed to get or set next block to process"); + assert_eq!(next_block, 100); + + // Test with PriorityTransactions + let next_block = dal + .get_or_set_next_block_to_process(EventType::PriorityTransactions, SLChainId(1), 200) + .await + .expect("Failed to get or set next block to process"); + assert_eq!(next_block, 200); + + // Test with PriorityTransactions + let next_block = dal + .get_or_set_next_block_to_process(EventType::PriorityTransactions, SLChainId(2), 300) + .await + .expect("Failed to get or set next block to process"); + assert_eq!(next_block, 300); + + // Verify that the initial block is not updated for ProtocolUpgrades + let next_block = dal + .get_or_set_next_block_to_process(EventType::ProtocolUpgrades, SLChainId(1), 150) + .await + .expect("Failed to get or set next block to process"); + assert_eq!(next_block, 100); + + // Verify that the initial block is not updated for PriorityTransactions + let next_block = dal + .get_or_set_next_block_to_process(EventType::PriorityTransactions, SLChainId(1), 250) + .await + .expect("Failed to get or set next block to process"); + assert_eq!(next_block, 200); + + // Verify that the initial block is not updated for PriorityTransactions + let next_block = dal + .get_or_set_next_block_to_process(EventType::PriorityTransactions, SLChainId(2), 350) + .await + .expect("Failed to get or set next block to process"); + assert_eq!(next_block, 300); + } +} diff --git a/core/lib/dal/src/lib.rs b/core/lib/dal/src/lib.rs index 0e1badb9af7..f0d2f0c1671 100644 --- a/core/lib/dal/src/lib.rs +++ b/core/lib/dal/src/lib.rs @@ -15,8 +15,9 @@ use crate::{ base_token_dal::BaseTokenDal, blocks_dal::BlocksDal, blocks_web3_dal::BlocksWeb3Dal, consensus_dal::ConsensusDal, contract_verification_dal::ContractVerificationDal, data_availability_dal::DataAvailabilityDal, eth_sender_dal::EthSenderDal, - events_dal::EventsDal, events_web3_dal::EventsWeb3Dal, factory_deps_dal::FactoryDepsDal, - proof_generation_dal::ProofGenerationDal, protocol_versions_dal::ProtocolVersionsDal, + eth_watcher_dal::EthWatcherDal, events_dal::EventsDal, events_web3_dal::EventsWeb3Dal, + factory_deps_dal::FactoryDepsDal, proof_generation_dal::ProofGenerationDal, + protocol_versions_dal::ProtocolVersionsDal, protocol_versions_web3_dal::ProtocolVersionsWeb3Dal, pruning_dal::PruningDal, snapshot_recovery_dal::SnapshotRecoveryDal, snapshots_creator_dal::SnapshotsCreatorDal, snapshots_dal::SnapshotsDal, storage_logs_dal::StorageLogsDal, @@ -35,6 +36,7 @@ pub mod consensus_dal; pub mod contract_verification_dal; mod data_availability_dal; pub mod eth_sender_dal; +pub mod eth_watcher_dal; pub mod events_dal; pub mod events_web3_dal; pub mod factory_deps_dal; @@ -132,6 +134,8 @@ where fn vm_runner_dal(&mut self) -> VmRunnerDal<'_, 'a>; fn base_token_dal(&mut self) -> BaseTokenDal<'_, 'a>; + + fn processed_events_dal(&mut self) -> EthWatcherDal<'_, 'a>; } #[derive(Clone, Debug)] @@ -258,4 +262,8 @@ impl<'a> CoreDal<'a> for Connection<'a, Core> { fn base_token_dal(&mut self) -> BaseTokenDal<'_, 'a> { BaseTokenDal { storage: self } } + + fn processed_events_dal(&mut self) -> EthWatcherDal<'_, 'a> { + EthWatcherDal { storage: self } + } } diff --git a/core/node/eth_sender/src/aggregator.rs b/core/node/eth_sender/src/aggregator.rs index 1e0bd315b9d..4045e9ca3d8 100644 --- a/core/node/eth_sender/src/aggregator.rs +++ b/core/node/eth_sender/src/aggregator.rs @@ -526,22 +526,5 @@ pub async fn load_wrapped_fri_proofs_for_range( } } - // We also check file with deprecated name if patch 0 is allowed. - // TODO: remove in the next release. - let is_patch_0_present = allowed_versions.iter().any(|v| v.patch.0 == 0); - if is_patch_0_present { - match blob_store - .get_by_encoded_key(format!("l1_batch_proof_{l1_batch_number}.bin")) - .await - { - Ok(proof) => return Some(proof), - Err(ObjectStoreError::KeyNotFound(_)) => (), // do nothing, proof is not ready yet - Err(err) => panic!( - "Failed to load proof for batch {}: {}", - l1_batch_number.0, err - ), - } - } - None } diff --git a/core/node/eth_watch/Cargo.toml b/core/node/eth_watch/Cargo.toml index a3d6325f4a2..985649c35da 100644 --- a/core/node/eth_watch/Cargo.toml +++ b/core/node/eth_watch/Cargo.toml @@ -28,3 +28,4 @@ async-recursion.workspace = true [dev-dependencies] zksync_concurrency.workspace = true +test-log.workspace = true diff --git a/core/node/eth_watch/src/client.rs b/core/node/eth_watch/src/client.rs index 237c8e5bc2e..2dbaf869390 100644 --- a/core/node/eth_watch/src/client.rs +++ b/core/node/eth_watch/src/client.rs @@ -1,7 +1,9 @@ use std::fmt; use anyhow::Context; -use zksync_contracts::{state_transition_manager_contract, verifier_contract}; +use zksync_contracts::{ + getters_facet_contract, state_transition_manager_contract, verifier_contract, +}; use zksync_eth_client::{ clients::{DynClient, L1}, CallFunctionArgs, ClientError, ContractCallError, EnrichedClientError, EnrichedClientResult, @@ -10,7 +12,7 @@ use zksync_eth_client::{ use zksync_types::{ ethabi::Contract, web3::{BlockId, BlockNumber, FilterBuilder, Log}, - Address, H256, + Address, SLChainId, H256, U256, }; /// L1 client functionality used by [`EthWatch`](crate::EthWatch) and constituent event processors. @@ -21,10 +23,14 @@ pub trait EthClient: 'static + fmt::Debug + Send + Sync { &self, from: BlockNumber, to: BlockNumber, + topic1: H256, + topic2: Option, retries_left: usize, ) -> EnrichedClientResult>; /// Returns finalized L1 block number. async fn finalized_block_number(&self) -> EnrichedClientResult; + + async fn get_total_priority_txs(&self) -> Result; /// Returns scheduler verification key hash by verifier address. async fn scheduler_vk_hash(&self, verifier_address: Address) -> Result; @@ -33,8 +39,8 @@ pub trait EthClient: 'static + fmt::Debug + Send + Sync { &self, packed_version: H256, ) -> EnrichedClientResult>>; - /// Sets list of topics to return events for. - fn set_topics(&mut self, topics: Vec); + + async fn chain_id(&self) -> EnrichedClientResult; } pub const RETRY_LIMIT: usize = 5; @@ -43,10 +49,9 @@ const TOO_MANY_RESULTS_ALCHEMY: &str = "response size exceeded"; const TOO_MANY_RESULTS_RETH: &str = "query exceeds max block range"; /// Implementation of [`EthClient`] based on HTTP JSON-RPC (encapsulated via [`EthInterface`]). -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct EthHttpQueryClient { client: Box>, - topics: Vec, diamond_proxy_addr: Address, governance_address: Address, new_upgrade_cut_data_signature: H256, @@ -54,6 +59,7 @@ pub struct EthHttpQueryClient { state_transition_manager_address: Option
, chain_admin_address: Option
, verifier_contract_abi: Contract, + getters_facet_contract_abi: Contract, confirmations_for_eth_event: Option, } @@ -73,7 +79,6 @@ impl EthHttpQueryClient { ); Self { client: client.for_component("watch"), - topics: Vec::new(), diamond_proxy_addr, state_transition_manager_address, chain_admin_address, @@ -84,6 +89,7 @@ impl EthHttpQueryClient { .unwrap() .signature(), verifier_contract_abi: verifier_contract(), + getters_facet_contract_abi: getters_facet_contract(), confirmations_for_eth_event, } } @@ -249,13 +255,15 @@ impl EthClient for EthHttpQueryClient { &self, from: BlockNumber, to: BlockNumber, + topic1: H256, + topic2: Option, retries_left: usize, ) -> EnrichedClientResult> { self.get_events_inner( from, to, - Some(self.topics.clone()), - None, + Some(vec![topic1]), + topic2.map(|topic2| vec![topic2]), Some(self.get_default_address_list()), retries_left, ) @@ -283,7 +291,15 @@ impl EthClient for EthHttpQueryClient { } } - fn set_topics(&mut self, topics: Vec) { - self.topics = topics; + async fn get_total_priority_txs(&self) -> Result { + CallFunctionArgs::new("getTotalPriorityTxs", ()) + .for_contract(self.diamond_proxy_addr, &self.getters_facet_contract_abi) + .call(&self.client) + .await + .map(|x: U256| x.try_into().unwrap()) + } + + async fn chain_id(&self) -> EnrichedClientResult { + Ok(self.client.fetch_chain_id().await?) } } diff --git a/core/node/eth_watch/src/event_processors/decentralized_upgrades.rs b/core/node/eth_watch/src/event_processors/decentralized_upgrades.rs index dff10662e98..aa43e7239f8 100644 --- a/core/node/eth_watch/src/event_processors/decentralized_upgrades.rs +++ b/core/node/eth_watch/src/event_processors/decentralized_upgrades.rs @@ -1,5 +1,5 @@ use anyhow::Context as _; -use zksync_dal::{Connection, Core, CoreDal, DalError}; +use zksync_dal::{eth_watcher_dal::EventType, Connection, Core, CoreDal, DalError}; use zksync_types::{ ethabi::Contract, protocol_version::ProtocolSemanticVersion, web3::Log, ProtocolUpgrade, H256, U256, @@ -7,7 +7,7 @@ use zksync_types::{ use crate::{ client::EthClient, - event_processors::{EventProcessor, EventProcessorError}, + event_processors::{EventProcessor, EventProcessorError, EventsSource}, metrics::{PollStage, METRICS}, }; @@ -40,18 +40,18 @@ impl EventProcessor for DecentralizedUpgradesEventProcessor { async fn process_events( &mut self, storage: &mut Connection<'_, Core>, - client: &dyn EthClient, + sl_client: &dyn EthClient, events: Vec, - ) -> Result<(), EventProcessorError> { + ) -> Result { let mut upgrades = Vec::new(); - for event in events { + for event in &events { let version = event.topics.get(1).copied().context("missing topic 1")?; let timestamp: u64 = U256::from_big_endian(&event.data.0) .try_into() .ok() .context("upgrade timestamp is too big")?; - let diamond_cut = client + let diamond_cut = sl_client .diamond_cut_by_version(version) .await? .context("missing upgrade data on STM")?; @@ -62,7 +62,7 @@ impl EventProcessor for DecentralizedUpgradesEventProcessor { }; // Scheduler VK is not present in proposal event. It is hard coded in verifier contract. let scheduler_vk_hash = if let Some(address) = upgrade.verifier_address { - Some(client.scheduler_vk_hash(address).await?) + Some(sl_client.scheduler_vk_hash(address).await?) } else { None }; @@ -75,7 +75,7 @@ impl EventProcessor for DecentralizedUpgradesEventProcessor { .collect(); let Some((last_upgrade, _)) = new_upgrades.last() else { - return Ok(()); + return Ok(events.len()); }; let versions: Vec<_> = new_upgrades .iter() @@ -125,10 +125,18 @@ impl EventProcessor for DecentralizedUpgradesEventProcessor { stage_latency.observe(); self.last_seen_protocol_version = last_version; - Ok(()) + Ok(events.len()) } fn relevant_topic(&self) -> H256 { self.update_upgrade_timestamp_signature } + + fn event_source(&self) -> EventsSource { + EventsSource::SL + } + + fn event_type(&self) -> EventType { + EventType::ProtocolUpgrades + } } diff --git a/core/node/eth_watch/src/event_processors/governance_upgrades.rs b/core/node/eth_watch/src/event_processors/governance_upgrades.rs deleted file mode 100644 index 72f5c411892..00000000000 --- a/core/node/eth_watch/src/event_processors/governance_upgrades.rs +++ /dev/null @@ -1,142 +0,0 @@ -use anyhow::Context as _; -use zksync_dal::{Connection, Core, CoreDal, DalError}; -use zksync_types::{ - ethabi::Contract, protocol_upgrade::GovernanceOperation, - protocol_version::ProtocolSemanticVersion, web3::Log, Address, ProtocolUpgrade, H256, -}; - -use crate::{ - client::EthClient, - event_processors::{EventProcessor, EventProcessorError}, - metrics::{PollStage, METRICS}, -}; - -/// Listens to operation events coming from the governance contract and saves new protocol upgrade proposals to the database. -#[derive(Debug)] -pub struct GovernanceUpgradesEventProcessor { - // ZKsync diamond proxy - target_contract_address: Address, - /// Last protocol version seen. Used to skip events for already known upgrade proposals. - last_seen_protocol_version: ProtocolSemanticVersion, - upgrade_proposal_signature: H256, -} - -impl GovernanceUpgradesEventProcessor { - pub fn new( - target_contract_address: Address, - last_seen_protocol_version: ProtocolSemanticVersion, - governance_contract: &Contract, - ) -> Self { - Self { - target_contract_address, - last_seen_protocol_version, - upgrade_proposal_signature: governance_contract - .event("TransparentOperationScheduled") - .context("TransparentOperationScheduled event is missing in ABI") - .unwrap() - .signature(), - } - } -} - -#[async_trait::async_trait] -impl EventProcessor for GovernanceUpgradesEventProcessor { - async fn process_events( - &mut self, - storage: &mut Connection<'_, Core>, - client: &dyn EthClient, - events: Vec, - ) -> Result<(), EventProcessorError> { - let mut upgrades = Vec::new(); - for event in events { - assert_eq!(event.topics[0], self.upgrade_proposal_signature); // guaranteed by the watcher - - let governance_operation = GovernanceOperation::try_from(event) - .map_err(|err| EventProcessorError::log_parse(err, "governance operation"))?; - // Some calls can target other contracts than Diamond proxy, skip them. - for call in governance_operation - .calls - .into_iter() - .filter(|call| call.target == self.target_contract_address) - { - // We might not get an upgrade operation here, but something else instead - // (e.g. `acceptGovernor` call), so if parsing doesn't work, just skip the call. - let Ok(upgrade) = ProtocolUpgrade::try_from(call) else { - tracing::warn!( - "Failed to parse governance operation call as protocol upgrade, skipping" - ); - continue; - }; - // Scheduler VK is not present in proposal event. It is hard coded 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)); - } - } - - let new_upgrades: Vec<_> = upgrades - .into_iter() - .skip_while(|(v, _)| v.version <= self.last_seen_protocol_version) - .collect(); - - let Some((last_upgrade, _)) = new_upgrades.last() else { - return Ok(()); - }; - let versions: Vec<_> = new_upgrades - .iter() - .map(|(u, _)| u.version.to_string()) - .collect(); - tracing::debug!("Received upgrades with versions: {versions:?}"); - - let last_version = last_upgrade.version; - let stage_latency = METRICS.poll_eth_node[&PollStage::PersistUpgrades].start(); - for (upgrade, scheduler_vk_hash) in new_upgrades { - let latest_semantic_version = storage - .protocol_versions_dal() - .latest_semantic_version() - .await - .map_err(DalError::generalize)? - .context("expected some version to be present in DB")?; - - if upgrade.version > latest_semantic_version { - let latest_version = storage - .protocol_versions_dal() - .get_protocol_version_with_latest_patch(latest_semantic_version.minor) - .await - .map_err(DalError::generalize)? - .with_context(|| { - format!( - "expected minor version {} to be present in DB", - latest_semantic_version.minor as u16 - ) - })?; - - let new_version = latest_version.apply_upgrade(upgrade, scheduler_vk_hash); - if new_version.version.minor == latest_semantic_version.minor { - // Only verification parameters may change if only patch is bumped. - assert_eq!( - new_version.base_system_contracts_hashes, - latest_version.base_system_contracts_hashes - ); - assert!(new_version.tx.is_none()); - } - storage - .protocol_versions_dal() - .save_protocol_version_with_tx(&new_version) - .await - .map_err(DalError::generalize)?; - } - } - stage_latency.observe(); - - self.last_seen_protocol_version = last_version; - Ok(()) - } - - fn relevant_topic(&self) -> H256 { - self.upgrade_proposal_signature - } -} diff --git a/core/node/eth_watch/src/event_processors/mod.rs b/core/node/eth_watch/src/event_processors/mod.rs index 43ae259305a..f145181b0cf 100644 --- a/core/node/eth_watch/src/event_processors/mod.rs +++ b/core/node/eth_watch/src/event_processors/mod.rs @@ -1,18 +1,17 @@ use std::fmt; -use zksync_dal::{Connection, Core}; +use zksync_dal::{eth_watcher_dal::EventType, Connection, Core}; use zksync_eth_client::{ContractCallError, EnrichedClientError}; use zksync_types::{web3::Log, H256}; pub(crate) use self::{ decentralized_upgrades::DecentralizedUpgradesEventProcessor, - governance_upgrades::GovernanceUpgradesEventProcessor, priority_ops::PriorityOpsEventProcessor, + priority_ops::PriorityOpsEventProcessor, }; use crate::client::EthClient; mod decentralized_upgrades; -mod governance_upgrades; -mod priority_ops; +pub mod priority_ops; /// Errors issued by an [`EventProcessor`]. #[derive(Debug, thiserror::Error)] @@ -32,6 +31,12 @@ pub(super) enum EventProcessorError { Internal(#[from] anyhow::Error), } +#[derive(Debug)] +pub(super) enum EventsSource { + L1, + SL, +} + impl EventProcessorError { pub fn log_parse(source: impl Into, log_kind: &'static str) -> Self { Self::LogParse { @@ -46,13 +51,18 @@ impl EventProcessorError { #[async_trait::async_trait] pub(super) trait EventProcessor: 'static + fmt::Debug + Send + Sync { /// Processes given events. All events are guaranteed to match [`Self::relevant_topic()`]. + /// Returns number of processed events, this result is used to update last processed block. async fn process_events( &mut self, storage: &mut Connection<'_, Core>, - client: &dyn EthClient, + sl_client: &dyn EthClient, events: Vec, - ) -> Result<(), EventProcessorError>; + ) -> Result; /// Relevant topic which defines what events to be processed fn relevant_topic(&self) -> H256; + + fn event_source(&self) -> EventsSource; + + fn event_type(&self) -> EventType; } diff --git a/core/node/eth_watch/src/event_processors/priority_ops.rs b/core/node/eth_watch/src/event_processors/priority_ops.rs index 2783637fdb9..051c076850e 100644 --- a/core/node/eth_watch/src/event_processors/priority_ops.rs +++ b/core/node/eth_watch/src/event_processors/priority_ops.rs @@ -2,13 +2,13 @@ use std::convert::TryFrom; use anyhow::Context; use zksync_contracts::hyperchain_contract; -use zksync_dal::{Connection, Core, CoreDal, DalError}; +use zksync_dal::{eth_watcher_dal::EventType, Connection, Core, CoreDal, DalError}; use zksync_shared_metrics::{TxStage, APP_METRICS}; use zksync_types::{l1::L1Tx, web3::Log, PriorityOpId, H256}; use crate::{ client::EthClient, - event_processors::{EventProcessor, EventProcessorError}, + event_processors::{EventProcessor, EventProcessorError, EventsSource}, metrics::{PollStage, METRICS}, }; @@ -36,10 +36,11 @@ impl EventProcessor for PriorityOpsEventProcessor { async fn process_events( &mut self, storage: &mut Connection<'_, Core>, - _client: &dyn EthClient, + sl_client: &dyn EthClient, events: Vec, - ) -> Result<(), EventProcessorError> { + ) -> Result { let mut priority_ops = Vec::new(); + let events_count = events.len(); for event in events { assert_eq!(event.topics[0], self.new_priority_request_signature); // guaranteed by the watcher let tx = L1Tx::try_from(event) @@ -48,7 +49,7 @@ impl EventProcessor for PriorityOpsEventProcessor { } if priority_ops.is_empty() { - return Ok(()); + return Ok(events_count); } let first = &priority_ops[0]; @@ -70,33 +71,49 @@ impl EventProcessor for PriorityOpsEventProcessor { .into_iter() .skip_while(|tx| tx.serial_id() < self.next_expected_priority_id) .collect(); - let (Some(first_new), Some(last_new)) = (new_ops.first(), new_ops.last()) else { - return Ok(()); + let skipped_ops = events_count - new_ops.len(); + let Some(first_new) = new_ops.first() else { + return Ok(events_count); }; assert_eq!( first_new.serial_id(), self.next_expected_priority_id, "priority transaction serial id mismatch" ); - let next_expected_priority_id = last_new.serial_id().next(); let stage_latency = METRICS.poll_eth_node[&PollStage::PersistL1Txs].start(); APP_METRICS.processed_txs[&TxStage::added_to_mempool()].inc(); APP_METRICS.processed_l1_txs[&TxStage::added_to_mempool()].inc(); - for new_op in new_ops { - let eth_block = new_op.eth_block(); + let processed_priority_transactions = sl_client.get_total_priority_txs().await?; + let ops_to_insert: Vec<&L1Tx> = new_ops + .iter() + .take_while(|op| processed_priority_transactions > op.serial_id().0) + .collect(); + + for new_op in &ops_to_insert { storage .transactions_dal() - .insert_transaction_l1(&new_op, eth_block) + .insert_transaction_l1(new_op, new_op.eth_block()) .await .map_err(DalError::generalize)?; } stage_latency.observe(); - self.next_expected_priority_id = next_expected_priority_id; - Ok(()) + if let Some(last_op) = ops_to_insert.last() { + self.next_expected_priority_id = last_op.serial_id().next(); + } + + Ok(skipped_ops + ops_to_insert.len()) } fn relevant_topic(&self) -> H256 { self.new_priority_request_signature } + + fn event_source(&self) -> EventsSource { + EventsSource::L1 + } + + fn event_type(&self) -> EventType { + EventType::PriorityTransactions + } } diff --git a/core/node/eth_watch/src/lib.rs b/core/node/eth_watch/src/lib.rs index e964d63bb19..a832733b355 100644 --- a/core/node/eth_watch/src/lib.rs +++ b/core/node/eth_watch/src/lib.rs @@ -6,23 +6,20 @@ use std::time::Duration; use anyhow::Context as _; use tokio::sync::watch; -use zksync_dal::{Connection, ConnectionPool, Core, CoreDal}; +use zksync_dal::{Connection, ConnectionPool, Core, CoreDal, DalError}; use zksync_system_constants::PRIORITY_EXPIRATION; use zksync_types::{ ethabi::Contract, protocol_version::ProtocolSemanticVersion, - web3::BlockNumber as Web3BlockNumber, Address, PriorityOpId, + web3::BlockNumber as Web3BlockNumber, PriorityOpId, }; pub use self::client::EthHttpQueryClient; use self::{ client::{EthClient, RETRY_LIMIT}, - event_processors::{ - EventProcessor, EventProcessorError, GovernanceUpgradesEventProcessor, - PriorityOpsEventProcessor, - }, - metrics::{PollStage, METRICS}, + event_processors::{EventProcessor, EventProcessorError, PriorityOpsEventProcessor}, + metrics::METRICS, }; -use crate::event_processors::DecentralizedUpgradesEventProcessor; +use crate::event_processors::{DecentralizedUpgradesEventProcessor, EventsSource}; mod client; mod event_processors; @@ -34,70 +31,53 @@ mod tests; struct EthWatchState { last_seen_protocol_version: ProtocolSemanticVersion, next_expected_priority_id: PriorityOpId, - last_processed_ethereum_block: u64, } /// Ethereum watcher component. #[derive(Debug)] pub struct EthWatch { - client: Box, + l1_client: Box, + sl_client: Box, poll_interval: Duration, event_processors: Vec>, - last_processed_ethereum_block: u64, pool: ConnectionPool, } impl EthWatch { pub async fn new( - diamond_proxy_addr: Address, - governance_contract: &Contract, chain_admin_contract: &Contract, - mut client: Box, + l1_client: Box, + sl_client: Box, pool: ConnectionPool, poll_interval: Duration, ) -> anyhow::Result { let mut storage = pool.connection_tagged("eth_watch").await?; - let state = Self::initialize_state(&*client, &mut storage).await?; + let state = Self::initialize_state(&mut storage).await?; tracing::info!("initialized state: {state:?}"); drop(storage); let priority_ops_processor = PriorityOpsEventProcessor::new(state.next_expected_priority_id)?; - let governance_upgrades_processor = GovernanceUpgradesEventProcessor::new( - diamond_proxy_addr, - state.last_seen_protocol_version, - governance_contract, - ); let decentralized_upgrades_processor = DecentralizedUpgradesEventProcessor::new( state.last_seen_protocol_version, chain_admin_contract, ); let event_processors: Vec> = vec![ Box::new(priority_ops_processor), - Box::new(governance_upgrades_processor), Box::new(decentralized_upgrades_processor), ]; - let topics = event_processors - .iter() - .map(|processor| processor.relevant_topic()) - .collect(); - client.set_topics(topics); - Ok(Self { - client, + l1_client, + sl_client, poll_interval, event_processors, - last_processed_ethereum_block: state.last_processed_ethereum_block, pool, }) } #[tracing::instrument(name = "EthWatch::initialize_state", skip_all)] - async fn initialize_state( - client: &dyn EthClient, - storage: &mut Connection<'_, Core>, - ) -> anyhow::Result { + async fn initialize_state(storage: &mut Connection<'_, Core>) -> anyhow::Result { let next_expected_priority_id: PriorityOpId = storage .transactions_dal() .last_priority_id() @@ -110,26 +90,9 @@ impl EthWatch { .await? .context("expected at least one (genesis) version to be present in DB")?; - let last_processed_ethereum_block = match storage - .transactions_dal() - .get_last_processed_l1_block() - .await? - { - // There are some priority ops processed - start from the last processed eth block - // but subtract 1 in case the server stopped mid-block. - Some(block) => block.0.saturating_sub(1).into(), - // There are no priority ops processed - to be safe, scan the last 50k blocks. - None => client - .finalized_block_number() - .await - .context("cannot get current Ethereum block")? - .saturating_sub(PRIORITY_EXPIRATION), - }; - Ok(EthWatchState { next_expected_priority_id, last_seen_protocol_version, - last_processed_ethereum_block, }) } @@ -155,10 +118,6 @@ impl EthWatch { // This is an error because otherwise we could potentially miss a priority operation // thus entering priority mode, which is not desired. tracing::error!("Failed to process new blocks: {err}"); - self.last_processed_ethereum_block = - Self::initialize_state(&*self.client, &mut storage) - .await? - .last_processed_ethereum_block; } } } @@ -172,34 +131,64 @@ impl EthWatch { &mut self, storage: &mut Connection<'_, Core>, ) -> Result<(), EventProcessorError> { - let stage_latency = METRICS.poll_eth_node[&PollStage::Request].start(); - let to_block = self.client.finalized_block_number().await?; - if to_block <= self.last_processed_ethereum_block { - return Ok(()); - } - - let events = self - .client - .get_events( - Web3BlockNumber::Number(self.last_processed_ethereum_block.into()), - Web3BlockNumber::Number(to_block.into()), - RETRY_LIMIT, - ) - .await?; - stage_latency.observe(); - for processor in &mut self.event_processors { - let relevant_topic = processor.relevant_topic(); - let processor_events = events - .iter() - .filter(|event| event.topics.first() == Some(&relevant_topic)) - .cloned() - .collect(); - processor - .process_events(storage, &*self.client, processor_events) + let client = match processor.event_source() { + EventsSource::L1 => self.l1_client.as_ref(), + EventsSource::SL => self.sl_client.as_ref(), + }; + let chain_id = client.chain_id().await?; + let finalized_block = client.finalized_block_number().await?; + + let from_block = storage + .processed_events_dal() + .get_or_set_next_block_to_process( + processor.event_type(), + chain_id, + finalized_block.saturating_sub(PRIORITY_EXPIRATION), + ) + .await + .map_err(DalError::generalize)?; + + // There are no new blocks so there is nothing to be done + if from_block > finalized_block { + continue; + } + let processor_events = client + .get_events( + Web3BlockNumber::Number(from_block.into()), + Web3BlockNumber::Number(finalized_block.into()), + processor.relevant_topic(), + None, + RETRY_LIMIT, + ) + .await?; + let processed_events_count = processor + .process_events(storage, &*self.sl_client, processor_events.clone()) .await?; + + let next_block_to_process = if processed_events_count == processor_events.len() { + finalized_block + 1 + } else if processed_events_count == 0 { + //nothing was processed + from_block + } else { + processor_events[processed_events_count - 1] + .block_number + .expect("Event block number is missing") + .try_into() + .unwrap() + }; + + storage + .processed_events_dal() + .update_next_block_to_process( + processor.event_type(), + chain_id, + next_block_to_process, + ) + .await + .map_err(DalError::generalize)?; } - self.last_processed_ethereum_block = to_block; Ok(()) } } diff --git a/core/node/eth_watch/src/metrics.rs b/core/node/eth_watch/src/metrics.rs index a3684cc6e72..a942d4a6e61 100644 --- a/core/node/eth_watch/src/metrics.rs +++ b/core/node/eth_watch/src/metrics.rs @@ -7,7 +7,6 @@ use vise::{Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Histogram #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelValue, EncodeLabelSet)] #[metrics(label = "stage", rename_all = "snake_case")] pub(super) enum PollStage { - Request, PersistL1Txs, PersistUpgrades, } diff --git a/core/node/eth_watch/src/tests.rs b/core/node/eth_watch/src/tests.rs index e6e343f50bc..8c37b5c9920 100644 --- a/core/node/eth_watch/src/tests.rs +++ b/core/node/eth_watch/src/tests.rs @@ -1,37 +1,48 @@ use std::{collections::HashMap, convert::TryInto, sync::Arc}; use tokio::sync::RwLock; -use zksync_contracts::{chain_admin_contract, governance_contract, hyperchain_contract}; +use zksync_contracts::{ + chain_admin_contract, hyperchain_contract, state_transition_manager_contract, +}; use zksync_dal::{Connection, ConnectionPool, Core, CoreDal}; use zksync_eth_client::{ContractCallError, EnrichedClientResult}; use zksync_types::{ - abi, ethabi, - ethabi::{Hash, Token}, + abi, + abi::ProposedUpgrade, + ethabi, + ethabi::Token, l1::{L1Tx, OpProcessingType, PriorityQueueType}, protocol_upgrade::{ProtocolUpgradeTx, ProtocolUpgradeTxCommonData}, protocol_version::ProtocolSemanticVersion, - web3::{BlockNumber, Log}, + web3::{contract::Tokenizable, BlockNumber, Log}, Address, Execute, L1TxCommonData, PriorityOpId, ProtocolUpgrade, ProtocolVersion, - ProtocolVersionId, Transaction, H256, U256, + ProtocolVersionId, SLChainId, Transaction, H160, H256, U256, U64, }; -use crate::{client::EthClient, EthWatch}; +use crate::{ + client::{EthClient, RETRY_LIMIT}, + EthWatch, +}; #[derive(Debug)] struct FakeEthClientData { transactions: HashMap>, diamond_upgrades: HashMap>, - governance_upgrades: HashMap>, + upgrade_timestamp: HashMap>, last_finalized_block_number: u64, + chain_id: SLChainId, + processed_priority_transactions_count: u64, } impl FakeEthClientData { - fn new() -> Self { + fn new(chain_id: SLChainId) -> Self { Self { transactions: Default::default(), diamond_upgrades: Default::default(), - governance_upgrades: Default::default(), + upgrade_timestamp: Default::default(), last_finalized_block_number: 0, + chain_id, + processed_priority_transactions_count: 0, } } @@ -42,21 +53,30 @@ impl FakeEthClientData { .entry(eth_block.0 as u64) .or_default() .push(tx_into_log(transaction.clone())); + self.processed_priority_transactions_count += 1; } } - fn add_governance_upgrades(&mut self, upgrades: &[(ProtocolUpgrade, u64)]) { + fn add_upgrade_timestamp(&mut self, upgrades: &[(ProtocolUpgrade, u64)]) { for (upgrade, eth_block) in upgrades { - self.governance_upgrades + self.upgrade_timestamp + .entry(*eth_block) + .or_default() + .push(upgrade_timestamp_log(*eth_block)); + self.diamond_upgrades .entry(*eth_block) .or_default() - .push(upgrade_into_governor_log(upgrade.clone(), *eth_block)); + .push(diamond_upgrade_log(upgrade.clone(), *eth_block)); } } fn set_last_finalized_block_number(&mut self, number: u64) { self.last_finalized_block_number = number; } + + fn set_processed_priority_transactions_count(&mut self, number: u64) { + self.processed_priority_transactions_count = number; + } } #[derive(Debug, Clone)] @@ -65,9 +85,9 @@ struct MockEthClient { } impl MockEthClient { - fn new() -> Self { + fn new(chain_id: SLChainId) -> Self { Self { - inner: Arc::new(RwLock::new(FakeEthClientData::new())), + inner: Arc::new(RwLock::new(FakeEthClientData::new(chain_id))), } } @@ -75,8 +95,8 @@ impl MockEthClient { self.inner.write().await.add_transactions(transactions); } - async fn add_governance_upgrades(&mut self, upgrades: &[(ProtocolUpgrade, u64)]) { - self.inner.write().await.add_governance_upgrades(upgrades); + async fn add_upgrade_timestamp(&mut self, upgrades: &[(ProtocolUpgrade, u64)]) { + self.inner.write().await.add_upgrade_timestamp(upgrades); } async fn set_last_finalized_block_number(&mut self, number: u64) { @@ -86,6 +106,13 @@ impl MockEthClient { .set_last_finalized_block_number(number); } + async fn set_processed_priority_transactions_count(&mut self, number: u64) { + self.inner + .write() + .await + .set_processed_priority_transactions_count(number) + } + async fn block_to_number(&self, block: BlockNumber) -> u64 { match block { BlockNumber::Earliest => 0, @@ -104,6 +131,8 @@ impl EthClient for MockEthClient { &self, from: BlockNumber, to: BlockNumber, + topic1: H256, + topic2: Option, _retries_left: usize, ) -> EnrichedClientResult> { let from = self.block_to_number(from).await; @@ -116,15 +145,19 @@ impl EthClient for MockEthClient { 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) { + if let Some(ops) = self.inner.read().await.upgrade_timestamp.get(&number) { logs.extend_from_slice(ops); } } - Ok(logs) + Ok(logs + .into_iter() + .filter(|log| { + log.topics.get(0) == Some(&topic1) + && (topic2.is_none() || log.topics.get(1) == topic2.as_ref()) + }) + .collect()) } - fn set_topics(&mut self, _topics: Vec) {} - async fn scheduler_vk_hash( &self, _verifier_address: Address, @@ -138,9 +171,51 @@ impl EthClient for MockEthClient { async fn diamond_cut_by_version( &self, - _packed_version: H256, + packed_version: H256, ) -> EnrichedClientResult>> { - unimplemented!() + let from_block = *self + .inner + .read() + .await + .diamond_upgrades + .keys() + .min() + .unwrap_or(&0); + let to_block = *self + .inner + .read() + .await + .diamond_upgrades + .keys() + .max() + .unwrap_or(&0); + + let logs = self + .get_events( + U64::from(from_block).into(), + U64::from(to_block).into(), + state_transition_manager_contract() + .event("NewUpgradeCutData") + .unwrap() + .signature(), + Some(packed_version), + RETRY_LIMIT, + ) + .await?; + + Ok(logs.into_iter().next().map(|log| log.data.0)) + } + + async fn get_total_priority_txs(&self) -> Result { + Ok(self + .inner + .read() + .await + .processed_priority_transactions_count) + } + + async fn chain_id(&self) -> EnrichedClientResult { + Ok(self.inner.read().await.chain_id) } } @@ -203,27 +278,47 @@ fn build_upgrade_tx(id: ProtocolVersionId, eth_block: u64) -> ProtocolUpgradeTx .unwrap() } -async fn create_test_watcher(connection_pool: ConnectionPool) -> (EthWatch, MockEthClient) { - let client = MockEthClient::new(); +async fn create_test_watcher( + connection_pool: ConnectionPool, + is_gateway: bool, +) -> (EthWatch, MockEthClient, MockEthClient) { + let l1_client = MockEthClient::new(SLChainId(42)); + let sl_client = if is_gateway { + MockEthClient::new(SLChainId(123)) + } else { + l1_client.clone() + }; let watcher = EthWatch::new( - Address::default(), - &governance_contract(), &chain_admin_contract(), - Box::new(client.clone()), + Box::new(l1_client.clone()), + Box::new(sl_client.clone()), connection_pool, std::time::Duration::from_nanos(1), ) .await .unwrap(); - (watcher, client) + (watcher, l1_client, sl_client) } -#[tokio::test] +async fn create_l1_test_watcher( + connection_pool: ConnectionPool, +) -> (EthWatch, MockEthClient) { + let (watcher, l1_client, _) = create_test_watcher(connection_pool, false).await; + (watcher, l1_client) +} + +async fn create_gateway_test_watcher( + connection_pool: ConnectionPool, +) -> (EthWatch, MockEthClient, MockEthClient) { + create_test_watcher(connection_pool, true).await +} + +#[test_log::test(tokio::test)] async fn test_normal_operation_l1_txs() { let connection_pool = ConnectionPool::::test_pool().await; setup_db(&connection_pool).await; - let (mut watcher, mut client) = create_test_watcher(connection_pool.clone()).await; + let (mut watcher, mut client) = create_l1_test_watcher(connection_pool.clone()).await; let mut storage = connection_pool.connection().await.unwrap(); client @@ -258,15 +353,15 @@ async fn test_normal_operation_l1_txs() { assert_eq!(db_tx.common_data.serial_id.0, 2); } -#[tokio::test] -async fn test_gap_in_governance_upgrades() { +#[test_log::test(tokio::test)] +async fn test_gap_in_upgrade_timestamp() { let connection_pool = ConnectionPool::::test_pool().await; setup_db(&connection_pool).await; - let (mut watcher, mut client) = create_test_watcher(connection_pool.clone()).await; + let (mut watcher, mut client) = create_l1_test_watcher(connection_pool.clone()).await; let mut storage = connection_pool.connection().await.unwrap(); client - .add_governance_upgrades(&[( + .add_upgrade_timestamp(&[( ProtocolUpgrade { version: ProtocolSemanticVersion { minor: ProtocolVersionId::next(), @@ -291,18 +386,17 @@ async fn test_gap_in_governance_upgrades() { assert_eq!(db_versions[1].minor, next_version); } -#[tokio::test] -async fn test_normal_operation_governance_upgrades() { +#[test_log::test(tokio::test)] +async fn test_normal_operation_upgrade_timestamp() { zksync_concurrency::testonly::abort_on_panic(); let connection_pool = ConnectionPool::::test_pool().await; setup_db(&connection_pool).await; - let mut client = MockEthClient::new(); + let mut client = MockEthClient::new(SLChainId(42)); let mut watcher = EthWatch::new( - Address::default(), - &governance_contract(), &chain_admin_contract(), Box::new(client.clone()), + Box::new(client.clone()), connection_pool.clone(), std::time::Duration::from_nanos(1), ) @@ -311,7 +405,7 @@ async fn test_normal_operation_governance_upgrades() { let mut storage = connection_pool.connection().await.unwrap(); client - .add_governance_upgrades(&[ + .add_upgrade_timestamp(&[ ( ProtocolUpgrade { tx: None, @@ -375,12 +469,12 @@ async fn test_normal_operation_governance_upgrades() { assert_eq!(tx.common_data.upgrade_id, ProtocolVersionId::next()); } -#[tokio::test] +#[test_log::test(tokio::test)] #[should_panic] async fn test_gap_in_single_batch() { let connection_pool = ConnectionPool::::test_pool().await; setup_db(&connection_pool).await; - let (mut watcher, mut client) = create_test_watcher(connection_pool.clone()).await; + let (mut watcher, mut client) = create_l1_test_watcher(connection_pool.clone()).await; let mut storage = connection_pool.connection().await.unwrap(); client @@ -396,12 +490,12 @@ async fn test_gap_in_single_batch() { watcher.loop_iteration(&mut storage).await.unwrap(); } -#[tokio::test] +#[test_log::test(tokio::test)] #[should_panic] async fn test_gap_between_batches() { let connection_pool = ConnectionPool::::test_pool().await; setup_db(&connection_pool).await; - let (mut watcher, mut client) = create_test_watcher(connection_pool.clone()).await; + let (mut watcher, mut client) = create_l1_test_watcher(connection_pool.clone()).await; let mut storage = connection_pool.connection().await.unwrap(); client @@ -424,12 +518,12 @@ async fn test_gap_between_batches() { watcher.loop_iteration(&mut storage).await.unwrap(); } -#[tokio::test] +#[test_log::test(tokio::test)] async fn test_overlapping_batches() { zksync_concurrency::testonly::abort_on_panic(); let connection_pool = ConnectionPool::::test_pool().await; setup_db(&connection_pool).await; - let (mut watcher, mut client) = create_test_watcher(connection_pool.clone()).await; + let (mut watcher, mut client) = create_l1_test_watcher(connection_pool.clone()).await; let mut storage = connection_pool.connection().await.unwrap(); client @@ -467,6 +561,52 @@ async fn test_overlapping_batches() { assert_eq!(tx.common_data.serial_id.0, 4); } +#[test_log::test(tokio::test)] +async fn test_transactions_get_gradually_processed_by_gateway() { + zksync_concurrency::testonly::abort_on_panic(); + let connection_pool = ConnectionPool::::test_pool().await; + setup_db(&connection_pool).await; + let (mut watcher, mut l1_client, mut gateway_client) = + create_gateway_test_watcher(connection_pool.clone()).await; + + let mut storage = connection_pool.connection().await.unwrap(); + l1_client + .add_transactions(&[ + build_l1_tx(0, 10), + build_l1_tx(1, 14), + build_l1_tx(2, 14), + build_l1_tx(3, 20), + build_l1_tx(4, 22), + ]) + .await; + l1_client.set_last_finalized_block_number(15).await; + gateway_client + .set_processed_priority_transactions_count(2) + .await; + watcher.loop_iteration(&mut storage).await.unwrap(); + + let db_txs = get_all_db_txs(&mut storage).await; + assert_eq!(db_txs.len(), 2); + + l1_client.set_last_finalized_block_number(25).await; + gateway_client + .set_processed_priority_transactions_count(4) + .await; + watcher.loop_iteration(&mut storage).await.unwrap(); + + let db_txs = get_all_db_txs(&mut storage).await; + assert_eq!(db_txs.len(), 4); + let mut db_txs: Vec = db_txs + .into_iter() + .map(|tx| tx.try_into().unwrap()) + .collect(); + db_txs.sort_by_key(|tx| tx.common_data.serial_id); + let tx = db_txs[2].clone(); + assert_eq!(tx.common_data.serial_id.0, 2); + let tx = db_txs[3].clone(); + assert_eq!(tx.common_data.serial_id.0, 3); +} + async fn get_all_db_txs(storage: &mut Connection<'_, Core>) -> Vec { storage.transactions_dal().reset_mempool().await.unwrap(); storage @@ -518,37 +658,69 @@ fn tx_into_log(tx: L1Tx) -> Log { } } -fn upgrade_into_governor_log(upgrade: ProtocolUpgrade, eth_block: u64) -> Log { - let diamond_cut = upgrade_into_diamond_cut(upgrade); +fn init_calldata(protocol_upgrade: ProtocolUpgrade) -> Vec { + let upgrade_token = upgrade_into_diamond_cut(protocol_upgrade); + + let encoded_params = ethabi::encode(&[upgrade_token]); + let execute_upgrade_selector = hyperchain_contract() .function("executeUpgrade") .unwrap() .short_signature(); - let diamond_upgrade_calldata = execute_upgrade_selector - .iter() - .copied() - .chain(ethabi::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 = ethabi::encode(&[Token::FixedBytes(vec![0u8; 32]), governance_operation]); + + // Concatenate the function selector with the encoded parameters + let mut calldata = Vec::with_capacity(4 + encoded_params.len()); + calldata.extend_from_slice(&execute_upgrade_selector); + calldata.extend_from_slice(&encoded_params); + + calldata +} + +fn diamond_upgrade_log(upgrade: ProtocolUpgrade, eth_block: u64) -> Log { + // struct DiamondCutData { + // FacetCut[] facetCuts; + // address initAddress; + // bytes initCalldata; + // } + let final_data = ethabi::encode(&[Token::Tuple(vec![ + Token::Array(vec![]), + Token::Address(H160::zero()), + Token::Bytes(init_calldata(upgrade.clone())), + ])]); + tracing::info!("{:?}", Token::Bytes(init_calldata(upgrade))); + + Log { + address: Address::repeat_byte(0x1), + topics: vec![ + state_transition_manager_contract() + .event("NewUpgradeCutData") + .unwrap() + .signature(), + H256::from_low_u64_be(eth_block), + ], + 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, + block_timestamp: None, + } +} +fn upgrade_timestamp_log(eth_block: u64) -> Log { + let final_data = ethabi::encode(&[U256::from(12345).into_token()]); Log { address: Address::repeat_byte(0x1), topics: vec![ - governance_contract() - .event("TransparentOperationScheduled") - .expect("TransparentOperationScheduled event is missing in abi") + chain_admin_contract() + .event("UpdateUpgradeTimestamp") + .expect("UpdateUpgradeTimestamp event is missing in ABI") .signature(), - Default::default(), + H256::from_low_u64_be(eth_block), ], data: final_data.into(), block_hash: Some(H256::repeat_byte(0x11)), @@ -577,7 +749,7 @@ fn upgrade_into_diamond_cut(upgrade: ProtocolUpgrade) -> Token { else { unreachable!() }; - let upgrade_token = abi::ProposedUpgrade { + ProposedUpgrade { l2_protocol_upgrade_tx: tx, factory_deps, bootloader_hash: upgrade.bootloader_code_hash.unwrap_or_default().into(), @@ -589,17 +761,7 @@ fn upgrade_into_diamond_cut(upgrade: ProtocolUpgrade) -> Token { upgrade_timestamp: upgrade.timestamp.into(), new_protocol_version: upgrade.version.pack(), } - .encode(); - Token::Tuple(vec![ - Token::Array(vec![]), - Token::Address(Default::default()), - Token::Bytes( - vec![0u8; 4] - .into_iter() - .chain(ethabi::encode(&[upgrade_token])) - .collect(), - ), - ]) + .encode() } async fn setup_db(connection_pool: &ConnectionPool) { diff --git a/core/node/node_framework/src/implementations/layers/eth_watch.rs b/core/node/node_framework/src/implementations/layers/eth_watch.rs index 53eeb1c5280..e19828d85cc 100644 --- a/core/node/node_framework/src/implementations/layers/eth_watch.rs +++ b/core/node/node_framework/src/implementations/layers/eth_watch.rs @@ -1,5 +1,5 @@ use zksync_config::{ContractsConfig, EthWatchConfig}; -use zksync_contracts::{chain_admin_contract, governance_contract}; +use zksync_contracts::chain_admin_contract; use zksync_eth_watch::{EthHttpQueryClient, EthWatch}; use crate::{ @@ -71,9 +71,8 @@ impl WiringLayer for EthWatchLayer { ); let eth_watch = EthWatch::new( - self.contracts_config.diamond_proxy_addr, - &governance_contract(), &chain_admin_contract(), + Box::new(eth_client.clone()), Box::new(eth_client), main_pool, self.eth_watch_config.poll_interval(), diff --git a/docs/guides/external-node/building-from-scratch/Dockerfile b/docs/guides/external-node/building-from-scratch/Dockerfile deleted file mode 100644 index 5b015a4545b..00000000000 --- a/docs/guides/external-node/building-from-scratch/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -FROM matterlabs/zk-environment:latest2.0-lightweight - -RUN git clone https://github.com/matter-labs/zksync-era - -WORKDIR /usr/src/zksync/zksync-era - -# core 24.16.0 (#2608), see: https://github.com/matter-labs/zksync-era/releases -RUN git reset --hard 1ac52c5 - -ENV ZKSYNC_HOME=/usr/src/zksync/zksync-era -ENV PATH="${ZKSYNC_HOME}/bin:${PATH}" - -# build zk tool -RUN zkt - -# build rust -RUN cargo build --release -RUN cp target/release/zksync_external_node /usr/bin - -# build contracts -RUN git submodule update --init --recursive -RUN zk_supervisor contracts - -# copy migrations (node expects them to be in specific directory) -RUN cp -r core/lib/dal/migrations/ migrations - -ENTRYPOINT [ "sh", "docker/external-node/entrypoint.sh"] diff --git a/prover/crates/bin/witness_generator/src/artifacts.rs b/prover/crates/bin/witness_generator/src/artifacts.rs index 7c444da047b..0c6044692dd 100644 --- a/prover/crates/bin/witness_generator/src/artifacts.rs +++ b/prover/crates/bin/witness_generator/src/artifacts.rs @@ -1,20 +1,20 @@ -use std::time::Instant; +use std::{sync::Arc, time::Instant}; use async_trait::async_trait; use zksync_object_store::ObjectStore; use zksync_prover_dal::{ConnectionPool, Prover}; #[derive(Debug)] -pub(crate) struct AggregationBlobUrls { +pub struct AggregationBlobUrls { pub aggregation_urls: String, pub circuit_ids_and_urls: Vec<(u8, String)>, } #[async_trait] -pub(crate) trait ArtifactsManager { +pub trait ArtifactsManager { type InputMetadata; type InputArtifacts; - type OutputArtifacts; + type OutputArtifacts: Send + Clone + 'static; type BlobUrls; async fn get_artifacts( @@ -26,6 +26,8 @@ pub(crate) trait ArtifactsManager { job_id: u32, artifacts: Self::OutputArtifacts, object_store: &dyn ObjectStore, + shall_save_to_public_bucket: bool, + public_blob_store: Option>, ) -> Self::BlobUrls; async fn save_to_database( diff --git a/prover/crates/bin/witness_generator/src/basic_circuits/job_processor.rs b/prover/crates/bin/witness_generator/src/basic_circuits/job_processor.rs deleted file mode 100644 index 50e747b1ce1..00000000000 --- a/prover/crates/bin/witness_generator/src/basic_circuits/job_processor.rs +++ /dev/null @@ -1,144 +0,0 @@ -use std::{sync::Arc, time::Instant}; - -use anyhow::Context as _; -use tracing::Instrument; -use zksync_prover_dal::ProverDal; -use zksync_prover_fri_types::{get_current_pod_name, AuxOutputWitnessWrapper}; -use zksync_prover_keystore::keystore::Keystore; -use zksync_queued_job_processor::{async_trait, JobProcessor}; -use zksync_types::{basic_fri_types::AggregationRound, L1BatchNumber}; - -use crate::{ - artifacts::ArtifactsManager, - basic_circuits::{BasicCircuitArtifacts, BasicWitnessGenerator, BasicWitnessGeneratorJob}, - metrics::WITNESS_GENERATOR_METRICS, - witness_generator::WitnessGenerator, -}; - -#[async_trait] -impl JobProcessor for BasicWitnessGenerator { - type Job = BasicWitnessGeneratorJob; - type JobId = L1BatchNumber; - // The artifact is optional to support skipping blocks when sampling is enabled. - type JobArtifacts = Option; - - const SERVICE_NAME: &'static str = "fri_basic_circuit_witness_generator"; - - async fn get_next_job(&self) -> anyhow::Result> { - let mut prover_connection = self.prover_connection_pool.connection().await?; - let last_l1_batch_to_process = self.config.last_l1_batch_to_process(); - let pod_name = get_current_pod_name(); - match prover_connection - .fri_witness_generator_dal() - .get_next_basic_circuit_witness_job( - last_l1_batch_to_process, - self.protocol_version, - &pod_name, - ) - .await - { - Some(block_number) => Ok(Some(( - block_number, - ::prepare_job( - block_number, - &*self.object_store, - Keystore::locate(), // todo: this should be removed - ) - .await?, - ))), - None => Ok(None), - } - } - - async fn save_failure(&self, job_id: L1BatchNumber, _started_at: Instant, error: String) -> () { - self.prover_connection_pool - .connection() - .await - .unwrap() - .fri_witness_generator_dal() - .mark_witness_job_failed(&error, job_id) - .await; - } - - #[allow(clippy::async_yields_async)] - async fn process_job( - &self, - _job_id: &Self::JobId, - job: BasicWitnessGeneratorJob, - started_at: Instant, - ) -> tokio::task::JoinHandle>> { - let object_store = Arc::clone(&self.object_store); - let max_circuits_in_flight = self.config.max_circuits_in_flight; - tokio::spawn(async move { - let block_number = job.block_number; - ::process_job( - job, - object_store, - Some(max_circuits_in_flight), - started_at, - ) - .instrument(tracing::info_span!("basic_circuit", %block_number)) - .await - .map(Some) - }) - } - - #[tracing::instrument(skip_all, fields(l1_batch = %job_id))] - async fn save_result( - &self, - job_id: L1BatchNumber, - started_at: Instant, - optional_artifacts: Option, - ) -> anyhow::Result<()> { - match optional_artifacts { - None => Ok(()), - Some(artifacts) => { - let blob_started_at = Instant::now(); - - let aux_output_witness_wrapper = - AuxOutputWitnessWrapper(artifacts.aux_output_witness.clone()); - if self.config.shall_save_to_public_bucket { - self.public_blob_store.as_deref() - .expect("public_object_store shall not be empty while running with shall_save_to_public_bucket config") - .put(job_id, &aux_output_witness_wrapper) - .await - .unwrap(); - } - - let blob_urls = - Self::save_to_bucket(job_id.0, artifacts.clone(), &*self.object_store).await; - - WITNESS_GENERATOR_METRICS.blob_save_time[&AggregationRound::BasicCircuits.into()] - .observe(blob_started_at.elapsed()); - - Self::save_to_database( - &self.prover_connection_pool, - job_id.0, - started_at, - blob_urls, - artifacts, - ) - .await?; - Ok(()) - } - } - } - - fn max_attempts(&self) -> u32 { - self.config.max_attempts - } - - async fn get_job_attempts(&self, job_id: &L1BatchNumber) -> anyhow::Result { - let mut prover_storage = self - .prover_connection_pool - .connection() - .await - .context("failed to acquire DB connection for BasicWitnessGenerator")?; - prover_storage - .fri_witness_generator_dal() - .get_basic_circuit_witness_job_attempts(*job_id) - .await - .map(|attempts| attempts.unwrap_or(0)) - .context("failed to get job attempts for BasicWitnessGenerator") - } -} diff --git a/prover/crates/bin/witness_generator/src/leaf_aggregation/job_processor.rs b/prover/crates/bin/witness_generator/src/leaf_aggregation/job_processor.rs deleted file mode 100644 index 440636b85fa..00000000000 --- a/prover/crates/bin/witness_generator/src/leaf_aggregation/job_processor.rs +++ /dev/null @@ -1,135 +0,0 @@ -use std::time::Instant; - -use anyhow::Context as _; -use async_trait::async_trait; -use zksync_prover_dal::ProverDal; -use zksync_prover_fri_types::get_current_pod_name; -use zksync_queued_job_processor::JobProcessor; -use zksync_types::basic_fri_types::AggregationRound; - -use crate::{ - artifacts::ArtifactsManager, - leaf_aggregation::{ - LeafAggregationArtifacts, LeafAggregationWitnessGenerator, - LeafAggregationWitnessGeneratorJob, - }, - metrics::WITNESS_GENERATOR_METRICS, - witness_generator::WitnessGenerator, -}; - -#[async_trait] -impl JobProcessor for LeafAggregationWitnessGenerator { - type Job = LeafAggregationWitnessGeneratorJob; - type JobId = u32; - type JobArtifacts = LeafAggregationArtifacts; - - const SERVICE_NAME: &'static str = "fri_leaf_aggregation_witness_generator"; - - async fn get_next_job(&self) -> anyhow::Result> { - let mut prover_connection = self.prover_connection_pool.connection().await?; - let pod_name = get_current_pod_name(); - let Some(metadata) = prover_connection - .fri_witness_generator_dal() - .get_next_leaf_aggregation_job(self.protocol_version, &pod_name) - .await - else { - return Ok(None); - }; - tracing::info!("Processing leaf aggregation job {:?}", metadata.id); - Ok(Some(( - metadata.id, - ::prepare_job( - metadata, - &*self.object_store, - self.keystore.clone(), - ) - .await - .context("prepare_leaf_aggregation_job()")?, - ))) - } - - async fn save_failure(&self, job_id: u32, _started_at: Instant, error: String) -> () { - self.prover_connection_pool - .connection() - .await - .unwrap() - .fri_witness_generator_dal() - .mark_leaf_aggregation_job_failed(&error, job_id) - .await; - } - - #[allow(clippy::async_yields_async)] - async fn process_job( - &self, - _job_id: &Self::JobId, - job: LeafAggregationWitnessGeneratorJob, - started_at: Instant, - ) -> tokio::task::JoinHandle> { - let object_store = self.object_store.clone(); - let max_circuits_in_flight = self.config.max_circuits_in_flight; - tokio::spawn(async move { - ::process_job( - job, - object_store, - Some(max_circuits_in_flight), - started_at, - ) - .await - }) - } - - async fn save_result( - &self, - job_id: u32, - started_at: Instant, - artifacts: LeafAggregationArtifacts, - ) -> anyhow::Result<()> { - let block_number = artifacts.block_number; - let circuit_id = artifacts.circuit_id; - tracing::info!( - "Saving leaf aggregation artifacts for block {} with circuit {}", - block_number.0, - circuit_id, - ); - - let blob_save_started_at = Instant::now(); - - let blob_urls = Self::save_to_bucket(job_id, artifacts.clone(), &*self.object_store).await; - - WITNESS_GENERATOR_METRICS.blob_save_time[&AggregationRound::LeafAggregation.into()] - .observe(blob_save_started_at.elapsed()); - - tracing::info!( - "Saved leaf aggregation artifacts for block {} with circuit {}", - block_number.0, - circuit_id, - ); - Self::save_to_database( - &self.prover_connection_pool, - job_id, - started_at, - blob_urls, - artifacts, - ) - .await?; - Ok(()) - } - - fn max_attempts(&self) -> u32 { - self.config.max_attempts - } - - async fn get_job_attempts(&self, job_id: &u32) -> anyhow::Result { - let mut prover_storage = self - .prover_connection_pool - .connection() - .await - .context("failed to acquire DB connection for LeafAggregationWitnessGenerator")?; - prover_storage - .fri_witness_generator_dal() - .get_leaf_aggregation_job_attempts(*job_id) - .await - .map(|attempts| attempts.unwrap_or(0)) - .context("failed to get job attempts for LeafAggregationWitnessGenerator") - } -} diff --git a/prover/crates/bin/witness_generator/src/leaf_aggregation/mod.rs b/prover/crates/bin/witness_generator/src/leaf_aggregation/mod.rs deleted file mode 100644 index 960843259c3..00000000000 --- a/prover/crates/bin/witness_generator/src/leaf_aggregation/mod.rs +++ /dev/null @@ -1,277 +0,0 @@ -use std::{sync::Arc, time::Instant}; - -use anyhow::Context as _; -use async_trait::async_trait; -use circuit_definitions::circuit_definitions::recursion_layer::base_circuit_type_into_recursive_leaf_circuit_type; -use tokio::sync::Semaphore; -use zkevm_test_harness::{ - witness::recursive_aggregation::{ - compute_leaf_params, create_leaf_witness, split_recursion_queue, - }, - zkevm_circuits::scheduler::aux::BaseLayerCircuitType, -}; -use zksync_config::configs::FriWitnessGeneratorConfig; -use zksync_object_store::ObjectStore; -use zksync_prover_dal::{ConnectionPool, Prover}; -use zksync_prover_fri_types::{ - circuit_definitions::{ - boojum::field::goldilocks::GoldilocksField, - circuit_definitions::base_layer::{ - ZkSyncBaseLayerClosedFormInput, ZkSyncBaseLayerVerificationKey, - }, - encodings::recursion_request::RecursionQueueSimulator, - zkevm_circuits::recursion::leaf_layer::input::RecursionLeafParametersWitness, - }, - FriProofWrapper, -}; -use zksync_prover_keystore::keystore::Keystore; -use zksync_types::{ - basic_fri_types::AggregationRound, protocol_version::ProtocolSemanticVersion, - prover_dal::LeafAggregationJobMetadata, L1BatchNumber, -}; - -use crate::{ - artifacts::ArtifactsManager, - metrics::WITNESS_GENERATOR_METRICS, - utils::{ - load_proofs_for_job_ids, save_recursive_layer_prover_input_artifacts, - ClosedFormInputWrapper, - }, - witness_generator::WitnessGenerator, -}; - -mod artifacts; -mod job_processor; - -pub struct LeafAggregationWitnessGeneratorJob { - pub(crate) circuit_id: u8, - pub(crate) block_number: L1BatchNumber, - pub(crate) closed_form_inputs: ClosedFormInputWrapper, - pub(crate) proofs_ids: Vec, - pub(crate) base_vk: ZkSyncBaseLayerVerificationKey, - pub(crate) leaf_params: RecursionLeafParametersWitness, -} - -#[derive(Debug)] -pub struct LeafAggregationWitnessGenerator { - config: FriWitnessGeneratorConfig, - object_store: Arc, - prover_connection_pool: ConnectionPool, - protocol_version: ProtocolSemanticVersion, - keystore: Keystore, -} - -#[derive(Clone)] -pub struct LeafAggregationArtifacts { - circuit_id: u8, - block_number: L1BatchNumber, - pub aggregations: Vec<(u64, RecursionQueueSimulator)>, - pub circuit_ids_and_urls: Vec<(u8, String)>, - #[allow(dead_code)] - closed_form_inputs: Vec>, -} - -impl LeafAggregationWitnessGenerator { - pub fn new( - config: FriWitnessGeneratorConfig, - object_store: Arc, - prover_connection_pool: ConnectionPool, - protocol_version: ProtocolSemanticVersion, - keystore: Keystore, - ) -> Self { - Self { - config, - object_store, - prover_connection_pool, - protocol_version, - keystore, - } - } -} - -#[tracing::instrument( - skip_all, - fields(l1_batch = %job.block_number, circuit_id = %job.circuit_id) -)] -pub async fn process_leaf_aggregation_job( - started_at: Instant, - job: LeafAggregationWitnessGeneratorJob, - object_store: Arc, - max_circuits_in_flight: usize, -) -> LeafAggregationArtifacts { - let circuit_id = job.circuit_id; - let queues = split_recursion_queue(job.closed_form_inputs.1); - - assert_eq!(circuit_id, job.base_vk.numeric_circuit_type()); - - let aggregations = queues - .iter() - .cloned() - .map(|queue| (circuit_id as u64, queue)) - .collect(); - - let mut proof_ids_iter = job.proofs_ids.into_iter(); - let mut proofs_ids = vec![]; - for queue in queues.iter() { - let proofs_ids_for_queue: Vec<_> = (&mut proof_ids_iter) - .take(queue.num_items as usize) - .collect(); - assert_eq!(queue.num_items as usize, proofs_ids_for_queue.len()); - proofs_ids.push(proofs_ids_for_queue); - } - - let semaphore = Arc::new(Semaphore::new(max_circuits_in_flight)); - - let mut handles = vec![]; - for (circuit_idx, (queue, proofs_ids_for_queue)) in - queues.into_iter().zip(proofs_ids).enumerate() - { - let semaphore = semaphore.clone(); - - let object_store = object_store.clone(); - let queue = queue.clone(); - let base_vk = job.base_vk.clone(); - let leaf_params = (circuit_id, job.leaf_params.clone()); - - let handle = tokio::task::spawn(async move { - let _permit = semaphore - .acquire() - .await - .expect("failed to get permit to process queues chunk"); - - let proofs = load_proofs_for_job_ids(&proofs_ids_for_queue, &*object_store).await; - let base_proofs = proofs - .into_iter() - .map(|wrapper| match wrapper { - FriProofWrapper::Base(base_proof) => base_proof, - FriProofWrapper::Recursive(_) => { - panic!( - "Expected only base proofs for leaf agg {} {}", - job.circuit_id, job.block_number - ); - } - }) - .collect(); - - let (_, circuit) = create_leaf_witness( - circuit_id.into(), - queue, - base_proofs, - &base_vk, - &leaf_params, - ); - - save_recursive_layer_prover_input_artifacts( - job.block_number, - circuit_idx, - vec![circuit], - AggregationRound::LeafAggregation, - 0, - &*object_store, - None, - ) - .await - }); - - handles.push(handle); - } - - let circuit_ids_and_urls_results = futures::future::join_all(handles).await; - let circuit_ids_and_urls = circuit_ids_and_urls_results - .into_iter() - .flat_map(|x| x.unwrap()) - .collect(); - - WITNESS_GENERATOR_METRICS.witness_generation_time[&AggregationRound::LeafAggregation.into()] - .observe(started_at.elapsed()); - - tracing::info!( - "Leaf witness generation for block {} with circuit id {}: is complete in {:?}.", - job.block_number.0, - circuit_id, - started_at.elapsed(), - ); - - LeafAggregationArtifacts { - circuit_id, - block_number: job.block_number, - aggregations, - circuit_ids_and_urls, - closed_form_inputs: job.closed_form_inputs.0, - } -} - -#[async_trait] -impl WitnessGenerator for LeafAggregationWitnessGenerator { - type Job = LeafAggregationWitnessGeneratorJob; - type Metadata = LeafAggregationJobMetadata; - type Artifacts = LeafAggregationArtifacts; - - #[tracing::instrument( - skip_all, - fields(l1_batch = %job.block_number, circuit_id = %job.circuit_id) - )] - async fn process_job( - job: LeafAggregationWitnessGeneratorJob, - object_store: Arc, - max_circuits_in_flight: Option, - started_at: Instant, - ) -> anyhow::Result { - tracing::info!( - "Starting witness generation of type {:?} for block {} with circuit {}", - AggregationRound::LeafAggregation, - job.block_number.0, - job.circuit_id, - ); - Ok(process_leaf_aggregation_job( - started_at, - job, - object_store, - max_circuits_in_flight.unwrap(), - ) - .await) - } - - #[tracing::instrument( - skip_all, - fields(l1_batch = %metadata.block_number, circuit_id = %metadata.circuit_id) - )] - async fn prepare_job( - metadata: LeafAggregationJobMetadata, - object_store: &dyn ObjectStore, - keystore: Keystore, - ) -> anyhow::Result { - let started_at = Instant::now(); - let closed_form_input = - LeafAggregationWitnessGenerator::get_artifacts(&metadata, object_store).await?; - - WITNESS_GENERATOR_METRICS.blob_fetch_time[&AggregationRound::LeafAggregation.into()] - .observe(started_at.elapsed()); - - let started_at = Instant::now(); - let base_vk = keystore - .load_base_layer_verification_key(metadata.circuit_id) - .context("get_base_layer_vk_for_circuit_type()")?; - - let leaf_circuit_id = base_circuit_type_into_recursive_leaf_circuit_type( - BaseLayerCircuitType::from_numeric_value(metadata.circuit_id), - ) as u8; - - let leaf_vk = keystore - .load_recursive_layer_verification_key(leaf_circuit_id) - .context("get_recursive_layer_vk_for_circuit_type()")?; - let leaf_params = compute_leaf_params(metadata.circuit_id, base_vk.clone(), leaf_vk); - - WITNESS_GENERATOR_METRICS.prepare_job_time[&AggregationRound::LeafAggregation.into()] - .observe(started_at.elapsed()); - - Ok(LeafAggregationWitnessGeneratorJob { - circuit_id: metadata.circuit_id, - block_number: metadata.block_number, - closed_form_inputs: closed_form_input, - proofs_ids: metadata.prover_job_ids_for_proofs, - base_vk, - leaf_params, - }) - } -} diff --git a/prover/crates/bin/witness_generator/src/lib.rs b/prover/crates/bin/witness_generator/src/lib.rs index b24b548a49b..651535e2e80 100644 --- a/prover/crates/bin/witness_generator/src/lib.rs +++ b/prover/crates/bin/witness_generator/src/lib.rs @@ -2,16 +2,11 @@ #![feature(generic_const_exprs)] pub mod artifacts; -pub mod basic_circuits; -pub mod leaf_aggregation; pub mod metrics; -pub mod node_aggregation; pub mod precalculated_merkle_paths_provider; -pub mod recursion_tip; -pub mod scheduler; +pub mod rounds; mod storage_oracle; #[cfg(test)] mod tests; pub mod utils; mod witness; -pub mod witness_generator; diff --git a/prover/crates/bin/witness_generator/src/main.rs b/prover/crates/bin/witness_generator/src/main.rs index 9d75d8ddc6f..38aacf5d7a5 100644 --- a/prover/crates/bin/witness_generator/src/main.rs +++ b/prover/crates/bin/witness_generator/src/main.rs @@ -20,9 +20,10 @@ use zksync_types::{basic_fri_types::AggregationRound, protocol_version::Protocol use zksync_utils::wait_for_tasks::ManagedTasks; use zksync_vlog::prometheus::PrometheusExporterConfig; use zksync_witness_generator::{ - basic_circuits::BasicWitnessGenerator, leaf_aggregation::LeafAggregationWitnessGenerator, - metrics::SERVER_METRICS, node_aggregation::NodeAggregationWitnessGenerator, - recursion_tip::RecursionTipWitnessGenerator, scheduler::SchedulerWitnessGenerator, + metrics::SERVER_METRICS, + rounds::{ + BasicCircuits, LeafAggregation, NodeAggregation, RecursionTip, Scheduler, WitnessGenerator, + }, }; #[cfg(not(target_env = "msvc"))] @@ -132,15 +133,14 @@ async fn main() -> anyhow::Result<()> { .listener_port }; - let prover_connection_pool = - ConnectionPool::::singleton(database_secrets.prover_url()?) - .build() - .await - .context("failed to build a prover_connection_pool")?; + let connection_pool = ConnectionPool::::singleton(database_secrets.prover_url()?) + .build() + .await + .context("failed to build a prover_connection_pool")?; let (stop_sender, stop_receiver) = watch::channel(false); let protocol_version = PROVER_PROTOCOL_SEMANTIC_VERSION; - ensure_protocol_alignment(&prover_connection_pool, protocol_version, &keystore) + ensure_protocol_alignment(&connection_pool, protocol_version, &keystore) .await .unwrap_or_else(|err| panic!("Protocol alignment check failed: {:?}", err)); @@ -191,65 +191,71 @@ async fn main() -> anyhow::Result<()> { &protocol_version ); + let public_blob_store = match config.shall_save_to_public_bucket { + false => None, + true => Some( + ObjectStoreFactory::new( + prover_config + .public_object_store + .clone() + .expect("public_object_store"), + ) + .create_store() + .await?, + ), + }; + let witness_generator_task = match round { AggregationRound::BasicCircuits => { - let public_blob_store = match config.shall_save_to_public_bucket { - false => None, - true => Some( - ObjectStoreFactory::new( - prover_config - .public_object_store - .clone() - .expect("public_object_store"), - ) - .create_store() - .await?, - ), - }; - let generator = BasicWitnessGenerator::new( + let generator = WitnessGenerator::::new( config.clone(), store_factory.create_store().await?, public_blob_store, - prover_connection_pool.clone(), + connection_pool.clone(), protocol_version, + keystore.clone(), ); generator.run(stop_receiver.clone(), opt.batch_size) } AggregationRound::LeafAggregation => { - let generator = LeafAggregationWitnessGenerator::new( + let generator = WitnessGenerator::::new( config.clone(), store_factory.create_store().await?, - prover_connection_pool.clone(), + public_blob_store, + connection_pool.clone(), protocol_version, keystore.clone(), ); generator.run(stop_receiver.clone(), opt.batch_size) } AggregationRound::NodeAggregation => { - let generator = NodeAggregationWitnessGenerator::new( + let generator = WitnessGenerator::::new( config.clone(), store_factory.create_store().await?, - prover_connection_pool.clone(), + public_blob_store, + connection_pool.clone(), protocol_version, keystore.clone(), ); generator.run(stop_receiver.clone(), opt.batch_size) } AggregationRound::RecursionTip => { - let generator = RecursionTipWitnessGenerator::new( + let generator = WitnessGenerator::::new( config.clone(), store_factory.create_store().await?, - prover_connection_pool.clone(), + public_blob_store, + connection_pool.clone(), protocol_version, keystore.clone(), ); generator.run(stop_receiver.clone(), opt.batch_size) } AggregationRound::Scheduler => { - let generator = SchedulerWitnessGenerator::new( + let generator = WitnessGenerator::::new( config.clone(), store_factory.create_store().await?, - prover_connection_pool.clone(), + public_blob_store, + connection_pool.clone(), protocol_version, keystore.clone(), ); diff --git a/prover/crates/bin/witness_generator/src/node_aggregation/job_processor.rs b/prover/crates/bin/witness_generator/src/node_aggregation/job_processor.rs deleted file mode 100644 index 0f66c988c10..00000000000 --- a/prover/crates/bin/witness_generator/src/node_aggregation/job_processor.rs +++ /dev/null @@ -1,126 +0,0 @@ -use std::time::Instant; - -use anyhow::Context as _; -use async_trait::async_trait; -use zksync_prover_dal::ProverDal; -use zksync_prover_fri_types::get_current_pod_name; -use zksync_queued_job_processor::JobProcessor; -use zksync_types::basic_fri_types::AggregationRound; - -use crate::{ - artifacts::ArtifactsManager, - metrics::WITNESS_GENERATOR_METRICS, - node_aggregation::{ - NodeAggregationArtifacts, NodeAggregationWitnessGenerator, - NodeAggregationWitnessGeneratorJob, - }, - witness_generator::WitnessGenerator, -}; - -#[async_trait] -impl JobProcessor for NodeAggregationWitnessGenerator { - type Job = NodeAggregationWitnessGeneratorJob; - type JobId = u32; - type JobArtifacts = NodeAggregationArtifacts; - - const SERVICE_NAME: &'static str = "fri_node_aggregation_witness_generator"; - - async fn get_next_job(&self) -> anyhow::Result> { - let mut prover_connection = self.prover_connection_pool.connection().await?; - let pod_name = get_current_pod_name(); - let Some(metadata) = prover_connection - .fri_witness_generator_dal() - .get_next_node_aggregation_job(self.protocol_version, &pod_name) - .await - else { - return Ok(None); - }; - tracing::info!("Processing node aggregation job {:?}", metadata.id); - Ok(Some(( - metadata.id, - ::prepare_job( - metadata, - &*self.object_store, - self.keystore.clone(), - ) - .await - .context("prepare_job()")?, - ))) - } - - async fn save_failure(&self, job_id: u32, _started_at: Instant, error: String) -> () { - self.prover_connection_pool - .connection() - .await - .unwrap() - .fri_witness_generator_dal() - .mark_node_aggregation_job_failed(&error, job_id) - .await; - } - - #[allow(clippy::async_yields_async)] - async fn process_job( - &self, - _job_id: &Self::JobId, - job: NodeAggregationWitnessGeneratorJob, - started_at: Instant, - ) -> tokio::task::JoinHandle> { - let object_store = self.object_store.clone(); - let max_circuits_in_flight = self.config.max_circuits_in_flight; - tokio::spawn(async move { - ::process_job( - job, - object_store, - Some(max_circuits_in_flight), - started_at, - ) - .await - }) - } - - #[tracing::instrument( - skip_all, - fields(l1_batch = % artifacts.block_number, circuit_id = % artifacts.circuit_id) - )] - async fn save_result( - &self, - job_id: u32, - started_at: Instant, - artifacts: NodeAggregationArtifacts, - ) -> anyhow::Result<()> { - let blob_save_started_at = Instant::now(); - - let blob_urls = Self::save_to_bucket(job_id, artifacts.clone(), &*self.object_store).await; - - WITNESS_GENERATOR_METRICS.blob_save_time[&AggregationRound::NodeAggregation.into()] - .observe(blob_save_started_at.elapsed()); - - Self::save_to_database( - &self.prover_connection_pool, - job_id, - started_at, - blob_urls, - artifacts, - ) - .await?; - Ok(()) - } - - fn max_attempts(&self) -> u32 { - self.config.max_attempts - } - - async fn get_job_attempts(&self, job_id: &u32) -> anyhow::Result { - let mut prover_storage = self - .prover_connection_pool - .connection() - .await - .context("failed to acquire DB connection for NodeAggregationWitnessGenerator")?; - prover_storage - .fri_witness_generator_dal() - .get_node_aggregation_job_attempts(*job_id) - .await - .map(|attempts| attempts.unwrap_or(0)) - .context("failed to get job attempts for NodeAggregationWitnessGenerator") - } -} diff --git a/prover/crates/bin/witness_generator/src/recursion_tip/job_processor.rs b/prover/crates/bin/witness_generator/src/recursion_tip/job_processor.rs deleted file mode 100644 index 9ab7d934a3e..00000000000 --- a/prover/crates/bin/witness_generator/src/recursion_tip/job_processor.rs +++ /dev/null @@ -1,136 +0,0 @@ -use std::time::Instant; - -use anyhow::Context as _; -use async_trait::async_trait; -use zksync_prover_dal::ProverDal; -use zksync_prover_fri_types::get_current_pod_name; -use zksync_queued_job_processor::JobProcessor; -use zksync_types::{basic_fri_types::AggregationRound, L1BatchNumber}; - -use crate::{ - artifacts::ArtifactsManager, - metrics::WITNESS_GENERATOR_METRICS, - recursion_tip::{ - RecursionTipArtifacts, RecursionTipJobMetadata, RecursionTipWitnessGenerator, - RecursionTipWitnessGeneratorJob, - }, - witness_generator::WitnessGenerator, -}; - -#[async_trait] -impl JobProcessor for RecursionTipWitnessGenerator { - type Job = RecursionTipWitnessGeneratorJob; - type JobId = L1BatchNumber; - type JobArtifacts = RecursionTipArtifacts; - - const SERVICE_NAME: &'static str = "recursion_tip_witness_generator"; - - async fn get_next_job(&self) -> anyhow::Result> { - let mut prover_connection = self.prover_connection_pool.connection().await?; - let pod_name = get_current_pod_name(); - let Some((l1_batch_number, number_of_final_node_jobs)) = prover_connection - .fri_witness_generator_dal() - .get_next_recursion_tip_witness_job(self.protocol_version, &pod_name) - .await - else { - return Ok(None); - }; - - let final_node_proof_job_ids = prover_connection - .fri_prover_jobs_dal() - .get_final_node_proof_job_ids_for(l1_batch_number) - .await; - - assert_eq!( - final_node_proof_job_ids.len(), - number_of_final_node_jobs as usize, - "recursion tip witness job was scheduled without all final node jobs being completed; expected {}, got {}", - number_of_final_node_jobs, final_node_proof_job_ids.len() - ); - - Ok(Some(( - l1_batch_number, - ::prepare_job( - RecursionTipJobMetadata { - l1_batch_number, - final_node_proof_job_ids, - }, - &*self.object_store, - self.keystore.clone(), - ) - .await - .context("prepare_job()")?, - ))) - } - - async fn save_failure(&self, job_id: L1BatchNumber, _started_at: Instant, error: String) -> () { - self.prover_connection_pool - .connection() - .await - .unwrap() - .fri_witness_generator_dal() - .mark_recursion_tip_job_failed(&error, job_id) - .await; - } - - #[allow(clippy::async_yields_async)] - async fn process_job( - &self, - _job_id: &Self::JobId, - job: RecursionTipWitnessGeneratorJob, - started_at: Instant, - ) -> tokio::task::JoinHandle> { - let object_store = self.object_store.clone(); - tokio::spawn(async move { - ::process_job(job, object_store, None, started_at).await - }) - } - - #[tracing::instrument( - skip_all, - fields(l1_batch = %job_id) - )] - async fn save_result( - &self, - job_id: L1BatchNumber, - started_at: Instant, - artifacts: RecursionTipArtifacts, - ) -> anyhow::Result<()> { - let blob_save_started_at = Instant::now(); - - let blob_urls = - Self::save_to_bucket(job_id.0, artifacts.clone(), &*self.object_store).await; - - WITNESS_GENERATOR_METRICS.blob_save_time[&AggregationRound::RecursionTip.into()] - .observe(blob_save_started_at.elapsed()); - - Self::save_to_database( - &self.prover_connection_pool, - job_id.0, - started_at, - blob_urls, - artifacts, - ) - .await?; - - Ok(()) - } - - fn max_attempts(&self) -> u32 { - self.config.max_attempts - } - - async fn get_job_attempts(&self, job_id: &L1BatchNumber) -> anyhow::Result { - let mut prover_storage = self - .prover_connection_pool - .connection() - .await - .context("failed to acquire DB connection for RecursionTipWitnessGenerator")?; - prover_storage - .fri_witness_generator_dal() - .get_recursion_tip_witness_job_attempts(*job_id) - .await - .map(|attempts| attempts.unwrap_or(0)) - .context("failed to get job attempts for RecursionTipWitnessGenerator") - } -} diff --git a/prover/crates/bin/witness_generator/src/basic_circuits/artifacts.rs b/prover/crates/bin/witness_generator/src/rounds/basic_circuits/artifacts.rs similarity index 81% rename from prover/crates/bin/witness_generator/src/basic_circuits/artifacts.rs rename to prover/crates/bin/witness_generator/src/rounds/basic_circuits/artifacts.rs index aa85d185e66..2936634fc97 100644 --- a/prover/crates/bin/witness_generator/src/basic_circuits/artifacts.rs +++ b/prover/crates/bin/witness_generator/src/rounds/basic_circuits/artifacts.rs @@ -1,4 +1,4 @@ -use std::time::Instant; +use std::{sync::Arc, time::Instant}; use async_trait::async_trait; use zksync_object_store::ObjectStore; @@ -9,12 +9,12 @@ use zksync_types::{basic_fri_types::AggregationRound, L1BatchNumber}; use crate::{ artifacts::ArtifactsManager, - basic_circuits::{BasicCircuitArtifacts, BasicWitnessGenerator, BasicWitnessGeneratorJob}, + rounds::basic_circuits::{BasicCircuitArtifacts, BasicCircuits, BasicWitnessGeneratorJob}, utils::SchedulerPartialInputWrapper, }; #[async_trait] -impl ArtifactsManager for BasicWitnessGenerator { +impl ArtifactsManager for BasicCircuits { type InputMetadata = L1BatchNumber; type InputArtifacts = BasicWitnessGeneratorJob; type OutputArtifacts = BasicCircuitArtifacts; @@ -36,8 +36,19 @@ impl ArtifactsManager for BasicWitnessGenerator { job_id: u32, artifacts: Self::OutputArtifacts, object_store: &dyn ObjectStore, + shall_save_to_public_bucket: bool, + public_blob_store: Option>, ) -> String { - let aux_output_witness_wrapper = AuxOutputWitnessWrapper(artifacts.aux_output_witness); + let aux_output_witness_wrapper = + AuxOutputWitnessWrapper(artifacts.aux_output_witness.clone()); + if shall_save_to_public_bucket { + public_blob_store.as_deref() + .expect("public_object_store shall not be empty while running with shall_save_to_public_bucket config") + .put(L1BatchNumber(job_id), &aux_output_witness_wrapper) + .await + .unwrap(); + } + object_store .put(L1BatchNumber(job_id), &aux_output_witness_wrapper) .await diff --git a/prover/crates/bin/witness_generator/src/rounds/basic_circuits/mod.rs b/prover/crates/bin/witness_generator/src/rounds/basic_circuits/mod.rs new file mode 100644 index 00000000000..adb2bf72d04 --- /dev/null +++ b/prover/crates/bin/witness_generator/src/rounds/basic_circuits/mod.rs @@ -0,0 +1,136 @@ +use std::{sync::Arc, time::Instant}; + +use async_trait::async_trait; +use circuit_definitions::zkevm_circuits::scheduler::{ + block_header::BlockAuxilaryOutputWitness, input::SchedulerCircuitInstanceWitness, +}; +use zksync_multivm::circuit_sequencer_api_latest::boojum::{ + field::goldilocks::{GoldilocksExt2, GoldilocksField}, + gadgets::recursion::recursive_tree_hasher::CircuitGoldilocksPoseidon2Sponge, +}; +use zksync_object_store::ObjectStore; +use zksync_prover_dal::{ConnectionPool, Prover, ProverDal}; +use zksync_prover_fri_types::get_current_pod_name; +use zksync_prover_interface::inputs::WitnessInputData; +use zksync_prover_keystore::keystore::Keystore; +use zksync_types::{ + basic_fri_types::AggregationRound, protocol_version::ProtocolSemanticVersion, L1BatchNumber, +}; + +use crate::{ + artifacts::ArtifactsManager, + metrics::WITNESS_GENERATOR_METRICS, + rounds::{basic_circuits::utils::generate_witness, JobManager}, +}; + +mod artifacts; +mod utils; + +#[derive(Clone)] +pub struct BasicCircuitArtifacts { + pub(super) circuit_urls: Vec<(u8, String)>, + pub(super) queue_urls: Vec<(u8, String, usize)>, + pub(super) scheduler_witness: SchedulerCircuitInstanceWitness< + GoldilocksField, + CircuitGoldilocksPoseidon2Sponge, + GoldilocksExt2, + >, + pub(super) aux_output_witness: BlockAuxilaryOutputWitness, +} + +#[derive(Clone)] +pub struct BasicWitnessGeneratorJob { + pub(super) block_number: L1BatchNumber, + pub(super) data: WitnessInputData, +} + +type Witness = ( + Vec<(u8, String)>, + Vec<(u8, String, usize)>, + SchedulerCircuitInstanceWitness< + GoldilocksField, + CircuitGoldilocksPoseidon2Sponge, + GoldilocksExt2, + >, + BlockAuxilaryOutputWitness, +); + +pub struct BasicCircuits; + +#[async_trait] +impl JobManager for BasicCircuits { + type Job = BasicWitnessGeneratorJob; + type Metadata = L1BatchNumber; + + const ROUND: AggregationRound = AggregationRound::BasicCircuits; + const SERVICE_NAME: &'static str = "fri_basic_circuit_witness_generator"; + + async fn process_job( + job: BasicWitnessGeneratorJob, + object_store: Arc, + max_circuits_in_flight: usize, + started_at: Instant, + ) -> anyhow::Result { + let BasicWitnessGeneratorJob { + block_number, + data: job, + } = job; + + tracing::info!( + "Starting witness generation of type {:?} for block {}", + AggregationRound::BasicCircuits, + block_number.0 + ); + + let (circuit_urls, queue_urls, scheduler_witness, aux_output_witness) = + generate_witness(block_number, object_store, job, max_circuits_in_flight).await; + WITNESS_GENERATOR_METRICS.witness_generation_time[&AggregationRound::BasicCircuits.into()] + .observe(started_at.elapsed()); + tracing::info!( + "Witness generation for block {} is complete in {:?}", + block_number.0, + started_at.elapsed() + ); + + Ok(BasicCircuitArtifacts { + circuit_urls, + queue_urls, + scheduler_witness, + aux_output_witness, + }) + } + + async fn prepare_job( + metadata: L1BatchNumber, + object_store: &dyn ObjectStore, + _keystore: Keystore, + ) -> anyhow::Result { + tracing::info!("Processing FRI basic witness-gen for block {}", metadata.0); + let started_at = Instant::now(); + let job = Self::get_artifacts(&metadata, object_store).await?; + + WITNESS_GENERATOR_METRICS.blob_fetch_time[&AggregationRound::BasicCircuits.into()] + .observe(started_at.elapsed()); + + Ok(job) + } + + async fn get_metadata( + connection_pool: ConnectionPool, + protocol_version: ProtocolSemanticVersion, + ) -> anyhow::Result> { + let pod_name = get_current_pod_name(); + if let Some(l1_batch_number) = connection_pool + .connection() + .await + .unwrap() + .fri_witness_generator_dal() + .get_next_basic_circuit_witness_job(protocol_version, &pod_name) + .await + { + Ok(Some((l1_batch_number.0, l1_batch_number))) + } else { + Ok(None) + } + } +} diff --git a/prover/crates/bin/witness_generator/src/basic_circuits/mod.rs b/prover/crates/bin/witness_generator/src/rounds/basic_circuits/utils.rs similarity index 74% rename from prover/crates/bin/witness_generator/src/basic_circuits/mod.rs rename to prover/crates/bin/witness_generator/src/rounds/basic_circuits/utils.rs index e76ef180c52..23ae1b0f2af 100644 --- a/prover/crates/bin/witness_generator/src/basic_circuits/mod.rs +++ b/prover/crates/bin/witness_generator/src/rounds/basic_circuits/utils.rs @@ -2,182 +2,43 @@ use std::{ collections::HashSet, hash::{DefaultHasher, Hash, Hasher}, sync::Arc, - time::Instant, }; -use async_trait::async_trait; use circuit_definitions::{ circuit_definitions::base_layer::{ZkSyncBaseLayerCircuit, ZkSyncBaseLayerStorage}, encodings::recursion_request::RecursionQueueSimulator, - zkevm_circuits::{ - fsm_input_output::ClosedFormInputCompactFormWitness, - scheduler::{ - block_header::BlockAuxilaryOutputWitness, input::SchedulerCircuitInstanceWitness, - }, - }, + zkevm_circuits::fsm_input_output::ClosedFormInputCompactFormWitness, }; use tokio::sync::Semaphore; use tracing::Instrument; use zkevm_test_harness::witness::oracle::WitnessGenerationArtifact; -use zksync_config::configs::FriWitnessGeneratorConfig; use zksync_multivm::{ circuit_sequencer_api_latest::{ - boojum::{ - field::goldilocks::{GoldilocksExt2, GoldilocksField}, - gadgets::recursion::recursive_tree_hasher::CircuitGoldilocksPoseidon2Sponge, - }, - geometry_config::get_geometry_config, + boojum::field::goldilocks::GoldilocksField, geometry_config::get_geometry_config, }, interface::storage::StorageView, vm_latest::{constants::MAX_CYCLES_FOR_TX, HistoryDisabled, StorageOracle as VmStorageOracle}, zk_evm_latest::ethereum_types::Address, }; use zksync_object_store::ObjectStore; -use zksync_prover_dal::{ConnectionPool, Prover}; use zksync_prover_fri_types::{keys::ClosedFormInputKey, CircuitAuxData}; use zksync_prover_interface::inputs::WitnessInputData; -use zksync_prover_keystore::keystore::Keystore; use zksync_system_constants::BOOTLOADER_ADDRESS; -use zksync_types::{ - basic_fri_types::AggregationRound, protocol_version::ProtocolSemanticVersion, L1BatchNumber, -}; +use zksync_types::L1BatchNumber; use crate::{ - artifacts::ArtifactsManager, - metrics::WITNESS_GENERATOR_METRICS, precalculated_merkle_paths_provider::PrecalculatedMerklePathsProvider, + rounds::basic_circuits::Witness, storage_oracle::StorageOracle, utils::{ expand_bootloader_contents, save_circuit, save_ram_premutation_queue_witness, ClosedFormInputWrapper, KZG_TRUSTED_SETUP_FILE, }, witness::WitnessStorage, - witness_generator::WitnessGenerator, }; -mod artifacts; -pub mod job_processor; - -#[derive(Clone)] -pub struct BasicCircuitArtifacts { - pub(super) circuit_urls: Vec<(u8, String)>, - pub(super) queue_urls: Vec<(u8, String, usize)>, - pub(super) scheduler_witness: SchedulerCircuitInstanceWitness< - GoldilocksField, - CircuitGoldilocksPoseidon2Sponge, - GoldilocksExt2, - >, - pub(super) aux_output_witness: BlockAuxilaryOutputWitness, -} - -#[derive(Clone)] -pub struct BasicWitnessGeneratorJob { - pub(super) block_number: L1BatchNumber, - pub(super) data: WitnessInputData, -} - -#[derive(Debug)] -pub struct BasicWitnessGenerator { - config: Arc, - object_store: Arc, - public_blob_store: Option>, - prover_connection_pool: ConnectionPool, - protocol_version: ProtocolSemanticVersion, -} - -type Witness = ( - Vec<(u8, String)>, - Vec<(u8, String, usize)>, - SchedulerCircuitInstanceWitness< - GoldilocksField, - CircuitGoldilocksPoseidon2Sponge, - GoldilocksExt2, - >, - BlockAuxilaryOutputWitness, -); - -impl BasicWitnessGenerator { - pub fn new( - config: FriWitnessGeneratorConfig, - object_store: Arc, - public_blob_store: Option>, - prover_connection_pool: ConnectionPool, - protocol_version: ProtocolSemanticVersion, - ) -> Self { - Self { - config: Arc::new(config), - object_store, - public_blob_store, - prover_connection_pool, - protocol_version, - } - } -} - -#[async_trait] -impl WitnessGenerator for BasicWitnessGenerator { - type Job = BasicWitnessGeneratorJob; - type Metadata = L1BatchNumber; - type Artifacts = BasicCircuitArtifacts; - - async fn process_job( - job: BasicWitnessGeneratorJob, - object_store: Arc, - max_circuits_in_flight: Option, - started_at: Instant, - ) -> anyhow::Result { - let BasicWitnessGeneratorJob { - block_number, - data: job, - } = job; - - tracing::info!( - "Starting witness generation of type {:?} for block {}", - AggregationRound::BasicCircuits, - block_number.0 - ); - - let (circuit_urls, queue_urls, scheduler_witness, aux_output_witness) = generate_witness( - block_number, - object_store, - job, - max_circuits_in_flight.unwrap(), - ) - .await; - WITNESS_GENERATOR_METRICS.witness_generation_time[&AggregationRound::BasicCircuits.into()] - .observe(started_at.elapsed()); - tracing::info!( - "Witness generation for block {} is complete in {:?}", - block_number.0, - started_at.elapsed() - ); - - Ok(BasicCircuitArtifacts { - circuit_urls, - queue_urls, - scheduler_witness, - aux_output_witness, - }) - } - - async fn prepare_job( - metadata: L1BatchNumber, - object_store: &dyn ObjectStore, - _keystore: Keystore, - ) -> anyhow::Result { - tracing::info!("Processing FRI basic witness-gen for block {}", metadata.0); - let started_at = Instant::now(); - let job = Self::get_artifacts(&metadata, object_store).await?; - - WITNESS_GENERATOR_METRICS.blob_fetch_time[&AggregationRound::BasicCircuits.into()] - .observe(started_at.elapsed()); - - Ok(job) - } -} - #[tracing::instrument(skip_all, fields(l1_batch = %block_number))] -async fn generate_witness( +pub(super) async fn generate_witness( block_number: L1BatchNumber, object_store: Arc, input: WitnessInputData, diff --git a/prover/crates/bin/witness_generator/src/leaf_aggregation/artifacts.rs b/prover/crates/bin/witness_generator/src/rounds/leaf_aggregation/artifacts.rs similarity index 95% rename from prover/crates/bin/witness_generator/src/leaf_aggregation/artifacts.rs rename to prover/crates/bin/witness_generator/src/rounds/leaf_aggregation/artifacts.rs index c83997e36b8..e3c97dd257c 100644 --- a/prover/crates/bin/witness_generator/src/leaf_aggregation/artifacts.rs +++ b/prover/crates/bin/witness_generator/src/rounds/leaf_aggregation/artifacts.rs @@ -9,13 +9,13 @@ use zksync_types::{basic_fri_types::AggregationRound, prover_dal::LeafAggregatio use crate::{ artifacts::{AggregationBlobUrls, ArtifactsManager}, - leaf_aggregation::{LeafAggregationArtifacts, LeafAggregationWitnessGenerator}, metrics::WITNESS_GENERATOR_METRICS, + rounds::leaf_aggregation::{LeafAggregation, LeafAggregationArtifacts}, utils::{AggregationWrapper, ClosedFormInputWrapper}, }; #[async_trait] -impl ArtifactsManager for LeafAggregationWitnessGenerator { +impl ArtifactsManager for LeafAggregation { type InputMetadata = LeafAggregationJobMetadata; type InputArtifacts = ClosedFormInputWrapper; type OutputArtifacts = LeafAggregationArtifacts; @@ -46,6 +46,8 @@ impl ArtifactsManager for LeafAggregationWitnessGenerator { _job_id: u32, artifacts: Self::OutputArtifacts, object_store: &dyn ObjectStore, + _shall_save_to_public_bucket: bool, + _public_blob_store: Option>, ) -> AggregationBlobUrls { let started_at = Instant::now(); let key = AggregationsKey { diff --git a/prover/crates/bin/witness_generator/src/rounds/leaf_aggregation/mod.rs b/prover/crates/bin/witness_generator/src/rounds/leaf_aggregation/mod.rs new file mode 100644 index 00000000000..451ceee390d --- /dev/null +++ b/prover/crates/bin/witness_generator/src/rounds/leaf_aggregation/mod.rs @@ -0,0 +1,250 @@ +use std::{sync::Arc, time::Instant}; + +use anyhow::Context as _; +use async_trait::async_trait; +use circuit_definitions::circuit_definitions::recursion_layer::base_circuit_type_into_recursive_leaf_circuit_type; +use tokio::sync::Semaphore; +use zkevm_test_harness::{ + witness::recursive_aggregation::{ + compute_leaf_params, create_leaf_witness, split_recursion_queue, + }, + zkevm_circuits::scheduler::aux::BaseLayerCircuitType, +}; +use zksync_object_store::ObjectStore; +use zksync_prover_dal::{ConnectionPool, Prover, ProverDal}; +use zksync_prover_fri_types::{ + circuit_definitions::{ + boojum::field::goldilocks::GoldilocksField, + circuit_definitions::base_layer::{ + ZkSyncBaseLayerClosedFormInput, ZkSyncBaseLayerVerificationKey, + }, + encodings::recursion_request::RecursionQueueSimulator, + zkevm_circuits::recursion::leaf_layer::input::RecursionLeafParametersWitness, + }, + get_current_pod_name, FriProofWrapper, +}; +use zksync_prover_keystore::keystore::Keystore; +use zksync_types::{ + basic_fri_types::AggregationRound, protocol_version::ProtocolSemanticVersion, + prover_dal::LeafAggregationJobMetadata, L1BatchNumber, +}; + +use crate::{ + artifacts::ArtifactsManager, + metrics::WITNESS_GENERATOR_METRICS, + rounds::JobManager, + utils::{ + load_proofs_for_job_ids, save_recursive_layer_prover_input_artifacts, + ClosedFormInputWrapper, + }, +}; + +mod artifacts; + +pub struct LeafAggregationWitnessGeneratorJob { + pub(crate) circuit_id: u8, + pub(crate) block_number: L1BatchNumber, + pub(crate) closed_form_inputs: ClosedFormInputWrapper, + pub(crate) proofs_ids: Vec, + pub(crate) base_vk: ZkSyncBaseLayerVerificationKey, + pub(crate) leaf_params: RecursionLeafParametersWitness, +} + +#[derive(Clone)] +pub struct LeafAggregationArtifacts { + circuit_id: u8, + block_number: L1BatchNumber, + pub aggregations: Vec<(u64, RecursionQueueSimulator)>, + pub circuit_ids_and_urls: Vec<(u8, String)>, + #[allow(dead_code)] + closed_form_inputs: Vec>, +} + +pub struct LeafAggregation; + +#[async_trait] +impl JobManager for LeafAggregation { + type Job = LeafAggregationWitnessGeneratorJob; + type Metadata = LeafAggregationJobMetadata; + + const ROUND: AggregationRound = AggregationRound::LeafAggregation; + const SERVICE_NAME: &'static str = "fri_leaf_aggregation_witness_generator"; + + #[tracing::instrument( + skip_all, + fields(l1_batch = %job.block_number, circuit_id = %job.circuit_id) + )] + async fn process_job( + job: LeafAggregationWitnessGeneratorJob, + object_store: Arc, + max_circuits_in_flight: usize, + started_at: Instant, + ) -> anyhow::Result { + tracing::info!( + "Starting witness generation of type {:?} for block {} with circuit {}", + AggregationRound::LeafAggregation, + job.block_number.0, + job.circuit_id, + ); + let circuit_id = job.circuit_id; + let queues = split_recursion_queue(job.closed_form_inputs.1); + + assert_eq!(circuit_id, job.base_vk.numeric_circuit_type()); + + let aggregations = queues + .iter() + .cloned() + .map(|queue| (circuit_id as u64, queue)) + .collect(); + + let mut proof_ids_iter = job.proofs_ids.into_iter(); + let mut proofs_ids = vec![]; + for queue in queues.iter() { + let proofs_ids_for_queue: Vec<_> = (&mut proof_ids_iter) + .take(queue.num_items as usize) + .collect(); + assert_eq!(queue.num_items as usize, proofs_ids_for_queue.len()); + proofs_ids.push(proofs_ids_for_queue); + } + + let semaphore = Arc::new(Semaphore::new(max_circuits_in_flight)); + + let mut handles = vec![]; + for (circuit_idx, (queue, proofs_ids_for_queue)) in + queues.into_iter().zip(proofs_ids).enumerate() + { + let semaphore = semaphore.clone(); + + let object_store = object_store.clone(); + let queue = queue.clone(); + let base_vk = job.base_vk.clone(); + let leaf_params = (circuit_id, job.leaf_params.clone()); + + let handle = tokio::task::spawn(async move { + let _permit = semaphore + .acquire() + .await + .expect("failed to get permit to process queues chunk"); + + let proofs = load_proofs_for_job_ids(&proofs_ids_for_queue, &*object_store).await; + let base_proofs = proofs + .into_iter() + .map(|wrapper| match wrapper { + FriProofWrapper::Base(base_proof) => base_proof, + FriProofWrapper::Recursive(_) => { + panic!( + "Expected only base proofs for leaf agg {} {}", + job.circuit_id, job.block_number + ); + } + }) + .collect(); + + let (_, circuit) = create_leaf_witness( + circuit_id.into(), + queue, + base_proofs, + &base_vk, + &leaf_params, + ); + + save_recursive_layer_prover_input_artifacts( + job.block_number, + circuit_idx, + vec![circuit], + AggregationRound::LeafAggregation, + 0, + &*object_store, + None, + ) + .await + }); + + handles.push(handle); + } + + let circuit_ids_and_urls_results = futures::future::join_all(handles).await; + let circuit_ids_and_urls = circuit_ids_and_urls_results + .into_iter() + .flat_map(|x| x.unwrap()) + .collect(); + + WITNESS_GENERATOR_METRICS.witness_generation_time + [&AggregationRound::LeafAggregation.into()] + .observe(started_at.elapsed()); + + tracing::info!( + "Leaf witness generation for block {} with circuit id {}: is complete in {:?}.", + job.block_number.0, + circuit_id, + started_at.elapsed(), + ); + + Ok(LeafAggregationArtifacts { + circuit_id, + block_number: job.block_number, + aggregations, + circuit_ids_and_urls, + closed_form_inputs: job.closed_form_inputs.0, + }) + } + + #[tracing::instrument( + skip_all, + fields(l1_batch = %metadata.block_number, circuit_id = %metadata.circuit_id) + )] + async fn prepare_job( + metadata: LeafAggregationJobMetadata, + object_store: &dyn ObjectStore, + keystore: Keystore, + ) -> anyhow::Result { + let started_at = Instant::now(); + let closed_form_input = Self::get_artifacts(&metadata, object_store).await?; + + WITNESS_GENERATOR_METRICS.blob_fetch_time[&AggregationRound::LeafAggregation.into()] + .observe(started_at.elapsed()); + + let started_at = Instant::now(); + let base_vk = keystore + .load_base_layer_verification_key(metadata.circuit_id) + .context("get_base_layer_vk_for_circuit_type()")?; + + let leaf_circuit_id = base_circuit_type_into_recursive_leaf_circuit_type( + BaseLayerCircuitType::from_numeric_value(metadata.circuit_id), + ) as u8; + + let leaf_vk = keystore + .load_recursive_layer_verification_key(leaf_circuit_id) + .context("get_recursive_layer_vk_for_circuit_type()")?; + let leaf_params = compute_leaf_params(metadata.circuit_id, base_vk.clone(), leaf_vk); + + WITNESS_GENERATOR_METRICS.prepare_job_time[&AggregationRound::LeafAggregation.into()] + .observe(started_at.elapsed()); + + Ok(LeafAggregationWitnessGeneratorJob { + circuit_id: metadata.circuit_id, + block_number: metadata.block_number, + closed_form_inputs: closed_form_input, + proofs_ids: metadata.prover_job_ids_for_proofs, + base_vk, + leaf_params, + }) + } + + async fn get_metadata( + connection_pool: ConnectionPool, + protocol_version: ProtocolSemanticVersion, + ) -> anyhow::Result> { + let pod_name = get_current_pod_name(); + let Some(metadata) = connection_pool + .connection() + .await? + .fri_witness_generator_dal() + .get_next_leaf_aggregation_job(protocol_version, &pod_name) + .await + else { + return Ok(None); + }; + Ok(Some((metadata.id, metadata))) + } +} diff --git a/prover/crates/bin/witness_generator/src/rounds/mod.rs b/prover/crates/bin/witness_generator/src/rounds/mod.rs new file mode 100644 index 00000000000..6fd72c96869 --- /dev/null +++ b/prover/crates/bin/witness_generator/src/rounds/mod.rs @@ -0,0 +1,195 @@ +use std::{marker::PhantomData, sync::Arc, time::Instant}; + +use anyhow::Context; +use async_trait::async_trait; +use tokio::task::JoinHandle; +use zksync_config::configs::FriWitnessGeneratorConfig; +use zksync_object_store::ObjectStore; +use zksync_prover_dal::{ConnectionPool, Prover, ProverDal}; +use zksync_prover_keystore::keystore::Keystore; +use zksync_queued_job_processor::JobProcessor; +use zksync_types::protocol_version::ProtocolSemanticVersion; + +use crate::artifacts::ArtifactsManager; + +mod basic_circuits; +mod leaf_aggregation; +mod node_aggregation; +mod recursion_tip; +mod scheduler; + +pub use basic_circuits::BasicCircuits; +pub use leaf_aggregation::LeafAggregation; +pub use node_aggregation::NodeAggregation; +pub use recursion_tip::RecursionTip; +pub use scheduler::Scheduler; +use zksync_types::basic_fri_types::AggregationRound; + +use crate::metrics::WITNESS_GENERATOR_METRICS; + +#[async_trait] +pub trait JobManager: ArtifactsManager { + type Job: Send + 'static; + type Metadata: Send + 'static; + + const ROUND: AggregationRound; + const SERVICE_NAME: &'static str; + + async fn process_job( + job: Self::Job, + object_store: Arc, + max_circuits_in_flight: usize, + started_at: Instant, + ) -> anyhow::Result; + + async fn prepare_job( + metadata: Self::Metadata, + object_store: &dyn ObjectStore, + keystore: Keystore, + ) -> anyhow::Result; + + async fn get_metadata( + connection_pool: ConnectionPool, + protocol_version: ProtocolSemanticVersion, + ) -> anyhow::Result>; +} + +#[derive(Debug)] +pub struct WitnessGenerator { + pub config: FriWitnessGeneratorConfig, + pub object_store: Arc, + pub public_blob_store: Option>, + pub connection_pool: ConnectionPool, + pub protocol_version: ProtocolSemanticVersion, + pub keystore: Keystore, + _round: PhantomData, +} + +impl WitnessGenerator +where + R: JobManager + ArtifactsManager, +{ + pub fn new( + config: FriWitnessGeneratorConfig, + object_store: Arc, + public_blob_store: Option>, + connection_pool: ConnectionPool, + protocol_version: ProtocolSemanticVersion, + keystore: Keystore, + ) -> Self { + Self { + config, + object_store, + public_blob_store, + connection_pool, + protocol_version, + keystore, + _round: Default::default(), + } + } +} + +#[async_trait] +impl JobProcessor for WitnessGenerator +where + R: JobManager + ArtifactsManager + Send + Sync, +{ + type Job = R::Job; + type JobId = u32; + type JobArtifacts = R::OutputArtifacts; + + const SERVICE_NAME: &'static str = R::SERVICE_NAME; + + async fn get_next_job(&self) -> anyhow::Result> { + if let Some((id, metadata)) = + R::get_metadata(self.connection_pool.clone(), self.protocol_version) + .await + .context("get_metadata()")? + { + tracing::info!("Processing {:?} job {:?}", R::ROUND, id); + Ok(Some(( + id, + R::prepare_job(metadata, &*self.object_store, self.keystore.clone()) + .await + .context("prepare_job()")?, + ))) + } else { + Ok(None) + } + } + + async fn save_failure(&self, job_id: Self::JobId, _started_at: Instant, error: String) { + self.connection_pool + .connection() + .await + .unwrap() + .fri_witness_generator_dal() + .mark_witness_job_failed(&error, job_id, R::ROUND) + .await; + } + + async fn process_job( + &self, + _job_id: &Self::JobId, + job: Self::Job, + started_at: Instant, + ) -> JoinHandle> { + let object_store = self.object_store.clone(); + let max_circuits_in_flight = self.config.max_circuits_in_flight; + tokio::spawn(async move { + R::process_job(job, object_store, max_circuits_in_flight, started_at).await + }) + } + + #[tracing::instrument(skip_all, fields(job_id = %job_id))] + async fn save_result( + &self, + job_id: Self::JobId, + started_at: Instant, + artifacts: Self::JobArtifacts, + ) -> anyhow::Result<()> { + tracing::info!("Saving {:?} artifacts for job {:?}", R::ROUND, job_id); + + let blob_save_started_at = Instant::now(); + + let blob_urls = R::save_to_bucket( + job_id, + artifacts.clone(), + &*self.object_store, + self.config.shall_save_to_public_bucket, + self.public_blob_store.clone(), + ) + .await; + + WITNESS_GENERATOR_METRICS.blob_save_time[&R::ROUND.into()] + .observe(blob_save_started_at.elapsed()); + + tracing::info!("Saved {:?} artifacts for job {:?}", R::ROUND, job_id); + R::save_to_database( + &self.connection_pool, + job_id, + started_at, + blob_urls, + artifacts, + ) + .await?; + Ok(()) + } + + fn max_attempts(&self) -> u32 { + self.config.max_attempts + } + + async fn get_job_attempts(&self, job_id: &Self::JobId) -> anyhow::Result { + let mut prover_storage = self.connection_pool.connection().await.context(format!( + "failed to acquire DB connection for {:?}", + R::ROUND + ))?; + prover_storage + .fri_witness_generator_dal() + .get_witness_job_attempts(*job_id, R::ROUND) + .await + .map(|attempts| attempts.unwrap_or(0)) + .context(format!("failed to get job attempts for {:?}", R::ROUND)) + } +} diff --git a/prover/crates/bin/witness_generator/src/node_aggregation/artifacts.rs b/prover/crates/bin/witness_generator/src/rounds/node_aggregation/artifacts.rs similarity index 95% rename from prover/crates/bin/witness_generator/src/node_aggregation/artifacts.rs rename to prover/crates/bin/witness_generator/src/rounds/node_aggregation/artifacts.rs index 09f01899bf3..e4f5c90080d 100644 --- a/prover/crates/bin/witness_generator/src/node_aggregation/artifacts.rs +++ b/prover/crates/bin/witness_generator/src/rounds/node_aggregation/artifacts.rs @@ -9,12 +9,12 @@ use zksync_types::{basic_fri_types::AggregationRound, prover_dal::NodeAggregatio use crate::{ artifacts::{AggregationBlobUrls, ArtifactsManager}, metrics::WITNESS_GENERATOR_METRICS, - node_aggregation::{NodeAggregationArtifacts, NodeAggregationWitnessGenerator}, + rounds::node_aggregation::{NodeAggregation, NodeAggregationArtifacts}, utils::AggregationWrapper, }; #[async_trait] -impl ArtifactsManager for NodeAggregationWitnessGenerator { +impl ArtifactsManager for NodeAggregation { type InputMetadata = NodeAggregationJobMetadata; type InputArtifacts = AggregationWrapper; type OutputArtifacts = NodeAggregationArtifacts; @@ -51,6 +51,8 @@ impl ArtifactsManager for NodeAggregationWitnessGenerator { _job_id: u32, artifacts: Self::OutputArtifacts, object_store: &dyn ObjectStore, + _shall_save_to_public_bucket: bool, + _public_blob_store: Option>, ) -> AggregationBlobUrls { let started_at = Instant::now(); let key = AggregationsKey { diff --git a/prover/crates/bin/witness_generator/src/node_aggregation/mod.rs b/prover/crates/bin/witness_generator/src/rounds/node_aggregation/mod.rs similarity index 87% rename from prover/crates/bin/witness_generator/src/node_aggregation/mod.rs rename to prover/crates/bin/witness_generator/src/rounds/node_aggregation/mod.rs index f2c9a6fb891..e891d313ffc 100644 --- a/prover/crates/bin/witness_generator/src/node_aggregation/mod.rs +++ b/prover/crates/bin/witness_generator/src/rounds/node_aggregation/mod.rs @@ -7,9 +7,8 @@ use tokio::sync::Semaphore; use zkevm_test_harness::witness::recursive_aggregation::{ compute_node_vk_commitment, create_node_witness, }; -use zksync_config::configs::FriWitnessGeneratorConfig; use zksync_object_store::ObjectStore; -use zksync_prover_dal::{ConnectionPool, Prover}; +use zksync_prover_dal::{ConnectionPool, Prover, ProverDal}; use zksync_prover_fri_types::{ circuit_definitions::{ boojum::field::goldilocks::GoldilocksField, @@ -19,7 +18,7 @@ use zksync_prover_fri_types::{ encodings::recursion_request::RecursionQueueSimulator, zkevm_circuits::recursion::leaf_layer::input::RecursionLeafParametersWitness, }, - FriProofWrapper, + get_current_pod_name, FriProofWrapper, }; use zksync_prover_keystore::{keystore::Keystore, utils::get_leaf_vk_params}; use zksync_types::{ @@ -30,12 +29,10 @@ use zksync_types::{ use crate::{ artifacts::ArtifactsManager, metrics::WITNESS_GENERATOR_METRICS, + rounds::JobManager, utils::{load_proofs_for_job_ids, save_recursive_layer_prover_input_artifacts}, - witness_generator::WitnessGenerator, }; - mod artifacts; -mod job_processor; #[derive(Clone)] pub struct NodeAggregationArtifacts { @@ -58,38 +55,15 @@ pub struct NodeAggregationWitnessGeneratorJob { all_leafs_layer_params: Vec<(u8, RecursionLeafParametersWitness)>, } -#[derive(Debug)] -pub struct NodeAggregationWitnessGenerator { - config: FriWitnessGeneratorConfig, - object_store: Arc, - prover_connection_pool: ConnectionPool, - protocol_version: ProtocolSemanticVersion, - keystore: Keystore, -} - -impl NodeAggregationWitnessGenerator { - pub fn new( - config: FriWitnessGeneratorConfig, - object_store: Arc, - prover_connection_pool: ConnectionPool, - protocol_version: ProtocolSemanticVersion, - keystore: Keystore, - ) -> Self { - Self { - config, - object_store, - prover_connection_pool, - protocol_version, - keystore, - } - } -} +pub struct NodeAggregation; #[async_trait] -impl WitnessGenerator for NodeAggregationWitnessGenerator { +impl JobManager for NodeAggregation { type Job = NodeAggregationWitnessGeneratorJob; type Metadata = NodeAggregationJobMetadata; - type Artifacts = NodeAggregationArtifacts; + + const ROUND: AggregationRound = AggregationRound::NodeAggregation; + const SERVICE_NAME: &'static str = "fri_node_aggregation_witness_generator"; #[tracing::instrument( skip_all, @@ -98,7 +72,7 @@ impl WitnessGenerator for NodeAggregationWitnessGenerator { async fn process_job( job: NodeAggregationWitnessGeneratorJob, object_store: Arc, - max_circuits_in_flight: Option, + max_circuits_in_flight: usize, started_at: Instant, ) -> anyhow::Result { let node_vk_commitment = compute_node_vk_commitment(job.node_vk.clone()); @@ -126,7 +100,7 @@ impl WitnessGenerator for NodeAggregationWitnessGenerator { proofs_ids.len() ); - let semaphore = Arc::new(Semaphore::new(max_circuits_in_flight.unwrap())); + let semaphore = Arc::new(Semaphore::new(max_circuits_in_flight)); let mut handles = vec![]; for (circuit_idx, (chunk, proofs_ids_for_chunk)) in job @@ -233,8 +207,7 @@ impl WitnessGenerator for NodeAggregationWitnessGenerator { keystore: Keystore, ) -> anyhow::Result { let started_at = Instant::now(); - let artifacts = - NodeAggregationWitnessGenerator::get_artifacts(&metadata, object_store).await?; + let artifacts = Self::get_artifacts(&metadata, object_store).await?; WITNESS_GENERATOR_METRICS.blob_fetch_time[&AggregationRound::NodeAggregation.into()] .observe(started_at.elapsed()); @@ -264,4 +237,22 @@ impl WitnessGenerator for NodeAggregationWitnessGenerator { .context("get_leaf_vk_params()")?, }) } + + async fn get_metadata( + connection_pool: ConnectionPool, + protocol_version: ProtocolSemanticVersion, + ) -> anyhow::Result> { + let pod_name = get_current_pod_name(); + let Some(metadata) = connection_pool + .connection() + .await? + .fri_witness_generator_dal() + .get_next_node_aggregation_job(protocol_version, &pod_name) + .await + else { + return Ok(None); + }; + + Ok(Some((metadata.id, metadata))) + } } diff --git a/prover/crates/bin/witness_generator/src/recursion_tip/artifacts.rs b/prover/crates/bin/witness_generator/src/rounds/recursion_tip/artifacts.rs similarity index 95% rename from prover/crates/bin/witness_generator/src/recursion_tip/artifacts.rs rename to prover/crates/bin/witness_generator/src/rounds/recursion_tip/artifacts.rs index b61aa948100..6d18795c2b3 100644 --- a/prover/crates/bin/witness_generator/src/recursion_tip/artifacts.rs +++ b/prover/crates/bin/witness_generator/src/rounds/recursion_tip/artifacts.rs @@ -13,11 +13,11 @@ use zksync_types::{basic_fri_types::AggregationRound, L1BatchNumber}; use crate::{ artifacts::ArtifactsManager, - recursion_tip::{RecursionTipArtifacts, RecursionTipWitnessGenerator}, + rounds::recursion_tip::{RecursionTip, RecursionTipArtifacts}, }; #[async_trait] -impl ArtifactsManager for RecursionTipWitnessGenerator { +impl ArtifactsManager for RecursionTip { type InputMetadata = Vec<(u8, u32)>; type InputArtifacts = Vec; type OutputArtifacts = RecursionTipArtifacts; @@ -78,6 +78,8 @@ impl ArtifactsManager for RecursionTipWitnessGenerator { job_id: u32, artifacts: Self::OutputArtifacts, object_store: &dyn ObjectStore, + _shall_save_to_public_bucket: bool, + _public_blob_store: Option>, ) -> String { let key = FriCircuitKey { block_number: L1BatchNumber(job_id), diff --git a/prover/crates/bin/witness_generator/src/recursion_tip/mod.rs b/prover/crates/bin/witness_generator/src/rounds/recursion_tip/mod.rs similarity index 80% rename from prover/crates/bin/witness_generator/src/recursion_tip/mod.rs rename to prover/crates/bin/witness_generator/src/rounds/recursion_tip/mod.rs index 40abb756c8a..873f6798481 100644 --- a/prover/crates/bin/witness_generator/src/recursion_tip/mod.rs +++ b/prover/crates/bin/witness_generator/src/rounds/recursion_tip/mod.rs @@ -35,22 +35,20 @@ use zkevm_test_harness::{ scheduler::aux::BaseLayerCircuitType, }, }; -use zksync_config::configs::FriWitnessGeneratorConfig; use zksync_object_store::ObjectStore; -use zksync_prover_dal::{ConnectionPool, Prover}; -use zksync_prover_fri_types::keys::ClosedFormInputKey; +use zksync_prover_dal::{ConnectionPool, Prover, ProverDal}; +use zksync_prover_fri_types::{get_current_pod_name, keys::ClosedFormInputKey}; use zksync_prover_keystore::{keystore::Keystore, utils::get_leaf_vk_params}; use zksync_types::{ basic_fri_types::AggregationRound, protocol_version::ProtocolSemanticVersion, L1BatchNumber, }; use crate::{ - artifacts::ArtifactsManager, metrics::WITNESS_GENERATOR_METRICS, utils::ClosedFormInputWrapper, - witness_generator::WitnessGenerator, + artifacts::ArtifactsManager, metrics::WITNESS_GENERATOR_METRICS, rounds::JobManager, + utils::ClosedFormInputWrapper, }; mod artifacts; -mod job_processor; #[derive(Clone)] pub struct RecursionTipWitnessGeneratorJob { @@ -73,38 +71,15 @@ pub struct RecursionTipJobMetadata { pub final_node_proof_job_ids: Vec<(u8, u32)>, } -#[derive(Debug)] -pub struct RecursionTipWitnessGenerator { - config: FriWitnessGeneratorConfig, - object_store: Arc, - prover_connection_pool: ConnectionPool, - protocol_version: ProtocolSemanticVersion, - keystore: Keystore, -} - -impl RecursionTipWitnessGenerator { - pub fn new( - config: FriWitnessGeneratorConfig, - object_store: Arc, - prover_connection_pool: ConnectionPool, - protocol_version: ProtocolSemanticVersion, - keystore: Keystore, - ) -> Self { - Self { - config, - object_store, - prover_connection_pool, - protocol_version, - keystore, - } - } -} +pub struct RecursionTip; #[async_trait] -impl WitnessGenerator for RecursionTipWitnessGenerator { +impl JobManager for RecursionTip { type Job = RecursionTipWitnessGeneratorJob; type Metadata = RecursionTipJobMetadata; - type Artifacts = RecursionTipArtifacts; + + const ROUND: AggregationRound = AggregationRound::RecursionTip; + const SERVICE_NAME: &'static str = "recursion_tip_witness_generator"; #[tracing::instrument( skip_all, @@ -113,7 +88,7 @@ impl WitnessGenerator for RecursionTipWitnessGenerator { async fn process_job( job: Self::Job, _object_store: Arc, - _max_circuits_in_flight: Option, + _max_circuits_in_flight: usize, started_at: Instant, ) -> anyhow::Result { tracing::info!( @@ -160,11 +135,8 @@ impl WitnessGenerator for RecursionTipWitnessGenerator { keystore: Keystore, ) -> anyhow::Result { let started_at = Instant::now(); - let recursion_tip_proofs = RecursionTipWitnessGenerator::get_artifacts( - &metadata.final_node_proof_job_ids, - object_store, - ) - .await?; + let recursion_tip_proofs = + Self::get_artifacts(&metadata.final_node_proof_job_ids, object_store).await?; WITNESS_GENERATOR_METRICS.blob_fetch_time[&AggregationRound::RecursionTip.into()] .observe(started_at.elapsed()); @@ -241,4 +213,42 @@ impl WitnessGenerator for RecursionTipWitnessGenerator { node_vk, }) } + + async fn get_metadata( + connection_pool: ConnectionPool, + protocol_version: ProtocolSemanticVersion, + ) -> anyhow::Result> { + let pod_name = get_current_pod_name(); + let Some((l1_batch_number, number_of_final_node_jobs)) = connection_pool + .connection() + .await? + .fri_witness_generator_dal() + .get_next_recursion_tip_witness_job(protocol_version, &pod_name) + .await + else { + return Ok(None); + }; + + let final_node_proof_job_ids = connection_pool + .connection() + .await? + .fri_prover_jobs_dal() + .get_final_node_proof_job_ids_for(l1_batch_number) + .await; + + assert_eq!( + final_node_proof_job_ids.len(), + number_of_final_node_jobs as usize, + "recursion tip witness job was scheduled without all final node jobs being completed; expected {}, got {}", + number_of_final_node_jobs, final_node_proof_job_ids.len() + ); + + Ok(Some(( + l1_batch_number.0, + RecursionTipJobMetadata { + l1_batch_number, + final_node_proof_job_ids, + }, + ))) + } } diff --git a/prover/crates/bin/witness_generator/src/scheduler/artifacts.rs b/prover/crates/bin/witness_generator/src/rounds/scheduler/artifacts.rs similarity index 92% rename from prover/crates/bin/witness_generator/src/scheduler/artifacts.rs rename to prover/crates/bin/witness_generator/src/rounds/scheduler/artifacts.rs index 77d1da685d0..ce9b32559b2 100644 --- a/prover/crates/bin/witness_generator/src/scheduler/artifacts.rs +++ b/prover/crates/bin/witness_generator/src/rounds/scheduler/artifacts.rs @@ -9,11 +9,11 @@ use zksync_types::{basic_fri_types::AggregationRound, L1BatchNumber}; use crate::{ artifacts::ArtifactsManager, - scheduler::{SchedulerArtifacts, SchedulerWitnessGenerator}, + rounds::scheduler::{Scheduler, SchedulerArtifacts}, }; #[async_trait] -impl ArtifactsManager for SchedulerWitnessGenerator { +impl ArtifactsManager for Scheduler { type InputMetadata = u32; type InputArtifacts = FriProofWrapper; type OutputArtifacts = SchedulerArtifacts; @@ -32,6 +32,8 @@ impl ArtifactsManager for SchedulerWitnessGenerator { job_id: u32, artifacts: Self::OutputArtifacts, object_store: &dyn ObjectStore, + _shall_save_to_public_bucket: bool, + _public_blob_store: Option>, ) -> String { let key = FriCircuitKey { block_number: L1BatchNumber(job_id), diff --git a/prover/crates/bin/witness_generator/src/scheduler/mod.rs b/prover/crates/bin/witness_generator/src/rounds/scheduler/mod.rs similarity index 79% rename from prover/crates/bin/witness_generator/src/scheduler/mod.rs rename to prover/crates/bin/witness_generator/src/rounds/scheduler/mod.rs index 7af3d68d5a7..fc7dfa2accb 100644 --- a/prover/crates/bin/witness_generator/src/scheduler/mod.rs +++ b/prover/crates/bin/witness_generator/src/rounds/scheduler/mod.rs @@ -5,9 +5,8 @@ use async_trait::async_trait; use zkevm_test_harness::zkevm_circuits::recursion::{ leaf_layer::input::RecursionLeafParametersWitness, NUM_BASE_LAYER_CIRCUITS, }; -use zksync_config::configs::FriWitnessGeneratorConfig; use zksync_object_store::ObjectStore; -use zksync_prover_dal::{ConnectionPool, Prover}; +use zksync_prover_dal::{ConnectionPool, Prover, ProverDal}; use zksync_prover_fri_types::{ circuit_definitions::{ boojum::{ @@ -21,7 +20,7 @@ use zksync_prover_fri_types::{ recursion_layer_proof_config, zkevm_circuits::scheduler::{input::SchedulerCircuitInstanceWitness, SchedulerConfig}, }, - FriProofWrapper, + get_current_pod_name, FriProofWrapper, }; use zksync_prover_keystore::{keystore::Keystore, utils::get_leaf_vk_params}; use zksync_types::{ @@ -29,12 +28,11 @@ use zksync_types::{ }; use crate::{ - artifacts::ArtifactsManager, metrics::WITNESS_GENERATOR_METRICS, - utils::SchedulerPartialInputWrapper, witness_generator::WitnessGenerator, + artifacts::ArtifactsManager, metrics::WITNESS_GENERATOR_METRICS, rounds::JobManager, + utils::SchedulerPartialInputWrapper, }; mod artifacts; -mod job_processor; #[derive(Clone)] pub struct SchedulerArtifacts { @@ -60,38 +58,15 @@ pub struct SchedulerWitnessJobMetadata { pub recursion_tip_job_id: u32, } -#[derive(Debug)] -pub struct SchedulerWitnessGenerator { - config: FriWitnessGeneratorConfig, - object_store: Arc, - prover_connection_pool: ConnectionPool, - protocol_version: ProtocolSemanticVersion, - keystore: Keystore, -} - -impl SchedulerWitnessGenerator { - pub fn new( - config: FriWitnessGeneratorConfig, - object_store: Arc, - prover_connection_pool: ConnectionPool, - protocol_version: ProtocolSemanticVersion, - keystore: Keystore, - ) -> Self { - Self { - config, - object_store, - prover_connection_pool, - protocol_version, - keystore, - } - } -} +pub struct Scheduler; #[async_trait] -impl WitnessGenerator for SchedulerWitnessGenerator { +impl JobManager for Scheduler { type Job = SchedulerWitnessGeneratorJob; type Metadata = SchedulerWitnessJobMetadata; - type Artifacts = SchedulerArtifacts; + + const ROUND: AggregationRound = AggregationRound::Scheduler; + const SERVICE_NAME: &'static str = "fri_scheduler_witness_generator"; #[tracing::instrument( skip_all, @@ -100,7 +75,7 @@ impl WitnessGenerator for SchedulerWitnessGenerator { async fn process_job( job: SchedulerWitnessGeneratorJob, _object_store: Arc, - _max_circuits_in_flight: Option, + _max_circuits_in_flight: usize, started_at: Instant, ) -> anyhow::Result { tracing::info!( @@ -148,9 +123,7 @@ impl WitnessGenerator for SchedulerWitnessGenerator { keystore: Keystore, ) -> anyhow::Result { let started_at = Instant::now(); - let wrapper = - SchedulerWitnessGenerator::get_artifacts(&metadata.recursion_tip_job_id, object_store) - .await?; + let wrapper = Self::get_artifacts(&metadata.recursion_tip_job_id, object_store).await?; let recursion_tip_proof = match wrapper { FriProofWrapper::Base(_) => Err(anyhow::anyhow!( "Expected only recursive proofs for scheduler l1 batch {}, got Base", @@ -196,4 +169,38 @@ impl WitnessGenerator for SchedulerWitnessGenerator { recursion_tip_vk, }) } + + async fn get_metadata( + connection_pool: ConnectionPool, + protocol_version: ProtocolSemanticVersion, + ) -> anyhow::Result> { + let pod_name = get_current_pod_name(); + let Some(l1_batch_number) = connection_pool + .connection() + .await? + .fri_witness_generator_dal() + .get_next_scheduler_witness_job(protocol_version, &pod_name) + .await + else { + return Ok(None); + }; + let recursion_tip_job_id = connection_pool + .connection() + .await? + .fri_prover_jobs_dal() + .get_recursion_tip_proof_job_id(l1_batch_number) + .await + .context(format!( + "could not find recursion tip proof for l1 batch {}", + l1_batch_number + ))?; + + Ok(Some(( + l1_batch_number.0, + SchedulerWitnessJobMetadata { + l1_batch_number, + recursion_tip_job_id, + }, + ))) + } } diff --git a/prover/crates/bin/witness_generator/src/scheduler/job_processor.rs b/prover/crates/bin/witness_generator/src/scheduler/job_processor.rs deleted file mode 100644 index b5745f98091..00000000000 --- a/prover/crates/bin/witness_generator/src/scheduler/job_processor.rs +++ /dev/null @@ -1,132 +0,0 @@ -use std::time::Instant; - -use anyhow::Context as _; -use async_trait::async_trait; -use zksync_prover_dal::ProverDal; -use zksync_prover_fri_types::get_current_pod_name; -use zksync_queued_job_processor::JobProcessor; -use zksync_types::{basic_fri_types::AggregationRound, L1BatchNumber}; - -use crate::{ - artifacts::ArtifactsManager, - metrics::WITNESS_GENERATOR_METRICS, - scheduler::{ - SchedulerArtifacts, SchedulerWitnessGenerator, SchedulerWitnessGeneratorJob, - SchedulerWitnessJobMetadata, - }, - witness_generator::WitnessGenerator, -}; - -#[async_trait] -impl JobProcessor for SchedulerWitnessGenerator { - type Job = SchedulerWitnessGeneratorJob; - type JobId = L1BatchNumber; - type JobArtifacts = SchedulerArtifacts; - - const SERVICE_NAME: &'static str = "fri_scheduler_witness_generator"; - - async fn get_next_job(&self) -> anyhow::Result> { - let mut prover_connection = self.prover_connection_pool.connection().await?; - let pod_name = get_current_pod_name(); - let Some(l1_batch_number) = prover_connection - .fri_witness_generator_dal() - .get_next_scheduler_witness_job(self.protocol_version, &pod_name) - .await - else { - return Ok(None); - }; - let recursion_tip_job_id = prover_connection - .fri_prover_jobs_dal() - .get_recursion_tip_proof_job_id(l1_batch_number) - .await - .context(format!( - "could not find recursion tip proof for l1 batch {}", - l1_batch_number - ))?; - - Ok(Some(( - l1_batch_number, - ::prepare_job( - SchedulerWitnessJobMetadata { - l1_batch_number, - recursion_tip_job_id, - }, - &*self.object_store, - self.keystore.clone(), - ) - .await - .context("prepare_job()")?, - ))) - } - - async fn save_failure(&self, job_id: L1BatchNumber, _started_at: Instant, error: String) -> () { - self.prover_connection_pool - .connection() - .await - .unwrap() - .fri_witness_generator_dal() - .mark_scheduler_job_failed(&error, job_id) - .await; - } - - #[allow(clippy::async_yields_async)] - async fn process_job( - &self, - _job_id: &Self::JobId, - job: SchedulerWitnessGeneratorJob, - started_at: Instant, - ) -> tokio::task::JoinHandle> { - let object_store = self.object_store.clone(); - tokio::spawn(async move { - ::process_job(job, object_store, None, started_at).await - }) - } - - #[tracing::instrument( - skip_all, - fields(l1_batch = %job_id) - )] - async fn save_result( - &self, - job_id: L1BatchNumber, - started_at: Instant, - artifacts: SchedulerArtifacts, - ) -> anyhow::Result<()> { - let blob_save_started_at = Instant::now(); - - let blob_urls = - Self::save_to_bucket(job_id.0, artifacts.clone(), &*self.object_store).await; - - WITNESS_GENERATOR_METRICS.blob_save_time[&AggregationRound::Scheduler.into()] - .observe(blob_save_started_at.elapsed()); - - Self::save_to_database( - &self.prover_connection_pool, - job_id.0, - started_at, - blob_urls, - artifacts, - ) - .await?; - - Ok(()) - } - - fn max_attempts(&self) -> u32 { - self.config.max_attempts - } - - async fn get_job_attempts(&self, job_id: &L1BatchNumber) -> anyhow::Result { - let mut prover_storage = self - .prover_connection_pool - .connection() - .await - .context("failed to acquire DB connection for SchedulerWitnessGenerator")?; - prover_storage - .fri_witness_generator_dal() - .get_scheduler_witness_job_attempts(*job_id) - .await - .map(|attempts| attempts.unwrap_or(0)) - .context("failed to get job attempts for SchedulerWitnessGenerator") - } -} diff --git a/prover/crates/bin/witness_generator/src/witness_generator.rs b/prover/crates/bin/witness_generator/src/witness_generator.rs deleted file mode 100644 index eb9200d7950..00000000000 --- a/prover/crates/bin/witness_generator/src/witness_generator.rs +++ /dev/null @@ -1,25 +0,0 @@ -use std::{sync::Arc, time::Instant}; - -use async_trait::async_trait; -use zksync_object_store::ObjectStore; -use zksync_prover_keystore::keystore::Keystore; - -#[async_trait] -pub trait WitnessGenerator { - type Job: Send + 'static; - type Metadata; - type Artifacts; - - async fn process_job( - job: Self::Job, - object_store: Arc, - max_circuits_in_flight: Option, - started_at: Instant, - ) -> anyhow::Result; - - async fn prepare_job( - metadata: Self::Metadata, - object_store: &dyn ObjectStore, - keystore: Keystore, - ) -> anyhow::Result; -} diff --git a/prover/crates/bin/witness_generator/tests/basic_test.rs b/prover/crates/bin/witness_generator/tests/basic_test.rs index 379ddc3a4eb..be6452dfc7d 100644 --- a/prover/crates/bin/witness_generator/tests/basic_test.rs +++ b/prover/crates/bin/witness_generator/tests/basic_test.rs @@ -15,9 +15,8 @@ use zksync_types::{ L1BatchNumber, }; use zksync_witness_generator::{ - leaf_aggregation::LeafAggregationWitnessGenerator, - node_aggregation::NodeAggregationWitnessGenerator, utils::AggregationWrapper, - witness_generator::WitnessGenerator, + rounds::{JobManager, LeafAggregation, NodeAggregation}, + utils::AggregationWrapper, }; fn compare_serialized(expected: &T, actual: &T) { @@ -52,22 +51,13 @@ async fn test_leaf_witness_gen() { .unwrap(); let keystore = Keystore::locate(); - let job = LeafAggregationWitnessGenerator::prepare_job( - leaf_aggregation_job_metadata, - &*object_store, - keystore, - ) - .await - .unwrap(); - - let artifacts = LeafAggregationWitnessGenerator::process_job( - job, - object_store.clone(), - Some(500), - Instant::now(), - ) - .await - .unwrap(); + let job = LeafAggregation::prepare_job(leaf_aggregation_job_metadata, &*object_store, keystore) + .await + .unwrap(); + + let artifacts = LeafAggregation::process_job(job, object_store.clone(), 500, Instant::now()) + .await + .unwrap(); let aggregations = AggregationWrapper(artifacts.aggregations); @@ -147,22 +137,13 @@ async fn test_node_witness_gen() { }; let keystore = Keystore::locate(); - let job = NodeAggregationWitnessGenerator::prepare_job( - node_aggregation_job_metadata, - &*object_store, - keystore, - ) - .await - .unwrap(); - - let artifacts = NodeAggregationWitnessGenerator::process_job( - job, - object_store.clone(), - Some(500), - Instant::now(), - ) - .await - .unwrap(); + let job = NodeAggregation::prepare_job(node_aggregation_job_metadata, &*object_store, keystore) + .await + .unwrap(); + + let artifacts = NodeAggregation::process_job(job, object_store.clone(), 500, Instant::now()) + .await + .unwrap(); let aggregations = AggregationWrapper(artifacts.next_aggregations); diff --git a/prover/crates/lib/prover_dal/.sqlx/query-16548daf69e9ff0528904be2e142254a457665179d9cf0a3c0b18c3fe09e4838.json b/prover/crates/lib/prover_dal/.sqlx/query-16548daf69e9ff0528904be2e142254a457665179d9cf0a3c0b18c3fe09e4838.json deleted file mode 100644 index 5fe5032746e..00000000000 --- a/prover/crates/lib/prover_dal/.sqlx/query-16548daf69e9ff0528904be2e142254a457665179d9cf0a3c0b18c3fe09e4838.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE node_aggregation_witness_jobs_fri\n SET\n status = 'failed',\n error = $1,\n updated_at = NOW()\n WHERE\n id = $2\n AND status != 'successful'\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Int8" - ] - }, - "nullable": [] - }, - "hash": "16548daf69e9ff0528904be2e142254a457665179d9cf0a3c0b18c3fe09e4838" -} diff --git a/prover/crates/lib/prover_dal/.sqlx/query-53f78fdee39b113d2f55f6f951bd94f28b7b2b60d551d552a9b0bab1f1791e39.json b/prover/crates/lib/prover_dal/.sqlx/query-53f78fdee39b113d2f55f6f951bd94f28b7b2b60d551d552a9b0bab1f1791e39.json deleted file mode 100644 index 15a10f7ce3c..00000000000 --- a/prover/crates/lib/prover_dal/.sqlx/query-53f78fdee39b113d2f55f6f951bd94f28b7b2b60d551d552a9b0bab1f1791e39.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n attempts\n FROM\n leaf_aggregation_witness_jobs_fri\n WHERE\n id = $1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "attempts", - "type_info": "Int2" - } - ], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [ - false - ] - }, - "hash": "53f78fdee39b113d2f55f6f951bd94f28b7b2b60d551d552a9b0bab1f1791e39" -} diff --git a/prover/crates/lib/prover_dal/.sqlx/query-5db868e03dc6901a0afa06f82a37a1a04821495487a80595cc9b523dac6ac8e9.json b/prover/crates/lib/prover_dal/.sqlx/query-5db868e03dc6901a0afa06f82a37a1a04821495487a80595cc9b523dac6ac8e9.json deleted file mode 100644 index 94dbaa80a10..00000000000 --- a/prover/crates/lib/prover_dal/.sqlx/query-5db868e03dc6901a0afa06f82a37a1a04821495487a80595cc9b523dac6ac8e9.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE scheduler_witness_jobs_fri\n SET\n status = 'failed',\n error = $1,\n updated_at = NOW()\n WHERE\n l1_batch_number = $2\n AND status != 'successful'\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Int8" - ] - }, - "nullable": [] - }, - "hash": "5db868e03dc6901a0afa06f82a37a1a04821495487a80595cc9b523dac6ac8e9" -} diff --git a/prover/crates/lib/prover_dal/.sqlx/query-67f5f3a015dc478f02f4f701c90d0fc9ac9a7f3dce2ba48c2d0e6f38b6ba455a.json b/prover/crates/lib/prover_dal/.sqlx/query-67f5f3a015dc478f02f4f701c90d0fc9ac9a7f3dce2ba48c2d0e6f38b6ba455a.json deleted file mode 100644 index 29838881a52..00000000000 --- a/prover/crates/lib/prover_dal/.sqlx/query-67f5f3a015dc478f02f4f701c90d0fc9ac9a7f3dce2ba48c2d0e6f38b6ba455a.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE leaf_aggregation_witness_jobs_fri\n SET\n status = 'failed',\n error = $1,\n updated_at = NOW()\n WHERE\n id = $2\n AND status != 'successful'\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Int8" - ] - }, - "nullable": [] - }, - "hash": "67f5f3a015dc478f02f4f701c90d0fc9ac9a7f3dce2ba48c2d0e6f38b6ba455a" -} diff --git a/prover/crates/lib/prover_dal/.sqlx/query-6f20d468efe916f8e92cbf259b37ac83cd32a628d3e01e5cd1949c519683a352.json b/prover/crates/lib/prover_dal/.sqlx/query-6f20d468efe916f8e92cbf259b37ac83cd32a628d3e01e5cd1949c519683a352.json deleted file mode 100644 index 9053a0f5abb..00000000000 --- a/prover/crates/lib/prover_dal/.sqlx/query-6f20d468efe916f8e92cbf259b37ac83cd32a628d3e01e5cd1949c519683a352.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n attempts\n FROM\n recursion_tip_witness_jobs_fri\n WHERE\n l1_batch_number = $1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "attempts", - "type_info": "Int2" - } - ], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [ - false - ] - }, - "hash": "6f20d468efe916f8e92cbf259b37ac83cd32a628d3e01e5cd1949c519683a352" -} diff --git a/prover/crates/lib/prover_dal/.sqlx/query-806b82a9effd885ba537a2a1c7d7227120a8279db1875d26ccae5ee0785f46a9.json b/prover/crates/lib/prover_dal/.sqlx/query-806b82a9effd885ba537a2a1c7d7227120a8279db1875d26ccae5ee0785f46a9.json deleted file mode 100644 index c8e8a7aa603..00000000000 --- a/prover/crates/lib/prover_dal/.sqlx/query-806b82a9effd885ba537a2a1c7d7227120a8279db1875d26ccae5ee0785f46a9.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n attempts\n FROM\n node_aggregation_witness_jobs_fri\n WHERE\n id = $1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "attempts", - "type_info": "Int2" - } - ], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [ - false - ] - }, - "hash": "806b82a9effd885ba537a2a1c7d7227120a8279db1875d26ccae5ee0785f46a9" -} diff --git a/prover/crates/lib/prover_dal/.sqlx/query-b321c5ba22358cbb1fd9c627f1e7b56187686173327498ac75424593547c19c5.json b/prover/crates/lib/prover_dal/.sqlx/query-b321c5ba22358cbb1fd9c627f1e7b56187686173327498ac75424593547c19c5.json deleted file mode 100644 index bdd22927d38..00000000000 --- a/prover/crates/lib/prover_dal/.sqlx/query-b321c5ba22358cbb1fd9c627f1e7b56187686173327498ac75424593547c19c5.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n attempts\n FROM\n scheduler_witness_jobs_fri\n WHERE\n l1_batch_number = $1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "attempts", - "type_info": "Int2" - } - ], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [ - false - ] - }, - "hash": "b321c5ba22358cbb1fd9c627f1e7b56187686173327498ac75424593547c19c5" -} diff --git a/prover/crates/lib/prover_dal/.sqlx/query-b3d71dbe14bcd94131b29b64dcb49b6370c211a7fc24ad03a5f0e327f9d18040.json b/prover/crates/lib/prover_dal/.sqlx/query-b3d71dbe14bcd94131b29b64dcb49b6370c211a7fc24ad03a5f0e327f9d18040.json deleted file mode 100644 index 0ca284a3f57..00000000000 --- a/prover/crates/lib/prover_dal/.sqlx/query-b3d71dbe14bcd94131b29b64dcb49b6370c211a7fc24ad03a5f0e327f9d18040.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n attempts\n FROM\n witness_inputs_fri\n WHERE\n l1_batch_number = $1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "attempts", - "type_info": "Int2" - } - ], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [ - false - ] - }, - "hash": "b3d71dbe14bcd94131b29b64dcb49b6370c211a7fc24ad03a5f0e327f9d18040" -} diff --git a/prover/crates/lib/prover_dal/.sqlx/query-bf06bd08d8ccf67fc00bbc364715263556f258565f79cbb40f5ecc1a4f6402f5.json b/prover/crates/lib/prover_dal/.sqlx/query-bf06bd08d8ccf67fc00bbc364715263556f258565f79cbb40f5ecc1a4f6402f5.json deleted file mode 100644 index c1f9806625d..00000000000 --- a/prover/crates/lib/prover_dal/.sqlx/query-bf06bd08d8ccf67fc00bbc364715263556f258565f79cbb40f5ecc1a4f6402f5.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE recursion_tip_witness_jobs_fri\n SET\n status = 'failed',\n error = $1,\n updated_at = NOW()\n WHERE\n l1_batch_number = $2\n AND status != 'successful'\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Int8" - ] - }, - "nullable": [] - }, - "hash": "bf06bd08d8ccf67fc00bbc364715263556f258565f79cbb40f5ecc1a4f6402f5" -} diff --git a/prover/crates/lib/prover_dal/.sqlx/query-d4949debfe0dc5112204cd196c68b02c44b099e27e3c45c5c810cd5fcd8884ed.json b/prover/crates/lib/prover_dal/.sqlx/query-d4949debfe0dc5112204cd196c68b02c44b099e27e3c45c5c810cd5fcd8884ed.json deleted file mode 100644 index 9121539b317..00000000000 --- a/prover/crates/lib/prover_dal/.sqlx/query-d4949debfe0dc5112204cd196c68b02c44b099e27e3c45c5c810cd5fcd8884ed.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE witness_inputs_fri\n SET\n status = 'failed',\n error = $1,\n updated_at = NOW()\n WHERE\n l1_batch_number = $2\n AND status != 'successful'\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Int8" - ] - }, - "nullable": [] - }, - "hash": "d4949debfe0dc5112204cd196c68b02c44b099e27e3c45c5c810cd5fcd8884ed" -} diff --git a/prover/crates/lib/prover_dal/.sqlx/query-d91c931e2a14cf1183a608d041fc6fadb8e12a9218399d189b4d95e2ca4fcc48.json b/prover/crates/lib/prover_dal/.sqlx/query-e438a4f0c705fcb39e017912ce8e1bb675a86ae14a863fa31eb513af65d606ed.json similarity index 54% rename from prover/crates/lib/prover_dal/.sqlx/query-d91c931e2a14cf1183a608d041fc6fadb8e12a9218399d189b4d95e2ca4fcc48.json rename to prover/crates/lib/prover_dal/.sqlx/query-e438a4f0c705fcb39e017912ce8e1bb675a86ae14a863fa31eb513af65d606ed.json index c353ecf1bad..cf9ff8396ef 100644 --- a/prover/crates/lib/prover_dal/.sqlx/query-d91c931e2a14cf1183a608d041fc6fadb8e12a9218399d189b4d95e2ca4fcc48.json +++ b/prover/crates/lib/prover_dal/.sqlx/query-e438a4f0c705fcb39e017912ce8e1bb675a86ae14a863fa31eb513af65d606ed.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n UPDATE witness_inputs_fri\n SET\n status = 'in_progress',\n attempts = attempts + 1,\n updated_at = NOW(),\n processing_started_at = NOW(),\n picked_by = $3\n WHERE\n l1_batch_number = (\n SELECT\n l1_batch_number\n FROM\n witness_inputs_fri\n WHERE\n l1_batch_number <= $1\n AND status = 'queued'\n AND protocol_version = $2\n AND protocol_version_patch = $4\n ORDER BY\n l1_batch_number ASC\n LIMIT\n 1\n FOR UPDATE\n SKIP LOCKED\n )\n RETURNING\n witness_inputs_fri.l1_batch_number\n ", + "query": "\n UPDATE witness_inputs_fri\n SET\n status = 'in_progress',\n attempts = attempts + 1,\n updated_at = NOW(),\n processing_started_at = NOW(),\n picked_by = $2\n WHERE\n l1_batch_number = (\n SELECT\n l1_batch_number\n FROM\n witness_inputs_fri\n WHERE\n status = 'queued'\n AND protocol_version = $1\n AND protocol_version_patch = $3\n ORDER BY\n l1_batch_number ASC\n LIMIT\n 1\n FOR UPDATE\n SKIP LOCKED\n )\n RETURNING\n witness_inputs_fri.l1_batch_number\n ", "describe": { "columns": [ { @@ -11,7 +11,6 @@ ], "parameters": { "Left": [ - "Int8", "Int4", "Text", "Int4" @@ -21,5 +20,5 @@ false ] }, - "hash": "d91c931e2a14cf1183a608d041fc6fadb8e12a9218399d189b4d95e2ca4fcc48" + "hash": "e438a4f0c705fcb39e017912ce8e1bb675a86ae14a863fa31eb513af65d606ed" } diff --git a/prover/crates/lib/prover_dal/src/fri_witness_generator_dal.rs b/prover/crates/lib/prover_dal/src/fri_witness_generator_dal.rs index c7ba0f60ef3..2040b444044 100644 --- a/prover/crates/lib/prover_dal/src/fri_witness_generator_dal.rs +++ b/prover/crates/lib/prover_dal/src/fri_witness_generator_dal.rs @@ -76,7 +76,6 @@ impl FriWitnessGeneratorDal<'_, '_> { /// The blobs arrive from core via prover gateway, as pubdata, this method loads the blobs. pub async fn get_next_basic_circuit_witness_job( &mut self, - last_l1_batch_to_process: u32, protocol_version: ProtocolSemanticVersion, picked_by: &str, ) -> Option { @@ -88,7 +87,7 @@ impl FriWitnessGeneratorDal<'_, '_> { attempts = attempts + 1, updated_at = NOW(), processing_started_at = NOW(), - picked_by = $3 + picked_by = $2 WHERE l1_batch_number = ( SELECT @@ -96,10 +95,9 @@ impl FriWitnessGeneratorDal<'_, '_> { FROM witness_inputs_fri WHERE - l1_batch_number <= $1 - AND status = 'queued' - AND protocol_version = $2 - AND protocol_version_patch = $4 + status = 'queued' + AND protocol_version = $1 + AND protocol_version_patch = $3 ORDER BY l1_batch_number ASC LIMIT @@ -110,7 +108,6 @@ impl FriWitnessGeneratorDal<'_, '_> { RETURNING witness_inputs_fri.l1_batch_number "#, - i64::from(last_l1_batch_to_process), protocol_version.minor as i32, picked_by, protocol_version.patch.0 as i32, @@ -121,28 +118,6 @@ impl FriWitnessGeneratorDal<'_, '_> { .map(|row| L1BatchNumber(row.l1_batch_number as u32)) } - pub async fn get_basic_circuit_witness_job_attempts( - &mut self, - l1_batch_number: L1BatchNumber, - ) -> sqlx::Result> { - let attempts = sqlx::query!( - r#" - SELECT - attempts - FROM - witness_inputs_fri - WHERE - l1_batch_number = $1 - "#, - i64::from(l1_batch_number.0) - ) - .fetch_optional(self.storage.conn()) - .await? - .map(|row| row.attempts as u32); - - Ok(attempts) - } - pub async fn mark_witness_job( &mut self, status: FriWitnessJobStatus, @@ -189,46 +164,6 @@ impl FriWitnessGeneratorDal<'_, '_> { .unwrap(); } - pub async fn mark_witness_job_failed(&mut self, error: &str, block_number: L1BatchNumber) { - sqlx::query!( - r#" - UPDATE witness_inputs_fri - SET - status = 'failed', - error = $1, - updated_at = NOW() - WHERE - l1_batch_number = $2 - AND status != 'successful' - "#, - error, - i64::from(block_number.0) - ) - .execute(self.storage.conn()) - .await - .unwrap(); - } - - pub async fn mark_leaf_aggregation_job_failed(&mut self, error: &str, id: u32) { - sqlx::query!( - r#" - UPDATE leaf_aggregation_witness_jobs_fri - SET - status = 'failed', - error = $1, - updated_at = NOW() - WHERE - id = $2 - AND status != 'successful' - "#, - error, - i64::from(id) - ) - .execute(self.storage.conn()) - .await - .unwrap(); - } - pub async fn mark_leaf_aggregation_as_successful(&mut self, id: u32, time_taken: Duration) { sqlx::query!( r#" @@ -481,29 +416,6 @@ impl FriWitnessGeneratorDal<'_, '_> { }) } - pub async fn get_leaf_aggregation_job_attempts( - &mut self, - id: u32, - ) -> sqlx::Result> { - let attempts = sqlx::query!( - r#" - SELECT - attempts - FROM - leaf_aggregation_witness_jobs_fri - WHERE - id = $1 - "#, - i64::from(id) - ) - .fetch_optional(self.storage.conn()) - .await - .unwrap() - .map(|row| row.attempts as u32); - - Ok(attempts) - } - async fn prover_job_ids_for( &mut self, block_number: L1BatchNumber, @@ -674,49 +586,6 @@ impl FriWitnessGeneratorDal<'_, '_> { }) } - pub async fn get_node_aggregation_job_attempts( - &mut self, - id: u32, - ) -> sqlx::Result> { - let attempts = sqlx::query!( - r#" - SELECT - attempts - FROM - node_aggregation_witness_jobs_fri - WHERE - id = $1 - "#, - i64::from(id) - ) - .fetch_optional(self.storage.conn()) - .await - .unwrap() - .map(|row| row.attempts as u32); - - Ok(attempts) - } - - pub async fn mark_node_aggregation_job_failed(&mut self, error: &str, id: u32) { - sqlx::query!( - r#" - UPDATE node_aggregation_witness_jobs_fri - SET - status = 'failed', - error = $1, - updated_at = NOW() - WHERE - id = $2 - AND status != 'successful' - "#, - error, - i64::from(id) - ) - .execute(self.storage.conn()) - .await - .unwrap(); - } - pub async fn mark_node_aggregation_as_successful(&mut self, id: u32, time_taken: Duration) { sqlx::query!( r#" @@ -1241,46 +1110,42 @@ impl FriWitnessGeneratorDal<'_, '_> { .map(|row| L1BatchNumber(row.l1_batch_number as u32)) } - pub async fn get_recursion_tip_witness_job_attempts( + pub async fn get_witness_job_attempts( &mut self, - l1_batch_number: L1BatchNumber, + job_id: u32, + aggregation_round: AggregationRound, ) -> sqlx::Result> { - let attempts = sqlx::query!( - r#" - SELECT - attempts - FROM - recursion_tip_witness_jobs_fri - WHERE - l1_batch_number = $1 - "#, - l1_batch_number.0 as i64 - ) - .fetch_optional(self.storage.conn()) - .await? - .map(|row| row.attempts as u32); + let table = match aggregation_round { + AggregationRound::BasicCircuits => "witness_inputs_fri", + AggregationRound::LeafAggregation => "leaf_aggregation_witness_jobs_fri", + AggregationRound::NodeAggregation => "node_aggregation_witness_jobs_fri", + AggregationRound::RecursionTip => "recursion_tip_witness_jobs_fri", + AggregationRound::Scheduler => "scheduler_witness_jobs_fri", + }; - Ok(attempts) - } + let job_id_column = match aggregation_round { + AggregationRound::BasicCircuits => "l1_batch_number", + AggregationRound::LeafAggregation => "id", + AggregationRound::NodeAggregation => "id", + AggregationRound::RecursionTip => "l1_batch_number", + AggregationRound::Scheduler => "l1_batch_number ", + }; - pub async fn get_scheduler_witness_job_attempts( - &mut self, - l1_batch_number: L1BatchNumber, - ) -> sqlx::Result> { - let attempts = sqlx::query!( + let query = format!( r#" SELECT attempts FROM - scheduler_witness_jobs_fri + {table} WHERE - l1_batch_number = $1 + {job_id_column} = {job_id} "#, - i64::from(l1_batch_number.0) - ) - .fetch_optional(self.storage.conn()) - .await? - .map(|row| row.attempts as u32); + ); + + let attempts = sqlx::query(&query) + .fetch_optional(self.storage.conn()) + .await? + .map(|row| row.get::("attempts") as u32); Ok(attempts) } @@ -1331,48 +1196,45 @@ impl FriWitnessGeneratorDal<'_, '_> { .unwrap(); } - pub async fn mark_recursion_tip_job_failed( + pub async fn mark_witness_job_failed( &mut self, error: &str, - l1_batch_number: L1BatchNumber, + job_id: u32, + aggregation_round: AggregationRound, ) { - sqlx::query!( - r#" - UPDATE recursion_tip_witness_jobs_fri - SET - status = 'failed', - error = $1, - updated_at = NOW() - WHERE - l1_batch_number = $2 - AND status != 'successful' - "#, - error, - l1_batch_number.0 as i64 - ) - .execute(self.storage.conn()) - .await - .unwrap(); - } + let table = match aggregation_round { + AggregationRound::BasicCircuits => "witness_inputs_fri", + AggregationRound::LeafAggregation => "leaf_aggregation_witness_jobs_fri", + AggregationRound::NodeAggregation => "node_aggregation_witness_jobs_fri", + AggregationRound::RecursionTip => "recursion_tip_witness_jobs_fri", + AggregationRound::Scheduler => "scheduler_witness_jobs_fri", + }; - pub async fn mark_scheduler_job_failed(&mut self, error: &str, block_number: L1BatchNumber) { - sqlx::query!( + let job_id_column = match aggregation_round { + AggregationRound::BasicCircuits => "l1_batch_number", + AggregationRound::LeafAggregation => "id", + AggregationRound::NodeAggregation => "id", + AggregationRound::RecursionTip => "l1_batch_number", + AggregationRound::Scheduler => "l1_batch_number ", + }; + + let query = format!( r#" - UPDATE scheduler_witness_jobs_fri + UPDATE {table} SET status = 'failed', - error = $1, + error = {error}, updated_at = NOW() WHERE - l1_batch_number = $2 - AND status != 'successful' + {job_id_column} = {job_id} + AND status != 'successful "#, - error, - i64::from(block_number.0) - ) - .execute(self.storage.conn()) - .await - .unwrap(); + ); + + sqlx::query(&query) + .execute(self.storage.conn()) + .await + .unwrap(); } pub async fn get_witness_jobs_stats(