From 1696e6e45abe8c32bf3f02e87ab526997adf8399 Mon Sep 17 00:00:00 2001 From: Alexander Melnikov Date: Thu, 15 Aug 2024 19:34:37 +0200 Subject: [PATCH 1/8] feat: enable CORS for local reth (#2665) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ This PR adds the `--http.corsdomain "*"` option to Docker Compose files to enable CORS (Cross-Origin Resource Sharing) on locally running Reth nodes. ## Why ❔ Enables web applications like dapp-portal and block-explorer to make requests to the local RPC endpoint. Without CORS enabled, browser policies block these requests, causing the applications to fail. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint` --- docker-compose-cpu-runner.yml | 2 +- docker-compose-gpu-runner-cuda-12-0.yml | 2 +- docker-compose-gpu-runner.yml | 2 +- docker-compose.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docker-compose-cpu-runner.yml b/docker-compose-cpu-runner.yml index 38ae8788940..08d01390d77 100644 --- a/docker-compose-cpu-runner.yml +++ b/docker-compose-cpu-runner.yml @@ -11,7 +11,7 @@ services: source: ./etc/reth/chaindata target: /chaindata - command: node --dev --datadir /rethdata --http --http.addr 0.0.0.0 --http.port 8545 --dev.block-time 300ms --chain /chaindata/reth_config + command: node --dev --datadir /rethdata --http --http.addr 0.0.0.0 --http.port 8545 --http.corsdomain "*" --dev.block-time 300ms --chain /chaindata/reth_config ports: - 127.0.0.1:8545:8545 diff --git a/docker-compose-gpu-runner-cuda-12-0.yml b/docker-compose-gpu-runner-cuda-12-0.yml index eedacee81d6..92a7b0b0088 100644 --- a/docker-compose-gpu-runner-cuda-12-0.yml +++ b/docker-compose-gpu-runner-cuda-12-0.yml @@ -11,7 +11,7 @@ services: source: ./etc/reth/chaindata target: /chaindata - command: node --dev --datadir /rethdata --http --http.addr 0.0.0.0 --http.port 8545 --dev.block-time 300ms --chain /chaindata/reth_config + command: node --dev --datadir /rethdata --http --http.addr 0.0.0.0 --http.port 8545 --http.corsdomain "*" --dev.block-time 300ms --chain /chaindata/reth_config ports: - 127.0.0.1:8545:8545 diff --git a/docker-compose-gpu-runner.yml b/docker-compose-gpu-runner.yml index 74afb598539..bbd61715842 100644 --- a/docker-compose-gpu-runner.yml +++ b/docker-compose-gpu-runner.yml @@ -11,7 +11,7 @@ services: source: ./etc/reth/chaindata target: /chaindata - command: node --dev --datadir /rethdata --http --http.addr 0.0.0.0 --http.port 8545 --dev.block-time 300ms --chain /chaindata/reth_config + command: node --dev --datadir /rethdata --http --http.addr 0.0.0.0 --http.port 8545 --http.corsdomain "*" --dev.block-time 300ms --chain /chaindata/reth_config ports: - 127.0.0.1:8545:8545 diff --git a/docker-compose.yml b/docker-compose.yml index 116cc347818..68feb0769c2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,7 @@ services: source: ./etc/reth/chaindata target: /chaindata - command: node --dev --datadir /rethdata --http --http.addr 0.0.0.0 --http.port 8545 --dev.block-time 300ms --chain /chaindata/reth_config + command: node --dev --datadir /rethdata --http --http.addr 0.0.0.0 --http.port 8545 --http.corsdomain "*" --dev.block-time 300ms --chain /chaindata/reth_config ports: - 127.0.0.1:8545:8545 From 47a082b3312cae7aa0f2317a45a26fa5f22d043c Mon Sep 17 00:00:00 2001 From: Alex Ostrovski Date: Fri, 16 Aug 2024 10:15:55 +0300 Subject: [PATCH 2/8] feat(db): Allow creating owned Postgres connections (#2654) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ - Changes `Connection` so that it has `'static` lifetime if created from a pool (i.e., when it is non-transactional). - Simplifies `ReadStorageFactory` and `MainBatchExecutor` accordingly. ## Why ❔ Reduces complexity. `'static` connections can be sent to a Tokio task etc., meaning improved DevEx. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --- core/lib/db_connection/src/connection.rs | 35 ++-- core/lib/db_connection/src/connection_pool.rs | 8 +- core/lib/state/src/lib.rs | 3 +- core/lib/state/src/storage_factory.rs | 79 +++------ core/node/api_server/src/web3/state.rs | 2 +- .../src/batch_executor/main_executor.rs | 153 +++++++++--------- .../tests/read_storage_factory.rs | 4 +- .../state_keeper/src/state_keeper_storage.rs | 29 ++-- core/node/vm_runner/src/storage.rs | 20 +-- core/node/vm_runner/src/tests/mod.rs | 6 +- core/node/vm_runner/src/tests/storage.rs | 6 +- 11 files changed, 158 insertions(+), 187 deletions(-) diff --git a/core/lib/db_connection/src/connection.rs b/core/lib/db_connection/src/connection.rs index 22a63765b3b..e178395b333 100644 --- a/core/lib/db_connection/src/connection.rs +++ b/core/lib/db_connection/src/connection.rs @@ -1,10 +1,11 @@ use std::{ collections::HashMap, fmt, io, + marker::PhantomData, panic::Location, sync::{ atomic::{AtomicUsize, Ordering}, - Mutex, + Arc, Mutex, Weak, }, time::{Instant, SystemTime}, }; @@ -98,14 +99,14 @@ impl TracedConnections { } } -struct PooledConnection<'a> { +struct PooledConnection { connection: PoolConnection, tags: Option, created_at: Instant, - traced: Option<(&'a TracedConnections, usize)>, + traced: (Weak, usize), } -impl fmt::Debug for PooledConnection<'_> { +impl fmt::Debug for PooledConnection { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter .debug_struct("PooledConnection") @@ -115,7 +116,7 @@ impl fmt::Debug for PooledConnection<'_> { } } -impl Drop for PooledConnection<'_> { +impl Drop for PooledConnection { fn drop(&mut self) { if let Some(tags) = &self.tags { let lifetime = self.created_at.elapsed(); @@ -132,15 +133,17 @@ impl Drop for PooledConnection<'_> { ); } } - if let Some((connections, id)) = self.traced { - connections.mark_as_dropped(id); + + let (traced_connections, id) = &self.traced; + if let Some(connections) = traced_connections.upgrade() { + connections.mark_as_dropped(*id); } } } #[derive(Debug)] enum ConnectionInner<'a> { - Pooled(PooledConnection<'a>), + Pooled(PooledConnection), Transaction { transaction: Transaction<'a, Postgres>, tags: Option<&'a ConnectionTags>, @@ -156,7 +159,7 @@ pub trait DbMarker: 'static + Send + Sync + Clone {} #[derive(Debug)] pub struct Connection<'a, DB: DbMarker> { inner: ConnectionInner<'a>, - _marker: std::marker::PhantomData, + _marker: PhantomData, } impl<'a, DB: DbMarker> Connection<'a, DB> { @@ -166,21 +169,23 @@ impl<'a, DB: DbMarker> Connection<'a, DB> { pub(crate) fn from_pool( connection: PoolConnection, tags: Option, - traced_connections: Option<&'a TracedConnections>, + traced_connections: Option<&Arc>, ) -> Self { let created_at = Instant::now(); let inner = ConnectionInner::Pooled(PooledConnection { connection, tags, created_at, - traced: traced_connections.map(|connections| { + traced: if let Some(connections) = traced_connections { let id = connections.acquire(tags, created_at); - (connections, id) - }), + (Arc::downgrade(connections), id) + } else { + (Weak::new(), 0) + }, }); Self { inner, - _marker: Default::default(), + _marker: PhantomData, } } @@ -196,7 +201,7 @@ impl<'a, DB: DbMarker> Connection<'a, DB> { }; Ok(Connection { inner, - _marker: Default::default(), + _marker: PhantomData, }) } diff --git a/core/lib/db_connection/src/connection_pool.rs b/core/lib/db_connection/src/connection_pool.rs index 78d9184222d..7cf29632b7d 100644 --- a/core/lib/db_connection/src/connection_pool.rs +++ b/core/lib/db_connection/src/connection_pool.rs @@ -347,7 +347,7 @@ impl ConnectionPool { /// /// This method is intended to be used in crucial contexts, where the /// database access is must-have (e.g. block committer). - pub async fn connection(&self) -> DalResult> { + pub async fn connection(&self) -> DalResult> { self.connection_inner(None).await } @@ -361,7 +361,7 @@ impl ConnectionPool { pub fn connection_tagged( &self, requester: &'static str, - ) -> impl Future>> + '_ { + ) -> impl Future>> + '_ { let location = Location::caller(); async move { let tags = ConnectionTags { @@ -375,7 +375,7 @@ impl ConnectionPool { async fn connection_inner( &self, tags: Option, - ) -> DalResult> { + ) -> DalResult> { let acquire_latency = CONNECTION_METRICS.acquire.start(); let conn = self.acquire_connection_retried(tags.as_ref()).await?; let elapsed = acquire_latency.observe(); @@ -386,7 +386,7 @@ impl ConnectionPool { Ok(Connection::::from_pool( conn, tags, - self.traced_connections.as_deref(), + self.traced_connections.as_ref(), )) } diff --git a/core/lib/state/src/lib.rs b/core/lib/state/src/lib.rs index c386426d066..ad5361c4608 100644 --- a/core/lib/state/src/lib.rs +++ b/core/lib/state/src/lib.rs @@ -20,8 +20,7 @@ pub use self::{ }, shadow_storage::ShadowStorage, storage_factory::{ - BatchDiff, OwnedPostgresStorage, OwnedStorage, PgOrRocksdbStorage, ReadStorageFactory, - RocksdbWithMemory, + BatchDiff, OwnedStorage, PgOrRocksdbStorage, ReadStorageFactory, RocksdbWithMemory, }, }; diff --git a/core/lib/state/src/storage_factory.rs b/core/lib/state/src/storage_factory.rs index 4792200a463..e2b5275c48d 100644 --- a/core/lib/state/src/storage_factory.rs +++ b/core/lib/state/src/storage_factory.rs @@ -10,6 +10,9 @@ use zksync_vm_interface::storage::ReadStorage; use crate::{PostgresStorage, RocksdbStorage, RocksdbStorageBuilder, StateKeeperColumnFamily}; +/// Storage with a static lifetime that can be sent to Tokio tasks etc. +pub type OwnedStorage = PgOrRocksdbStorage<'static>; + /// Factory that can produce storage instances on demand. The storage type is encapsulated as a type param /// (mostly for testing purposes); the default is [`OwnedStorage`]. #[async_trait] @@ -35,8 +38,9 @@ impl ReadStorageFactory for ConnectionPool { _stop_receiver: &watch::Receiver, l1_batch_number: L1BatchNumber, ) -> anyhow::Result> { - let storage = OwnedPostgresStorage::new(self.clone(), l1_batch_number); - Ok(Some(storage.into())) + let connection = self.connection().await?; + let storage = OwnedStorage::postgres(connection, l1_batch_number).await?; + Ok(Some(storage)) } } @@ -61,31 +65,29 @@ pub struct RocksdbWithMemory { pub batch_diffs: Vec, } -/// Owned Postgres-backed VM storage for a certain L1 batch. +/// A [`ReadStorage`] implementation that uses either [`PostgresStorage`] or [`RocksdbStorage`] +/// underneath. #[derive(Debug)] -pub struct OwnedPostgresStorage { - connection_pool: ConnectionPool, - l1_batch_number: L1BatchNumber, +pub enum PgOrRocksdbStorage<'a> { + /// Implementation over a Postgres connection. + Postgres(PostgresStorage<'a>), + /// Implementation over a RocksDB cache instance. + Rocksdb(RocksdbStorage), + /// Implementation over a RocksDB cache instance with in-memory DB diffs. + RocksdbWithMemory(RocksdbWithMemory), } -impl OwnedPostgresStorage { - /// Creates a VM storage for the specified batch number. - pub fn new(connection_pool: ConnectionPool, l1_batch_number: L1BatchNumber) -> Self { - Self { - connection_pool, - l1_batch_number, - } - } - - /// Returns a [`ReadStorage`] implementation backed by Postgres +impl PgOrRocksdbStorage<'static> { + /// Creates a Postgres-based storage. Because of the `'static` lifetime requirement, `connection` must be + /// non-transactional. /// /// # Errors /// - /// Propagates Postgres errors. - pub async fn borrow(&self) -> anyhow::Result> { - let l1_batch_number = self.l1_batch_number; - let mut connection = self.connection_pool.connection().await?; - + /// Propagates Postgres I/O errors. + pub async fn postgres( + mut connection: Connection<'static, Core>, + l1_batch_number: L1BatchNumber, + ) -> anyhow::Result { let l2_block_number = if let Some((_, l2_block_number)) = connection .blocks_dal() .get_l2_block_range_of_l1_batch(l1_batch_number) @@ -114,42 +116,7 @@ impl OwnedPostgresStorage { .into(), ) } -} - -/// Owned version of [`PgOrRocksdbStorage`]. It is thus possible to send to blocking tasks for VM execution. -#[derive(Debug)] -pub enum OwnedStorage { - /// Readily initialized storage with a static lifetime. - Static(PgOrRocksdbStorage<'static>), - /// Storage that must be `borrow()`ed from. - Lending(OwnedPostgresStorage), -} -impl From for OwnedStorage { - fn from(storage: OwnedPostgresStorage) -> Self { - Self::Lending(storage) - } -} - -impl From> for OwnedStorage { - fn from(storage: PgOrRocksdbStorage<'static>) -> Self { - Self::Static(storage) - } -} - -/// A [`ReadStorage`] implementation that uses either [`PostgresStorage`] or [`RocksdbStorage`] -/// underneath. -#[derive(Debug)] -pub enum PgOrRocksdbStorage<'a> { - /// Implementation over a Postgres connection. - Postgres(PostgresStorage<'a>), - /// Implementation over a RocksDB cache instance. - Rocksdb(RocksdbStorage), - /// Implementation over a RocksDB cache instance with in-memory DB diffs. - RocksdbWithMemory(RocksdbWithMemory), -} - -impl PgOrRocksdbStorage<'static> { /// Catches up RocksDB synchronously (i.e. assumes the gap is small) and /// returns a [`ReadStorage`] implementation backed by caught-up RocksDB. /// diff --git a/core/node/api_server/src/web3/state.rs b/core/node/api_server/src/web3/state.rs index b0e74706e52..5c8b47dabeb 100644 --- a/core/node/api_server/src/web3/state.rs +++ b/core/node/api_server/src/web3/state.rs @@ -287,7 +287,7 @@ impl RpcState { #[track_caller] pub(crate) fn acquire_connection( &self, - ) -> impl Future, Web3Error>> + '_ { + ) -> impl Future, Web3Error>> + '_ { self.connection_pool .connection_tagged("api") .map_err(|err| err.generalize().into()) diff --git a/core/node/state_keeper/src/batch_executor/main_executor.rs b/core/node/state_keeper/src/batch_executor/main_executor.rs index b4090460116..43f1b8e59b1 100644 --- a/core/node/state_keeper/src/batch_executor/main_executor.rs +++ b/core/node/state_keeper/src/batch_executor/main_executor.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use anyhow::Context as _; use once_cell::sync::OnceCell; -use tokio::{runtime::Handle, sync::mpsc}; +use tokio::sync::mpsc; use zksync_multivm::{ interface::{ storage::{ReadStorage, StorageView}, @@ -14,7 +14,6 @@ use zksync_multivm::{ MultiVMTracer, VmInstance, }; use zksync_shared_metrics::{InteractionType, TxStage, APP_METRICS}; -use zksync_state::OwnedStorage; use zksync_types::{vm::FastVmMode, vm_trace::Call, Transaction}; use super::{BatchExecutor, BatchExecutorHandle, Command, TxExecutionResult}; @@ -57,10 +56,10 @@ impl MainBatchExecutor { } } -impl BatchExecutor for MainBatchExecutor { +impl BatchExecutor for MainBatchExecutor { fn init_batch( &mut self, - storage: OwnedStorage, + storage: S, l1_batch_params: L1BatchEnv, system_env: SystemEnv, ) -> BatchExecutorHandle { @@ -74,20 +73,19 @@ impl BatchExecutor for MainBatchExecutor { commands: commands_receiver, }; - let handle = tokio::task::spawn_blocking(move || { - let storage = match storage { - OwnedStorage::Static(storage) => storage, - OwnedStorage::Lending(ref storage) => Handle::current() - .block_on(storage.borrow()) - .context("failed accessing state keeper storage")?, - }; - executor.run(storage, l1_batch_params, system_env); - anyhow::Ok(()) - }); + let handle = + tokio::task::spawn_blocking(move || executor.run(storage, l1_batch_params, system_env)); BatchExecutorHandle::from_raw(handle, commands_sender) } } +#[derive(Debug)] +struct TransactionOutput { + tx_result: VmExecutionResultAndLogs, + compressed_bytecodes: Vec, + calls: Vec, +} + /// Implementation of the "primary" (non-test) batch executor. /// Upon launch, it initializes the VM object with provided block context and properties, and keeps invoking the commands /// sent to it one by one until the batch is finished. @@ -105,13 +103,13 @@ struct CommandReceiver { impl CommandReceiver { pub(super) fn run( mut self, - secondary_storage: S, + storage: S, l1_batch_params: L1BatchEnv, system_env: SystemEnv, - ) { + ) -> anyhow::Result<()> { tracing::info!("Starting executing L1 batch #{}", &l1_batch_params.number); - let storage_view = StorageView::new(secondary_storage).to_rc_ptr(); + let storage_view = StorageView::new(storage).to_rc_ptr(); let mut vm = VmInstance::maybe_fast( l1_batch_params, system_env, @@ -122,7 +120,9 @@ impl CommandReceiver { while let Some(cmd) = self.commands.blocking_recv() { match cmd { Command::ExecuteTx(tx, resp) => { - let result = self.execute_tx(&tx, &mut vm); + let result = self + .execute_tx(&tx, &mut vm) + .with_context(|| format!("fatal error executing transaction {tx:?}"))?; if resp.send(result).is_err() { break; } @@ -140,7 +140,7 @@ impl CommandReceiver { } } Command::FinishBatch(resp) => { - let vm_block_result = self.finish_batch(&mut vm); + let vm_block_result = self.finish_batch(&mut vm)?; if resp.send(vm_block_result).is_err() { break; } @@ -152,28 +152,28 @@ impl CommandReceiver { .observe(metrics.time_spent_on_get_value); EXECUTOR_METRICS.batch_storage_interaction_duration[&InteractionType::SetValue] .observe(metrics.time_spent_on_set_value); - return; + return Ok(()); } Command::FinishBatchWithCache(resp) => { - let vm_block_result = self.finish_batch(&mut vm); + let vm_block_result = self.finish_batch(&mut vm)?; let cache = (*storage_view).borrow().cache(); if resp.send((vm_block_result, cache)).is_err() { break; } - - return; + return Ok(()); } } } // State keeper can exit because of stop signal, so it's OK to exit mid-batch. tracing::info!("State keeper exited with an unfinished L1 batch"); + Ok(()) } fn execute_tx( &self, tx: &Transaction, vm: &mut VmInstance, - ) -> TxExecutionResult { + ) -> anyhow::Result { // Executing a next transaction means that a previous transaction was either rolled back (in which case its snapshot // was already removed), or that we build on top of it (in which case, it can be removed now). vm.pop_snapshot_no_rollback(); @@ -182,33 +182,38 @@ impl CommandReceiver { // Execute the transaction. let latency = KEEPER_METRICS.tx_execution_time[&TxExecutionStage::Execution].start(); - let (tx_result, compressed_bytecodes, call_tracer_result) = - if self.optional_bytecode_compression { - self.execute_tx_in_vm_with_optional_compression(tx, vm) - } else { - self.execute_tx_in_vm(tx, vm) - }; + let output = if self.optional_bytecode_compression { + self.execute_tx_in_vm_with_optional_compression(tx, vm)? + } else { + self.execute_tx_in_vm(tx, vm)? + }; latency.observe(); APP_METRICS.processed_txs[&TxStage::StateKeeper].inc(); APP_METRICS.processed_l1_txs[&TxStage::StateKeeper].inc_by(tx.is_l1().into()); + let TransactionOutput { + tx_result, + compressed_bytecodes, + calls, + } = output; + if let ExecutionResult::Halt { reason } = tx_result.result { - return match reason { + return Ok(match reason { Halt::BootloaderOutOfGas => TxExecutionResult::BootloaderOutOfGasForTx, _ => TxExecutionResult::RejectedByVm { reason }, - }; + }); } let tx_metrics = ExecutionMetricsForCriteria::new(Some(tx), &tx_result); let gas_remaining = vm.gas_remaining(); - TxExecutionResult::Success { + Ok(TxExecutionResult::Success { tx_result: Box::new(tx_result), tx_metrics: Box::new(tx_metrics), compressed_bytecodes, - call_tracer_result, + call_tracer_result: calls, gas_remaining, - } + }) } fn rollback_last_tx(&self, vm: &mut VmInstance) { @@ -228,19 +233,18 @@ impl CommandReceiver { fn finish_batch( &self, vm: &mut VmInstance, - ) -> FinishedL1Batch { + ) -> anyhow::Result { // The vm execution was paused right after the last transaction was executed. // There is some post-processing work that the VM needs to do before the block is fully processed. let result = vm.finish_batch(); - if result.block_tip_execution_result.result.is_failed() { - panic!( - "VM must not fail when finalizing block: {:#?}", - result.block_tip_execution_result.result - ); - } + anyhow::ensure!( + !result.block_tip_execution_result.result.is_failed(), + "VM must not fail when finalizing block: {:#?}", + result.block_tip_execution_result.result + ); BATCH_TIP_METRICS.observe(&result.block_tip_execution_result); - result + Ok(result) } /// Attempts to execute transaction with or without bytecode compression. @@ -249,11 +253,7 @@ impl CommandReceiver { &self, tx: &Transaction, vm: &mut VmInstance, - ) -> ( - VmExecutionResultAndLogs, - Vec, - Vec, - ) { + ) -> anyhow::Result { // Note, that the space where we can put the calldata for compressing transactions // is limited and the transactions do not pay for taking it. // In order to not let the accounts spam the space of compressed bytecodes with bytecodes @@ -270,16 +270,20 @@ impl CommandReceiver { vec![] }; - if let (Ok(()), result) = + if let (Ok(()), tx_result) = vm.inspect_transaction_with_bytecode_compression(tracer.into(), tx.clone(), true) { let compressed_bytecodes = vm.get_last_tx_compressed_bytecodes(); - let trace = Arc::try_unwrap(call_tracer_result) - .unwrap() + let calls = Arc::try_unwrap(call_tracer_result) + .map_err(|_| anyhow::anyhow!("failed extracting call traces"))? .take() .unwrap_or_default(); - return (result, compressed_bytecodes, trace); + return Ok(TransactionOutput { + tx_result, + compressed_bytecodes, + calls, + }); } // Roll back to the snapshot just before the transaction execution taken in `Self::execute_tx()` @@ -294,20 +298,22 @@ impl CommandReceiver { vec![] }; - let result = + let (compression_result, tx_result) = vm.inspect_transaction_with_bytecode_compression(tracer.into(), tx.clone(), false); - result - .0 - .expect("Compression can't fail if we don't apply it"); + compression_result.context("compression failed when it wasn't applied")?; let compressed_bytecodes = vm.get_last_tx_compressed_bytecodes(); // TODO implement tracer manager which will be responsible - // for collecting result from all tracers and save it to the database - let trace = Arc::try_unwrap(call_tracer_result) - .unwrap() + // for collecting result from all tracers and save it to the database + let calls = Arc::try_unwrap(call_tracer_result) + .map_err(|_| anyhow::anyhow!("failed extracting call traces"))? .take() .unwrap_or_default(); - (result.1, compressed_bytecodes, trace) + Ok(TransactionOutput { + tx_result, + compressed_bytecodes, + calls, + }) } /// Attempts to execute transaction with mandatory bytecode compression. @@ -316,11 +322,7 @@ impl CommandReceiver { &self, tx: &Transaction, vm: &mut VmInstance, - ) -> ( - VmExecutionResultAndLogs, - Vec, - Vec, - ) { + ) -> anyhow::Result { let call_tracer_result = Arc::new(OnceCell::default()); let tracer = if self.save_call_traces { vec![CallTracer::new(call_tracer_result.clone()).into_tracer_pointer()] @@ -328,22 +330,29 @@ impl CommandReceiver { vec![] }; - let (published_bytecodes, mut result) = + let (published_bytecodes, mut tx_result) = vm.inspect_transaction_with_bytecode_compression(tracer.into(), tx.clone(), true); if published_bytecodes.is_ok() { let compressed_bytecodes = vm.get_last_tx_compressed_bytecodes(); - - let trace = Arc::try_unwrap(call_tracer_result) - .unwrap() + let calls = Arc::try_unwrap(call_tracer_result) + .map_err(|_| anyhow::anyhow!("failed extracting call traces"))? .take() .unwrap_or_default(); - (result, compressed_bytecodes, trace) + Ok(TransactionOutput { + tx_result, + compressed_bytecodes, + calls, + }) } else { // Transaction failed to publish bytecodes, we reject it so initiator doesn't pay fee. - result.result = ExecutionResult::Halt { + tx_result.result = ExecutionResult::Halt { reason: Halt::FailedToPublishCompressedBytecodes, }; - (result, Default::default(), Default::default()) + Ok(TransactionOutput { + tx_result, + compressed_bytecodes: vec![], + calls: vec![], + }) } } } diff --git a/core/node/state_keeper/src/batch_executor/tests/read_storage_factory.rs b/core/node/state_keeper/src/batch_executor/tests/read_storage_factory.rs index 838b9240767..e0096cd0417 100644 --- a/core/node/state_keeper/src/batch_executor/tests/read_storage_factory.rs +++ b/core/node/state_keeper/src/batch_executor/tests/read_storage_factory.rs @@ -2,7 +2,7 @@ use anyhow::Context; use async_trait::async_trait; use tokio::sync::watch; use zksync_dal::{ConnectionPool, Core}; -use zksync_state::{OwnedStorage, PgOrRocksdbStorage, ReadStorageFactory, RocksdbStorage}; +use zksync_state::{OwnedStorage, ReadStorageFactory, RocksdbStorage}; use zksync_types::L1BatchNumber; #[derive(Debug, Clone)] @@ -33,7 +33,7 @@ impl ReadStorageFactory for RocksdbStorageFactory { else { return Ok(None); }; - Ok(Some(PgOrRocksdbStorage::Rocksdb(rocksdb_storage).into())) + Ok(Some(OwnedStorage::Rocksdb(rocksdb_storage))) } } diff --git a/core/node/state_keeper/src/state_keeper_storage.rs b/core/node/state_keeper/src/state_keeper_storage.rs index fbda064b5d7..1b35f8ef73d 100644 --- a/core/node/state_keeper/src/state_keeper_storage.rs +++ b/core/node/state_keeper/src/state_keeper_storage.rs @@ -5,8 +5,7 @@ use async_trait::async_trait; use tokio::sync::watch; use zksync_dal::{ConnectionPool, Core}; use zksync_state::{ - AsyncCatchupTask, OwnedPostgresStorage, OwnedStorage, PgOrRocksdbStorage, ReadStorageFactory, - RocksdbCell, RocksdbStorageOptions, + AsyncCatchupTask, OwnedStorage, ReadStorageFactory, RocksdbCell, RocksdbStorageOptions, }; use zksync_types::L1BatchNumber; @@ -58,24 +57,20 @@ impl ReadStorageFactory for AsyncRocksdbCache { self.rocksdb_cell.get() }; - if let Some(rocksdb) = rocksdb { - let mut connection = self - .pool - .connection_tagged("state_keeper") - .await - .context("Failed getting a Postgres connection")?; - let storage = PgOrRocksdbStorage::rocksdb( - &mut connection, - rocksdb, - stop_receiver, - l1_batch_number, - ) + let mut connection = self + .pool + .connection_tagged("state_keeper") .await - .context("Failed accessing RocksDB storage")?; - Ok(storage.map(Into::into)) + .context("Failed getting a Postgres connection")?; + if let Some(rocksdb) = rocksdb { + let storage = + OwnedStorage::rocksdb(&mut connection, rocksdb, stop_receiver, l1_batch_number) + .await + .context("Failed accessing RocksDB storage")?; + Ok(storage) } else { Ok(Some( - OwnedPostgresStorage::new(self.pool.clone(), l1_batch_number).into(), + OwnedStorage::postgres(connection, l1_batch_number).await?, )) } } diff --git a/core/node/vm_runner/src/storage.rs b/core/node/vm_runner/src/storage.rs index b7518903cae..e351b09ad2b 100644 --- a/core/node/vm_runner/src/storage.rs +++ b/core/node/vm_runner/src/storage.rs @@ -11,8 +11,8 @@ use tokio::sync::{watch, RwLock}; use zksync_dal::{Connection, ConnectionPool, Core, CoreDal}; use zksync_multivm::interface::{L1BatchEnv, SystemEnv}; use zksync_state::{ - AsyncCatchupTask, BatchDiff, OwnedPostgresStorage, OwnedStorage, PgOrRocksdbStorage, - RocksdbCell, RocksdbStorage, RocksdbStorageBuilder, RocksdbWithMemory, + AsyncCatchupTask, BatchDiff, OwnedStorage, RocksdbCell, RocksdbStorage, RocksdbStorageBuilder, + RocksdbWithMemory, }; use zksync_types::{block::L2BlockExecutionData, L1BatchNumber, L2ChainId}; use zksync_vm_utils::storage::L1BatchParamsProvider; @@ -140,12 +140,12 @@ impl StorageLoader for VmRunnerStorage { ) .await?; - return Ok(batch_data.map(|data| { - ( - data, - OwnedPostgresStorage::new(self.pool.clone(), l1_batch_number - 1).into(), - ) - })); + return Ok(if let Some(data) = batch_data { + let storage = OwnedStorage::postgres(conn, l1_batch_number - 1).await?; + Some((data, storage)) + } else { + None + }); }; match state.storage.get(&l1_batch_number) { @@ -166,11 +166,11 @@ impl StorageLoader for VmRunnerStorage { .filter(|(&num, _)| num < l1_batch_number) .map(|(_, data)| data.diff.clone()) .collect::>(); - let storage = PgOrRocksdbStorage::RocksdbWithMemory(RocksdbWithMemory { + let storage = OwnedStorage::RocksdbWithMemory(RocksdbWithMemory { rocksdb: rocksdb.clone(), batch_diffs, }); - Ok(Some((data, storage.into()))) + Ok(Some((data, storage))) } } } diff --git a/core/node/vm_runner/src/tests/mod.rs b/core/node/vm_runner/src/tests/mod.rs index 61f0a5ec3f6..dd14e4dd1b0 100644 --- a/core/node/vm_runner/src/tests/mod.rs +++ b/core/node/vm_runner/src/tests/mod.rs @@ -10,7 +10,7 @@ use zksync_node_test_utils::{ create_l1_batch_metadata, create_l2_block, execute_l2_transaction, l1_batch_metadata_to_commitment_artifacts, }; -use zksync_state::{OwnedPostgresStorage, OwnedStorage}; +use zksync_state::OwnedStorage; use zksync_state_keeper::{StateKeeperOutputHandler, UpdatesManager}; use zksync_test_account::Account; use zksync_types::{ @@ -58,8 +58,8 @@ impl StorageLoader for PostgresLoader { return Ok(None); }; - let storage = OwnedPostgresStorage::new(self.0.clone(), l1_batch_number - 1); - Ok(Some((data, storage.into()))) + let storage = OwnedStorage::postgres(conn, l1_batch_number - 1).await?; + Ok(Some((data, storage))) } } diff --git a/core/node/vm_runner/src/tests/storage.rs b/core/node/vm_runner/src/tests/storage.rs index 1dfb5a60135..f6f7a2ba9e6 100644 --- a/core/node/vm_runner/src/tests/storage.rs +++ b/core/node/vm_runner/src/tests/storage.rs @@ -301,12 +301,8 @@ async fn access_vm_runner_storage() -> anyhow::Result<()> { .unwrap(); let mut pg_storage = PostgresStorage::new(rt_handle.clone(), conn, last_l2_block_number, true); - let (_, vm_storage) = rt_handle + let (_, mut vm_storage) = rt_handle .block_on(vm_runner_storage.load_batch_eventually(L1BatchNumber(i + 1)))?; - let mut vm_storage = match vm_storage { - OwnedStorage::Lending(ref storage) => rt_handle.block_on(storage.borrow()).unwrap(), - OwnedStorage::Static(storage) => storage, - }; // Check that both storages have identical key-value pairs written in them for storage_log in &storage_logs { From 51259b62fffa15b15ff77c1212e02462fb9a5173 Mon Sep 17 00:00:00 2001 From: zksync-era-bot <147085853+zksync-era-bot@users.noreply.github.com> Date: Fri, 16 Aug 2024 11:01:09 +0300 Subject: [PATCH 3/8] chore(main): release prover 16.4.0 (#2615) :robot: I have created a release *beep* *boop* --- ## [16.4.0](https://github.com/matter-labs/zksync-era/compare/prover-v16.3.0...prover-v16.4.0) (2024-08-16) ### Features * Bump harness & gpu deps ([#2634](https://github.com/matter-labs/zksync-era/issues/2634)) ([2a7d566](https://github.com/matter-labs/zksync-era/commit/2a7d566ffeb63dc0a038d6b38cbda6bef7c7b105)) * Poll the main node API for attestation status - relaxed (BFT-496) ([#2583](https://github.com/matter-labs/zksync-era/issues/2583)) ([b45aa91](https://github.com/matter-labs/zksync-era/commit/b45aa9168dd66d07ca61c8bb4c01f73dda822040)) * **vlog:** Report observability config, flush, and shutdown ([#2622](https://github.com/matter-labs/zksync-era/issues/2622)) ([e23e661](https://github.com/matter-labs/zksync-era/commit/e23e6611731835ef3abd34f3f9867f9dc533eb21)) * **vm:** Extract VM interface to separate crate ([#2638](https://github.com/matter-labs/zksync-era/issues/2638)) ([cb9ac4e](https://github.com/matter-labs/zksync-era/commit/cb9ac4e59fd16e6c125586bc02ef90e3b97ff80b)) * **vm:** Fast VM integration ([#1949](https://github.com/matter-labs/zksync-era/issues/1949)) ([b752a54](https://github.com/matter-labs/zksync-era/commit/b752a54bebe6eb3bf0bea044996f5116cc5dc4e2)) ### Bug Fixes * Bump prover dependencies & rust toolchain ([#2600](https://github.com/matter-labs/zksync-era/issues/2600)) ([849c6a5](https://github.com/matter-labs/zksync-era/commit/849c6a5dcd095e8fead0630a2a403f282c26a2aa)) * **prover:** Fix NWG ([#2590](https://github.com/matter-labs/zksync-era/issues/2590)) ([9b58ae9](https://github.com/matter-labs/zksync-era/commit/9b58ae97875455d58d42fe203cfb1f51cb270f62)) * **prover:** Updated README.md ([#2604](https://github.com/matter-labs/zksync-era/issues/2604)) ([be9f357](https://github.com/matter-labs/zksync-era/commit/be9f357099ed281892c1ff4618514fc7c25f9b59)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --- .github/release-please/manifest.json | 2 +- prover/CHANGELOG.md | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/release-please/manifest.json b/.github/release-please/manifest.json index d437905ee16..0a8021b7bdb 100644 --- a/.github/release-please/manifest.json +++ b/.github/release-please/manifest.json @@ -1,5 +1,5 @@ { "core": "24.18.0", - "prover": "16.3.0", + "prover": "16.4.0", "zk_toolbox": "0.1.1" } diff --git a/prover/CHANGELOG.md b/prover/CHANGELOG.md index 778edf4a9bc..4df2039589e 100644 --- a/prover/CHANGELOG.md +++ b/prover/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## [16.4.0](https://github.com/matter-labs/zksync-era/compare/prover-v16.3.0...prover-v16.4.0) (2024-08-16) + + +### Features + +* Bump harness & gpu deps ([#2634](https://github.com/matter-labs/zksync-era/issues/2634)) ([2a7d566](https://github.com/matter-labs/zksync-era/commit/2a7d566ffeb63dc0a038d6b38cbda6bef7c7b105)) +* Poll the main node API for attestation status - relaxed (BFT-496) ([#2583](https://github.com/matter-labs/zksync-era/issues/2583)) ([b45aa91](https://github.com/matter-labs/zksync-era/commit/b45aa9168dd66d07ca61c8bb4c01f73dda822040)) +* **vlog:** Report observability config, flush, and shutdown ([#2622](https://github.com/matter-labs/zksync-era/issues/2622)) ([e23e661](https://github.com/matter-labs/zksync-era/commit/e23e6611731835ef3abd34f3f9867f9dc533eb21)) +* **vm:** Extract VM interface to separate crate ([#2638](https://github.com/matter-labs/zksync-era/issues/2638)) ([cb9ac4e](https://github.com/matter-labs/zksync-era/commit/cb9ac4e59fd16e6c125586bc02ef90e3b97ff80b)) +* **vm:** Fast VM integration ([#1949](https://github.com/matter-labs/zksync-era/issues/1949)) ([b752a54](https://github.com/matter-labs/zksync-era/commit/b752a54bebe6eb3bf0bea044996f5116cc5dc4e2)) + + +### Bug Fixes + +* Bump prover dependencies & rust toolchain ([#2600](https://github.com/matter-labs/zksync-era/issues/2600)) ([849c6a5](https://github.com/matter-labs/zksync-era/commit/849c6a5dcd095e8fead0630a2a403f282c26a2aa)) +* **prover:** Fix NWG ([#2590](https://github.com/matter-labs/zksync-era/issues/2590)) ([9b58ae9](https://github.com/matter-labs/zksync-era/commit/9b58ae97875455d58d42fe203cfb1f51cb270f62)) +* **prover:** Updated README.md ([#2604](https://github.com/matter-labs/zksync-era/issues/2604)) ([be9f357](https://github.com/matter-labs/zksync-era/commit/be9f357099ed281892c1ff4618514fc7c25f9b59)) + ## [16.3.0](https://github.com/matter-labs/zksync-era/compare/prover-v16.2.0...prover-v16.3.0) (2024-08-07) From a87358a877e076ab0f8c630456d03fa0227c34b8 Mon Sep 17 00:00:00 2001 From: Alex Ostrovski Date: Fri, 16 Aug 2024 12:10:04 +0300 Subject: [PATCH 4/8] refactor(vm): Move `Call` / `CallType` to VM interface crate (#2663) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Continuing from https://github.com/matter-labs/zksync-era/pull/2645, this PR moves VM call types to the VM interface crate. ## Why ❔ So that types are separated by domain rather than all collected in `zksync_types`. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --- core/lib/dal/src/blocks_web3_dal.rs | 2 +- .../vm_trace.rs => dal/src/models/call.rs} | 185 ++---------------- core/lib/dal/src/models/mod.rs | 1 + .../lib/dal/src/models/storage_transaction.rs | 3 +- core/lib/dal/src/transactions_dal.rs | 8 +- .../multivm/src/tracers/call_tracer/mod.rs | 5 +- .../src/tracers/call_tracer/vm_1_4_1/mod.rs | 8 +- .../src/tracers/call_tracer/vm_1_4_2/mod.rs | 8 +- .../call_tracer/vm_boojum_integration/mod.rs | 8 +- .../src/tracers/call_tracer/vm_latest/mod.rs | 8 +- .../call_tracer/vm_refunds_enhancement/mod.rs | 8 +- .../call_tracer/vm_virtual_blocks/mod.rs | 8 +- core/lib/multivm/src/tracers/old.rs | 3 +- core/lib/multivm/src/versions/shared.rs | 4 +- .../versions/vm_1_3_2/oracles/tracer/call.rs | 7 +- .../vm_1_3_2/oracles/tracer/one_tx.rs | 20 +- .../oracles/tracer/transaction_result.rs | 27 +-- .../src/versions/vm_1_3_2/vm_instance.rs | 3 +- .../src/versions/vm_m6/oracles/tracer/call.rs | 6 +- .../versions/vm_m6/oracles/tracer/one_tx.rs | 20 +- .../oracles/tracer/transaction_result.rs | 27 +-- .../multivm/src/versions/vm_m6/vm_instance.rs | 3 +- core/lib/types/src/api/mod.rs | 35 +--- core/lib/types/src/debug_flat_call.rs | 25 ++- core/lib/types/src/lib.rs | 1 - core/lib/vm_interface/src/lib.rs | 10 +- .../src/types/outputs/execution_result.rs | 112 ++++++++++- .../lib/vm_interface/src/types/outputs/mod.rs | 4 +- .../src/execution_sandbox/tracers.rs | 7 +- .../api_server/src/web3/namespaces/debug.rs | 52 +++-- core/node/api_server/src/web3/tests/debug.rs | 8 +- .../src/batch_executor/main_executor.rs | 6 +- .../state_keeper/src/batch_executor/mod.rs | 4 +- .../src/updates/l2_block_updates.rs | 3 +- core/node/state_keeper/src/updates/mod.rs | 8 +- 35 files changed, 285 insertions(+), 362 deletions(-) rename core/lib/{types/src/vm_trace.rs => dal/src/models/call.rs} (55%) diff --git a/core/lib/dal/src/blocks_web3_dal.rs b/core/lib/dal/src/blocks_web3_dal.rs index 281a44436a7..54ea7cc11f1 100644 --- a/core/lib/dal/src/blocks_web3_dal.rs +++ b/core/lib/dal/src/blocks_web3_dal.rs @@ -7,11 +7,11 @@ use zksync_types::{ api, fee_model::BatchFeeInput, l2_to_l1_log::L2ToL1Log, - vm_trace::Call, web3::{BlockHeader, Bytes}, Bloom, L1BatchNumber, L2BlockNumber, ProtocolVersionId, H160, H256, U256, U64, }; use zksync_utils::bigdecimal_to_u256; +use zksync_vm_interface::Call; use crate::{ models::{ diff --git a/core/lib/types/src/vm_trace.rs b/core/lib/dal/src/models/call.rs similarity index 55% rename from core/lib/types/src/vm_trace.rs rename to core/lib/dal/src/models/call.rs index 80a3eea92f6..3e81fbbeece 100644 --- a/core/lib/types/src/vm_trace.rs +++ b/core/lib/dal/src/models/call.rs @@ -1,24 +1,14 @@ -use std::fmt; +//! Legacy VM call representations. -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use zksync_system_constants::BOOTLOADER_ADDRESS; - -use crate::{zk_evm_types::FarCallOpcode, Address, U256}; - -#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq)] -pub enum CallType { - #[serde(serialize_with = "far_call_type_to_u8")] - #[serde(deserialize_with = "far_call_type_from_u8")] - Call(FarCallOpcode), - Create, - NearCall, -} +use serde::{Deserialize, Serialize}; +use zksync_types::{Address, U256}; +use zksync_vm_interface::{Call, CallType}; /// Represents a call in the VM trace. /// This version of the call represents the call structure before the 1.5.0 protocol version, where /// all the gas-related fields were represented as `u32` instead of `u64`. #[derive(Clone, Serialize, Deserialize)] -pub struct LegacyCall { +pub(super) struct LegacyCall { /// Type of the call. pub r#type: CallType, /// Address of the caller. @@ -48,7 +38,7 @@ pub struct LegacyCall { /// Represents a call in the VM trace. /// This version has subcalls in the form of "new" calls. #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct LegacyMixedCall { +pub(super) struct LegacyMixedCall { /// Type of the call. pub r#type: CallType, /// Address of the caller. @@ -75,44 +65,15 @@ pub struct LegacyMixedCall { pub calls: Vec, } -/// Represents a call in the VM trace. -#[derive(Clone, Serialize, Deserialize)] -pub struct Call { - /// Type of the call. - pub r#type: CallType, - /// Address of the caller. - pub from: Address, - /// Address of the callee. - pub to: Address, - /// Gas from the parent call. - pub parent_gas: u64, - /// Gas provided for the call. - pub gas: u64, - /// Gas used by the call. - pub gas_used: u64, - /// Value transferred. - pub value: U256, - /// Input data. - pub input: Vec, - /// Output data. - pub output: Vec, - /// Error message provided by vm or some unexpected errors. - pub error: Option, - /// Revert reason. - pub revert_reason: Option, - /// Subcalls. - pub calls: Vec, -} - impl From for Call { fn from(legacy_call: LegacyCall) -> Self { Self { r#type: legacy_call.r#type, from: legacy_call.from, to: legacy_call.to, - parent_gas: legacy_call.parent_gas as u64, - gas: legacy_call.gas as u64, - gas_used: legacy_call.gas_used as u64, + parent_gas: legacy_call.parent_gas.into(), + gas: legacy_call.gas.into(), + gas_used: legacy_call.gas_used.into(), value: legacy_call.value, input: legacy_call.input, output: legacy_call.output, @@ -129,9 +90,9 @@ impl From for Call { r#type: legacy_call.r#type, from: legacy_call.from, to: legacy_call.to, - parent_gas: legacy_call.parent_gas as u64, - gas: legacy_call.gas as u64, - gas_used: legacy_call.gas_used as u64, + parent_gas: legacy_call.parent_gas.into(), + gas: legacy_call.gas.into(), + gas_used: legacy_call.gas_used.into(), value: legacy_call.value, input: legacy_call.input, output: legacy_call.output, @@ -142,8 +103,8 @@ impl From for Call { } } -#[derive(Debug, Clone)] -pub struct LegacyCallConversionOverflowError; +#[derive(Debug)] +pub(super) struct LegacyCallConversionOverflowError; impl TryFrom for LegacyCall { type Error = LegacyCallConversionOverflowError; @@ -207,124 +168,6 @@ impl TryFrom for LegacyMixedCall { } } -impl Call { - pub fn new_high_level( - gas: u64, - gas_used: u64, - value: U256, - input: Vec, - output: Vec, - revert_reason: Option, - calls: Vec, - ) -> Self { - Self { - r#type: CallType::Call(FarCallOpcode::Normal), - from: Address::zero(), - to: BOOTLOADER_ADDRESS, - parent_gas: gas, - gas, - gas_used, - value, - input, - output, - error: None, - revert_reason, - calls, - } - } -} - -impl PartialEq for Call { - fn eq(&self, other: &Self) -> bool { - self.revert_reason == other.revert_reason - && self.input == other.input - && self.from == other.from - && self.to == other.to - && self.r#type == other.r#type - && self.value == other.value - && self.error == other.error - && self.output == other.output - && self.calls == other.calls - } -} - -fn far_call_type_from_u8<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - let res = u8::deserialize(deserializer)?; - match res { - 0 => Ok(FarCallOpcode::Normal), - 1 => Ok(FarCallOpcode::Delegate), - 2 => Ok(FarCallOpcode::Mimic), - _ => Err(serde::de::Error::custom("Invalid FarCallOpcode")), - } -} - -fn far_call_type_to_u8(far_call_type: &FarCallOpcode, s: S) -> Result -where - S: Serializer, -{ - s.serialize_u8(*far_call_type as u8) -} - -impl Default for Call { - fn default() -> Self { - Self { - r#type: CallType::Call(FarCallOpcode::Normal), - from: Default::default(), - to: Default::default(), - parent_gas: 0, - gas: 0, - gas_used: 0, - value: Default::default(), - input: vec![], - output: vec![], - error: None, - revert_reason: None, - calls: vec![], - } - } -} - -impl fmt::Debug for Call { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Call") - .field("type", &self.r#type) - .field("to", &self.to) - .field("from", &self.from) - .field("parent_gas", &self.parent_gas) - .field("gas_used", &self.gas_used) - .field("gas", &self.gas) - .field("value", &self.value) - .field("input", &format_args!("{:?}", self.input)) - .field("output", &format_args!("{:?}", self.output)) - .field("error", &self.error) - .field("revert_reason", &format_args!("{:?}", self.revert_reason)) - .field("call_traces", &self.calls) - .finish() - } -} - -impl fmt::Debug for LegacyCall { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("LegacyCall") - .field("type", &self.r#type) - .field("to", &self.to) - .field("from", &self.from) - .field("parent_gas", &self.parent_gas) - .field("gas_used", &self.gas_used) - .field("gas", &self.gas) - .field("value", &self.value) - .field("input", &format_args!("{:?}", self.input)) - .field("output", &format_args!("{:?}", self.output)) - .field("error", &self.error) - .field("revert_reason", &format_args!("{:?}", self.revert_reason)) - .field("call_traces", &self.calls) - .finish() - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/core/lib/dal/src/models/mod.rs b/core/lib/dal/src/models/mod.rs index d22541620f2..a9690dcb799 100644 --- a/core/lib/dal/src/models/mod.rs +++ b/core/lib/dal/src/models/mod.rs @@ -3,6 +3,7 @@ use anyhow::Context as _; use zksync_db_connection::error::SqlxContext; use zksync_types::{ProtocolVersionId, H160, H256}; +mod call; pub mod storage_base_token_ratio; pub(crate) mod storage_data_availability; pub mod storage_eth_tx; diff --git a/core/lib/dal/src/models/storage_transaction.rs b/core/lib/dal/src/models/storage_transaction.rs index 31a182a7eca..aca93ee8c5a 100644 --- a/core/lib/dal/src/models/storage_transaction.rs +++ b/core/lib/dal/src/models/storage_transaction.rs @@ -10,7 +10,6 @@ use zksync_types::{ l2::TransactionType, protocol_upgrade::ProtocolUpgradeTxCommonData, transaction_request::PaymasterParams, - vm_trace::{Call, LegacyCall, LegacyMixedCall}, web3::Bytes, Address, Execute, ExecuteTransactionCommon, L1TxCommonData, L2ChainId, L2TxCommonData, Nonce, PackedEthSignature, PriorityOpId, ProtocolVersionId, Transaction, EIP_1559_TX_TYPE, @@ -18,7 +17,9 @@ use zksync_types::{ PROTOCOL_UPGRADE_TX_TYPE, U256, U64, }; use zksync_utils::{bigdecimal_to_u256, h256_to_account_address}; +use zksync_vm_interface::Call; +use super::call::{LegacyCall, LegacyMixedCall}; use crate::BigDecimal; #[derive(Debug, Clone, sqlx::FromRow)] diff --git a/core/lib/dal/src/transactions_dal.rs b/core/lib/dal/src/transactions_dal.rs index 89d7499e49d..49791f776e0 100644 --- a/core/lib/dal/src/transactions_dal.rs +++ b/core/lib/dal/src/transactions_dal.rs @@ -10,13 +10,13 @@ use zksync_db_connection::{ utils::pg_interval_from_duration, }; use zksync_types::{ - block::L2BlockExecutionData, l1::L1Tx, l2::L2Tx, protocol_upgrade::ProtocolUpgradeTx, - vm_trace::Call, Address, ExecuteTransactionCommon, L1BatchNumber, L1BlockNumber, L2BlockNumber, - PriorityOpId, ProtocolVersionId, Transaction, H256, PROTOCOL_UPGRADE_TX_TYPE, U256, + block::L2BlockExecutionData, l1::L1Tx, l2::L2Tx, protocol_upgrade::ProtocolUpgradeTx, Address, + ExecuteTransactionCommon, L1BatchNumber, L1BlockNumber, L2BlockNumber, PriorityOpId, + ProtocolVersionId, Transaction, H256, PROTOCOL_UPGRADE_TX_TYPE, U256, }; use zksync_utils::u256_to_big_decimal; use zksync_vm_interface::{ - TransactionExecutionMetrics, TransactionExecutionResult, TxExecutionStatus, + Call, TransactionExecutionMetrics, TransactionExecutionResult, TxExecutionStatus, }; use crate::{ diff --git a/core/lib/multivm/src/tracers/call_tracer/mod.rs b/core/lib/multivm/src/tracers/call_tracer/mod.rs index 4013be101e5..44f27487603 100644 --- a/core/lib/multivm/src/tracers/call_tracer/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/mod.rs @@ -1,9 +1,10 @@ use std::sync::Arc; use once_cell::sync::OnceCell; -use zksync_types::vm_trace::Call; -use crate::{glue::tracers::IntoOldVmTracer, tracers::call_tracer::metrics::CALL_METRICS}; +use crate::{ + glue::tracers::IntoOldVmTracer, interface::Call, tracers::call_tracer::metrics::CALL_METRICS, +}; mod metrics; pub mod vm_1_4_1; diff --git a/core/lib/multivm/src/tracers/call_tracer/vm_1_4_1/mod.rs b/core/lib/multivm/src/tracers/call_tracer/vm_1_4_1/mod.rs index 10ea9ba250e..a48c9a75f62 100644 --- a/core/lib/multivm/src/tracers/call_tracer/vm_1_4_1/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/vm_1_4_1/mod.rs @@ -6,18 +6,14 @@ use zk_evm_1_4_1::{ }, }; use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; -use zksync_types::{ - vm_trace::{Call, CallType}, - zk_evm_types::FarCallOpcode, - U256, -}; +use zksync_types::{zk_evm_types::FarCallOpcode, U256}; use crate::{ glue::GlueInto, interface::{ storage::{StoragePtr, WriteStorage}, tracer::VmExecutionStopReason, - VmRevertReason, + Call, CallType, VmRevertReason, }, tracers::{dynamic::vm_1_4_1::DynTracer, CallTracer}, vm_1_4_1::{BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState}, diff --git a/core/lib/multivm/src/tracers/call_tracer/vm_1_4_2/mod.rs b/core/lib/multivm/src/tracers/call_tracer/vm_1_4_2/mod.rs index 0464164a50a..3493a0511ea 100644 --- a/core/lib/multivm/src/tracers/call_tracer/vm_1_4_2/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/vm_1_4_2/mod.rs @@ -6,18 +6,14 @@ use zk_evm_1_4_1::{ }, }; use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; -use zksync_types::{ - vm_trace::{Call, CallType}, - zk_evm_types::FarCallOpcode, - U256, -}; +use zksync_types::{zk_evm_types::FarCallOpcode, U256}; use crate::{ glue::GlueInto, interface::{ storage::{StoragePtr, WriteStorage}, tracer::VmExecutionStopReason, - VmRevertReason, + Call, CallType, VmRevertReason, }, tracers::{dynamic::vm_1_4_1::DynTracer, CallTracer}, vm_1_4_2::{BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState}, diff --git a/core/lib/multivm/src/tracers/call_tracer/vm_boojum_integration/mod.rs b/core/lib/multivm/src/tracers/call_tracer/vm_boojum_integration/mod.rs index a8d035e6c1c..75837211d32 100644 --- a/core/lib/multivm/src/tracers/call_tracer/vm_boojum_integration/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/vm_boojum_integration/mod.rs @@ -6,18 +6,14 @@ use zk_evm_1_4_0::{ }, }; use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; -use zksync_types::{ - vm_trace::{Call, CallType}, - zk_evm_types::FarCallOpcode, - U256, -}; +use zksync_types::{zk_evm_types::FarCallOpcode, U256}; use crate::{ glue::GlueInto, interface::{ storage::{StoragePtr, WriteStorage}, tracer::VmExecutionStopReason, - VmRevertReason, + Call, CallType, VmRevertReason, }, tracers::{dynamic::vm_1_4_0::DynTracer, CallTracer}, vm_boojum_integration::{BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState}, diff --git a/core/lib/multivm/src/tracers/call_tracer/vm_latest/mod.rs b/core/lib/multivm/src/tracers/call_tracer/vm_latest/mod.rs index 8b1ccfa5b7a..ed18a3eca47 100644 --- a/core/lib/multivm/src/tracers/call_tracer/vm_latest/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/vm_latest/mod.rs @@ -6,18 +6,14 @@ use zk_evm_1_5_0::{ }, }; use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; -use zksync_types::{ - vm_trace::{Call, CallType}, - zk_evm_types::FarCallOpcode, - U256, -}; +use zksync_types::{zk_evm_types::FarCallOpcode, U256}; use crate::{ glue::GlueInto, interface::{ storage::{StoragePtr, WriteStorage}, tracer::VmExecutionStopReason, - VmRevertReason, + Call, CallType, VmRevertReason, }, tracers::{dynamic::vm_1_5_0::DynTracer, CallTracer}, vm_latest::{BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState}, diff --git a/core/lib/multivm/src/tracers/call_tracer/vm_refunds_enhancement/mod.rs b/core/lib/multivm/src/tracers/call_tracer/vm_refunds_enhancement/mod.rs index 30a2effb9f5..ff341e50c45 100644 --- a/core/lib/multivm/src/tracers/call_tracer/vm_refunds_enhancement/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/vm_refunds_enhancement/mod.rs @@ -6,18 +6,14 @@ use zk_evm_1_3_3::{ }, }; use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; -use zksync_types::{ - vm_trace::{Call, CallType}, - zk_evm_types::FarCallOpcode, - U256, -}; +use zksync_types::{zk_evm_types::FarCallOpcode, U256}; use crate::{ glue::GlueInto, interface::{ storage::{StoragePtr, WriteStorage}, tracer::VmExecutionStopReason, - VmRevertReason, + Call, CallType, VmRevertReason, }, tracers::{dynamic::vm_1_3_3::DynTracer, CallTracer}, vm_refunds_enhancement::{BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState}, diff --git a/core/lib/multivm/src/tracers/call_tracer/vm_virtual_blocks/mod.rs b/core/lib/multivm/src/tracers/call_tracer/vm_virtual_blocks/mod.rs index 0e3bea139d6..41286ccd877 100644 --- a/core/lib/multivm/src/tracers/call_tracer/vm_virtual_blocks/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/vm_virtual_blocks/mod.rs @@ -6,17 +6,13 @@ use zk_evm_1_3_3::{ }, }; use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; -use zksync_types::{ - vm_trace::{Call, CallType}, - zk_evm_types::FarCallOpcode, - U256, -}; +use zksync_types::{zk_evm_types::FarCallOpcode, U256}; use crate::{ glue::GlueInto, interface::{ storage::{StoragePtr, WriteStorage}, - VmExecutionResultAndLogs, VmRevertReason, + Call, CallType, VmExecutionResultAndLogs, VmRevertReason, }, tracers::{dynamic::vm_1_3_3::DynTracer, CallTracer}, vm_virtual_blocks::{ diff --git a/core/lib/multivm/src/tracers/old.rs b/core/lib/multivm/src/tracers/old.rs index 54e5e45aa2c..f0a0fae0f65 100644 --- a/core/lib/multivm/src/tracers/old.rs +++ b/core/lib/multivm/src/tracers/old.rs @@ -1,7 +1,8 @@ use std::sync::Arc; use once_cell::sync::OnceCell; -use zksync_types::vm_trace::Call; + +use crate::interface::Call; /// For backward compatibility with vm before vm with virtual blocks. /// These tracers are tightly coupled with the VM implementation and we have to pass only params for them and not tracers by itself. diff --git a/core/lib/multivm/src/versions/shared.rs b/core/lib/multivm/src/versions/shared.rs index 97954043f42..fe7570fbb73 100644 --- a/core/lib/multivm/src/versions/shared.rs +++ b/core/lib/multivm/src/versions/shared.rs @@ -2,7 +2,9 @@ use std::collections::{HashMap, HashSet}; -use zksync_types::{vm_trace::Call, Address, U256}; +use zksync_types::{Address, U256}; + +use crate::interface::Call; #[derive(Debug, Clone, PartialEq)] pub enum VmTrace { diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/call.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/call.rs index a3d5f622286..f8674bbd77e 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/call.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/call.rs @@ -10,14 +10,11 @@ use zk_evm_1_3_3::{ }, }; use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; -use zksync_types::{ - vm_trace::{Call, CallType}, - zk_evm_types::FarCallOpcode, - U256, -}; +use zksync_types::{zk_evm_types::FarCallOpcode, U256}; use crate::{ glue::GlueInto, + interface::{Call, CallType}, vm_1_3_2::{errors::VmRevertReason, history_recorder::HistoryMode, memory::SimpleMemory}, }; diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/one_tx.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/one_tx.rs index 9bf5a9b7d22..8ef1e2fb746 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/one_tx.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/one_tx.rs @@ -4,18 +4,20 @@ use zk_evm_1_3_3::{ }, vm_state::VmLocalState, }; -use zksync_types::vm_trace::Call; use super::utils::{computational_gas_price, print_debug_if_needed}; -use crate::vm_1_3_2::{ - history_recorder::HistoryMode, - memory::SimpleMemory, - oracles::tracer::{ - utils::{gas_spent_on_bytecodes_and_long_messages_this_opcode, VmHook}, - BootloaderTracer, CallTracer, ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, - StorageInvocationTracer, +use crate::{ + interface::Call, + vm_1_3_2::{ + history_recorder::HistoryMode, + memory::SimpleMemory, + oracles::tracer::{ + utils::{gas_spent_on_bytecodes_and_long_messages_this_opcode, VmHook}, + BootloaderTracer, CallTracer, ExecutionEndTracer, PendingRefundTracer, + PubdataSpentTracer, StorageInvocationTracer, + }, + vm_instance::get_vm_hook_params, }, - vm_instance::get_vm_hook_params, }; /// Allows any opcodes, but tells the VM to end the execution once the tx is over. diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/transaction_result.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/transaction_result.rs index c74e9bb862d..efad575f783 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/transaction_result.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/transaction_result.rs @@ -5,20 +5,23 @@ use zk_evm_1_3_3::{ vm_state::VmLocalState, zkevm_opcode_defs::FatPointer, }; -use zksync_types::{vm_trace, U256}; +use zksync_types::U256; -use crate::vm_1_3_2::{ - history_recorder::HistoryMode, - memory::SimpleMemory, - oracles::tracer::{ - utils::{ - gas_spent_on_bytecodes_and_long_messages_this_opcode, print_debug_if_needed, - read_pointer, VmHook, +use crate::{ + interface::Call, + vm_1_3_2::{ + history_recorder::HistoryMode, + memory::SimpleMemory, + oracles::tracer::{ + utils::{ + gas_spent_on_bytecodes_and_long_messages_this_opcode, print_debug_if_needed, + read_pointer, VmHook, + }, + CallTracer, ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, + StorageInvocationTracer, }, - CallTracer, ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, - StorageInvocationTracer, + vm_instance::get_vm_hook_params, }, - vm_instance::get_vm_hook_params, }; #[derive(Debug)] @@ -45,7 +48,7 @@ impl TransactionResultTracer { call_tracer, } } - pub fn call_trace(&mut self) -> Option> { + pub fn call_trace(&mut self) -> Option> { self.call_tracer .as_mut() .map(|call_tracer| call_tracer.extract_calls()) diff --git a/core/lib/multivm/src/versions/vm_1_3_2/vm_instance.rs b/core/lib/multivm/src/versions/vm_1_3_2/vm_instance.rs index d9d0931e09b..e76c2abe2a9 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/vm_instance.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/vm_instance.rs @@ -12,13 +12,12 @@ use zk_evm_1_3_3::{ }; use zksync_types::{ l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}, - vm_trace::Call, L1BatchNumber, VmEvent, H256, U256, }; use crate::{ glue::GlueInto, - interface::{storage::WriteStorage, TxExecutionStatus, VmExecutionLogs}, + interface::{storage::WriteStorage, Call, TxExecutionStatus, VmExecutionLogs}, versions::shared::{VmExecutionTrace, VmTrace}, vm_1_3_2::{ bootloader_state::BootloaderState, diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/call.rs b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/call.rs index ed47ace7b89..e4906c5ede2 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/call.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/call.rs @@ -10,13 +10,11 @@ use zk_evm_1_3_1::{ }, }; use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; -use zksync_types::{ - vm_trace::{Call, CallType}, - U256, -}; +use zksync_types::U256; use crate::{ glue::GlueInto, + interface::{Call, CallType}, vm_m6::{errors::VmRevertReason, history_recorder::HistoryMode, memory::SimpleMemory}, }; diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/one_tx.rs b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/one_tx.rs index 53e5e4ee2f6..98f21732b68 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/one_tx.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/one_tx.rs @@ -4,18 +4,20 @@ use zk_evm_1_3_1::{ }, vm_state::VmLocalState, }; -use zksync_types::vm_trace::Call; use super::utils::{computational_gas_price, print_debug_if_needed}; -use crate::vm_m6::{ - history_recorder::HistoryMode, - memory::SimpleMemory, - oracles::tracer::{ - utils::{gas_spent_on_bytecodes_and_long_messages_this_opcode, VmHook}, - BootloaderTracer, CallTracer, ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, - StorageInvocationTracer, +use crate::{ + interface::Call, + vm_m6::{ + history_recorder::HistoryMode, + memory::SimpleMemory, + oracles::tracer::{ + utils::{gas_spent_on_bytecodes_and_long_messages_this_opcode, VmHook}, + BootloaderTracer, CallTracer, ExecutionEndTracer, PendingRefundTracer, + PubdataSpentTracer, StorageInvocationTracer, + }, + vm_instance::get_vm_hook_params, }, - vm_instance::get_vm_hook_params, }; /// Allows any opcodes, but tells the VM to end the execution once the tx is over. diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/transaction_result.rs b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/transaction_result.rs index 2ecf484b60a..176dc25bc69 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/transaction_result.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/transaction_result.rs @@ -5,20 +5,23 @@ use zk_evm_1_3_1::{ vm_state::VmLocalState, zkevm_opcode_defs::FatPointer, }; -use zksync_types::{vm_trace, U256}; +use zksync_types::U256; -use crate::vm_m6::{ - history_recorder::HistoryMode, - memory::SimpleMemory, - oracles::tracer::{ - utils::{ - gas_spent_on_bytecodes_and_long_messages_this_opcode, print_debug_if_needed, - read_pointer, VmHook, +use crate::{ + interface::Call, + vm_m6::{ + history_recorder::HistoryMode, + memory::SimpleMemory, + oracles::tracer::{ + utils::{ + gas_spent_on_bytecodes_and_long_messages_this_opcode, print_debug_if_needed, + read_pointer, VmHook, + }, + CallTracer, ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, + StorageInvocationTracer, }, - CallTracer, ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, - StorageInvocationTracer, + vm_instance::get_vm_hook_params, }, - vm_instance::get_vm_hook_params, }; #[derive(Debug)] @@ -45,7 +48,7 @@ impl TransactionResultTracer { call_tracer, } } - pub fn call_trace(&mut self) -> Option> { + pub fn call_trace(&mut self) -> Option> { self.call_tracer .as_mut() .map(|call_tracer| call_tracer.extract_calls()) diff --git a/core/lib/multivm/src/versions/vm_m6/vm_instance.rs b/core/lib/multivm/src/versions/vm_m6/vm_instance.rs index a5f0dd25811..121b83c02c1 100644 --- a/core/lib/multivm/src/versions/vm_m6/vm_instance.rs +++ b/core/lib/multivm/src/versions/vm_m6/vm_instance.rs @@ -11,13 +11,12 @@ use zk_evm_1_3_1::{ }; use zksync_types::{ l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}, - vm_trace::Call, L1BatchNumber, VmEvent, H256, U256, }; use crate::{ glue::GlueInto, - interface::{TxExecutionStatus, VmExecutionLogs}, + interface::{Call, TxExecutionStatus, VmExecutionLogs}, versions::shared::{VmExecutionTrace, VmTrace}, vm_m6::{ bootloader_state::BootloaderState, diff --git a/core/lib/types/src/api/mod.rs b/core/lib/types/src/api/mod.rs index 102a31438bb..916fae6a35b 100644 --- a/core/lib/types/src/api/mod.rs +++ b/core/lib/types/src/api/mod.rs @@ -12,11 +12,7 @@ use zksync_contracts::BaseSystemContractsHashes; pub use crate::transaction_request::{ Eip712Meta, SerializationTransactionError, TransactionRequest, }; -use crate::{ - protocol_version::L1VerifierConfig, - vm_trace::{Call, CallType}, - Address, L2BlockNumber, ProtocolVersionId, -}; +use crate::{protocol_version::L1VerifierConfig, Address, L2BlockNumber, ProtocolVersionId}; pub mod en; pub mod state_override; @@ -604,13 +600,14 @@ pub struct ResultDebugCall { pub result: DebugCall, } -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] pub enum DebugCallType { + #[default] Call, Create, } -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct DebugCall { pub r#type: DebugCallType, @@ -626,30 +623,6 @@ pub struct DebugCall { pub calls: Vec, } -impl From for DebugCall { - fn from(value: Call) -> Self { - let calls = value.calls.into_iter().map(DebugCall::from).collect(); - let debug_type = match value.r#type { - CallType::Call(_) => DebugCallType::Call, - CallType::Create => DebugCallType::Create, - CallType::NearCall => unreachable!("We have to filter our near calls before"), - }; - Self { - r#type: debug_type, - from: value.from, - to: value.to, - gas: U256::from(value.gas), - gas_used: U256::from(value.gas_used), - value: value.value, - output: Bytes::from(value.output.clone()), - input: Bytes::from(value.input.clone()), - error: value.error.clone(), - revert_reason: value.revert_reason, - calls, - } - } -} - // TODO (PLA-965): remove deprecated fields from the struct. It is currently in a "migration" phase // to keep compatibility between old and new versions. #[derive(Default, Serialize, Deserialize, Clone, Debug)] diff --git a/core/lib/types/src/debug_flat_call.rs b/core/lib/types/src/debug_flat_call.rs index 1b4bfdd21ce..b5c0d79c857 100644 --- a/core/lib/types/src/debug_flat_call.rs +++ b/core/lib/types/src/debug_flat_call.rs @@ -86,7 +86,6 @@ mod test { use super::*; use crate::{ api::{DebugCall, DebugCallType, ResultDebugCall}, - vm_trace::Call, Address, BOOTLOADER_ADDRESS, }; @@ -120,26 +119,24 @@ mod test { } fn new_testing_trace() -> Vec { - let first_call_trace = Call { + let first_call_trace = DebugCall { from: Address::zero(), to: Address::zero(), - gas: 100, - gas_used: 42, - ..Call::default() + gas: 100.into(), + gas_used: 42.into(), + ..DebugCall::default() }; - let second_call_trace = Call { + let second_call_trace = DebugCall { from: Address::zero(), to: Address::zero(), value: 123.into(), - gas: 58, - gas_used: 10, - input: b"input".to_vec(), - output: b"output".to_vec(), - ..Call::default() + gas: 58.into(), + gas_used: 10.into(), + input: Bytes(b"input".to_vec()), + output: Bytes(b"output".to_vec()), + ..DebugCall::default() }; - [first_call_trace, second_call_trace] - .map(|call_trace| call_trace.into()) - .into() + [first_call_trace, second_call_trace].into() } fn expected_flat_trace() -> Vec { diff --git a/core/lib/types/src/lib.rs b/core/lib/types/src/lib.rs index 9e24d7156f9..72c6bfeb13a 100644 --- a/core/lib/types/src/lib.rs +++ b/core/lib/types/src/lib.rs @@ -51,7 +51,6 @@ pub mod storage; pub mod system_contracts; pub mod tokens; pub mod tx; -pub mod vm_trace; pub mod zk_evm_types; pub mod api; diff --git a/core/lib/vm_interface/src/lib.rs b/core/lib/vm_interface/src/lib.rs index 3934709822d..3ce45cd34e2 100644 --- a/core/lib/vm_interface/src/lib.rs +++ b/core/lib/vm_interface/src/lib.rs @@ -25,11 +25,11 @@ pub use crate::{ }, inputs::{L1BatchEnv, L2BlockEnv, SystemEnv, TxExecutionMode, VmExecutionMode}, outputs::{ - BootloaderMemory, CircuitStatistic, CompressedBytecodeInfo, CurrentExecutionState, - DeduplicatedWritesMetrics, ExecutionResult, FinishedL1Batch, L2Block, Refunds, - TransactionExecutionMetrics, TransactionExecutionResult, TxExecutionStatus, - VmExecutionLogs, VmExecutionMetrics, VmExecutionResultAndLogs, VmExecutionStatistics, - VmMemoryMetrics, + BootloaderMemory, Call, CallType, CircuitStatistic, CompressedBytecodeInfo, + CurrentExecutionState, DeduplicatedWritesMetrics, ExecutionResult, FinishedL1Batch, + L2Block, Refunds, TransactionExecutionMetrics, TransactionExecutionResult, + TxExecutionStatus, VmExecutionLogs, VmExecutionMetrics, VmExecutionResultAndLogs, + VmExecutionStatistics, VmMemoryMetrics, }, tracer, }, diff --git a/core/lib/vm_interface/src/types/outputs/execution_result.rs b/core/lib/vm_interface/src/types/outputs/execution_result.rs index da96a3e15f8..ac709379ad1 100644 --- a/core/lib/vm_interface/src/types/outputs/execution_result.rs +++ b/core/lib/vm_interface/src/types/outputs/execution_result.rs @@ -1,9 +1,10 @@ -use zksync_system_constants::PUBLISH_BYTECODE_OVERHEAD; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use zksync_system_constants::{BOOTLOADER_ADDRESS, PUBLISH_BYTECODE_OVERHEAD}; use zksync_types::{ event::{extract_long_l2_to_l1_messages, extract_published_bytecodes}, l2_to_l1_log::{SystemL2ToL1Log, UserL2ToL1Log}, - vm_trace::Call, - StorageLogWithPreviousValue, Transaction, VmEvent, H256, + zk_evm_types::FarCallOpcode, + Address, StorageLogWithPreviousValue, Transaction, VmEvent, H256, U256, }; use crate::{ @@ -122,6 +123,111 @@ impl TxExecutionStatus { } } +#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq)] +pub enum CallType { + #[serde(serialize_with = "far_call_type_to_u8")] + #[serde(deserialize_with = "far_call_type_from_u8")] + Call(FarCallOpcode), + Create, + NearCall, +} + +impl Default for CallType { + fn default() -> Self { + Self::Call(FarCallOpcode::Normal) + } +} + +fn far_call_type_from_u8<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let res = u8::deserialize(deserializer)?; + match res { + 0 => Ok(FarCallOpcode::Normal), + 1 => Ok(FarCallOpcode::Delegate), + 2 => Ok(FarCallOpcode::Mimic), + _ => Err(serde::de::Error::custom("Invalid FarCallOpcode")), + } +} + +fn far_call_type_to_u8(far_call_type: &FarCallOpcode, s: S) -> Result +where + S: Serializer, +{ + s.serialize_u8(*far_call_type as u8) +} + +/// Represents a call in the VM trace. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct Call { + /// Type of the call. + pub r#type: CallType, + /// Address of the caller. + pub from: Address, + /// Address of the callee. + pub to: Address, + /// Gas from the parent call. + pub parent_gas: u64, + /// Gas provided for the call. + pub gas: u64, + /// Gas used by the call. + pub gas_used: u64, + /// Value transferred. + pub value: U256, + /// Input data. + pub input: Vec, + /// Output data. + pub output: Vec, + /// Error message provided by vm or some unexpected errors. + pub error: Option, + /// Revert reason. + pub revert_reason: Option, + /// Subcalls. + pub calls: Vec, +} + +impl PartialEq for Call { + fn eq(&self, other: &Self) -> bool { + self.revert_reason == other.revert_reason + && self.input == other.input + && self.from == other.from + && self.to == other.to + && self.r#type == other.r#type + && self.value == other.value + && self.error == other.error + && self.output == other.output + && self.calls == other.calls + } +} + +impl Call { + pub fn new_high_level( + gas: u64, + gas_used: u64, + value: U256, + input: Vec, + output: Vec, + revert_reason: Option, + calls: Vec, + ) -> Self { + Self { + r#type: CallType::Call(FarCallOpcode::Normal), + from: Address::zero(), + to: BOOTLOADER_ADDRESS, + parent_gas: gas, + gas, + gas_used, + value, + input, + output, + error: None, + revert_reason, + calls, + } + } +} + #[derive(Debug, Clone, PartialEq)] pub struct TransactionExecutionResult { pub transaction: Transaction, diff --git a/core/lib/vm_interface/src/types/outputs/mod.rs b/core/lib/vm_interface/src/types/outputs/mod.rs index 88b96aaafff..23be39ddc7c 100644 --- a/core/lib/vm_interface/src/types/outputs/mod.rs +++ b/core/lib/vm_interface/src/types/outputs/mod.rs @@ -1,8 +1,8 @@ pub use self::{ bytecode::CompressedBytecodeInfo, execution_result::{ - ExecutionResult, Refunds, TransactionExecutionResult, TxExecutionStatus, VmExecutionLogs, - VmExecutionResultAndLogs, + Call, CallType, ExecutionResult, Refunds, TransactionExecutionResult, TxExecutionStatus, + VmExecutionLogs, VmExecutionResultAndLogs, }, execution_state::{BootloaderMemory, CurrentExecutionState}, finished_l1batch::FinishedL1Batch, diff --git a/core/node/api_server/src/execution_sandbox/tracers.rs b/core/node/api_server/src/execution_sandbox/tracers.rs index f03c17a5fa4..8d61d896a36 100644 --- a/core/node/api_server/src/execution_sandbox/tracers.rs +++ b/core/node/api_server/src/execution_sandbox/tracers.rs @@ -2,10 +2,11 @@ use std::sync::Arc; use once_cell::sync::OnceCell; use zksync_multivm::{ - interface::storage::WriteStorage, tracers::CallTracer, vm_latest::HistoryMode, MultiVMTracer, - MultiVmTracerPointer, + interface::{storage::WriteStorage, Call}, + tracers::CallTracer, + vm_latest::HistoryMode, + MultiVMTracer, MultiVmTracerPointer, }; -use zksync_types::vm_trace::Call; /// Custom tracers supported by our API #[derive(Debug)] diff --git a/core/node/api_server/src/web3/namespaces/debug.rs b/core/node/api_server/src/web3/namespaces/debug.rs index 2f2d1d44cba..e71f4bd1e1e 100644 --- a/core/node/api_server/src/web3/namespaces/debug.rs +++ b/core/node/api_server/src/web3/namespaces/debug.rs @@ -4,17 +4,17 @@ use anyhow::Context as _; use once_cell::sync::OnceCell; use zksync_dal::{CoreDal, DalError}; use zksync_multivm::{ - interface::ExecutionResult, vm_latest::constants::BATCH_COMPUTATIONAL_GAS_LIMIT, + interface::{Call, CallType, ExecutionResult}, + vm_latest::constants::BATCH_COMPUTATIONAL_GAS_LIMIT, }; use zksync_system_constants::MAX_ENCODED_TX_SIZE; use zksync_types::{ - api::{BlockId, BlockNumber, DebugCall, ResultDebugCall, TracerConfig}, + api::{BlockId, BlockNumber, DebugCall, DebugCallType, ResultDebugCall, TracerConfig}, debug_flat_call::{flatten_debug_calls, DebugCallFlat}, fee_model::BatchFeeInput, l2::L2Tx, transaction_request::CallRequest, - vm_trace::Call, - AccountTreeId, H256, + web3, AccountTreeId, H256, U256, }; use zksync_web3_decl::error::Web3Error; @@ -51,6 +51,35 @@ impl DebugNamespace { }) } + pub(crate) fn map_call(call: Call, only_top_call: bool) -> DebugCall { + let calls = if only_top_call { + vec![] + } else { + call.calls + .into_iter() + .map(|call| Self::map_call(call, false)) + .collect() + }; + let debug_type = match call.r#type { + CallType::Call(_) => DebugCallType::Call, + CallType::Create => DebugCallType::Create, + CallType::NearCall => unreachable!("We have to filter our near calls before"), + }; + DebugCall { + r#type: debug_type, + from: call.from, + to: call.to, + gas: U256::from(call.gas), + gas_used: U256::from(call.gas_used), + value: call.value, + output: web3::Bytes::from(call.output), + input: web3::Bytes::from(call.input), + error: call.error, + revert_reason: call.revert_reason, + calls, + } + } + fn sender_config(&self) -> &TxSenderConfig { &self.state.tx_sender.0.sender_config } @@ -86,10 +115,7 @@ impl DebugNamespace { let call_trace = call_traces .into_iter() .map(|call_trace| { - let mut result: DebugCall = call_trace.into(); - if only_top_call { - result.calls = vec![]; - } + let result = Self::map_call(call_trace, only_top_call); ResultDebugCall { result } }) .collect(); @@ -120,13 +146,7 @@ impl DebugNamespace { .get_call_trace(tx_hash) .await .map_err(DalError::generalize)?; - Ok(call_trace.map(|call_trace| { - let mut result: DebugCall = call_trace.into(); - if only_top_call { - result.calls = vec![]; - } - result - })) + Ok(call_trace.map(|call_trace| Self::map_call(call_trace, only_top_call))) } pub async fn debug_trace_call_impl( @@ -226,7 +246,7 @@ impl DebugNamespace { revert_reason, trace, ); - Ok(call.into()) + Ok(Self::map_call(call, false)) } async fn shared_args(&self) -> TxSharedArgs { diff --git a/core/node/api_server/src/web3/tests/debug.rs b/core/node/api_server/src/web3/tests/debug.rs index dab53cb4b4d..76496b42cad 100644 --- a/core/node/api_server/src/web3/tests/debug.rs +++ b/core/node/api_server/src/web3/tests/debug.rs @@ -1,7 +1,7 @@ //! Tests for the `debug` Web3 namespace. -use zksync_multivm::interface::TransactionExecutionResult; -use zksync_types::{vm_trace::Call, BOOTLOADER_ADDRESS}; +use zksync_multivm::interface::{Call, TransactionExecutionResult}; +use zksync_types::BOOTLOADER_ADDRESS; use zksync_web3_decl::{ client::{DynClient, L2}, namespaces::DebugNamespaceClient, @@ -69,7 +69,7 @@ impl HttpTest for TraceBlockTest { let expected_calls: Vec<_> = tx_result .call_traces .iter() - .map(|call| api::DebugCall::from(call.clone())) + .map(|call| DebugNamespace::map_call(call.clone(), false)) .collect(); assert_eq!(result.calls, expected_calls); } @@ -198,7 +198,7 @@ impl HttpTest for TraceTransactionTest { let expected_calls: Vec<_> = tx_results[0] .call_traces .iter() - .map(|call| api::DebugCall::from(call.clone())) + .map(|call| DebugNamespace::map_call(call.clone(), false)) .collect(); let result = client diff --git a/core/node/state_keeper/src/batch_executor/main_executor.rs b/core/node/state_keeper/src/batch_executor/main_executor.rs index 43f1b8e59b1..db4daeb7744 100644 --- a/core/node/state_keeper/src/batch_executor/main_executor.rs +++ b/core/node/state_keeper/src/batch_executor/main_executor.rs @@ -6,15 +6,15 @@ use tokio::sync::mpsc; use zksync_multivm::{ interface::{ storage::{ReadStorage, StorageView}, - CompressedBytecodeInfo, ExecutionResult, FinishedL1Batch, Halt, L1BatchEnv, L2BlockEnv, - SystemEnv, VmExecutionResultAndLogs, VmInterface, VmInterfaceHistoryEnabled, + Call, CompressedBytecodeInfo, ExecutionResult, FinishedL1Batch, Halt, L1BatchEnv, + L2BlockEnv, SystemEnv, VmExecutionResultAndLogs, VmInterface, VmInterfaceHistoryEnabled, }, tracers::CallTracer, vm_latest::HistoryEnabled, MultiVMTracer, VmInstance, }; use zksync_shared_metrics::{InteractionType, TxStage, APP_METRICS}; -use zksync_types::{vm::FastVmMode, vm_trace::Call, Transaction}; +use zksync_types::{vm::FastVmMode, Transaction}; use super::{BatchExecutor, BatchExecutorHandle, Command, TxExecutionResult}; use crate::{ diff --git a/core/node/state_keeper/src/batch_executor/mod.rs b/core/node/state_keeper/src/batch_executor/mod.rs index 2040328ba79..235a8f581c8 100644 --- a/core/node/state_keeper/src/batch_executor/mod.rs +++ b/core/node/state_keeper/src/batch_executor/mod.rs @@ -6,11 +6,11 @@ use tokio::{ task::JoinHandle, }; use zksync_multivm::interface::{ - storage::StorageViewCache, CompressedBytecodeInfo, FinishedL1Batch, Halt, L1BatchEnv, + storage::StorageViewCache, Call, CompressedBytecodeInfo, FinishedL1Batch, Halt, L1BatchEnv, L2BlockEnv, SystemEnv, VmExecutionResultAndLogs, }; use zksync_state::OwnedStorage; -use zksync_types::{vm_trace::Call, Transaction}; +use zksync_types::Transaction; use crate::{ metrics::{ExecutorCommand, EXECUTOR_METRICS}, diff --git a/core/node/state_keeper/src/updates/l2_block_updates.rs b/core/node/state_keeper/src/updates/l2_block_updates.rs index 883db604aad..18ac6ee61e1 100644 --- a/core/node/state_keeper/src/updates/l2_block_updates.rs +++ b/core/node/state_keeper/src/updates/l2_block_updates.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use zksync_multivm::{ interface::{ - CompressedBytecodeInfo, ExecutionResult, L2BlockEnv, TransactionExecutionResult, + Call, CompressedBytecodeInfo, ExecutionResult, L2BlockEnv, TransactionExecutionResult, TxExecutionStatus, VmExecutionMetrics, VmExecutionResultAndLogs, }, vm_latest::TransactionVmExt, @@ -11,7 +11,6 @@ use zksync_types::{ block::{BlockGasCount, L2BlockHasher}, event::extract_bytecodes_marked_as_known, l2_to_l1_log::{SystemL2ToL1Log, UserL2ToL1Log}, - vm_trace::Call, L2BlockNumber, ProtocolVersionId, StorageLogWithPreviousValue, Transaction, VmEvent, H256, }; use zksync_utils::bytecode::hash_bytecode; diff --git a/core/node/state_keeper/src/updates/mod.rs b/core/node/state_keeper/src/updates/mod.rs index 1ac06a6a293..2fad56a9929 100644 --- a/core/node/state_keeper/src/updates/mod.rs +++ b/core/node/state_keeper/src/updates/mod.rs @@ -1,14 +1,14 @@ use zksync_contracts::BaseSystemContractsHashes; use zksync_multivm::{ interface::{ - storage::StorageViewCache, CompressedBytecodeInfo, FinishedL1Batch, L1BatchEnv, SystemEnv, - VmExecutionMetrics, VmExecutionResultAndLogs, + storage::StorageViewCache, Call, CompressedBytecodeInfo, FinishedL1Batch, L1BatchEnv, + SystemEnv, VmExecutionMetrics, VmExecutionResultAndLogs, }, utils::{get_batch_base_fee, StorageWritesDeduplicator}, }; use zksync_types::{ - block::BlockGasCount, fee_model::BatchFeeInput, vm_trace::Call, Address, L1BatchNumber, - L2BlockNumber, ProtocolVersionId, Transaction, + block::BlockGasCount, fee_model::BatchFeeInput, Address, L1BatchNumber, L2BlockNumber, + ProtocolVersionId, Transaction, }; pub(crate) use self::{l1_batch_updates::L1BatchUpdates, l2_block_updates::L2BlockUpdates}; From 6243399a9ebee88a80fbc6c7e794519712f6e955 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Fri, 16 Aug 2024 11:23:52 +0200 Subject: [PATCH 5/8] fix(prover): speed up LWG and NWG (#2661) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ - Loading of proofs for each recursive circuit is now done asynchronously - Each recursive circuit is now processed in a blocking thread, which speeds up serialization and other CPU-sensitive processing. Locally, with additional artificial network delays (500 ms), a x5 speed up is observed ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zk fmt` and `zk lint`. --- .../witness_generator/src/leaf_aggregation.rs | 80 ++++++++-------- .../witness_generator/src/node_aggregation.rs | 91 ++++++++++--------- .../crates/bin/witness_generator/src/utils.rs | 10 +- 3 files changed, 97 insertions(+), 84 deletions(-) diff --git a/prover/crates/bin/witness_generator/src/leaf_aggregation.rs b/prover/crates/bin/witness_generator/src/leaf_aggregation.rs index d8cad84e777..2cfae160028 100644 --- a/prover/crates/bin/witness_generator/src/leaf_aggregation.rs +++ b/prover/crates/bin/witness_generator/src/leaf_aggregation.rs @@ -3,7 +3,7 @@ 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 tokio::{runtime::Handle, sync::Semaphore}; use zkevm_test_harness::{ witness::recursive_aggregation::{ compute_leaf_params, create_leaf_witness, split_recursion_queue, @@ -298,44 +298,48 @@ pub async fn process_leaf_aggregation_job( 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() + let handle = tokio::task::spawn_blocking(move || { + let async_task = async { + 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 - .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 + }; + + Handle::current().block_on(async_task) }); handles.push(handle); diff --git a/prover/crates/bin/witness_generator/src/node_aggregation.rs b/prover/crates/bin/witness_generator/src/node_aggregation.rs index c9d5ab32bc5..2836d463cd4 100644 --- a/prover/crates/bin/witness_generator/src/node_aggregation.rs +++ b/prover/crates/bin/witness_generator/src/node_aggregation.rs @@ -3,7 +3,7 @@ use std::{sync::Arc, time::Instant}; use anyhow::Context as _; use async_trait::async_trait; use circuit_definitions::circuit_definitions::recursion_layer::RECURSION_ARITY; -use tokio::sync::Semaphore; +use tokio::{runtime::Handle, sync::Semaphore}; use zkevm_test_harness::witness::recursive_aggregation::{ compute_node_vk_commitment, create_node_witness, }; @@ -138,51 +138,56 @@ impl NodeAggregationWitnessGenerator { let vk = vk.clone(); let all_leafs_layer_params = job.all_leafs_layer_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_chunk, &*object_store).await; - let mut recursive_proofs = vec![]; - for wrapper in proofs { - match wrapper { - FriProofWrapper::Base(_) => { - panic!( - "Expected only recursive proofs for node agg {} {}", - job.circuit_id, job.block_number - ); - } - FriProofWrapper::Recursive(recursive_proof) => { - recursive_proofs.push(recursive_proof) + let handle = tokio::task::spawn_blocking(move || { + let async_task = async { + let _permit = semaphore + .acquire() + .await + .expect("failed to get permit to process queues chunk"); + + let proofs = + load_proofs_for_job_ids(&proofs_ids_for_chunk, &*object_store).await; + let mut recursive_proofs = vec![]; + for wrapper in proofs { + match wrapper { + FriProofWrapper::Base(_) => { + panic!( + "Expected only recursive proofs for node agg {} {}", + job.circuit_id, job.block_number + ); + } + FriProofWrapper::Recursive(recursive_proof) => { + recursive_proofs.push(recursive_proof) + } } } - } - - let (result_circuit_id, recursive_circuit, input_queue) = create_node_witness( - &chunk, - recursive_proofs, - &vk, - node_vk_commitment, - &all_leafs_layer_params, - ); - - let recursive_circuit_id_and_url = save_recursive_layer_prover_input_artifacts( - job.block_number, - circuit_idx, - vec![recursive_circuit], - AggregationRound::NodeAggregation, - job.depth + 1, - &*object_store, - Some(job.circuit_id), - ) - .await; - ( - (result_circuit_id, input_queue), - recursive_circuit_id_and_url, - ) + let (result_circuit_id, recursive_circuit, input_queue) = create_node_witness( + &chunk, + recursive_proofs, + &vk, + node_vk_commitment, + &all_leafs_layer_params, + ); + + let recursive_circuit_id_and_url = save_recursive_layer_prover_input_artifacts( + job.block_number, + circuit_idx, + vec![recursive_circuit], + AggregationRound::NodeAggregation, + job.depth + 1, + &*object_store, + Some(job.circuit_id), + ) + .await; + + ( + (result_circuit_id, input_queue), + recursive_circuit_id_and_url, + ) + }; + + Handle::current().block_on(async_task) }); handles.push(handle); diff --git a/prover/crates/bin/witness_generator/src/utils.rs b/prover/crates/bin/witness_generator/src/utils.rs index a21aabc5d6d..624d8ec1b40 100644 --- a/prover/crates/bin/witness_generator/src/utils.rs +++ b/prover/crates/bin/witness_generator/src/utils.rs @@ -227,11 +227,15 @@ pub async fn load_proofs_for_job_ids( job_ids: &[u32], object_store: &dyn ObjectStore, ) -> Vec { - let mut proofs = Vec::with_capacity(job_ids.len()); + let mut handles = Vec::with_capacity(job_ids.len()); for job_id in job_ids { - proofs.push(object_store.get(*job_id).await.unwrap()); + handles.push(object_store.get(*job_id)); } - proofs + futures::future::join_all(handles) + .await + .into_iter() + .map(|x| x.unwrap()) + .collect() } /// Loads all proofs for a given recursion tip's job ids. From cd6d648ce71f41182c911c29a106f1c4e7acc872 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Fri, 16 Aug 2024 13:45:30 +0200 Subject: [PATCH 6/8] chore(prover): remove legacy AggregationWrapper struct (#2617) Legacy structure handling was added to avoid problems when deploying update. --- .../witness_generator/src/node_aggregation.rs | 31 +++++-------------- .../crates/bin/witness_generator/src/utils.rs | 26 ---------------- 2 files changed, 8 insertions(+), 49 deletions(-) diff --git a/prover/crates/bin/witness_generator/src/node_aggregation.rs b/prover/crates/bin/witness_generator/src/node_aggregation.rs index 2836d463cd4..4f396fd4b5a 100644 --- a/prover/crates/bin/witness_generator/src/node_aggregation.rs +++ b/prover/crates/bin/witness_generator/src/node_aggregation.rs @@ -8,7 +8,7 @@ use zkevm_test_harness::witness::recursive_aggregation::{ compute_node_vk_commitment, create_node_witness, }; use zksync_config::configs::FriWitnessGeneratorConfig; -use zksync_object_store::{ObjectStore, ObjectStoreError}; +use zksync_object_store::ObjectStore; use zksync_prover_dal::{ConnectionPool, Prover, ProverDal}; use zksync_prover_fri_types::{ circuit_definitions::{ @@ -34,7 +34,7 @@ use crate::{ metrics::WITNESS_GENERATOR_METRICS, utils::{ load_proofs_for_job_ids, save_node_aggregations_artifacts, - save_recursive_layer_prover_input_artifacts, AggregationWrapper, AggregationWrapperLegacy, + save_recursive_layer_prover_input_artifacts, AggregationWrapper, }, }; @@ -449,27 +449,12 @@ async fn get_artifacts( circuit_id: metadata.circuit_id, depth: metadata.depth, }; - let result = object_store.get(key).await; - - // TODO: remove after transition - return match result { - Ok(aggregation_wrapper) => aggregation_wrapper, - Err(error) => { - // probably legacy struct is saved in GCS - if let ObjectStoreError::Serialization(serialization_error) = error { - let legacy_wrapper: AggregationWrapperLegacy = - object_store.get(key).await.unwrap_or_else(|inner_error| { - panic!( - "node aggregation job artifacts getting error. Key: {:?}, errors: {:?} {:?}", - key, serialization_error, inner_error - ) - }); - AggregationWrapper(legacy_wrapper.0.into_iter().map(|x| (x.0, x.1)).collect()) - } else { - panic!("node aggregation job artifacts missing: {:?}", key) - } - } - }; + object_store.get(key).await.unwrap_or_else(|error| { + panic!( + "node aggregation job artifacts getting error. Key: {:?}, error: {:?}", + key, error + ) + }) } #[tracing::instrument( diff --git a/prover/crates/bin/witness_generator/src/utils.rs b/prover/crates/bin/witness_generator/src/utils.rs index 624d8ec1b40..65fe26d63f5 100644 --- a/prover/crates/bin/witness_generator/src/utils.rs +++ b/prover/crates/bin/witness_generator/src/utils.rs @@ -97,32 +97,6 @@ impl StoredObject for AggregationWrapper { serialize_using_bincode!(); } -/// TODO: remove after transition -#[derive(serde::Serialize, serde::Deserialize)] -pub struct AggregationWrapperLegacy( - pub Vec<( - u64, - RecursionQueueSimulator, - ZkSyncRecursiveLayerCircuit, - )>, -); - -impl StoredObject for AggregationWrapperLegacy { - const BUCKET: Bucket = Bucket::NodeAggregationWitnessJobsFri; - type Key<'a> = AggregationsKey; - - fn encode_key(key: Self::Key<'_>) -> String { - let AggregationsKey { - block_number, - circuit_id, - depth, - } = key; - format!("aggregations_{block_number}_{circuit_id}_{depth}.bin") - } - - serialize_using_bincode!(); -} - #[derive(serde::Serialize, serde::Deserialize)] pub struct SchedulerPartialInputWrapper( pub SchedulerCircuitInstanceWitness< From b4ffcd237ee594fc659ccfa96668868f5a87d5e3 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Fri, 16 Aug 2024 13:49:36 +0200 Subject: [PATCH 7/8] feat(prover): parallelized memory queues simulation in BWG (#2652) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ The updated logic of memory queue simulation in the test_harness requires changing the structure of the BWG artifacts. When processing memory queues for ram permutation circuit, part of the witness is sent to storage separately. For this reason, a new type of circuit wrapper has been added - partial base circuit. Also added logic required to transform a partial base layer circuit into a full one (merging structures) for WVG and proving. These changes can significantly reduce peak RAM usage in BWG, while speeding up the slowest part of the witness generation process. ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zk fmt` and `zk lint`. --- Cargo.lock | 56 ++++---- Cargo.toml | 6 +- core/lib/object_store/src/file.rs | 1 + core/lib/object_store/src/raw.rs | 2 + prover/Cargo.lock | 100 ++++++------- prover/Cargo.toml | 10 +- .../prover_cli/src/commands/get_file_info.rs | 2 +- .../src/gpu_prover_job_processor.rs | 2 + .../prover_fri/src/prover_job_processor.rs | 1 + prover/crates/bin/prover_fri/src/utils.rs | 1 + .../witness_generator/src/basic_circuits.rs | 135 +++++++++++++++--- .../crates/bin/witness_generator/src/utils.rs | 55 +++++-- .../witness_vector_generator/src/generator.rs | 3 + .../crates/lib/prover_fri_types/src/keys.rs | 8 ++ prover/crates/lib/prover_fri_types/src/lib.rs | 32 +++++ prover/crates/lib/prover_fri_utils/src/lib.rs | 60 +++++++- 16 files changed, 349 insertions(+), 125 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b65826900d4..a8ecbd7636d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1045,14 +1045,14 @@ dependencies = [ [[package]] name = "circuit_encodings" -version = "0.150.2" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ba840a74f8d0b8b1334e93e4c87514a27c9be83d42d9f78d0c577572bb5f435" +checksum = "2593c02ad6b4b31ba63506c3f807f666133dd36bf47422f99b1d2947cf3c8dc1" dependencies = [ "derivative", "serde", - "zk_evm 0.150.0", - "zkevm_circuits 0.150.3", + "zk_evm 0.150.4", + "zkevm_circuits 0.150.4", ] [[package]] @@ -1112,12 +1112,12 @@ dependencies = [ [[package]] name = "circuit_sequencer_api" -version = "0.150.2" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79f3177b2bcd4ef5da9d2ca6916f6de31fb1973dfece27907a8dc7c69233494d" +checksum = "42d1a86b9c2207f3bb2dff5f00d1af1cb95004b6d07e9bacb6519fe08f12c04b" dependencies = [ "bellman_ce", - "circuit_encodings 0.150.2", + "circuit_encodings 0.150.4", "derivative", "rayon", "serde", @@ -7296,8 +7296,8 @@ source = "git+https://github.com/matter-labs/vm2.git?rev=9a38900d7af9b1d72b47ce3 dependencies = [ "enum_dispatch", "primitive-types", - "zk_evm_abstractions 0.150.0", - "zkevm_opcode_defs 0.150.0", + "zk_evm_abstractions 0.150.4", + "zkevm_opcode_defs 0.150.4", ] [[package]] @@ -7873,9 +7873,9 @@ dependencies = [ [[package]] name = "zk_evm" -version = "0.150.0" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5bf91304aa14827758afa3def8cf622f9a7f9fb65fe5d5099018dbacf0c5984" +checksum = "e2dbb0ed38d61fbd04bd7575755924d1303e129c04c909abba7f5bfcc6260bcf" dependencies = [ "anyhow", "lazy_static", @@ -7883,7 +7883,7 @@ dependencies = [ "serde", "serde_json", "static_assertions", - "zk_evm_abstractions 0.150.0", + "zk_evm_abstractions 0.150.4", ] [[package]] @@ -7914,15 +7914,15 @@ dependencies = [ [[package]] name = "zk_evm_abstractions" -version = "0.150.0" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc313cea4ac9ef6b855264b1425cbe9de30dd8f009559dabcb6b2896122da5db" +checksum = "31460aacfe65b39ac484a2a2e0bbb02baf141f65264bf48e1e4f59ab375fe933" dependencies = [ "anyhow", "num_enum 0.6.1", "serde", "static_assertions", - "zkevm_opcode_defs 0.150.0", + "zkevm_opcode_defs 0.150.4", ] [[package]] @@ -7971,9 +7971,9 @@ dependencies = [ [[package]] name = "zkevm_circuits" -version = "0.150.3" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d64bda28dec766324d2e5095a46fb141540d86a232106760dfb20ab4ae6e5c" +checksum = "abdfaa95dfe0878fda219dd17a6cc8c28711e2067785910c0e06d3ffdca78629" dependencies = [ "arrayvec 0.7.4", "boojum", @@ -7986,7 +7986,7 @@ dependencies = [ "seq-macro", "serde", "smallvec", - "zkevm_opcode_defs 0.150.0", + "zkevm_opcode_defs 0.150.4", ] [[package]] @@ -8033,9 +8033,9 @@ dependencies = [ [[package]] name = "zkevm_opcode_defs" -version = "0.150.0" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3328c012d444bdbfadb754a72c01a56879eb66584efc71eac457e89e7843608" +checksum = "bb7c5c7b4481a646f8696b08cee64a8dec097509a6378d18242f81022f327f1e" dependencies = [ "bitflags 2.6.0", "blake2 0.10.6", @@ -8129,7 +8129,7 @@ dependencies = [ "anyhow", "circuit_sequencer_api 0.140.0", "circuit_sequencer_api 0.141.1", - "circuit_sequencer_api 0.150.2", + "circuit_sequencer_api 0.150.4", "futures 0.3.28", "itertools 0.10.5", "num_cpus", @@ -8140,7 +8140,7 @@ dependencies = [ "vise", "zk_evm 0.133.0", "zk_evm 0.141.0", - "zk_evm 0.150.0", + "zk_evm 0.150.4", "zksync_contracts", "zksync_dal", "zksync_eth_client", @@ -8799,9 +8799,9 @@ dependencies = [ [[package]] name = "zksync_kzg" -version = "0.150.2" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b76d0e08b3e0970565f7a9a611278547f4f1dbd6184a250c8c5e743aed61c525" +checksum = "9949f48ea1a9f9a0e73242d4d1e87e681095181827486b3fcc2cf93e5aa03280" dependencies = [ "boojum", "derivative", @@ -8811,7 +8811,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "zkevm_circuits 0.150.3", + "zkevm_circuits 0.150.4", ] [[package]] @@ -8934,7 +8934,7 @@ dependencies = [ "circuit_sequencer_api 0.140.0", "circuit_sequencer_api 0.141.1", "circuit_sequencer_api 0.142.0", - "circuit_sequencer_api 0.150.2", + "circuit_sequencer_api 0.150.4", "ethabi", "hex", "itertools 0.10.5", @@ -8949,7 +8949,7 @@ dependencies = [ "zk_evm 0.133.0", "zk_evm 0.140.0", "zk_evm 0.141.0", - "zk_evm 0.150.0", + "zk_evm 0.150.4", "zksync_contracts", "zksync_eth_signer", "zksync_system_constants", @@ -9377,7 +9377,7 @@ version = "0.1.0" dependencies = [ "bincode", "chrono", - "circuit_sequencer_api 0.150.2", + "circuit_sequencer_api 0.150.4", "serde", "serde_json", "serde_with", diff --git a/Cargo.toml b/Cargo.toml index d32b6c6a673..f2c62efb453 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -207,15 +207,15 @@ circuit_sequencer_api_1_3_3 = { package = "circuit_sequencer_api", version = "0. circuit_sequencer_api_1_4_0 = { package = "circuit_sequencer_api", version = "0.140" } circuit_sequencer_api_1_4_1 = { package = "circuit_sequencer_api", version = "0.141" } circuit_sequencer_api_1_4_2 = { package = "circuit_sequencer_api", version = "0.142" } -circuit_sequencer_api_1_5_0 = { package = "circuit_sequencer_api", version = "=0.150.2" } +circuit_sequencer_api_1_5_0 = { package = "circuit_sequencer_api", version = "=0.150.4" } crypto_codegen = { package = "zksync_solidity_vk_codegen", version = "=0.1.0" } -kzg = { package = "zksync_kzg", version = "=0.150.2" } +kzg = { package = "zksync_kzg", version = "=0.150.4" } zk_evm = { version = "=0.133.0" } zk_evm_1_3_1 = { package = "zk_evm", version = "0.131.0-rc.2" } zk_evm_1_3_3 = { package = "zk_evm", version = "0.133.0" } zk_evm_1_4_0 = { package = "zk_evm", version = "0.140.0" } zk_evm_1_4_1 = { package = "zk_evm", version = "0.141.0" } -zk_evm_1_5_0 = { package = "zk_evm", version = "=0.150.0" } +zk_evm_1_5_0 = { package = "zk_evm", version = "=0.150.4" } # New VM; pinned to a specific commit because of instability vm2 = { git = "https://github.com/matter-labs/vm2.git", rev = "9a38900d7af9b1d72b47ce3be980e77c1239a61d" } diff --git a/core/lib/object_store/src/file.rs b/core/lib/object_store/src/file.rs index decba534d23..e62f40fb943 100644 --- a/core/lib/object_store/src/file.rs +++ b/core/lib/object_store/src/file.rs @@ -43,6 +43,7 @@ impl FileBackedObjectStore { Bucket::ProofsFri, Bucket::StorageSnapshot, Bucket::TeeVerifierInput, + Bucket::RamPermutationCircuitQueueWitness, ] { let bucket_path = format!("{base_dir}/{bucket}"); fs::create_dir_all(&bucket_path).await?; diff --git a/core/lib/object_store/src/raw.rs b/core/lib/object_store/src/raw.rs index 3c5a89f160a..32deec061bd 100644 --- a/core/lib/object_store/src/raw.rs +++ b/core/lib/object_store/src/raw.rs @@ -20,6 +20,7 @@ pub enum Bucket { StorageSnapshot, DataAvailability, TeeVerifierInput, + RamPermutationCircuitQueueWitness, } impl Bucket { @@ -39,6 +40,7 @@ impl Bucket { Self::StorageSnapshot => "storage_logs_snapshots", Self::DataAvailability => "data_availability", Self::TeeVerifierInput => "tee_verifier_inputs", + Self::RamPermutationCircuitQueueWitness => "ram_permutation_witnesses", } } } diff --git a/prover/Cargo.lock b/prover/Cargo.lock index 65ef5e0eacc..582f15637b5 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -718,9 +718,9 @@ dependencies = [ [[package]] name = "boojum-cuda" -version = "0.2.0" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "407123a79308091866f0199d510ee2fb930727204dd77d6805b3437d6cb859eb" +checksum = "c861b4baec895cb8e53b10825407f0844b0eafda2ac79e7f02de95439f0f1e74" dependencies = [ "boojum", "cmake", @@ -872,11 +872,11 @@ dependencies = [ [[package]] name = "circuit_definitions" -version = "0.150.2" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "382960e9ff16705f95157bac88d2b0b556181229019eb57db6c990e3a0fff35f" +checksum = "fffaa17c1585fbf010b9340bb1fd7f4c4eedec2c15cb74a72162fd2d16435d55" dependencies = [ - "circuit_encodings 0.150.2", + "circuit_encodings 0.150.4", "crossbeam 0.8.4", "derivative", "seq-macro", @@ -922,14 +922,14 @@ dependencies = [ [[package]] name = "circuit_encodings" -version = "0.150.2" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ba840a74f8d0b8b1334e93e4c87514a27c9be83d42d9f78d0c577572bb5f435" +checksum = "2593c02ad6b4b31ba63506c3f807f666133dd36bf47422f99b1d2947cf3c8dc1" dependencies = [ "derivative", "serde", - "zk_evm 0.150.0", - "zkevm_circuits 0.150.3", + "zk_evm 0.150.4", + "zkevm_circuits 0.150.4", ] [[package]] @@ -989,12 +989,12 @@ dependencies = [ [[package]] name = "circuit_sequencer_api" -version = "0.150.2" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79f3177b2bcd4ef5da9d2ca6916f6de31fb1973dfece27907a8dc7c69233494d" +checksum = "42d1a86b9c2207f3bb2dff5f00d1af1cb95004b6d07e9bacb6519fe08f12c04b" dependencies = [ "bellman_ce 0.7.0", - "circuit_encodings 0.150.2", + "circuit_encodings 0.150.4", "derivative", "rayon", "serde", @@ -1824,9 +1824,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "era_cudart" -version = "0.2.0" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6592e1277ac1ab0f3925151784a3809f4f973b1a63a0244b6d44e3872b413199" +checksum = "4ac97d833b861e32bc0a71d0542bf5c92094f9818c52d65c695227bfa95ffbe3" dependencies = [ "bitflags 2.6.0", "era_cudart_sys", @@ -1835,9 +1835,9 @@ dependencies = [ [[package]] name = "era_cudart_sys" -version = "0.2.0" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21767c452b418a7fb2bb9ffb07c744e4616da8d14176db4dcab76649c3206ece" +checksum = "ee6aed60cf09cb6d0b954d74351acb9beb13daab0bacad279691f6b97504b7e6" dependencies = [ "serde_json", ] @@ -5507,9 +5507,9 @@ checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" [[package]] name = "shivini" -version = "0.150.3" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee96349e7395922586c312936b259cb80b3d0a27f227dc3adee480a79d52a4e6" +checksum = "c5e5d862287bb883a4cb0bc4f8ea938ba3fdaa5e495f1a59bc3515231017a0e2" dependencies = [ "bincode", "blake2 0.10.6", @@ -6781,8 +6781,8 @@ source = "git+https://github.com/matter-labs/vm2.git?rev=9a38900d7af9b1d72b47ce3 dependencies = [ "enum_dispatch", "primitive-types", - "zk_evm_abstractions 0.150.0", - "zkevm_opcode_defs 0.150.0", + "zk_evm_abstractions 0.150.4", + "zkevm_opcode_defs 0.150.4", ] [[package]] @@ -7286,9 +7286,9 @@ dependencies = [ [[package]] name = "zk_evm" -version = "0.150.0" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5bf91304aa14827758afa3def8cf622f9a7f9fb65fe5d5099018dbacf0c5984" +checksum = "e2dbb0ed38d61fbd04bd7575755924d1303e129c04c909abba7f5bfcc6260bcf" dependencies = [ "anyhow", "lazy_static", @@ -7296,7 +7296,7 @@ dependencies = [ "serde", "serde_json", "static_assertions", - "zk_evm_abstractions 0.150.0", + "zk_evm_abstractions 0.150.4", ] [[package]] @@ -7327,22 +7327,22 @@ dependencies = [ [[package]] name = "zk_evm_abstractions" -version = "0.150.0" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc313cea4ac9ef6b855264b1425cbe9de30dd8f009559dabcb6b2896122da5db" +checksum = "31460aacfe65b39ac484a2a2e0bbb02baf141f65264bf48e1e4f59ab375fe933" dependencies = [ "anyhow", "num_enum 0.6.1", "serde", "static_assertions", - "zkevm_opcode_defs 0.150.0", + "zkevm_opcode_defs 0.150.4", ] [[package]] name = "zkevm-assembly" -version = "0.150.0" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d55e7082c5a313e46e1017d12ea5acfba9f961af3c260ff580490ce02d52067c" +checksum = "7b69d09d125b94767847c4cdc4ae399654b9e2a2f9304bd8935a7033bef4b07c" dependencies = [ "env_logger 0.9.3", "hex", @@ -7355,7 +7355,7 @@ dependencies = [ "smallvec", "structopt", "thiserror", - "zkevm_opcode_defs 0.150.0", + "zkevm_opcode_defs 0.150.4", ] [[package]] @@ -7404,9 +7404,9 @@ dependencies = [ [[package]] name = "zkevm_circuits" -version = "0.150.3" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d64bda28dec766324d2e5095a46fb141540d86a232106760dfb20ab4ae6e5c" +checksum = "abdfaa95dfe0878fda219dd17a6cc8c28711e2067785910c0e06d3ffdca78629" dependencies = [ "arrayvec 0.7.4", "boojum", @@ -7419,7 +7419,7 @@ dependencies = [ "seq-macro", "serde", "smallvec", - "zkevm_opcode_defs 0.150.0", + "zkevm_opcode_defs 0.150.4", ] [[package]] @@ -7466,9 +7466,9 @@ dependencies = [ [[package]] name = "zkevm_opcode_defs" -version = "0.150.0" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3328c012d444bdbfadb754a72c01a56879eb66584efc71eac457e89e7843608" +checksum = "bb7c5c7b4481a646f8696b08cee64a8dec097509a6378d18242f81022f327f1e" dependencies = [ "bitflags 2.6.0", "blake2 0.10.6", @@ -7483,13 +7483,13 @@ dependencies = [ [[package]] name = "zkevm_test_harness" -version = "0.150.2" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be67d84d0ac41145a4daed8333feac0936ade29feda6448f46d80ae80285911d" +checksum = "9416dc5fcf7bc403d4c24d37f0e9a492a81926ff0e89a7792dc8a29de69aec1b" dependencies = [ "bincode", "circuit_definitions", - "circuit_sequencer_api 0.150.2", + "circuit_sequencer_api 0.150.4", "codegen", "crossbeam 0.8.4", "derivative", @@ -7510,9 +7510,9 @@ dependencies = [ [[package]] name = "zksync-gpu-ffi" -version = "0.150.0" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3143200cfbf1dd8e2e14c2bf2a2b89da8fa5628c7192a4739f13269b9707656e" +checksum = "82fe099f4f4a2cc8ca8ca591d7619ac00b8054f63b712fa6ceee2b84c6e04c62" dependencies = [ "bindgen 0.59.2", "crossbeam 0.8.4", @@ -7524,9 +7524,9 @@ dependencies = [ [[package]] name = "zksync-gpu-prover" -version = "0.150.0" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aeacd406321241ecbcedf9f3025af23511a83e666ecdec2c971935225ea5b98" +checksum = "f73d27e0e4589c7445f5a22e511cb5186e2d205172ca4b26acd7a334b3af9492" dependencies = [ "bit-vec", "cfg-if 1.0.0", @@ -7541,9 +7541,9 @@ dependencies = [ [[package]] name = "zksync-wrapper-prover" -version = "0.150.0" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf646f359c7275451c218dcf3cd99c06afb0d21da9cc518a1aa5222ee44ee8c" +checksum = "1cf4c09adf0a84af0d7ded1fd85a2487fef4cbf1cfc1925412717d0eef03dd5a" dependencies = [ "circuit_definitions", "zkevm_test_harness", @@ -7831,9 +7831,9 @@ dependencies = [ [[package]] name = "zksync_kzg" -version = "0.150.2" +version = "0.150.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b76d0e08b3e0970565f7a9a611278547f4f1dbd6184a250c8c5e743aed61c525" +checksum = "9949f48ea1a9f9a0e73242d4d1e87e681095181827486b3fcc2cf93e5aa03280" dependencies = [ "boojum", "derivative", @@ -7843,7 +7843,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "zkevm_circuits 0.150.3", + "zkevm_circuits 0.150.4", ] [[package]] @@ -7883,7 +7883,7 @@ dependencies = [ "circuit_sequencer_api 0.140.0", "circuit_sequencer_api 0.141.1", "circuit_sequencer_api 0.142.0", - "circuit_sequencer_api 0.150.2", + "circuit_sequencer_api 0.150.4", "hex", "itertools 0.10.5", "once_cell", @@ -7896,7 +7896,7 @@ dependencies = [ "zk_evm 0.133.0", "zk_evm 0.140.0", "zk_evm 0.141.0", - "zk_evm 0.150.0", + "zk_evm 0.150.4", "zksync_contracts", "zksync_system_constants", "zksync_types", @@ -7955,7 +7955,7 @@ dependencies = [ "anyhow", "async-trait", "bincode", - "circuit_sequencer_api 0.150.2", + "circuit_sequencer_api 0.150.4", "clap 4.5.4", "ctrlc", "futures 0.3.30", @@ -8141,7 +8141,7 @@ name = "zksync_prover_interface" version = "0.1.0" dependencies = [ "chrono", - "circuit_sequencer_api 0.150.2", + "circuit_sequencer_api 0.150.4", "serde", "serde_with", "strum", diff --git a/prover/Cargo.toml b/prover/Cargo.toml index 8be6f355223..4ce85833250 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -56,13 +56,13 @@ tracing-subscriber = { version = "0.3" } vise = "0.2.0" # Proving dependencies -circuit_definitions = "=0.150.2" -circuit_sequencer_api = "=0.150.2" -zkevm_test_harness = "=0.150.2" +circuit_definitions = "=0.150.4" +circuit_sequencer_api = "=0.150.4" +zkevm_test_harness = "=0.150.4" # GPU proving dependencies -wrapper_prover = { package = "zksync-wrapper-prover", version = "=0.150.0" } -shivini = "=0.150.3" +wrapper_prover = { package = "zksync-wrapper-prover", version = "=0.150.4" } +shivini = "=0.150.4" # Core workspace dependencies zksync_multivm = { path = "../core/lib/multivm", version = "0.1.0" } diff --git a/prover/crates/bin/prover_cli/src/commands/get_file_info.rs b/prover/crates/bin/prover_cli/src/commands/get_file_info.rs index 63d7f25f615..cb4a45ca390 100644 --- a/prover/crates/bin/prover_cli/src/commands/get_file_info.rs +++ b/prover/crates/bin/prover_cli/src/commands/get_file_info.rs @@ -73,7 +73,7 @@ fn pretty_print_scheduler_witness( fn pretty_print_circuit_wrapper(circuit: &CircuitWrapper) { println!(" == Circuit =="); match circuit { - CircuitWrapper::Base(circuit) => { + CircuitWrapper::Base(circuit) | CircuitWrapper::BasePartial((circuit, _)) => { println!( "Type: basic. Id: {:?} ({})", circuit.numeric_circuit_type(), diff --git a/prover/crates/bin/prover_fri/src/gpu_prover_job_processor.rs b/prover/crates/bin/prover_fri/src/gpu_prover_job_processor.rs index 04146473f64..4407dbcd852 100644 --- a/prover/crates/bin/prover_fri/src/gpu_prover_job_processor.rs +++ b/prover/crates/bin/prover_fri/src/gpu_prover_job_processor.rs @@ -154,6 +154,7 @@ pub mod gpu_prover { recursion_layer_proof_config(), circuit.numeric_circuit_type(), ), + CircuitWrapper::BasePartial(_) => panic!("Invalid CircuitWrapper received"), }; let started_at = Instant::now(); @@ -196,6 +197,7 @@ pub mod gpu_prover { CircuitWrapper::Recursive(_) => FriProofWrapper::Recursive( ZkSyncRecursionLayerProof::from_inner(circuit_id, proof), ), + CircuitWrapper::BasePartial(_) => panic!("Received partial base circuit"), }; ProverArtifacts::new(prover_job.block_number, proof_wrapper) } diff --git a/prover/crates/bin/prover_fri/src/prover_job_processor.rs b/prover/crates/bin/prover_fri/src/prover_job_processor.rs index f06f1bbab93..09c9d38348f 100644 --- a/prover/crates/bin/prover_fri/src/prover_job_processor.rs +++ b/prover/crates/bin/prover_fri/src/prover_job_processor.rs @@ -109,6 +109,7 @@ impl Prover { CircuitWrapper::Recursive(recursive_circuit) => { Self::prove_recursive_layer(job.job_id, recursive_circuit, config, setup_data) } + CircuitWrapper::BasePartial(_) => panic!("Received partial base circuit"), }; ProverArtifacts::new(job.block_number, proof) } diff --git a/prover/crates/bin/prover_fri/src/utils.rs b/prover/crates/bin/prover_fri/src/utils.rs index 15a2a6c18bb..2941c15439a 100644 --- a/prover/crates/bin/prover_fri/src/utils.rs +++ b/prover/crates/bin/prover_fri/src/utils.rs @@ -128,6 +128,7 @@ pub fn verify_proof( verify_recursion_layer_proof::(recursive_circuit, proof, vk), recursive_circuit.numeric_circuit_type(), ), + CircuitWrapper::BasePartial(_) => panic!("Invalid CircuitWrapper received"), }; METRICS.proof_verification_time[&circuit_id.to_string()].observe(started_at.elapsed()); diff --git a/prover/crates/bin/witness_generator/src/basic_circuits.rs b/prover/crates/bin/witness_generator/src/basic_circuits.rs index 75326ace7f6..6dc19bd022b 100644 --- a/prover/crates/bin/witness_generator/src/basic_circuits.rs +++ b/prover/crates/bin/witness_generator/src/basic_circuits.rs @@ -8,13 +8,15 @@ use std::{ use anyhow::Context as _; use async_trait::async_trait; use circuit_definitions::{ - circuit_definitions::base_layer::ZkSyncBaseLayerStorage, + circuit_definitions::base_layer::{ZkSyncBaseLayerCircuit, ZkSyncBaseLayerStorage}, encodings::recursion_request::RecursionQueueSimulator, zkevm_circuits::fsm_input_output::ClosedFormInputCompactFormWitness, }; use tokio::sync::Semaphore; use tracing::Instrument; -use zkevm_test_harness::geometry_config::get_geometry_config; +use zkevm_test_harness::{ + geometry_config::get_geometry_config, witness::oracle::WitnessGenerationArtifact, +}; use zksync_config::configs::FriWitnessGeneratorConfig; use zksync_multivm::{ interface::storage::StorageView, @@ -34,7 +36,7 @@ use zksync_prover_fri_types::{ }, get_current_pod_name, keys::ClosedFormInputKey, - AuxOutputWitnessWrapper, + AuxOutputWitnessWrapper, CircuitAuxData, }; use zksync_prover_fri_utils::get_recursive_layer_circuit_id_for_base_layer; use zksync_prover_interface::inputs::WitnessInputData; @@ -49,8 +51,8 @@ use crate::{ precalculated_merkle_paths_provider::PrecalculatedMerklePathsProvider, storage_oracle::StorageOracle, utils::{ - expand_bootloader_contents, save_circuit, ClosedFormInputWrapper, - SchedulerPartialInputWrapper, KZG_TRUSTED_SETUP_FILE, + expand_bootloader_contents, save_circuit, save_ram_premutation_queue_witness, + ClosedFormInputWrapper, SchedulerPartialInputWrapper, KZG_TRUSTED_SETUP_FILE, }, witness::WitnessStorage, }; @@ -432,6 +434,8 @@ async fn generate_witness( let (circuit_sender, mut circuit_receiver) = tokio::sync::mpsc::channel(1); let (queue_sender, mut queue_receiver) = tokio::sync::mpsc::channel(1); + let (ram_permutation_queue_sender, mut ram_permutation_queue_receiver) = + tokio::sync::mpsc::channel(1); let make_circuits_span = tracing::info_span!("make_circuits"); let make_circuits_span_copy = make_circuits_span.clone(); @@ -457,6 +461,29 @@ async fn generate_witness( .to_str() .expect("Path to KZG trusted setup is not a UTF-8 string"); + let artifacts_callback = |artifact: WitnessGenerationArtifact| match artifact { + WitnessGenerationArtifact::BaseLayerCircuit(circuit) => { + let parent_span = span.clone(); + tracing::info_span!(parent: parent_span, "send_circuit").in_scope(|| { + circuit_sender + .blocking_send(circuit) + .expect("failed to send circuit from harness"); + }); + } + WitnessGenerationArtifact::RecursionQueue((a, b, c)) => queue_sender + .blocking_send((a as u8, b, c)) + .expect("failed to send recursion queue from harness"), + a @ WitnessGenerationArtifact::MemoryQueueWitness(_) => { + let parent_span = span.clone(); + tracing::info_span!(parent: parent_span, "send_ram_permutation_queue_witness") + .in_scope(|| { + ram_permutation_queue_sender + .blocking_send(a) + .expect("failed to send ram permutation queue sitness from harness"); + }); + } + }; + let (scheduler_witness, block_witness) = zkevm_test_harness::external_calls::run( Address::zero(), BOOTLOADER_ADDRESS, @@ -474,24 +501,14 @@ async fn generate_witness( tree, path, input.eip_4844_blobs.blobs(), - |circuit| { - let parent_span = span.clone(); - tracing::info_span!(parent: parent_span, "send_circuit").in_scope(|| { - circuit_sender - .blocking_send(circuit) - .expect("failed to send circuit from harness"); - }); - }, - |a, b, c| { - queue_sender - .blocking_send((a as u8, b, c)) - .expect("failed to send recursion queue from harness") - }, + artifacts_callback, ); (scheduler_witness, block_witness) }) .instrument(make_circuits_span); + let semaphore = Arc::new(Semaphore::new(max_circuits_in_flight)); + let mut save_circuit_handles = vec![]; let save_circuits_span = tracing::info_span!("save_circuits"); @@ -503,7 +520,7 @@ async fn generate_witness( // If the order is tampered with, proving will fail (as the proof would be computed for a different sequence of instruction). let mut circuit_sequence = 0; - let semaphore = Arc::new(Semaphore::new(max_circuits_in_flight)); + let mut ram_circuit_sequence = 0; while let Some(circuit) = circuit_receiver .recv() @@ -518,9 +535,27 @@ async fn generate_witness( .acquire_owned() .await .expect("failed to get permit for running save circuit task"); + + let partial_circuit_aux_data = match &circuit { + ZkSyncBaseLayerCircuit::RAMPermutation(_) => { + let circuit_subsequence_number = ram_circuit_sequence; + ram_circuit_sequence += 1; + Some(CircuitAuxData { + circuit_subsequence_number, + }) + } + _ => None, + }; + save_circuit_handles.push(tokio::task::spawn(async move { - let (circuit_id, circuit_url) = - save_circuit(block_number, circuit, sequence, object_store).await; + let (circuit_id, circuit_url) = save_circuit( + block_number, + circuit, + sequence, + partial_circuit_aux_data, + object_store, + ) + .await; drop(permit); (circuit_id, circuit_url) })); @@ -528,6 +563,57 @@ async fn generate_witness( } .instrument(save_circuits_span); + let mut save_ram_queue_witness_handles = vec![]; + + let save_ram_queue_witness_span = tracing::info_span!("save_circuits"); + + // Future which receives part of RAM permutation circuits witnesses and saves them async. + // Uses semaphore because these artifacts are of significant size + let ram_queue_witness_receiver_handle = async { + let mut sorted_sequence = 0; + let mut unsorted_sequence = 0; + + while let Some(witness_artifact) = ram_permutation_queue_receiver + .recv() + .instrument(tracing::info_span!("wait_for_ram_witness")) + .await + { + let object_store = object_store.clone(); + let semaphore = semaphore.clone(); + let permit = semaphore + .acquire_owned() + .await + .expect("failed to get permit for running save ram permutation queue witness task"); + let (is_sorted, witness, sequence) = match witness_artifact { + WitnessGenerationArtifact::MemoryQueueWitness((witness, sorted)) => { + let sequence = if sorted { + let sequence = sorted_sequence; + sorted_sequence += 1; + sequence + } else { + let sequence = unsorted_sequence; + unsorted_sequence += 1; + sequence + }; + (sorted, witness, sequence) + } + _ => panic!("Invalid artifact received"), + }; + save_ram_queue_witness_handles.push(tokio::task::spawn(async move { + let _ = save_ram_premutation_queue_witness( + block_number, + sequence, + is_sorted, + witness, + object_store, + ) + .await; + drop(permit); + })); + } + } + .instrument(save_ram_queue_witness_span); + let mut save_queue_handles = vec![]; let save_queues_span = tracing::info_span!("save_queues"); @@ -553,10 +639,11 @@ async fn generate_witness( } .instrument(save_queues_span); - let (witnesses, _, _) = tokio::join!( + let (witnesses, _, _, _) = tokio::join!( make_circuits_handle, circuit_receiver_handle, - queue_receiver_handle + queue_receiver_handle, + ram_queue_witness_receiver_handle ); let (mut scheduler_witness, block_aux_witness) = witnesses.unwrap(); @@ -581,6 +668,8 @@ async fn generate_witness( .filter(|(circuit_id, _, _)| circuits_present.contains(circuit_id)) .collect(); + futures::future::join_all(save_ram_queue_witness_handles).await; + scheduler_witness.previous_block_meta_hash = input.previous_batch_metadata.meta_hash.0; scheduler_witness.previous_block_aux_hash = input.previous_batch_metadata.aux_hash.0; diff --git a/prover/crates/bin/witness_generator/src/utils.rs b/prover/crates/bin/witness_generator/src/utils.rs index 65fe26d63f5..f8656ac90f4 100644 --- a/prover/crates/bin/witness_generator/src/utils.rs +++ b/prover/crates/bin/witness_generator/src/utils.rs @@ -4,9 +4,12 @@ use std::{ sync::Arc, }; -use circuit_definitions::circuit_definitions::{ - base_layer::ZkSyncBaseLayerCircuit, - recursion_layer::{ZkSyncRecursionLayerStorageType, ZkSyncRecursionProof}, +use circuit_definitions::{ + circuit_definitions::{ + base_layer::ZkSyncBaseLayerCircuit, + recursion_layer::{ZkSyncRecursionLayerStorageType, ZkSyncRecursionProof}, + }, + encodings::memory_query::MemoryQueueStateWitnesses, }; use once_cell::sync::Lazy; use zkevm_test_harness::{ @@ -28,8 +31,8 @@ use zksync_prover_fri_types::{ encodings::recursion_request::RecursionQueueSimulator, zkevm_circuits::scheduler::input::SchedulerCircuitInstanceWitness, }, - keys::{AggregationsKey, ClosedFormInputKey, FriCircuitKey}, - CircuitWrapper, FriProofWrapper, + keys::{AggregationsKey, ClosedFormInputKey, FriCircuitKey, RamPermutationQueueWitnessKey}, + CircuitAuxData, CircuitWrapper, FriProofWrapper, RamPermutationQueueWitness, }; use zksync_types::{basic_fri_types::AggregationRound, L1BatchNumber, ProtocolVersionId, U256}; @@ -125,6 +128,7 @@ pub async fn save_circuit( block_number: L1BatchNumber, circuit: ZkSyncBaseLayerCircuit, sequence_number: usize, + aux_data_for_partial_circuit: Option, object_store: Arc, ) -> (u8, String) { let circuit_id = circuit.numeric_circuit_type(); @@ -135,13 +139,46 @@ pub async fn save_circuit( aggregation_round: AggregationRound::BasicCircuits, depth: 0, }; - let blob_url = object_store - .put(circuit_key, &CircuitWrapper::Base(circuit)) - .await - .unwrap(); + + let blob_url = if let Some(aux_data_for_partial_circuit) = aux_data_for_partial_circuit { + object_store + .put( + circuit_key, + &CircuitWrapper::BasePartial((circuit, aux_data_for_partial_circuit)), + ) + .await + .unwrap() + } else { + object_store + .put(circuit_key, &CircuitWrapper::Base(circuit)) + .await + .unwrap() + }; (circuit_id, blob_url) } +#[tracing::instrument( + skip_all, + fields(l1_batch = %block_number) +)] +pub async fn save_ram_premutation_queue_witness( + block_number: L1BatchNumber, + circuit_subsequence_number: usize, + is_sorted: bool, + witness: MemoryQueueStateWitnesses, + object_store: Arc, +) -> String { + let witness_key = RamPermutationQueueWitnessKey { + block_number, + circuit_subsequence_number, + is_sorted, + }; + object_store + .put(witness_key, &RamPermutationQueueWitness { witness }) + .await + .unwrap() +} + #[tracing::instrument( skip_all, fields(l1_batch = %block_number) diff --git a/prover/crates/bin/witness_vector_generator/src/generator.rs b/prover/crates/bin/witness_vector_generator/src/generator.rs index e26173067fb..800931f5d7c 100644 --- a/prover/crates/bin/witness_vector_generator/src/generator.rs +++ b/prover/crates/bin/witness_vector_generator/src/generator.rs @@ -79,6 +79,9 @@ impl WitnessVectorGenerator { CircuitWrapper::Recursive(recursive_circuit) => { recursive_circuit.synthesis::(&finalization_hints) } + CircuitWrapper::BasePartial(_) => { + panic!("Invalid circuit wrapper received for witness vector generation"); + } }; Ok(WitnessVectorArtifacts::new(cs.witness.unwrap(), job)) } diff --git a/prover/crates/lib/prover_fri_types/src/keys.rs b/prover/crates/lib/prover_fri_types/src/keys.rs index 729db754178..2948fc5f84e 100644 --- a/prover/crates/lib/prover_fri_types/src/keys.rs +++ b/prover/crates/lib/prover_fri_types/src/keys.rs @@ -35,3 +35,11 @@ pub struct CircuitKey<'a> { pub circuit_type: &'a str, pub aggregation_round: AggregationRound, } + +/// Storage key for a [`RamPermutationQueueWitness`]. +#[derive(Debug, Clone, Copy)] +pub struct RamPermutationQueueWitnessKey { + pub block_number: L1BatchNumber, + pub circuit_subsequence_number: usize, + pub is_sorted: bool, +} diff --git a/prover/crates/lib/prover_fri_types/src/lib.rs b/prover/crates/lib/prover_fri_types/src/lib.rs index 423be1f88fa..a327111fe6f 100644 --- a/prover/crates/lib/prover_fri_types/src/lib.rs +++ b/prover/crates/lib/prover_fri_types/src/lib.rs @@ -9,10 +9,12 @@ use circuit_definitions::{ ZkSyncRecursionLayerProof, ZkSyncRecursionLayerStorageType, ZkSyncRecursiveLayerCircuit, }, }, + encodings::memory_query::MemoryQueueStateWitnesses, zkevm_circuits::scheduler::{ aux::BaseLayerCircuitType, block_header::BlockAuxilaryOutputWitness, }, }; +use keys::RamPermutationQueueWitnessKey; use zksync_object_store::{serialize_using_bincode, Bucket, StoredObject}; use zksync_types::{ basic_fri_types::AggregationRound, @@ -33,11 +35,17 @@ pub const PROVER_PROTOCOL_SEMANTIC_VERSION: ProtocolSemanticVersion = ProtocolSe patch: PROVER_PROTOCOL_PATCH, }; +#[derive(serde::Serialize, serde::Deserialize, Clone)] +pub struct CircuitAuxData { + pub circuit_subsequence_number: u32, +} + #[derive(serde::Serialize, serde::Deserialize, Clone)] #[allow(clippy::large_enum_variant)] pub enum CircuitWrapper { Base(ZkSyncBaseLayerCircuit), Recursive(ZkSyncRecursiveLayerCircuit), + BasePartial((ZkSyncBaseLayerCircuit, CircuitAuxData)), } impl StoredObject for CircuitWrapper { @@ -214,3 +222,27 @@ impl StoredObject for AuxOutputWitnessWrapper { pub fn get_current_pod_name() -> String { env::var("POD_NAME").unwrap_or("UNKNOWN_POD".to_owned()) } + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct RamPermutationQueueWitness { + pub witness: MemoryQueueStateWitnesses, +} + +impl StoredObject for RamPermutationQueueWitness { + const BUCKET: Bucket = Bucket::RamPermutationCircuitQueueWitness; + type Key<'a> = RamPermutationQueueWitnessKey; + + fn encode_key(key: Self::Key<'_>) -> String { + let RamPermutationQueueWitnessKey { + block_number, + circuit_subsequence_number, + is_sorted, + } = key; + format!( + "queue_witness_{block_number}_{circuit_subsequence_number}_{}.bin", + is_sorted as u64 + ) + } + + serialize_using_bincode!(); +} diff --git a/prover/crates/lib/prover_fri_utils/src/lib.rs b/prover/crates/lib/prover_fri_utils/src/lib.rs index 0873d505628..02c6da3d5f5 100644 --- a/prover/crates/lib/prover_fri_utils/src/lib.rs +++ b/prover/crates/lib/prover_fri_utils/src/lib.rs @@ -4,14 +4,18 @@ use zksync_object_store::ObjectStore; use zksync_prover_dal::{Connection, Prover, ProverDal}; use zksync_prover_fri_types::{ circuit_definitions::{ - circuit_definitions::recursion_layer::{ - base_circuit_type_into_recursive_leaf_circuit_type, ZkSyncRecursionLayerStorageType, + boojum::gadgets::queue::full_state_queue::FullStateCircuitQueueRawWitness, + circuit_definitions::{ + base_layer::ZkSyncBaseLayerCircuit, + recursion_layer::{ + base_circuit_type_into_recursive_leaf_circuit_type, ZkSyncRecursionLayerStorageType, + }, }, zkevm_circuits::scheduler::aux::BaseLayerCircuitType, }, get_current_pod_name, - keys::FriCircuitKey, - CircuitWrapper, ProverJob, ProverServiceDataKey, + keys::{FriCircuitKey, RamPermutationQueueWitnessKey}, + CircuitWrapper, ProverJob, ProverServiceDataKey, RamPermutationQueueWitness, }; use zksync_types::{ basic_fri_types::{AggregationRound, CircuitIdRoundTuple}, @@ -61,10 +65,52 @@ pub async fn fetch_next_circuit( depth: prover_job.depth, }; let started_at = Instant::now(); - let input = blob_store + let circuit_wrapper = blob_store .get(circuit_key) .await .unwrap_or_else(|err| panic!("{err:?}")); + let input = match circuit_wrapper { + a @ CircuitWrapper::Base(_) => a, + a @ CircuitWrapper::Recursive(_) => a, + CircuitWrapper::BasePartial((circuit, aux_data)) => { + // inject additional data + if let ZkSyncBaseLayerCircuit::RAMPermutation(circuit_instance) = circuit { + let sorted_witness_key = RamPermutationQueueWitnessKey { + block_number: prover_job.block_number, + circuit_subsequence_number: aux_data.circuit_subsequence_number as usize, + is_sorted: true, + }; + + let sorted_witness_handle = blob_store.get(sorted_witness_key); + + let unsorted_witness_key = RamPermutationQueueWitnessKey { + block_number: prover_job.block_number, + circuit_subsequence_number: aux_data.circuit_subsequence_number as usize, + is_sorted: false, + }; + + let unsorted_witness_handle = blob_store.get(unsorted_witness_key); + + let unsorted_witness: RamPermutationQueueWitness = + unsorted_witness_handle.await.unwrap(); + let sorted_witness: RamPermutationQueueWitness = + sorted_witness_handle.await.unwrap(); + + let mut witness = circuit_instance.witness.take().unwrap(); + witness.unsorted_queue_witness = FullStateCircuitQueueRawWitness { + elements: unsorted_witness.witness.into(), + }; + witness.sorted_queue_witness = FullStateCircuitQueueRawWitness { + elements: sorted_witness.witness.into(), + }; + circuit_instance.witness.store(Some(witness)); + + CircuitWrapper::Base(ZkSyncBaseLayerCircuit::RAMPermutation(circuit_instance)) + } else { + panic!("Unexpected circuit received with partial witness"); + } + } + }; let label = CircuitLabels { circuit_type: prover_job.circuit_id, @@ -97,7 +143,9 @@ pub fn get_base_layer_circuit_id_for_recursive_layer(recursive_layer_circuit_id: pub fn get_numeric_circuit_id(circuit_wrapper: &CircuitWrapper) -> u8 { match circuit_wrapper { - CircuitWrapper::Base(circuit) => circuit.numeric_circuit_type(), + CircuitWrapper::Base(circuit) | CircuitWrapper::BasePartial((circuit, _)) => { + circuit.numeric_circuit_type() + } CircuitWrapper::Recursive(circuit) => circuit.numeric_circuit_type(), } } From 8b8397a1cca6c29b25e22d3ccbfa349c83740d66 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Fri, 16 Aug 2024 14:37:26 +0200 Subject: [PATCH 8/8] refactor: updated attestation logic to the new algorithm (#2657) The new algorithm (https://github.com/matter-labs/era-consensus/pull/175) supports dynamic attestation committee. This pr does NOT implement committee rotation, it will be done once the consensus registry contract is merged. ENs no longer store the certificates. I've also implemented a test of the attestation logic. --- Cargo.lock | 53 ++-- Cargo.toml | 20 +- core/lib/dal/src/consensus_dal.rs | 12 +- core/node/consensus/src/en.rs | 152 +++++---- core/node/consensus/src/mn.rs | 122 +++++-- core/node/consensus/src/storage/connection.rs | 47 +-- core/node/consensus/src/storage/store.rs | 142 --------- core/node/consensus/src/storage/testonly.rs | 89 +++++- core/node/consensus/src/testonly.rs | 155 +++++---- core/node/consensus/src/tests/attestation.rs | 166 ++++++++++ core/node/consensus/src/tests/batch.rs | 120 +++++++ .../consensus/src/{tests.rs => tests/mod.rs} | 298 ++++-------------- prover/Cargo.lock | 28 +- zk_toolbox/Cargo.lock | 16 +- zk_toolbox/Cargo.toml | 2 +- 15 files changed, 810 insertions(+), 612 deletions(-) create mode 100644 core/node/consensus/src/tests/attestation.rs create mode 100644 core/node/consensus/src/tests/batch.rs rename core/node/consensus/src/{tests.rs => tests/mod.rs} (69%) diff --git a/Cargo.lock b/Cargo.lock index a8ecbd7636d..5bbd7217f4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3147,15 +3147,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.12.0" @@ -4717,7 +4708,7 @@ checksum = "8bdf592881d821b83d471f8af290226c8d51402259e9bb5be7f9f8bdebbb11ac" dependencies = [ "bytes", "heck 0.4.1", - "itertools 0.11.0", + "itertools 0.10.5", "log", "multimap", "once_cell", @@ -4738,7 +4729,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32" dependencies = [ "anyhow", - "itertools 0.11.0", + "itertools 0.10.5", "proc-macro2 1.0.86", "quote 1.0.36", "syn 2.0.72", @@ -8156,9 +8147,9 @@ dependencies = [ [[package]] name = "zksync_concurrency" -version = "0.1.0-rc.10" +version = "0.1.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a463106f37cfa589896e6a165b5bb0533013377990e19f10e8c4894346a62e8b" +checksum = "b0e31a9fc9a390b440cd12bbe040330dc64f64697a8a8ecbc3beb98cd0747909" dependencies = [ "anyhow", "once_cell", @@ -8192,9 +8183,9 @@ dependencies = [ [[package]] name = "zksync_consensus_bft" -version = "0.1.0-rc.10" +version = "0.1.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1bed5bd7e219cc1429ae36732f6d943e4d98a1b4ddcbb60cff89a3a4d3bcd6" +checksum = "e22e3bfe96fa30a57313e774a5e8c74ffee884abff57ecacc10e8832315ee8a2" dependencies = [ "anyhow", "async-trait", @@ -8214,9 +8205,9 @@ dependencies = [ [[package]] name = "zksync_consensus_crypto" -version = "0.1.0-rc.10" +version = "0.1.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f0883af373e9198fd27c0148e7e47b37f912cb4b444bec3f7eed0af0b0dfc69" +checksum = "efb7ff3ec44b7b92fd4e28d9d92b83d61dc74125ccfc90bcfb27a5750d8a8580" dependencies = [ "anyhow", "blst", @@ -8238,9 +8229,9 @@ dependencies = [ [[package]] name = "zksync_consensus_executor" -version = "0.1.0-rc.10" +version = "0.1.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d70afdfc07658d6bb309237c5da2cab40ab7efed95538c92fd0340b1b967818c" +checksum = "a7fcde1275970a6b8a33ea2ade5cc994d6392f95509ce374e0e7a26cde4cd6db" dependencies = [ "anyhow", "async-trait", @@ -8259,9 +8250,9 @@ dependencies = [ [[package]] name = "zksync_consensus_network" -version = "0.1.0-rc.10" +version = "0.1.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82f6f2dbd122b60a199843bd70b9b979190e81458fe17180e23f930ea2194e1" +checksum = "e6ee48bee7dae8adb2769c7315adde1780832d05ecb6a77c08cdda53a315992a" dependencies = [ "anyhow", "async-trait", @@ -8294,9 +8285,9 @@ dependencies = [ [[package]] name = "zksync_consensus_roles" -version = "0.1.0-rc.10" +version = "0.1.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e426aa7c68a12dde702c3ec4ef49de24d9054ef908384232b7887e043ca3f2fe" +checksum = "72223c0b20621775db51bcc4b043addafeaf784d444af2ad4bc8bcdee477367c" dependencies = [ "anyhow", "bit-vec", @@ -8316,9 +8307,9 @@ dependencies = [ [[package]] name = "zksync_consensus_storage" -version = "0.1.0-rc.10" +version = "0.1.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8388c33fd5bc3725e58c26db2d3016538c6221c6448b3e92cf5df07f6074a028" +checksum = "41d1750ad93f7e3a0c2f5880f9bcc1244a3b46d3e6c124c4f65f545032b87464" dependencies = [ "anyhow", "async-trait", @@ -8336,9 +8327,9 @@ dependencies = [ [[package]] name = "zksync_consensus_utils" -version = "0.1.0-rc.10" +version = "0.1.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612920e56dcb99f227bc23e1254f4dabc7cb4c5cd1a9ec400ceba0ec6fa77c1e" +checksum = "2ff679f8b5f671d887a750b8107f3b5c01fd6085f68eef37ab01de8d2bd0736b" dependencies = [ "anyhow", "rand 0.8.5", @@ -9306,9 +9297,9 @@ dependencies = [ [[package]] name = "zksync_protobuf" -version = "0.1.0-rc.10" +version = "0.1.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0d82fd63f27681b9c01f0e01e3060e71b72809db8e21d9130663ee92bd1e391" +checksum = "f4f6ba3bf0aac20de18b4ae18a22d8c81b83f8f72e8fdec1c879525ecdacd2f5" dependencies = [ "anyhow", "bit-vec", @@ -9327,9 +9318,9 @@ dependencies = [ [[package]] name = "zksync_protobuf_build" -version = "0.1.0-rc.10" +version = "0.1.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee3c158ab4d211053886371d4a00514bdf8ebdf826d40ee03b98fee2e0d1605e" +checksum = "7798c248b9a64505f0586bd5fadad6b26c999be4a8dec6b1a86b10b3888169c5" dependencies = [ "anyhow", "heck 0.5.0", diff --git a/Cargo.toml b/Cargo.toml index f2c62efb453..d4855a34b9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -221,16 +221,16 @@ zk_evm_1_5_0 = { package = "zk_evm", version = "=0.150.4" } vm2 = { git = "https://github.com/matter-labs/vm2.git", rev = "9a38900d7af9b1d72b47ce3be980e77c1239a61d" } # Consensus dependencies. -zksync_concurrency = "=0.1.0-rc.10" -zksync_consensus_bft = "=0.1.0-rc.10" -zksync_consensus_crypto = "=0.1.0-rc.10" -zksync_consensus_executor = "=0.1.0-rc.10" -zksync_consensus_network = "=0.1.0-rc.10" -zksync_consensus_roles = "=0.1.0-rc.10" -zksync_consensus_storage = "=0.1.0-rc.10" -zksync_consensus_utils = "=0.1.0-rc.10" -zksync_protobuf = "=0.1.0-rc.10" -zksync_protobuf_build = "=0.1.0-rc.10" +zksync_concurrency = "=0.1.0-rc.11" +zksync_consensus_bft = "=0.1.0-rc.11" +zksync_consensus_crypto = "=0.1.0-rc.11" +zksync_consensus_executor = "=0.1.0-rc.11" +zksync_consensus_network = "=0.1.0-rc.11" +zksync_consensus_roles = "=0.1.0-rc.11" +zksync_consensus_storage = "=0.1.0-rc.11" +zksync_consensus_utils = "=0.1.0-rc.11" +zksync_protobuf = "=0.1.0-rc.11" +zksync_protobuf_build = "=0.1.0-rc.11" # "Local" dependencies zksync_multivm = { version = "0.1.0", path = "core/lib/multivm" } diff --git a/core/lib/dal/src/consensus_dal.rs b/core/lib/dal/src/consensus_dal.rs index d8f28705421..8f05cb38177 100644 --- a/core/lib/dal/src/consensus_dal.rs +++ b/core/lib/dal/src/consensus_dal.rs @@ -454,7 +454,7 @@ impl ConsensusDal<'_, '_> { /// Gets a number of the last L1 batch that was inserted. It might have gaps before it, /// depending on the order in which votes have been collected over gossip by consensus. - pub async fn get_last_batch_certificate_number( + pub async fn last_batch_certificate_number( &mut self, ) -> anyhow::Result> { let row = sqlx::query!( @@ -465,7 +465,7 @@ impl ConsensusDal<'_, '_> { l1_batches_consensus "# ) - .instrument("get_last_batch_certificate_number") + .instrument("last_batch_certificate_number") .report_latency() .fetch_one(self.storage) .await?; @@ -480,7 +480,7 @@ impl ConsensusDal<'_, '_> { /// Number of L1 batch that the L2 block belongs to. /// None if the L2 block doesn't exist. - async fn batch_of_block( + pub async fn batch_of_block( &mut self, block: validator::BlockNumber, ) -> anyhow::Result> { @@ -535,9 +535,9 @@ impl ConsensusDal<'_, '_> { let Some(next_batch_to_attest) = async { // First batch that we don't have a certificate for. if let Some(last) = self - .get_last_batch_certificate_number() + .last_batch_certificate_number() .await - .context("get_last_batch_certificate_number()")? + .context("last_batch_certificate_number()")? { return Ok(Some(last + 1)); } @@ -669,7 +669,7 @@ mod tests { // Retrieve the latest certificate. let number = conn .consensus_dal() - .get_last_batch_certificate_number() + .last_batch_certificate_number() .await .unwrap() .unwrap(); diff --git a/core/node/consensus/src/en.rs b/core/node/consensus/src/en.rs index ce8a555e06d..259cac5d074 100644 --- a/core/node/consensus/src/en.rs +++ b/core/node/consensus/src/en.rs @@ -1,10 +1,8 @@ +use std::sync::Arc; + use anyhow::Context as _; -use async_trait::async_trait; use zksync_concurrency::{ctx, error::Wrap as _, scope, time}; -use zksync_consensus_executor::{ - self as executor, - attestation::{AttestationStatusClient, AttestationStatusRunner}, -}; +use zksync_consensus_executor::{self as executor, attestation}; use zksync_consensus_roles::{attester, validator}; use zksync_consensus_storage::{BatchStore, BlockStore}; use zksync_dal::consensus_dal; @@ -38,9 +36,7 @@ impl EN { cfg: ConsensusConfig, secrets: ConsensusSecrets, ) -> anyhow::Result<()> { - let attester = config::attester_key(&secrets) - .context("attester_key")? - .map(|key| executor::Attester { key }); + let attester = config::attester_key(&secrets).context("attester_key")?; tracing::debug!( is_attester = attester.is_some(), @@ -53,7 +49,6 @@ impl EN { // Initialize genesis. let genesis = self.fetch_genesis(ctx).await.wrap("fetch_genesis()")?; - let genesis_hash = genesis.hash(); let mut conn = self.pool.connection(ctx).await.wrap("connection()")?; conn.try_update_genesis(ctx, &genesis) @@ -74,18 +69,21 @@ impl EN { // Monitor the genesis of the main node. // If it changes, it means that a hard fork occurred and we need to reset the consensus state. - s.spawn_bg::<()>(async { - let old = genesis; - loop { - if let Ok(new) = self.fetch_genesis(ctx).await { - if new != old { - return Err(anyhow::format_err!( - "genesis changed: old {old:?}, new {new:?}" - ) - .into()); + s.spawn_bg::<()>({ + let old = genesis.clone(); + async { + let old = old; + loop { + if let Ok(new) = self.fetch_genesis(ctx).await { + if new != old { + return Err(anyhow::format_err!( + "genesis changed: old {old:?}, new {new:?}" + ) + .into()); + } } + ctx.sleep(time::Duration::seconds(5)).await?; } - ctx.sleep(time::Duration::seconds(5)).await?; } }); @@ -106,17 +104,8 @@ impl EN { .wrap("BatchStore::new()")?; s.spawn_bg(async { Ok(runner.run(ctx).await?) }); - let (attestation_status, runner) = { - AttestationStatusRunner::init( - ctx, - Box::new(MainNodeAttestationStatus(self.client.clone())), - time::Duration::seconds(5), - genesis_hash, - ) - .await - .wrap("AttestationStatusRunner::init()")? - }; - s.spawn_bg(async { Ok(runner.run(ctx).await?) }); + let attestation = Arc::new(attestation::Controller::new(attester)); + s.spawn_bg(self.run_attestation_updater(ctx, genesis.clone(), attestation.clone())); let executor = executor::Executor { config: config::executor(&cfg, &secrets)?, @@ -129,8 +118,7 @@ impl EN { replica_store: Box::new(store.clone()), payload_manager: Box::new(store.clone()), }), - attester, - attestation_status, + attestation, }; tracing::info!("running the external node executor"); executor.run(ctx).await?; @@ -174,6 +162,62 @@ impl EN { } } + /// Monitors the `AttestationStatus` on the main node, + /// and updates the attestation config accordingly. + async fn run_attestation_updater( + &self, + ctx: &ctx::Ctx, + genesis: validator::Genesis, + attestation: Arc, + ) -> ctx::Result<()> { + const POLL_INTERVAL: time::Duration = time::Duration::seconds(5); + let Some(committee) = &genesis.attesters else { + return Ok(()); + }; + let committee = Arc::new(committee.clone()); + let mut next = attester::BatchNumber(0); + loop { + let status = loop { + match self.fetch_attestation_status(ctx).await { + Err(err) => tracing::warn!("{err:#}"), + Ok(status) => { + if status.genesis != genesis.hash() { + return Err(anyhow::format_err!("genesis mismatch").into()); + } + if status.next_batch_to_attest >= next { + break status; + } + } + } + ctx.sleep(POLL_INTERVAL).await?; + }; + tracing::info!( + "waiting for hash of batch {:?}", + status.next_batch_to_attest + ); + let hash = self + .pool + .wait_for_batch_hash(ctx, status.next_batch_to_attest) + .await?; + tracing::info!( + "attesting batch {:?} with hash {hash:?}", + status.next_batch_to_attest + ); + attestation + .start_attestation(Arc::new(attestation::Info { + batch_to_attest: attester::Batch { + genesis: status.genesis, + hash, + number: status.next_batch_to_attest, + }, + committee: committee.clone(), + })) + .await + .context("start_attestation()")?; + next = status.next_batch_to_attest.next(); + } + } + /// Periodically fetches the head of the main node /// and updates `SyncState` accordingly. async fn fetch_state_loop(&self, ctx: &ctx::Ctx) -> ctx::Result<()> { @@ -213,6 +257,22 @@ impl EN { .with_hash()) } + #[tracing::instrument(skip_all)] + async fn fetch_attestation_status( + &self, + ctx: &ctx::Ctx, + ) -> ctx::Result { + match ctx.wait(self.client.fetch_attestation_status()).await? { + Ok(Some(status)) => Ok(zksync_protobuf::serde::deserialize(&status.0) + .context("deserialize(AttestationStatus")?), + Ok(None) => Err(anyhow::format_err!("empty response").into()), + Err(err) => Err(anyhow::format_err!( + "AttestationStatus call to main node HTTP RPC failed: {err:#}" + ) + .into()), + } + } + /// Fetches (with retries) the given block from the main node. async fn fetch_block(&self, ctx: &ctx::Ctx, n: L2BlockNumber) -> ctx::Result { const RETRY_INTERVAL: time::Duration = time::Duration::seconds(5); @@ -269,31 +329,3 @@ impl EN { Ok(()) } } - -/// Wrapper to call [MainNodeClient::fetch_attestation_status] and adapt the return value to [AttestationStatusClient]. -struct MainNodeAttestationStatus(Box>); - -#[async_trait] -impl AttestationStatusClient for MainNodeAttestationStatus { - async fn attestation_status( - &self, - ctx: &ctx::Ctx, - ) -> ctx::Result> { - match ctx.wait(self.0.fetch_attestation_status()).await? { - Ok(Some(status)) => { - // If this fails the AttestationStatusRunner will log it an retry it later, - // but it won't stop the whole node. - let status: consensus_dal::AttestationStatus = - zksync_protobuf::serde::deserialize(&status.0) - .context("deserialize(AttestationStatus")?; - - Ok(Some((status.genesis, status.next_batch_to_attest))) - } - Ok(None) => Ok(None), - Err(err) => { - tracing::warn!("AttestationStatus call to main node HTTP RPC failed: {err}"); - Ok(None) - } - } - } -} diff --git a/core/node/consensus/src/mn.rs b/core/node/consensus/src/mn.rs index b5e76afd63e..7de86b4d8ba 100644 --- a/core/node/consensus/src/mn.rs +++ b/core/node/consensus/src/mn.rs @@ -1,13 +1,15 @@ +use std::sync::Arc; + use anyhow::Context as _; use zksync_concurrency::{ctx, error::Wrap as _, scope, time}; use zksync_config::configs::consensus::{ConsensusConfig, ConsensusSecrets}; -use zksync_consensus_executor::{self as executor, attestation::AttestationStatusRunner, Attester}; -use zksync_consensus_roles::validator; +use zksync_consensus_executor::{self as executor, attestation}; +use zksync_consensus_roles::{attester, validator}; use zksync_consensus_storage::{BatchStore, BlockStore}; use crate::{ config, - storage::{ConnectionPool, Store}, + storage::{ConnectionPool, InsertCertificateError, Store}, }; /// Task running a consensus validator for the main node. @@ -23,9 +25,7 @@ pub async fn run_main_node( .context("validator_key")? .context("missing validator_key")?; - let attester = config::attester_key(&secrets) - .context("attester_key")? - .map(|key| Attester { key }); + let attester = config::attester_key(&secrets).context("attester_key")?; tracing::debug!(is_attester = attester.is_some(), "main node attester mode"); @@ -42,7 +42,9 @@ pub async fn run_main_node( } // The main node doesn't have a payload queue as it produces all the L2 blocks itself. - let (store, runner) = Store::new(ctx, pool, None).await.wrap("Store::new()")?; + let (store, runner) = Store::new(ctx, pool.clone(), None) + .await + .wrap("Store::new()")?; s.spawn_bg(runner.run(ctx)); let (block_store, runner) = BlockStore::new(ctx, Box::new(store.clone())) @@ -50,8 +52,9 @@ pub async fn run_main_node( .wrap("BlockStore::new()")?; s.spawn_bg(runner.run(ctx)); + let genesis = block_store.genesis().clone(); anyhow::ensure!( - block_store.genesis().leader_selection + genesis.leader_selection == validator::LeaderSelectionMode::Sticky(validator_key.public()), "unsupported leader selection mode - main node has to be the leader" ); @@ -61,17 +64,13 @@ pub async fn run_main_node( .wrap("BatchStore::new()")?; s.spawn_bg(runner.run(ctx)); - let (attestation_status, runner) = { - AttestationStatusRunner::init_from_store( - ctx, - batch_store.clone(), - time::Duration::seconds(1), - block_store.genesis().hash(), - ) - .await - .wrap("AttestationStatusRunner::init_from_store()")? - }; - s.spawn_bg(runner.run(ctx)); + let attestation = Arc::new(attestation::Controller::new(attester)); + s.spawn_bg(run_attestation_updater( + ctx, + &pool, + genesis, + attestation.clone(), + )); let executor = executor::Executor { config: config::executor(&cfg, &secrets)?, @@ -82,8 +81,7 @@ pub async fn run_main_node( replica_store: Box::new(store.clone()), payload_manager: Box::new(store.clone()), }), - attester, - attestation_status, + attestation, }; tracing::info!("running the main node executor"); @@ -91,3 +89,85 @@ pub async fn run_main_node( }) .await } + +/// Manages attestation state by configuring the +/// next batch to attest and storing the collected +/// certificates. +async fn run_attestation_updater( + ctx: &ctx::Ctx, + pool: &ConnectionPool, + genesis: validator::Genesis, + attestation: Arc, +) -> anyhow::Result<()> { + const POLL_INTERVAL: time::Duration = time::Duration::seconds(5); + let res = async { + let Some(committee) = &genesis.attesters else { + return Ok(()); + }; + let committee = Arc::new(committee.clone()); + loop { + // After regenesis it might happen that the batch number for the first block + // is not immediately known (the first block was not produced yet), + // therefore we need to wait for it. + let status = loop { + match pool + .connection(ctx) + .await + .wrap("connection()")? + .attestation_status(ctx) + .await + .wrap("attestation_status()")? + { + Some(status) => break status, + None => ctx.sleep(POLL_INTERVAL).await?, + } + }; + tracing::info!( + "waiting for hash of batch {:?}", + status.next_batch_to_attest + ); + let hash = pool + .wait_for_batch_hash(ctx, status.next_batch_to_attest) + .await?; + tracing::info!( + "attesting batch {:?} with hash {hash:?}", + status.next_batch_to_attest + ); + attestation + .start_attestation(Arc::new(attestation::Info { + batch_to_attest: attester::Batch { + hash, + number: status.next_batch_to_attest, + genesis: status.genesis, + }, + committee: committee.clone(), + })) + .await + .context("start_attestation()")?; + // Main node is the only node which can update the global AttestationStatus, + // therefore we can synchronously wait for the certificate. + let qc = attestation + .wait_for_cert(ctx, status.next_batch_to_attest) + .await? + .context("attestation config has changed unexpectedly")?; + tracing::info!( + "collected certificate for batch {:?}", + status.next_batch_to_attest + ); + pool.connection(ctx) + .await + .wrap("connection()")? + .insert_batch_certificate(ctx, &qc) + .await + .map_err(|err| match err { + InsertCertificateError::Canceled(err) => ctx::Error::Canceled(err), + InsertCertificateError::Inner(err) => ctx::Error::Internal(err.into()), + })?; + } + } + .await; + match res { + Ok(()) | Err(ctx::Error::Canceled(_)) => Ok(()), + Err(ctx::Error::Internal(err)) => Err(err), + } +} diff --git a/core/node/consensus/src/storage/connection.rs b/core/node/consensus/src/storage/connection.rs index 0e2039ae6bc..6ff2fb1ce0a 100644 --- a/core/node/consensus/src/storage/connection.rs +++ b/core/node/consensus/src/storage/connection.rs @@ -27,6 +27,7 @@ impl ConnectionPool { } /// Waits for the `number` L2 block. + #[tracing::instrument(skip_all)] pub async fn wait_for_payload( &self, ctx: &ctx::Ctx, @@ -47,6 +48,29 @@ impl ConnectionPool { ctx.sleep(POLL_INTERVAL).await?; } } + + /// Waits for the `number` L1 batch hash. + #[tracing::instrument(skip_all)] + pub async fn wait_for_batch_hash( + &self, + ctx: &ctx::Ctx, + number: attester::BatchNumber, + ) -> ctx::Result { + const POLL_INTERVAL: time::Duration = time::Duration::milliseconds(500); + loop { + if let Some(hash) = self + .connection(ctx) + .await + .wrap("connection()")? + .batch_hash(ctx, number) + .await + .with_wrap(|| format!("batch_hash({number})"))? + { + return Ok(hash); + } + ctx.sleep(POLL_INTERVAL).await?; + } + } } /// Context-aware `zksync_dal::Connection` wrapper. @@ -321,29 +345,6 @@ impl<'a> Connection<'a> { .map(|nr| attester::BatchNumber(nr.0 as u64))) } - /// Wrapper for `consensus_dal().get_last_batch_certificate_number()`. - pub async fn get_last_batch_certificate_number( - &mut self, - ctx: &ctx::Ctx, - ) -> ctx::Result> { - Ok(ctx - .wait(self.0.consensus_dal().get_last_batch_certificate_number()) - .await? - .context("get_last_batch_certificate_number()")?) - } - - /// Wrapper for `consensus_dal().batch_certificate()`. - pub async fn batch_certificate( - &mut self, - ctx: &ctx::Ctx, - number: attester::BatchNumber, - ) -> ctx::Result> { - Ok(ctx - .wait(self.0.consensus_dal().batch_certificate(number)) - .await? - .context("batch_certificate()")?) - } - /// Wrapper for `blocks_dal().get_l2_block_range_of_l1_batch()`. pub async fn get_l2_block_range_of_l1_batch( &mut self, diff --git a/core/node/consensus/src/storage/store.rs b/core/node/consensus/src/storage/store.rs index 0e08811c237..6a96812ae40 100644 --- a/core/node/consensus/src/storage/store.rs +++ b/core/node/consensus/src/storage/store.rs @@ -57,8 +57,6 @@ pub(crate) struct Store { block_payloads: Arc>>, /// L2 block QCs received from consensus block_certificates: ctx::channel::UnboundedSender, - /// L1 batch QCs received from consensus - batch_certificates: ctx::channel::UnboundedSender, /// Range of L2 blocks for which we have a QC persisted. blocks_persisted: sync::watch::Receiver, /// Range of L1 batches we have persisted. @@ -73,7 +71,6 @@ pub struct StoreRunner { blocks_persisted: PersistedBlockState, batches_persisted: sync::watch::Sender, block_certificates: ctx::channel::UnboundedReceiver, - batch_certificates: ctx::channel::UnboundedReceiver, } impl Store { @@ -98,13 +95,11 @@ impl Store { let blocks_persisted = sync::watch::channel(blocks_persisted).0; let batches_persisted = sync::watch::channel(batches_persisted).0; let (block_certs_send, block_certs_recv) = ctx::channel::unbounded(); - let (batch_certs_send, batch_certs_recv) = ctx::channel::unbounded(); Ok(( Store { pool: pool.clone(), block_certificates: block_certs_send, - batch_certificates: batch_certs_send, block_payloads: Arc::new(sync::Mutex::new(payload_queue)), blocks_persisted: blocks_persisted.subscribe(), batches_persisted: batches_persisted.subscribe(), @@ -114,7 +109,6 @@ impl Store { blocks_persisted: PersistedBlockState(blocks_persisted), batches_persisted, block_certificates: block_certs_recv, - batch_certificates: batch_certs_recv, }, )) } @@ -171,7 +165,6 @@ impl StoreRunner { blocks_persisted, batches_persisted, mut block_certificates, - mut batch_certificates, } = self; let res = scope::run!(ctx, |ctx, s| async { @@ -256,60 +249,6 @@ impl StoreRunner { } }); - #[tracing::instrument(skip_all)] - async fn insert_batch_certificates_iteration( - ctx: &ctx::Ctx, - pool: &ConnectionPool, - batch_certificates: &mut ctx::channel::UnboundedReceiver, - ) -> ctx::Result<()> { - const POLL_INTERVAL: time::Duration = time::Duration::milliseconds(50); - - let cert = batch_certificates - .recv(ctx) - .instrument(tracing::info_span!("wait_for_batch_certificate")) - .await?; - - loop { - use consensus_dal::InsertCertificateError as E; - // Try to insert the cert. - let res = pool - .connection(ctx) - .await? - .insert_batch_certificate(ctx, &cert) - .await; - - match res { - Ok(()) => { - break; - } - Err(InsertCertificateError::Inner(E::MissingPayload)) => { - // The L1 batch isn't available yet. - // We can wait until it's produced/received, or we could modify gossip - // so that we don't even accept votes until we have the corresponding batch. - ctx.sleep(POLL_INTERVAL) - .instrument(tracing::info_span!("wait_for_batch")) - .await?; - } - Err(InsertCertificateError::Inner(err)) => { - return Err(ctx::Error::Internal(anyhow::Error::from(err))) - } - Err(InsertCertificateError::Canceled(err)) => { - return Err(ctx::Error::Canceled(err)) - } - } - } - - Ok(()) - } - - s.spawn::<()>(async { - // Loop inserting batch certificates into storage - loop { - insert_batch_certificates_iteration(ctx, &pool, &mut batch_certificates) - .await?; - } - }); - #[tracing::instrument(skip_all)] async fn insert_block_certificates_iteration( ctx: &ctx::Ctx, @@ -523,39 +462,6 @@ impl storage::PersistentBatchStore for Store { self.batches_persisted.clone() } - /// Get the next L1 batch number which has to be signed by attesters. - async fn next_batch_to_attest( - &self, - ctx: &ctx::Ctx, - ) -> ctx::Result> { - Ok(self - .conn(ctx) - .await? - .attestation_status(ctx) - .await - .wrap("next_batch_to_attest")? - .map(|s| s.next_batch_to_attest)) - } - - /// Get the L1 batch QC from storage with the highest number. - /// - /// This might have gaps before it. Until there is a way to catch up with missing - /// certificates by fetching from the main node, returning the last inserted one - /// is the best we can do. - async fn last_batch_qc(&self, ctx: &ctx::Ctx) -> ctx::Result> { - let Some(number) = self - .conn(ctx) - .await? - .get_last_batch_certificate_number(ctx) - .await - .wrap("get_last_batch_certificate_number")? - else { - return Ok(None); - }; - - self.get_batch_qc(ctx, number).await - } - /// Returns the batch with the given number. async fn get_batch( &self, @@ -569,54 +475,6 @@ impl storage::PersistentBatchStore for Store { .wrap("get_batch") } - /// Returns the [attester::Batch] with the given number, which is the `message` that - /// appears in [attester::BatchQC], and represents the content that needs to be signed - /// by the attesters. - async fn get_batch_to_sign( - &self, - ctx: &ctx::Ctx, - number: attester::BatchNumber, - ) -> ctx::Result> { - let mut conn = self.conn(ctx).await?; - - let Some(hash) = conn.batch_hash(ctx, number).await.wrap("batch_hash()")? else { - return Ok(None); - }; - - let Some(genesis) = conn.genesis(ctx).await.wrap("genesis()")? else { - return Ok(None); - }; - - Ok(Some(attester::Batch { - number, - hash, - genesis: genesis.hash(), - })) - } - - /// Returns the QC of the batch with the given number. - async fn get_batch_qc( - &self, - ctx: &ctx::Ctx, - number: attester::BatchNumber, - ) -> ctx::Result> { - self.conn(ctx) - .await? - .batch_certificate(ctx, number) - .await - .wrap("batch_certificate") - } - - /// Store the given QC in the storage. - /// - /// Storing a QC is allowed even if it creates a gap in the L1 batch history. - /// If we need the last batch QC that still needs to be signed then the queries need to look for gaps. - async fn store_qc(&self, _ctx: &ctx::Ctx, qc: attester::BatchQC) -> ctx::Result<()> { - // Storing asynchronously because we might get the QC before the L1 batch itself. - self.batch_certificates.send(qc); - Ok(()) - } - /// Queue the batch to be persisted in storage. /// /// The caller [BatchStore] ensures that this is only called when the batch is the next expected one. diff --git a/core/node/consensus/src/storage/testonly.rs b/core/node/consensus/src/storage/testonly.rs index c73d20982c1..5d1279afbbf 100644 --- a/core/node/consensus/src/storage/testonly.rs +++ b/core/node/consensus/src/storage/testonly.rs @@ -2,7 +2,7 @@ use anyhow::Context as _; use zksync_concurrency::{ctx, error::Wrap as _, time}; -use zksync_consensus_roles::validator; +use zksync_consensus_roles::{attester, validator}; use zksync_contracts::BaseSystemContracts; use zksync_dal::CoreDal as _; use zksync_node_genesis::{insert_genesis_batch, mock_genesis_config, GenesisParams}; @@ -12,7 +12,41 @@ use zksync_types::{ system_contracts::get_system_smart_contracts, L1BatchNumber, L2BlockNumber, ProtocolVersionId, }; -use super::ConnectionPool; +use super::{Connection, ConnectionPool}; + +impl Connection<'_> { + /// Wrapper for `consensus_dal().batch_of_block()`. + pub async fn batch_of_block( + &mut self, + ctx: &ctx::Ctx, + block: validator::BlockNumber, + ) -> ctx::Result> { + Ok(ctx + .wait(self.0.consensus_dal().batch_of_block(block)) + .await??) + } + + /// Wrapper for `consensus_dal().last_batch_certificate_number()`. + pub async fn last_batch_certificate_number( + &mut self, + ctx: &ctx::Ctx, + ) -> ctx::Result> { + Ok(ctx + .wait(self.0.consensus_dal().last_batch_certificate_number()) + .await??) + } + + /// Wrapper for `consensus_dal().batch_certificate()`. + pub async fn batch_certificate( + &mut self, + ctx: &ctx::Ctx, + number: attester::BatchNumber, + ) -> ctx::Result> { + Ok(ctx + .wait(self.0.consensus_dal().batch_certificate(number)) + .await??) + } +} pub(crate) fn mock_genesis_params(protocol_version: ProtocolVersionId) -> GenesisParams { let mut cfg = mock_genesis_config(); @@ -161,6 +195,57 @@ impl ConnectionPool { Ok(blocks) } + pub async fn wait_for_batch_certificates_and_verify( + &self, + ctx: &ctx::Ctx, + want_last: attester::BatchNumber, + ) -> ctx::Result<()> { + // Wait for the last batch to be attested. + const POLL_INTERVAL: time::Duration = time::Duration::milliseconds(100); + while self + .connection(ctx) + .await + .wrap("connection()")? + .last_batch_certificate_number(ctx) + .await + .wrap("last_batch_certificate_number()")? + .map_or(true, |got| got < want_last) + { + ctx.sleep(POLL_INTERVAL).await?; + } + let mut conn = self.connection(ctx).await.wrap("connection()")?; + let genesis = conn + .genesis(ctx) + .await + .wrap("genesis()")? + .context("genesis is missing")?; + let first = conn + .batch_of_block(ctx, genesis.first_block) + .await + .wrap("batch_of_block()")? + .context("batch of first_block is missing")?; + let committee = genesis.attesters.as_ref().unwrap(); + for i in first.0..want_last.0 { + let i = attester::BatchNumber(i); + let hash = conn + .batch_hash(ctx, i) + .await + .wrap("batch_hash()")? + .context("hash missing")?; + let cert = conn + .batch_certificate(ctx, i) + .await + .wrap("batch_certificate")? + .context("cert missing")?; + if cert.message.hash != hash { + return Err(anyhow::format_err!("cert[{i:?}]: hash mismatch").into()); + } + cert.verify(genesis.hash(), committee) + .context("cert[{i:?}].verify()")?; + } + Ok(()) + } + pub async fn prune_batches( &self, ctx: &ctx::Ctx, diff --git a/core/node/consensus/src/testonly.rs b/core/node/consensus/src/testonly.rs index 9cf06b992e8..0537aaabc56 100644 --- a/core/node/consensus/src/testonly.rs +++ b/core/node/consensus/src/testonly.rs @@ -14,7 +14,7 @@ use zksync_config::{ }; use zksync_consensus_crypto::TextFmt as _; use zksync_consensus_network as network; -use zksync_consensus_roles::validator; +use zksync_consensus_roles::{attester, validator, validator::testonly::Setup}; use zksync_dal::{CoreDal, DalError}; use zksync_l1_contract_interface::i_executor::structures::StoredBatchInfo; use zksync_metadata_calculator::{ @@ -72,55 +72,105 @@ pub(super) struct StateKeeper { tree_reader: LazyAsyncTreeReader, } -pub(super) fn config(cfg: &network::Config) -> (config::ConsensusConfig, config::ConsensusSecrets) { - ( - config::ConsensusConfig { - server_addr: *cfg.server_addr, - public_addr: config::Host(cfg.public_addr.0.clone()), - max_payload_size: usize::MAX, - max_batch_size: usize::MAX, - gossip_dynamic_inbound_limit: cfg.gossip.dynamic_inbound_limit, - gossip_static_inbound: cfg - .gossip - .static_inbound - .iter() - .map(|k| config::NodePublicKey(k.encode())) - .collect(), - gossip_static_outbound: cfg - .gossip - .static_outbound - .iter() - .map(|(k, v)| (config::NodePublicKey(k.encode()), config::Host(v.0.clone()))) - .collect(), - genesis_spec: cfg.validator_key.as_ref().map(|key| config::GenesisSpec { - chain_id: L2ChainId::default(), - protocol_version: config::ProtocolVersion(validator::ProtocolVersion::CURRENT.0), - validators: vec![config::WeightedValidator { - key: config::ValidatorPublicKey(key.public().encode()), - weight: 1, - }], - // We only have access to the main node attester key in the `cfg`, which is fine - // for validators because at the moment there is only one leader. It doesn't - // allow us to form a full attester committee. However in the current tests - // the `new_configs` used to produce the array of `network::Config` doesn't - // assign an attester key, so it doesn't matter. - attesters: Vec::new(), - leader: config::ValidatorPublicKey(key.public().encode()), - }), - rpc: None, - }, - config::ConsensusSecrets { - node_key: Some(config::NodeSecretKey(cfg.gossip.key.encode().into())), - validator_key: cfg - .validator_key - .as_ref() - .map(|k| config::ValidatorSecretKey(k.encode().into())), - attester_key: cfg - .attester_key - .as_ref() - .map(|k| config::AttesterSecretKey(k.encode().into())), - }, - ) +#[derive(Clone)] +pub(super) struct ConfigSet { + net: network::Config, + pub(super) config: config::ConsensusConfig, + pub(super) secrets: config::ConsensusSecrets, +} + +impl ConfigSet { + pub(super) fn new_fullnode(&self, rng: &mut impl Rng) -> ConfigSet { + let net = network::testonly::new_fullnode(rng, &self.net); + ConfigSet { + config: make_config(&net, None), + secrets: make_secrets(&net, None), + net, + } + } +} + +pub(super) fn new_configs( + rng: &mut impl Rng, + setup: &Setup, + gossip_peers: usize, +) -> Vec { + let genesis_spec = config::GenesisSpec { + chain_id: setup.genesis.chain_id.0.try_into().unwrap(), + protocol_version: config::ProtocolVersion(setup.genesis.protocol_version.0), + validators: setup + .validator_keys + .iter() + .map(|k| config::WeightedValidator { + key: config::ValidatorPublicKey(k.public().encode()), + weight: 1, + }) + .collect(), + attesters: setup + .attester_keys + .iter() + .map(|k| config::WeightedAttester { + key: config::AttesterPublicKey(k.public().encode()), + weight: 1, + }) + .collect(), + leader: config::ValidatorPublicKey(setup.validator_keys[0].public().encode()), + }; + network::testonly::new_configs(rng, setup, gossip_peers) + .into_iter() + .enumerate() + .map(|(i, net)| ConfigSet { + config: make_config(&net, Some(genesis_spec.clone())), + secrets: make_secrets(&net, setup.attester_keys.get(i).cloned()), + net, + }) + .collect() +} + +fn make_secrets( + cfg: &network::Config, + attester_key: Option, +) -> config::ConsensusSecrets { + config::ConsensusSecrets { + node_key: Some(config::NodeSecretKey(cfg.gossip.key.encode().into())), + validator_key: cfg + .validator_key + .as_ref() + .map(|k| config::ValidatorSecretKey(k.encode().into())), + attester_key: attester_key.map(|k| config::AttesterSecretKey(k.encode().into())), + } +} + +fn make_config( + cfg: &network::Config, + genesis_spec: Option, +) -> config::ConsensusConfig { + config::ConsensusConfig { + server_addr: *cfg.server_addr, + public_addr: config::Host(cfg.public_addr.0.clone()), + max_payload_size: usize::MAX, + max_batch_size: usize::MAX, + gossip_dynamic_inbound_limit: cfg.gossip.dynamic_inbound_limit, + gossip_static_inbound: cfg + .gossip + .static_inbound + .iter() + .map(|k| config::NodePublicKey(k.encode())) + .collect(), + gossip_static_outbound: cfg + .gossip + .static_outbound + .iter() + .map(|(k, v)| (config::NodePublicKey(k.encode()), config::Host(v.0.clone()))) + .collect(), + // This is only relevant for the main node, which populates the genesis on the first run. + // Note that the spec doesn't match 100% the genesis provided. + // That's because not all genesis setups are currently supported in zksync-era. + // TODO: this might be misleading, so it would be better to write some more custom + // genesis generator for zksync-era tests. + genesis_spec, + rpc: None, + } } /// Fake StateKeeper task to be executed in the background. @@ -393,15 +443,14 @@ impl StateKeeper { self, ctx: &ctx::Ctx, client: Box>, - cfg: &network::Config, + cfgs: ConfigSet, ) -> anyhow::Result<()> { - let (cfg, secrets) = config(cfg); en::EN { pool: self.pool, client, sync_state: self.sync_state.clone(), } - .run(ctx, self.actions_sender, cfg, secrets) + .run(ctx, self.actions_sender, cfgs.config, cfgs.secrets) .await } } diff --git a/core/node/consensus/src/tests/attestation.rs b/core/node/consensus/src/tests/attestation.rs new file mode 100644 index 00000000000..b245d0524aa --- /dev/null +++ b/core/node/consensus/src/tests/attestation.rs @@ -0,0 +1,166 @@ +use anyhow::Context as _; +use test_casing::{test_casing, Product}; +use tracing::Instrument as _; +use zksync_concurrency::{ctx, error::Wrap, scope}; +use zksync_consensus_roles::{ + attester, + validator::testonly::{Setup, SetupSpec}, +}; +use zksync_dal::consensus_dal::AttestationStatus; +use zksync_node_sync::MainNodeClient; +use zksync_types::{L1BatchNumber, ProtocolVersionId}; + +use super::{FROM_SNAPSHOT, VERSIONS}; +use crate::{mn::run_main_node, storage::ConnectionPool, testonly}; + +#[test_casing(2, VERSIONS)] +#[tokio::test] +async fn test_attestation_status_api(version: ProtocolVersionId) { + zksync_concurrency::testonly::abort_on_panic(); + let ctx = &ctx::test_root(&ctx::RealClock); + let rng = &mut ctx.rng(); + + scope::run!(ctx, |ctx, s| async { + let pool = ConnectionPool::test(false, version).await; + let (mut sk, runner) = testonly::StateKeeper::new(ctx, pool.clone()).await?; + s.spawn_bg(runner.run(ctx).instrument(tracing::info_span!("validator"))); + + // Setup nontrivial genesis. + while sk.last_sealed_batch() < L1BatchNumber(3) { + sk.push_random_blocks(rng, 10).await; + } + let mut setup = SetupSpec::new(rng, 3); + setup.first_block = sk.last_block(); + let first_batch = sk.last_batch(); + let setup = Setup::from(setup); + let mut conn = pool.connection(ctx).await.wrap("connection()")?; + conn.try_update_genesis(ctx, &setup.genesis) + .await + .wrap("try_update_genesis()")?; + // Make sure that the first_batch is actually sealed. + sk.seal_batch().await; + pool.wait_for_batch(ctx, first_batch).await?; + + // Connect to API endpoint. + let api = sk.connect(ctx).await?; + let fetch_status = || async { + let s = api + .fetch_attestation_status() + .await? + .context("no attestation_status")?; + let s: AttestationStatus = + zksync_protobuf::serde::deserialize(&s.0).context("deserialize()")?; + anyhow::ensure!(s.genesis == setup.genesis.hash(), "genesis hash mismatch"); + Ok(s) + }; + + // If the main node has no L1 batch certificates, + // then the first one to sign should be the batch with the `genesis.first_block`. + let status = fetch_status().await?; + assert_eq!( + status.next_batch_to_attest, + attester::BatchNumber(first_batch.0.into()) + ); + + // Insert a (fake) cert, then check again. + { + let mut conn = pool.connection(ctx).await?; + let number = status.next_batch_to_attest; + let hash = conn.batch_hash(ctx, number).await?.unwrap(); + let genesis = conn.genesis(ctx).await?.unwrap().hash(); + let cert = attester::BatchQC { + signatures: attester::MultiSig::default(), + message: attester::Batch { + number, + hash, + genesis, + }, + }; + conn.insert_batch_certificate(ctx, &cert) + .await + .context("insert_batch_certificate()")?; + } + let want = status.next_batch_to_attest.next(); + let got = fetch_status().await?; + assert_eq!(want, got.next_batch_to_attest); + + Ok(()) + }) + .await + .unwrap(); +} + +// Test running a couple of attesters (which are also validators). +// Main node is expected to collect all certificates. +// External nodes are expected to just vote for the batch. +// +// TODO: it would be nice to use `StateKeeperRunner::run_real()` in this test, +// however as of now it doesn't work with ENs and it doesn't work with +// `ConnectionPool::from_snapshot`. +#[test_casing(4, Product((FROM_SNAPSHOT,VERSIONS)))] +#[tokio::test] +async fn test_multiple_attesters(from_snapshot: bool, version: ProtocolVersionId) { + const NODES: usize = 4; + + zksync_concurrency::testonly::abort_on_panic(); + let ctx = &ctx::test_root(&ctx::AffineClock::new(10.)); + let rng = &mut ctx.rng(); + let setup = Setup::new(rng, 4); + let cfgs = testonly::new_configs(rng, &setup, NODES); + + scope::run!(ctx, |ctx, s| async { + let validator_pool = ConnectionPool::test(from_snapshot, version).await; + let (mut validator, runner) = + testonly::StateKeeper::new(ctx, validator_pool.clone()).await?; + s.spawn_bg(async { + runner + .run(ctx) + .instrument(tracing::info_span!("validator")) + .await + .context("validator") + }); + // API server needs at least 1 L1 batch to start. + validator.seal_batch().await; + validator_pool + .wait_for_payload(ctx, validator.last_block()) + .await?; + + tracing::info!("Run validator."); + s.spawn_bg(run_main_node( + ctx, + cfgs[0].config.clone(), + cfgs[0].secrets.clone(), + validator_pool.clone(), + )); + + tracing::info!("Run nodes."); + let mut node_pools = vec![]; + for (i, cfg) in cfgs[1..].iter().enumerate() { + let i = ctx::NoCopy(i); + let pool = ConnectionPool::test(from_snapshot, version).await; + let (node, runner) = testonly::StateKeeper::new(ctx, pool.clone()).await?; + node_pools.push(pool.clone()); + s.spawn_bg(async { + let i = i; + runner + .run(ctx) + .instrument(tracing::info_span!("node", i = *i)) + .await + .with_context(|| format!("node{}", *i)) + }); + s.spawn_bg(node.run_consensus(ctx, validator.connect(ctx).await?, cfg.clone())); + } + + tracing::info!("Create some batches"); + validator.push_random_blocks(rng, 20).await; + validator.seal_batch().await; + tracing::info!("Wait for the batches to be attested"); + let want_last = attester::BatchNumber(validator.last_sealed_batch().0.into()); + validator_pool + .wait_for_batch_certificates_and_verify(ctx, want_last) + .await?; + Ok(()) + }) + .await + .unwrap(); +} diff --git a/core/node/consensus/src/tests/batch.rs b/core/node/consensus/src/tests/batch.rs new file mode 100644 index 00000000000..41d73fdb87c --- /dev/null +++ b/core/node/consensus/src/tests/batch.rs @@ -0,0 +1,120 @@ +use test_casing::{test_casing, Product}; +use zksync_concurrency::{ctx, scope}; +use zksync_consensus_roles::validator; +use zksync_types::{L1BatchNumber, ProtocolVersionId}; + +use super::{FROM_SNAPSHOT, VERSIONS}; +use crate::{storage::ConnectionPool, testonly}; + +#[test_casing(4, Product((FROM_SNAPSHOT,VERSIONS)))] +#[tokio::test] +async fn test_connection_get_batch(from_snapshot: bool, version: ProtocolVersionId) { + zksync_concurrency::testonly::abort_on_panic(); + let ctx = &ctx::test_root(&ctx::RealClock); + let rng = &mut ctx.rng(); + let pool = ConnectionPool::test(from_snapshot, version).await; + + // Fill storage with unsigned L2 blocks and L1 batches in a way that the + // last L1 batch is guaranteed to have some L2 blocks executed in it. + scope::run!(ctx, |ctx, s| async { + // Start state keeper. + let (mut sk, runner) = testonly::StateKeeper::new(ctx, pool.clone()).await?; + s.spawn_bg(runner.run(ctx)); + + for _ in 0..3 { + for _ in 0..2 { + sk.push_random_block(rng).await; + } + sk.seal_batch().await; + } + sk.push_random_block(rng).await; + + pool.wait_for_payload(ctx, sk.last_block()).await?; + + Ok(()) + }) + .await + .unwrap(); + + // Now we can try to retrieve the batch. + scope::run!(ctx, |ctx, _s| async { + let mut conn = pool.connection(ctx).await?; + let batches = conn.batches_range(ctx).await?; + let last = batches.last.expect("last is set"); + let (min, max) = conn + .get_l2_block_range_of_l1_batch(ctx, last) + .await? + .unwrap(); + + let last_batch = conn + .get_batch(ctx, last) + .await? + .expect("last batch can be retrieved"); + + assert_eq!( + last_batch.payloads.len(), + (max.0 - min.0) as usize, + "all block payloads present" + ); + + let first_payload = last_batch + .payloads + .first() + .expect("last batch has payloads"); + + let want_payload = conn.payload(ctx, min).await?.expect("payload is in the DB"); + let want_payload = want_payload.encode(); + + assert_eq!( + first_payload, &want_payload, + "first payload is the right number" + ); + + anyhow::Ok(()) + }) + .await + .unwrap(); +} + +/// Tests that generated L1 batch witnesses can be verified successfully. +/// TODO: add tests for verification failures. +#[test_casing(2, VERSIONS)] +#[tokio::test] +async fn test_batch_witness(version: ProtocolVersionId) { + zksync_concurrency::testonly::abort_on_panic(); + let ctx = &ctx::test_root(&ctx::RealClock); + let rng = &mut ctx.rng(); + + scope::run!(ctx, |ctx, s| async { + let pool = ConnectionPool::from_genesis(version).await; + let (mut node, runner) = testonly::StateKeeper::new(ctx, pool.clone()).await?; + s.spawn_bg(runner.run_real(ctx)); + + tracing::info!("analyzing storage"); + { + let mut conn = pool.connection(ctx).await.unwrap(); + let mut n = validator::BlockNumber(0); + while let Some(p) = conn.payload(ctx, n).await? { + tracing::info!("block[{n}] = {p:?}"); + n = n + 1; + } + } + + // Seal a bunch of batches. + node.push_random_blocks(rng, 10).await; + node.seal_batch().await; + pool.wait_for_batch(ctx, node.last_sealed_batch()).await?; + // We can verify only 2nd batch onward, because + // batch witness verifies parent of the last block of the + // previous batch (and 0th batch contains only 1 block). + for n in 2..=node.last_sealed_batch().0 { + let n = L1BatchNumber(n); + let batch_with_witness = node.load_batch_with_witness(ctx, n).await?; + let commit = node.load_batch_commit(ctx, n).await?; + batch_with_witness.verify(&commit)?; + } + Ok(()) + }) + .await + .unwrap(); +} diff --git a/core/node/consensus/src/tests.rs b/core/node/consensus/src/tests/mod.rs similarity index 69% rename from core/node/consensus/src/tests.rs rename to core/node/consensus/src/tests/mod.rs index 8e1594393ea..0b611d55f06 100644 --- a/core/node/consensus/src/tests.rs +++ b/core/node/consensus/src/tests/mod.rs @@ -2,17 +2,12 @@ use anyhow::Context as _; use test_casing::{test_casing, Product}; use tracing::Instrument as _; use zksync_concurrency::{ctx, error::Wrap, scope}; -use zksync_config::configs::consensus::{ValidatorPublicKey, WeightedValidator}; -use zksync_consensus_crypto::TextFmt as _; -use zksync_consensus_network::testonly::{new_configs, new_fullnode}; use zksync_consensus_roles::{ - attester, validator, + validator, validator::testonly::{Setup, SetupSpec}, }; use zksync_consensus_storage::BlockStore; -use zksync_dal::consensus_dal::AttestationStatus; -use zksync_node_sync::MainNodeClient; -use zksync_types::{L1BatchNumber, ProtocolVersionId}; +use zksync_types::ProtocolVersionId; use crate::{ mn::run_main_node, @@ -20,6 +15,9 @@ use crate::{ testonly, }; +mod attestation; +mod batch; + const VERSIONS: [ProtocolVersionId; 2] = [ProtocolVersionId::latest(), ProtocolVersionId::next()]; const FROM_SNAPSHOT: [bool; 2] = [true, false]; @@ -86,76 +84,6 @@ async fn test_validator_block_store(version: ProtocolVersionId) { } } -#[test_casing(4, Product((FROM_SNAPSHOT,VERSIONS)))] -#[tokio::test] -async fn test_connection_get_batch(from_snapshot: bool, version: ProtocolVersionId) { - zksync_concurrency::testonly::abort_on_panic(); - let ctx = &ctx::test_root(&ctx::RealClock); - let rng = &mut ctx.rng(); - let pool = ConnectionPool::test(from_snapshot, version).await; - - // Fill storage with unsigned L2 blocks and L1 batches in a way that the - // last L1 batch is guaranteed to have some L2 blocks executed in it. - scope::run!(ctx, |ctx, s| async { - // Start state keeper. - let (mut sk, runner) = testonly::StateKeeper::new(ctx, pool.clone()).await?; - s.spawn_bg(runner.run(ctx)); - - for _ in 0..3 { - for _ in 0..2 { - sk.push_random_block(rng).await; - } - sk.seal_batch().await; - } - sk.push_random_block(rng).await; - - pool.wait_for_payload(ctx, sk.last_block()).await?; - - Ok(()) - }) - .await - .unwrap(); - - // Now we can try to retrieve the batch. - scope::run!(ctx, |ctx, _s| async { - let mut conn = pool.connection(ctx).await?; - let batches = conn.batches_range(ctx).await?; - let last = batches.last.expect("last is set"); - let (min, max) = conn - .get_l2_block_range_of_l1_batch(ctx, last) - .await? - .unwrap(); - - let last_batch = conn - .get_batch(ctx, last) - .await? - .expect("last batch can be retrieved"); - - assert_eq!( - last_batch.payloads.len(), - (max.0 - min.0) as usize, - "all block payloads present" - ); - - let first_payload = last_batch - .payloads - .first() - .expect("last batch has payloads"); - - let want_payload = conn.payload(ctx, min).await?.expect("payload is in the DB"); - let want_payload = want_payload.encode(); - - assert_eq!( - first_payload, &want_payload, - "first payload is the right number" - ); - - anyhow::Ok(()) - }) - .await - .unwrap(); -} - // In the current implementation, consensus certificates are created asynchronously // for the L2 blocks constructed by the StateKeeper. This means that consensus actor // is effectively just back filling the consensus certificates for the L2 blocks in storage. @@ -166,7 +94,7 @@ async fn test_validator(from_snapshot: bool, version: ProtocolVersionId) { let ctx = &ctx::test_root(&ctx::AffineClock::new(10.)); let rng = &mut ctx.rng(); let setup = Setup::new(rng, 1); - let cfgs = new_configs(rng, &setup, 0); + let cfg = testonly::new_configs(rng, &setup, 0)[0].clone(); scope::run!(ctx, |ctx, s| async { tracing::info!("Start state keeper."); @@ -187,8 +115,7 @@ async fn test_validator(from_snapshot: bool, version: ProtocolVersionId) { scope::run!(ctx, |ctx, s| async { tracing::info!("Start consensus actor"); // In the first iteration it will initialize genesis. - let (cfg,secrets) = testonly::config(&cfgs[0]); - s.spawn_bg(run_main_node(ctx, cfg, secrets, pool.clone())); + s.spawn_bg(run_main_node(ctx, cfg.config.clone(), cfg.secrets.clone(), pool.clone())); tracing::info!("Generate couple more blocks and wait for consensus to catch up."); sk.push_random_blocks(rng, 3).await; @@ -230,7 +157,7 @@ async fn test_nodes_from_various_snapshots(version: ProtocolVersionId) { let ctx = &ctx::test_root(&ctx::AffineClock::new(10.)); let rng = &mut ctx.rng(); let setup = Setup::new(rng, 1); - let validator_cfg = new_configs(rng, &setup, 0).pop().unwrap(); + let validator_cfg = testonly::new_configs(rng, &setup, 0)[0].clone(); scope::run!(ctx, |ctx, s| async { tracing::info!("spawn validator"); @@ -238,8 +165,12 @@ async fn test_nodes_from_various_snapshots(version: ProtocolVersionId) { let (mut validator, runner) = testonly::StateKeeper::new(ctx, validator_pool.clone()).await?; s.spawn_bg(runner.run(ctx).instrument(tracing::info_span!("validator"))); - let (cfg, secrets) = testonly::config(&validator_cfg); - s.spawn_bg(run_main_node(ctx, cfg, secrets, validator_pool.clone())); + s.spawn_bg(run_main_node( + ctx, + validator_cfg.config.clone(), + validator_cfg.secrets.clone(), + validator_pool.clone(), + )); tracing::info!("produce some batches"); validator.push_random_blocks(rng, 5).await; @@ -255,8 +186,8 @@ async fn test_nodes_from_various_snapshots(version: ProtocolVersionId) { s.spawn_bg(runner.run(ctx).instrument(tracing::info_span!("node1"))); let conn = validator.connect(ctx).await?; s.spawn_bg(async { - let cfg = new_fullnode(&mut ctx.rng(), &validator_cfg); - node.run_consensus(ctx, conn, &cfg).await + let cfg = validator_cfg.new_fullnode(&mut ctx.rng()); + node.run_consensus(ctx, conn, cfg).await }); tracing::info!("produce more batches"); @@ -273,8 +204,8 @@ async fn test_nodes_from_various_snapshots(version: ProtocolVersionId) { s.spawn_bg(runner.run(ctx).instrument(tracing::info_span!("node2"))); let conn = validator.connect(ctx).await?; s.spawn_bg(async { - let cfg = new_fullnode(&mut ctx.rng(), &validator_cfg); - node.run_consensus(ctx, conn, &cfg).await + let cfg = validator_cfg.new_fullnode(&mut ctx.rng()); + node.run_consensus(ctx, conn, cfg).await }); tracing::info!("produce more blocks and compare storages"); @@ -311,16 +242,13 @@ async fn test_full_nodes(from_snapshot: bool, version: ProtocolVersionId) { let ctx = &ctx::test_root(&ctx::AffineClock::new(10.)); let rng = &mut ctx.rng(); let setup = Setup::new(rng, 1); - let validator_cfgs = new_configs(rng, &setup, 0); + let validator_cfg = testonly::new_configs(rng, &setup, 0)[0].clone(); // topology: // validator <-> node <-> node <-> ... let mut node_cfgs = vec![]; for _ in 0..NODES { - node_cfgs.push(new_fullnode( - rng, - node_cfgs.last().unwrap_or(&validator_cfgs[0]), - )); + node_cfgs.push(node_cfgs.last().unwrap_or(&validator_cfg).new_fullnode(rng)); } // Run validator and fetchers in parallel. @@ -344,8 +272,12 @@ async fn test_full_nodes(from_snapshot: bool, version: ProtocolVersionId) { .await?; tracing::info!("Run validator."); - let (cfg, secrets) = testonly::config(&validator_cfgs[0]); - s.spawn_bg(run_main_node(ctx, cfg, secrets, validator_pool.clone())); + s.spawn_bg(run_main_node( + ctx, + validator_cfg.config.clone(), + validator_cfg.secrets.clone(), + validator_pool.clone(), + )); tracing::info!("Run nodes."); let mut node_pools = vec![]; @@ -362,7 +294,7 @@ async fn test_full_nodes(from_snapshot: bool, version: ProtocolVersionId) { .await .with_context(|| format!("node{}", *i)) }); - s.spawn_bg(node.run_consensus(ctx, validator.connect(ctx).await?, cfg)); + s.spawn_bg(node.run_consensus(ctx, validator.connect(ctx).await?, cfg.clone())); } tracing::info!("Make validator produce blocks and wait for fetchers to get them."); @@ -395,7 +327,7 @@ async fn test_en_validators(from_snapshot: bool, version: ProtocolVersionId) { let ctx = &ctx::test_root(&ctx::AffineClock::new(10.)); let rng = &mut ctx.rng(); let setup = Setup::new(rng, NODES); - let cfgs = new_configs(rng, &setup, 1); + let cfgs = testonly::new_configs(rng, &setup, 1); // Run all nodes in parallel. scope::run!(ctx, |ctx, s| async { @@ -423,16 +355,12 @@ async fn test_en_validators(from_snapshot: bool, version: ProtocolVersionId) { main_node.connect(ctx).await?; tracing::info!("Run main node with all nodes being validators."); - let (mut cfg, secrets) = testonly::config(&cfgs[0]); - cfg.genesis_spec.as_mut().unwrap().validators = setup - .validator_keys - .iter() - .map(|k| WeightedValidator { - key: ValidatorPublicKey(k.public().encode()), - weight: 1, - }) - .collect(); - s.spawn_bg(run_main_node(ctx, cfg, secrets, main_node_pool.clone())); + s.spawn_bg(run_main_node( + ctx, + cfgs[0].config.clone(), + cfgs[0].secrets.clone(), + main_node_pool.clone(), + )); tracing::info!("Run external nodes."); let mut ext_node_pools = vec![]; @@ -449,7 +377,7 @@ async fn test_en_validators(from_snapshot: bool, version: ProtocolVersionId) { .await .with_context(|| format!("en{}", *i)) }); - s.spawn_bg(ext_node.run_consensus(ctx, main_node.connect(ctx).await?, cfg)); + s.spawn_bg(ext_node.run_consensus(ctx, main_node.connect(ctx).await?, cfg.clone())); } tracing::info!("Make the main node produce blocks and wait for consensus to finalize them"); @@ -479,8 +407,8 @@ async fn test_p2p_fetcher_backfill_certs(from_snapshot: bool, version: ProtocolV let ctx = &ctx::test_root(&ctx::AffineClock::new(10.)); let rng = &mut ctx.rng(); let setup = Setup::new(rng, 1); - let validator_cfg = new_configs(rng, &setup, 0)[0].clone(); - let node_cfg = new_fullnode(rng, &validator_cfg); + let validator_cfg = testonly::new_configs(rng, &setup, 0)[0].clone(); + let node_cfg = validator_cfg.new_fullnode(rng); scope::run!(ctx, |ctx, s| async { tracing::info!("Spawn validator."); @@ -488,8 +416,12 @@ async fn test_p2p_fetcher_backfill_certs(from_snapshot: bool, version: ProtocolV let (mut validator, runner) = testonly::StateKeeper::new(ctx, validator_pool.clone()).await?; s.spawn_bg(runner.run(ctx)); - let (cfg, secrets) = testonly::config(&validator_cfg); - s.spawn_bg(run_main_node(ctx, cfg, secrets, validator_pool.clone())); + s.spawn_bg(run_main_node( + ctx, + validator_cfg.config.clone(), + validator_cfg.secrets.clone(), + validator_pool.clone(), + )); // API server needs at least 1 L1 batch to start. validator.seal_batch().await; let client = validator.connect(ctx).await?; @@ -500,7 +432,7 @@ async fn test_p2p_fetcher_backfill_certs(from_snapshot: bool, version: ProtocolV scope::run!(ctx, |ctx, s| async { let (node, runner) = testonly::StateKeeper::new(ctx, node_pool.clone()).await?; s.spawn_bg(runner.run(ctx)); - s.spawn_bg(node.run_consensus(ctx, client.clone(), &node_cfg)); + s.spawn_bg(node.run_consensus(ctx, client.clone(), node_cfg.clone())); validator.push_random_blocks(rng, 3).await; node_pool .wait_for_block_certificate(ctx, validator.last_block()) @@ -528,7 +460,7 @@ async fn test_p2p_fetcher_backfill_certs(from_snapshot: bool, version: ProtocolV scope::run!(ctx, |ctx, s| async { let (node, runner) = testonly::StateKeeper::new(ctx, node_pool.clone()).await?; s.spawn_bg(runner.run(ctx)); - s.spawn_bg(node.run_consensus(ctx, client.clone(), &node_cfg)); + s.spawn_bg(node.run_consensus(ctx, client.clone(), node_cfg)); validator.push_random_blocks(rng, 3).await; let want = validator_pool .wait_for_block_certificates_and_verify(ctx, validator.last_block()) @@ -554,8 +486,8 @@ async fn test_with_pruning(version: ProtocolVersionId) { let ctx = &ctx::test_root(&ctx::RealClock); let rng = &mut ctx.rng(); let setup = Setup::new(rng, 1); - let validator_cfg = new_configs(rng, &setup, 0)[0].clone(); - let node_cfg = new_fullnode(rng, &validator_cfg); + let validator_cfg = testonly::new_configs(rng, &setup, 0)[0].clone(); + let node_cfg = validator_cfg.new_fullnode(rng); scope::run!(ctx, |ctx, s| async { let validator_pool = ConnectionPool::test(false, version).await; @@ -569,16 +501,20 @@ async fn test_with_pruning(version: ProtocolVersionId) { .context("validator") }); tracing::info!("Run validator."); - let (cfg, secrets) = testonly::config(&validator_cfg); s.spawn_bg({ let validator_pool = validator_pool.clone(); async { - run_main_node(ctx, cfg, secrets, validator_pool) - .await - .context("run_main_node()") + run_main_node( + ctx, + validator_cfg.config.clone(), + validator_cfg.secrets.clone(), + validator_pool, + ) + .await + .context("run_main_node()") } }); - // TODO: ensure at least L1 batch in `testonly::StateKeeper::new()` to make it fool proof. + // TODO: ensure at least 1 L1 batch in `testonly::StateKeeper::new()` to make it fool proof. validator.seal_batch().await; tracing::info!("Run node."); @@ -593,7 +529,7 @@ async fn test_with_pruning(version: ProtocolVersionId) { }); let conn = validator.connect(ctx).await?; s.spawn_bg(async { - node.run_consensus(ctx, conn, &node_cfg) + node.run_consensus(ctx, conn, node_cfg) .await .context("run_consensus()") }); @@ -678,123 +614,3 @@ async fn test_centralized_fetcher(from_snapshot: bool, version: ProtocolVersionI .await .unwrap(); } - -#[test_casing(2, VERSIONS)] -#[tokio::test] -async fn test_attestation_status_api(version: ProtocolVersionId) { - zksync_concurrency::testonly::abort_on_panic(); - let ctx = &ctx::test_root(&ctx::RealClock); - let rng = &mut ctx.rng(); - - scope::run!(ctx, |ctx, s| async { - let pool = ConnectionPool::test(false, version).await; - let (mut sk, runner) = testonly::StateKeeper::new(ctx, pool.clone()).await?; - s.spawn_bg(runner.run(ctx).instrument(tracing::info_span!("validator"))); - - // Setup nontrivial genesis. - while sk.last_sealed_batch() < L1BatchNumber(3) { - sk.push_random_blocks(rng, 10).await; - } - let mut setup = SetupSpec::new(rng, 3); - setup.first_block = sk.last_block(); - let first_batch = sk.last_batch(); - let setup = Setup::from(setup); - let mut conn = pool.connection(ctx).await.wrap("connection()")?; - conn.try_update_genesis(ctx, &setup.genesis) - .await - .wrap("try_update_genesis()")?; - // Make sure that the first_batch is actually sealed. - sk.seal_batch().await; - pool.wait_for_batch(ctx, first_batch).await?; - - // Connect to API endpoint. - let api = sk.connect(ctx).await?; - let fetch_status = || async { - let s = api - .fetch_attestation_status() - .await? - .context("no attestation_status")?; - let s: AttestationStatus = - zksync_protobuf::serde::deserialize(&s.0).context("deserialize()")?; - anyhow::ensure!(s.genesis == setup.genesis.hash(), "genesis hash mismatch"); - Ok(s) - }; - - // If the main node has no L1 batch certificates, - // then the first one to sign should be the batch with the `genesis.first_block`. - let status = fetch_status().await?; - assert_eq!( - status.next_batch_to_attest, - attester::BatchNumber(first_batch.0.into()) - ); - - // Insert a (fake) cert, then check again. - { - let mut conn = pool.connection(ctx).await?; - let number = status.next_batch_to_attest; - let hash = conn.batch_hash(ctx, number).await?.unwrap(); - let genesis = conn.genesis(ctx).await?.unwrap().hash(); - let cert = attester::BatchQC { - signatures: attester::MultiSig::default(), - message: attester::Batch { - number, - hash, - genesis, - }, - }; - conn.insert_batch_certificate(ctx, &cert) - .await - .context("insert_batch_certificate()")?; - } - let want = status.next_batch_to_attest.next(); - let got = fetch_status().await?; - assert_eq!(want, got.next_batch_to_attest); - - Ok(()) - }) - .await - .unwrap(); -} - -/// Tests that generated L1 batch witnesses can be verified successfully. -/// TODO: add tests for verification failures. -#[test_casing(2, VERSIONS)] -#[tokio::test] -async fn test_batch_witness(version: ProtocolVersionId) { - zksync_concurrency::testonly::abort_on_panic(); - let ctx = &ctx::test_root(&ctx::RealClock); - let rng = &mut ctx.rng(); - - scope::run!(ctx, |ctx, s| async { - let pool = ConnectionPool::from_genesis(version).await; - let (mut node, runner) = testonly::StateKeeper::new(ctx, pool.clone()).await?; - s.spawn_bg(runner.run_real(ctx)); - - tracing::info!("analyzing storage"); - { - let mut conn = pool.connection(ctx).await.unwrap(); - let mut n = validator::BlockNumber(0); - while let Some(p) = conn.payload(ctx, n).await? { - tracing::info!("block[{n}] = {p:?}"); - n = n + 1; - } - } - - // Seal a bunch of batches. - node.push_random_blocks(rng, 10).await; - node.seal_batch().await; - pool.wait_for_batch(ctx, node.last_sealed_batch()).await?; - // We can verify only 2nd batch onward, because - // batch witness verifies parent of the last block of the - // previous batch (and 0th batch contains only 1 block). - for n in 2..=node.last_sealed_batch().0 { - let n = L1BatchNumber(n); - let batch_with_witness = node.load_batch_with_witness(ctx, n).await?; - let commit = node.load_batch_commit(ctx, n).await?; - batch_with_witness.verify(&commit)?; - } - Ok(()) - }) - .await - .unwrap(); -} diff --git a/prover/Cargo.lock b/prover/Cargo.lock index 582f15637b5..5ac79d1dd0f 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -7570,9 +7570,9 @@ dependencies = [ [[package]] name = "zksync_concurrency" -version = "0.1.0-rc.10" +version = "0.1.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a463106f37cfa589896e6a165b5bb0533013377990e19f10e8c4894346a62e8b" +checksum = "b0e31a9fc9a390b440cd12bbe040330dc64f64697a8a8ecbc3beb98cd0747909" dependencies = [ "anyhow", "once_cell", @@ -7606,9 +7606,9 @@ dependencies = [ [[package]] name = "zksync_consensus_crypto" -version = "0.1.0-rc.10" +version = "0.1.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f0883af373e9198fd27c0148e7e47b37f912cb4b444bec3f7eed0af0b0dfc69" +checksum = "efb7ff3ec44b7b92fd4e28d9d92b83d61dc74125ccfc90bcfb27a5750d8a8580" dependencies = [ "anyhow", "blst", @@ -7630,9 +7630,9 @@ dependencies = [ [[package]] name = "zksync_consensus_roles" -version = "0.1.0-rc.10" +version = "0.1.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e426aa7c68a12dde702c3ec4ef49de24d9054ef908384232b7887e043ca3f2fe" +checksum = "72223c0b20621775db51bcc4b043addafeaf784d444af2ad4bc8bcdee477367c" dependencies = [ "anyhow", "bit-vec", @@ -7652,9 +7652,9 @@ dependencies = [ [[package]] name = "zksync_consensus_storage" -version = "0.1.0-rc.10" +version = "0.1.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8388c33fd5bc3725e58c26db2d3016538c6221c6448b3e92cf5df07f6074a028" +checksum = "41d1750ad93f7e3a0c2f5880f9bcc1244a3b46d3e6c124c4f65f545032b87464" dependencies = [ "anyhow", "async-trait", @@ -7672,9 +7672,9 @@ dependencies = [ [[package]] name = "zksync_consensus_utils" -version = "0.1.0-rc.10" +version = "0.1.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612920e56dcb99f227bc23e1254f4dabc7cb4c5cd1a9ec400ceba0ec6fa77c1e" +checksum = "2ff679f8b5f671d887a750b8107f3b5c01fd6085f68eef37ab01de8d2bd0736b" dependencies = [ "anyhow", "rand 0.8.5", @@ -7984,9 +7984,9 @@ dependencies = [ [[package]] name = "zksync_protobuf" -version = "0.1.0-rc.10" +version = "0.1.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0d82fd63f27681b9c01f0e01e3060e71b72809db8e21d9130663ee92bd1e391" +checksum = "f4f6ba3bf0aac20de18b4ae18a22d8c81b83f8f72e8fdec1c879525ecdacd2f5" dependencies = [ "anyhow", "bit-vec", @@ -8005,9 +8005,9 @@ dependencies = [ [[package]] name = "zksync_protobuf_build" -version = "0.1.0-rc.10" +version = "0.1.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee3c158ab4d211053886371d4a00514bdf8ebdf826d40ee03b98fee2e0d1605e" +checksum = "7798c248b9a64505f0586bd5fadad6b26c999be4a8dec6b1a86b10b3888169c5" dependencies = [ "anyhow", "heck 0.5.0", diff --git a/zk_toolbox/Cargo.lock b/zk_toolbox/Cargo.lock index 04a29f5b0f4..41b972a4cef 100644 --- a/zk_toolbox/Cargo.lock +++ b/zk_toolbox/Cargo.lock @@ -6337,9 +6337,9 @@ dependencies = [ [[package]] name = "zksync_concurrency" -version = "0.1.0-rc.10" +version = "0.1.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a463106f37cfa589896e6a165b5bb0533013377990e19f10e8c4894346a62e8b" +checksum = "b0e31a9fc9a390b440cd12bbe040330dc64f64697a8a8ecbc3beb98cd0747909" dependencies = [ "anyhow", "once_cell", @@ -6371,9 +6371,9 @@ dependencies = [ [[package]] name = "zksync_consensus_utils" -version = "0.1.0-rc.10" +version = "0.1.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612920e56dcb99f227bc23e1254f4dabc7cb4c5cd1a9ec400ceba0ec6fa77c1e" +checksum = "2ff679f8b5f671d887a750b8107f3b5c01fd6085f68eef37ab01de8d2bd0736b" dependencies = [ "anyhow", "rand", @@ -6422,9 +6422,9 @@ dependencies = [ [[package]] name = "zksync_protobuf" -version = "0.1.0-rc.10" +version = "0.1.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0d82fd63f27681b9c01f0e01e3060e71b72809db8e21d9130663ee92bd1e391" +checksum = "f4f6ba3bf0aac20de18b4ae18a22d8c81b83f8f72e8fdec1c879525ecdacd2f5" dependencies = [ "anyhow", "bit-vec", @@ -6443,9 +6443,9 @@ dependencies = [ [[package]] name = "zksync_protobuf_build" -version = "0.1.0-rc.10" +version = "0.1.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee3c158ab4d211053886371d4a00514bdf8ebdf826d40ee03b98fee2e0d1605e" +checksum = "7798c248b9a64505f0586bd5fadad6b26c999be4a8dec6b1a86b10b3888169c5" dependencies = [ "anyhow", "heck", diff --git a/zk_toolbox/Cargo.toml b/zk_toolbox/Cargo.toml index ab850d82770..ef2aed7c99c 100644 --- a/zk_toolbox/Cargo.toml +++ b/zk_toolbox/Cargo.toml @@ -30,7 +30,7 @@ types = { path = "crates/types" } zksync_config = { path = "../core/lib/config" } zksync_protobuf_config = { path = "../core/lib/protobuf_config" } zksync_basic_types = { path = "../core/lib/basic_types" } -zksync_protobuf = "=0.1.0-rc.10" +zksync_protobuf = "=0.1.0-rc.11" # External dependencies anyhow = "1.0.82"