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/Cargo.lock b/Cargo.lock index b65826900d4..5bbd7217f4d 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", @@ -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", @@ -7296,8 +7287,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 +7864,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 +7874,7 @@ dependencies = [ "serde", "serde_json", "static_assertions", - "zk_evm_abstractions 0.150.0", + "zk_evm_abstractions 0.150.4", ] [[package]] @@ -7914,15 +7905,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 +7962,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 +7977,7 @@ dependencies = [ "seq-macro", "serde", "smallvec", - "zkevm_opcode_defs 0.150.0", + "zkevm_opcode_defs 0.150.4", ] [[package]] @@ -8033,9 +8024,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 +8120,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 +8131,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", @@ -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", @@ -8799,9 +8790,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 +8802,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "zkevm_circuits 0.150.3", + "zkevm_circuits 0.150.4", ] [[package]] @@ -8934,7 +8925,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 +8940,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", @@ -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", @@ -9377,7 +9368,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..d4855a34b9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -207,30 +207,30 @@ 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" } # 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/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/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/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/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 d4d08a93415..d262e374aef 100644 --- a/core/lib/db_connection/src/connection_pool.rs +++ b/core/lib/db_connection/src/connection_pool.rs @@ -376,7 +376,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 } @@ -390,7 +390,7 @@ impl ConnectionPool { pub fn connection_tagged( &self, requester: &'static str, - ) -> impl Future>> + '_ { + ) -> impl Future>> + '_ { let location = Location::caller(); async move { let tags = ConnectionTags { @@ -404,7 +404,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(); @@ -415,7 +415,7 @@ impl ConnectionPool { Ok(Connection::::from_pool( conn, tags, - self.traced_connections.as_deref(), + self.traced_connections.as_ref(), )) } 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/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/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/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/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/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/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/core/node/state_keeper/src/batch_executor/main_executor.rs b/core/node/state_keeper/src/batch_executor/main_executor.rs index b4090460116..db4daeb7744 100644 --- a/core/node/state_keeper/src/batch_executor/main_executor.rs +++ b/core/node/state_keeper/src/batch_executor/main_executor.rs @@ -2,20 +2,19 @@ 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}, - 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_state::OwnedStorage; -use zksync_types::{vm::FastVmMode, vm_trace::Call, Transaction}; +use zksync_types::{vm::FastVmMode, Transaction}; use super::{BatchExecutor, BatchExecutorHandle, Command, TxExecutionResult}; use crate::{ @@ -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/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/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/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}; 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 { 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 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) diff --git a/prover/Cargo.lock b/prover/Cargo.lock index 453cc96cd8b..e48dc075b2f 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -733,9 +733,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", @@ -898,11 +898,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", @@ -948,14 +948,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]] @@ -1015,12 +1015,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", @@ -1862,9 +1862,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", @@ -1873,9 +1873,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", ] @@ -5572,9 +5572,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", @@ -6852,8 +6852,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]] @@ -7357,9 +7357,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", @@ -7367,7 +7367,7 @@ dependencies = [ "serde", "serde_json", "static_assertions", - "zk_evm_abstractions 0.150.0", + "zk_evm_abstractions 0.150.4", ] [[package]] @@ -7398,22 +7398,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", @@ -7426,7 +7426,7 @@ dependencies = [ "smallvec", "structopt", "thiserror", - "zkevm_opcode_defs 0.150.0", + "zkevm_opcode_defs 0.150.4", ] [[package]] @@ -7475,9 +7475,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", @@ -7490,7 +7490,7 @@ dependencies = [ "seq-macro", "serde", "smallvec", - "zkevm_opcode_defs 0.150.0", + "zkevm_opcode_defs 0.150.4", ] [[package]] @@ -7537,9 +7537,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", @@ -7554,13 +7554,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", @@ -7581,9 +7581,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", @@ -7595,9 +7595,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", @@ -7612,9 +7612,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", @@ -7641,9 +7641,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", @@ -7677,9 +7677,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", @@ -7701,9 +7701,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", @@ -7723,9 +7723,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", @@ -7743,9 +7743,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", @@ -7902,9 +7902,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", @@ -7914,7 +7914,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "zkevm_circuits 0.150.3", + "zkevm_circuits 0.150.4", ] [[package]] @@ -7954,7 +7954,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", @@ -7967,7 +7967,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", @@ -8026,7 +8026,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", @@ -8055,9 +8055,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", @@ -8076,9 +8076,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", @@ -8212,7 +8212,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 49a74aabe26..271cf38c37a 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/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..4f396fd4b5a 100644 --- a/prover/crates/bin/witness_generator/src/node_aggregation.rs +++ b/prover/crates/bin/witness_generator/src/node_aggregation.rs @@ -3,12 +3,12 @@ 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, }; 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, }, }; @@ -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); @@ -444,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 a21aabc5d6d..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}; @@ -97,32 +100,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< @@ -151,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(); @@ -161,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) @@ -227,11 +238,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. 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(), } } 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"