diff --git a/.github/workflows/ci-core-reusable.yml b/.github/workflows/ci-core-reusable.yml index 288bed7f9671..504f7761bb8e 100644 --- a/.github/workflows/ci-core-reusable.yml +++ b/.github/workflows/ci-core-reusable.yml @@ -135,7 +135,7 @@ jobs: base_token: ["Eth", "Custom"] deployment_mode: ["Rollup", "Validium"] env: - SERVER_COMPONENTS: "api,tree,eth,state_keeper,housekeeper,commitment_generator,vm_runner_protective_reads,da_dispatcher,base_token_ratio_persister${{ matrix.consensus && ',consensus' || '' }}" + SERVER_COMPONENTS: "api,tree,eth,state_keeper,housekeeper,commitment_generator,vm_runner_protective_reads,vm_runner_bwip,da_dispatcher,base_token_ratio_persister${{ matrix.consensus && ',consensus' || '' }}" runs-on: [matterlabs-ci-runner] steps: @@ -309,7 +309,7 @@ jobs: runs-on: [matterlabs-ci-runner] env: - SERVER_COMPONENTS: "api,tree,eth,state_keeper,housekeeper,commitment_generator,vm_runner_protective_reads,da_dispatcher,base_token_ratio_persister${{ matrix.consensus && ',consensus' || '' }}" + SERVER_COMPONENTS: "api,tree,eth,state_keeper,housekeeper,commitment_generator,vm_runner_protective_reads,vm_runner_bwip,da_dispatcher,base_token_ratio_persister${{ matrix.consensus && ',consensus' || '' }}" EXT_NODE_FLAGS: "${{ matrix.consensus && '-- --enable-consensus' || '' }}" steps: diff --git a/Cargo.lock b/Cargo.lock index 5cc13f2897ce..c9727438b52c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9213,6 +9213,7 @@ dependencies = [ "tokio", "zksync_multivm", "zksync_object_store", + "zksync_state", "zksync_types", ] @@ -9592,6 +9593,8 @@ dependencies = [ "zksync_multivm", "zksync_node_genesis", "zksync_node_test_utils", + "zksync_object_store", + "zksync_prover_interface", "zksync_state", "zksync_state_keeper", "zksync_storage", diff --git a/core/bin/zksync_server/src/main.rs b/core/bin/zksync_server/src/main.rs index 51fce8e2d8d3..654d4b772006 100644 --- a/core/bin/zksync_server/src/main.rs +++ b/core/bin/zksync_server/src/main.rs @@ -11,10 +11,10 @@ use zksync_config::{ }, fri_prover_group::FriProverGroupConfig, house_keeper::HouseKeeperConfig, - ContractsConfig, DatabaseSecrets, FriProofCompressorConfig, FriProverConfig, - FriProverGatewayConfig, FriWitnessGeneratorConfig, FriWitnessVectorGeneratorConfig, - L1Secrets, ObservabilityConfig, PrometheusConfig, ProofDataHandlerConfig, - ProtectiveReadsWriterConfig, Secrets, + BasicWitnessInputProducerConfig, ContractsConfig, DatabaseSecrets, + FriProofCompressorConfig, FriProverConfig, FriProverGatewayConfig, + FriWitnessGeneratorConfig, FriWitnessVectorGeneratorConfig, L1Secrets, ObservabilityConfig, + PrometheusConfig, ProofDataHandlerConfig, ProtectiveReadsWriterConfig, Secrets, }, ApiConfig, BaseTokenAdjusterConfig, ContractVerifierConfig, DADispatcherConfig, DBConfig, EthConfig, EthWatchConfig, GasAdjusterConfig, GenesisConfig, ObjectStoreConfig, PostgresConfig, @@ -271,6 +271,7 @@ fn load_env_config() -> anyhow::Result { snapshot_creator: SnapshotsCreatorConfig::from_env().ok(), da_dispatcher_config: DADispatcherConfig::from_env().ok(), protective_reads_writer_config: ProtectiveReadsWriterConfig::from_env().ok(), + basic_witness_input_producer_config: BasicWitnessInputProducerConfig::from_env().ok(), core_object_store: ObjectStoreConfig::from_env().ok(), base_token_adjuster_config: BaseTokenAdjusterConfig::from_env().ok(), commitment_generator: None, diff --git a/core/bin/zksync_server/src/node_builder.rs b/core/bin/zksync_server/src/node_builder.rs index d33abdbbf199..4a80898ca8dc 100644 --- a/core/bin/zksync_server/src/node_builder.rs +++ b/core/bin/zksync_server/src/node_builder.rs @@ -48,7 +48,9 @@ use zksync_node_framework::{ output_handler::OutputHandlerLayer, RocksdbStorageOptions, StateKeeperLayer, }, tee_verifier_input_producer::TeeVerifierInputProducerLayer, - vm_runner::protective_reads::ProtectiveReadsWriterLayer, + vm_runner::{ + bwip::BasicWitnessInputProducerLayer, protective_reads::ProtectiveReadsWriterLayer, + }, web3_api::{ caches::MempoolCacheLayer, server::{Web3ServerLayer, Web3ServerOptionalConfig}, @@ -503,6 +505,17 @@ impl MainNodeBuilder { Ok(self) } + fn add_vm_runner_bwip_layer(mut self) -> anyhow::Result { + let basic_witness_input_producer_config = + try_load_config!(self.configs.basic_witness_input_producer_config); + self.node.add_layer(BasicWitnessInputProducerLayer::new( + basic_witness_input_producer_config, + self.genesis_config.l2_chain_id, + )); + + Ok(self) + } + fn add_base_token_ratio_persister_layer(mut self) -> anyhow::Result { let config = try_load_config!(self.configs.base_token_adjuster); self.node @@ -604,6 +617,9 @@ impl MainNodeBuilder { Component::BaseTokenRatioPersister => { self = self.add_base_token_ratio_persister_layer()?; } + Component::VmRunnerBwip => { + self = self.add_vm_runner_bwip_layer()?; + } } } Ok(self.node.build()?) diff --git a/core/lib/basic_types/src/prover_dal.rs b/core/lib/basic_types/src/prover_dal.rs index 3215e7095e60..29d36cc91f8f 100644 --- a/core/lib/basic_types/src/prover_dal.rs +++ b/core/lib/basic_types/src/prover_dal.rs @@ -267,6 +267,7 @@ pub struct ProverJobFriInfo { pub struct BasicWitnessGeneratorJobInfo { pub l1_batch_number: L1BatchNumber, pub merkle_tree_paths_blob_url: Option, + pub witness_inputs_blob_url: Option, pub attempts: u32, pub status: WitnessJobStatus, pub error: Option, diff --git a/core/lib/config/src/configs/general.rs b/core/lib/config/src/configs/general.rs index b7b501364c65..9dbda3f845ee 100644 --- a/core/lib/config/src/configs/general.rs +++ b/core/lib/config/src/configs/general.rs @@ -7,7 +7,7 @@ use crate::{ house_keeper::HouseKeeperConfig, pruning::PruningConfig, snapshot_recovery::SnapshotRecoveryConfig, - vm_runner::ProtectiveReadsWriterConfig, + vm_runner::{BasicWitnessInputProducerConfig, ProtectiveReadsWriterConfig}, CommitmentGeneratorConfig, FriProofCompressorConfig, FriProverConfig, FriProverGatewayConfig, FriWitnessGeneratorConfig, FriWitnessVectorGeneratorConfig, ObservabilityConfig, PrometheusConfig, ProofDataHandlerConfig, @@ -40,6 +40,7 @@ pub struct GeneralConfig { pub observability: Option, pub da_dispatcher_config: Option, pub protective_reads_writer_config: Option, + pub basic_witness_input_producer_config: Option, pub commitment_generator: Option, pub snapshot_recovery: Option, pub pruning: Option, diff --git a/core/lib/config/src/configs/mod.rs b/core/lib/config/src/configs/mod.rs index 0e8730ac9141..f66b6f897125 100644 --- a/core/lib/config/src/configs/mod.rs +++ b/core/lib/config/src/configs/mod.rs @@ -25,7 +25,7 @@ pub use self::{ snapshot_recovery::SnapshotRecoveryConfig, snapshots_creator::SnapshotsCreatorConfig, utils::PrometheusConfig, - vm_runner::ProtectiveReadsWriterConfig, + vm_runner::{BasicWitnessInputProducerConfig, ProtectiveReadsWriterConfig}, }; pub mod api; diff --git a/core/lib/config/src/configs/vm_runner.rs b/core/lib/config/src/configs/vm_runner.rs index eb3d4a9d4b24..fa7c7c1a90a3 100644 --- a/core/lib/config/src/configs/vm_runner.rs +++ b/core/lib/config/src/configs/vm_runner.rs @@ -17,3 +17,20 @@ impl ProtectiveReadsWriterConfig { "./db/protective_reads_writer".to_owned() } } + +#[derive(Debug, Deserialize, Clone, PartialEq, Default)] +pub struct BasicWitnessInputProducerConfig { + /// Path to the RocksDB data directory that serves state cache. + #[serde(default = "BasicWitnessInputProducerConfig::default_db_path")] + pub db_path: String, + /// How many max batches should be processed at the same time. + pub window_size: u32, + /// All batches before this one (inclusive) are always considered to be processed. + pub first_processed_batch: L1BatchNumber, +} + +impl BasicWitnessInputProducerConfig { + fn default_db_path() -> String { + "./db/basic_witness_input_producer".to_owned() + } +} diff --git a/core/lib/dal/.sqlx/query-05c2a77d9f65d435e2df63a300850e42abbaf365a1b041d0e7a809796ef0fe63.json b/core/lib/dal/.sqlx/query-05c2a77d9f65d435e2df63a300850e42abbaf365a1b041d0e7a809796ef0fe63.json new file mode 100644 index 000000000000..f3c85b9b43dc --- /dev/null +++ b/core/lib/dal/.sqlx/query-05c2a77d9f65d435e2df63a300850e42abbaf365a1b041d0e7a809796ef0fe63.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE proof_generation_details\n SET\n status = 'picked_by_prover',\n updated_at = NOW(),\n prover_taken_at = NOW()\n WHERE\n l1_batch_number = (\n SELECT\n l1_batch_number\n FROM\n proof_generation_details\n LEFT JOIN l1_batches ON l1_batch_number = l1_batches.number\n WHERE\n (\n vm_run_data_blob_url IS NOT NULL\n AND proof_gen_data_blob_url IS NOT NULL\n AND l1_batches.hash IS NOT NULL\n AND l1_batches.aux_data_hash IS NOT NULL\n AND l1_batches.meta_parameters_hash IS NOT NULL\n AND status = 'unpicked'\n )\n OR (\n status = 'picked_by_prover'\n AND prover_taken_at < NOW() - $1::INTERVAL\n )\n ORDER BY\n l1_batch_number ASC\n LIMIT\n 1\n )\n RETURNING\n proof_generation_details.l1_batch_number\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "l1_batch_number", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Interval" + ] + }, + "nullable": [ + false + ] + }, + "hash": "05c2a77d9f65d435e2df63a300850e42abbaf365a1b041d0e7a809796ef0fe63" +} diff --git a/core/lib/dal/.sqlx/query-11af69fc254e54449b64c086667700a95e4c37a7a18531b3cdf120394cb055b9.json b/core/lib/dal/.sqlx/query-11af69fc254e54449b64c086667700a95e4c37a7a18531b3cdf120394cb055b9.json deleted file mode 100644 index ed211d7dc9d8..000000000000 --- a/core/lib/dal/.sqlx/query-11af69fc254e54449b64c086667700a95e4c37a7a18531b3cdf120394cb055b9.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE proof_generation_details\n SET\n status = 'picked_by_prover',\n updated_at = NOW(),\n prover_taken_at = NOW()\n WHERE\n l1_batch_number = (\n SELECT\n l1_batch_number\n FROM\n proof_generation_details\n WHERE\n status = 'ready_to_be_proven'\n OR (\n status = 'picked_by_prover'\n AND prover_taken_at < NOW() - $1::INTERVAL\n )\n ORDER BY\n l1_batch_number ASC\n LIMIT\n 1\n FOR UPDATE\n SKIP LOCKED\n )\n RETURNING\n proof_generation_details.l1_batch_number\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "l1_batch_number", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Interval" - ] - }, - "nullable": [ - false - ] - }, - "hash": "11af69fc254e54449b64c086667700a95e4c37a7a18531b3cdf120394cb055b9" -} diff --git a/core/lib/dal/.sqlx/query-2482716de397893c52840eb39391ad3349e4d932d3de64b6dade97481cd171a4.json b/core/lib/dal/.sqlx/query-2482716de397893c52840eb39391ad3349e4d932d3de64b6dade97481cd171a4.json new file mode 100644 index 000000000000..b5c9869d1467 --- /dev/null +++ b/core/lib/dal/.sqlx/query-2482716de397893c52840eb39391ad3349e4d932d3de64b6dade97481cd171a4.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "\n WITH\n available_batches AS (\n SELECT\n MAX(number) AS \"last_batch\"\n FROM\n l1_batches\n ),\n processed_batches AS (\n SELECT\n COALESCE(MAX(l1_batch_number), $1) + $2 AS \"last_ready_batch\"\n FROM\n vm_runner_bwip\n )\n SELECT\n LEAST(last_batch, last_ready_batch) AS \"last_ready_batch!\"\n FROM\n available_batches\n FULL JOIN processed_batches ON TRUE\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "last_ready_batch!", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + true + ] + }, + "hash": "2482716de397893c52840eb39391ad3349e4d932d3de64b6dade97481cd171a4" +} diff --git a/core/lib/dal/.sqlx/query-08e59ed8e2fd1a74e19d8bf0d131e4ee6682a89fb86f3b715a240805d44e6d87.json b/core/lib/dal/.sqlx/query-41a2731a3fe6ae441902632dcce15601ff39acd03e3c8a2265c9036b3dc54383.json similarity index 65% rename from core/lib/dal/.sqlx/query-08e59ed8e2fd1a74e19d8bf0d131e4ee6682a89fb86f3b715a240805d44e6d87.json rename to core/lib/dal/.sqlx/query-41a2731a3fe6ae441902632dcce15601ff39acd03e3c8a2265c9036b3dc54383.json index 0c3ca92c10c5..9ec433e52acb 100644 --- a/core/lib/dal/.sqlx/query-08e59ed8e2fd1a74e19d8bf0d131e4ee6682a89fb86f3b715a240805d44e6d87.json +++ b/core/lib/dal/.sqlx/query-41a2731a3fe6ae441902632dcce15601ff39acd03e3c8a2265c9036b3dc54383.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n INSERT INTO\n proof_generation_details (l1_batch_number, status, proof_gen_data_blob_url, created_at, updated_at)\n VALUES\n ($1, 'ready_to_be_proven', $2, NOW(), NOW())\n ON CONFLICT (l1_batch_number) DO NOTHING\n ", + "query": "\n INSERT INTO\n proof_generation_details (l1_batch_number, status, proof_gen_data_blob_url, created_at, updated_at)\n VALUES\n ($1, 'unpicked', $2, NOW(), NOW())\n ON CONFLICT (l1_batch_number) DO NOTHING\n ", "describe": { "columns": [], "parameters": { @@ -11,5 +11,5 @@ }, "nullable": [] }, - "hash": "08e59ed8e2fd1a74e19d8bf0d131e4ee6682a89fb86f3b715a240805d44e6d87" + "hash": "41a2731a3fe6ae441902632dcce15601ff39acd03e3c8a2265c9036b3dc54383" } diff --git a/core/lib/dal/.sqlx/query-703836a3f065b0aedf71ad0474cac5e5fccb3ec55aa1227f5f1ea5a11f9b36a9.json b/core/lib/dal/.sqlx/query-703836a3f065b0aedf71ad0474cac5e5fccb3ec55aa1227f5f1ea5a11f9b36a9.json new file mode 100644 index 000000000000..be9d5219665a --- /dev/null +++ b/core/lib/dal/.sqlx/query-703836a3f065b0aedf71ad0474cac5e5fccb3ec55aa1227f5f1ea5a11f9b36a9.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE proof_generation_details\n SET\n vm_run_data_blob_url = $1,\n updated_at = NOW()\n WHERE\n l1_batch_number = $2\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Int8" + ] + }, + "nullable": [] + }, + "hash": "703836a3f065b0aedf71ad0474cac5e5fccb3ec55aa1227f5f1ea5a11f9b36a9" +} diff --git a/core/lib/dal/.sqlx/query-58aed39245c72d231b268ce83105bb2036d21f60d4c6934f9145730ac35c04de.json b/core/lib/dal/.sqlx/query-815a7037a11dfc32e9d084d57178a9777126abebaf648c00fdcc24beb9967010.json similarity index 60% rename from core/lib/dal/.sqlx/query-58aed39245c72d231b268ce83105bb2036d21f60d4c6934f9145730ac35c04de.json rename to core/lib/dal/.sqlx/query-815a7037a11dfc32e9d084d57178a9777126abebaf648c00fdcc24beb9967010.json index 502d14e05ea5..a5419ff6706b 100644 --- a/core/lib/dal/.sqlx/query-58aed39245c72d231b268ce83105bb2036d21f60d4c6934f9145730ac35c04de.json +++ b/core/lib/dal/.sqlx/query-815a7037a11dfc32e9d084d57178a9777126abebaf648c00fdcc24beb9967010.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n l1_batch_number\n FROM\n proof_generation_details\n WHERE\n status = 'ready_to_be_proven'\n ORDER BY\n l1_batch_number ASC\n LIMIT\n 1\n ", + "query": "\n SELECT\n l1_batch_number\n FROM\n proof_generation_details\n WHERE\n status = 'unpicked'\n ORDER BY\n l1_batch_number ASC\n LIMIT\n 1\n ", "describe": { "columns": [ { @@ -16,5 +16,5 @@ false ] }, - "hash": "58aed39245c72d231b268ce83105bb2036d21f60d4c6934f9145730ac35c04de" + "hash": "815a7037a11dfc32e9d084d57178a9777126abebaf648c00fdcc24beb9967010" } diff --git a/core/lib/dal/.sqlx/query-a3f24c7f2298398517db009f7e5373c57d2dc6ec03d84f91a221ab8097e587cc.json b/core/lib/dal/.sqlx/query-a3f24c7f2298398517db009f7e5373c57d2dc6ec03d84f91a221ab8097e587cc.json new file mode 100644 index 000000000000..617fd4e81ea1 --- /dev/null +++ b/core/lib/dal/.sqlx/query-a3f24c7f2298398517db009f7e5373c57d2dc6ec03d84f91a221ab8097e587cc.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO\n vm_runner_bwip (l1_batch_number, created_at, updated_at)\n VALUES\n ($1, NOW(), NOW())\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [] + }, + "hash": "a3f24c7f2298398517db009f7e5373c57d2dc6ec03d84f91a221ab8097e587cc" +} diff --git a/core/lib/dal/.sqlx/query-a85a15aa2e0be1c1f50d15a8354afcf939e8352e21689baf861b61a666bdc1fd.json b/core/lib/dal/.sqlx/query-a85a15aa2e0be1c1f50d15a8354afcf939e8352e21689baf861b61a666bdc1fd.json new file mode 100644 index 000000000000..cf1fad78a462 --- /dev/null +++ b/core/lib/dal/.sqlx/query-a85a15aa2e0be1c1f50d15a8354afcf939e8352e21689baf861b61a666bdc1fd.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n MAX(l1_batch_number) AS \"last_processed_l1_batch\"\n FROM\n vm_runner_bwip\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "last_processed_l1_batch", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "a85a15aa2e0be1c1f50d15a8354afcf939e8352e21689baf861b61a666bdc1fd" +} diff --git a/core/lib/dal/migrations/20240619143458_add_vm_run_data_blob_url_column.down.sql b/core/lib/dal/migrations/20240619143458_add_vm_run_data_blob_url_column.down.sql new file mode 100644 index 000000000000..1f86ba3bb696 --- /dev/null +++ b/core/lib/dal/migrations/20240619143458_add_vm_run_data_blob_url_column.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE proof_generation_details DROP COLUMN IF EXISTS vm_run_data_blob_url; +DROP TABLE IF EXISTS vm_runner_bwip; diff --git a/core/lib/dal/migrations/20240619143458_add_vm_run_data_blob_url_column.up.sql b/core/lib/dal/migrations/20240619143458_add_vm_run_data_blob_url_column.up.sql new file mode 100644 index 000000000000..1fe90c191411 --- /dev/null +++ b/core/lib/dal/migrations/20240619143458_add_vm_run_data_blob_url_column.up.sql @@ -0,0 +1,10 @@ +ALTER TABLE proof_generation_details + ADD COLUMN IF NOT EXISTS vm_run_data_blob_url TEXT DEFAULT NULL; + +CREATE TABLE IF NOT EXISTS vm_runner_bwip +( + l1_batch_number BIGINT NOT NULL PRIMARY KEY, + created_at TIMESTAMP NOT NULL, + updated_at TIMESTAMP NOT NULL, + time_taken TIME +); diff --git a/core/lib/dal/src/proof_generation_dal.rs b/core/lib/dal/src/proof_generation_dal.rs index 88300cf08a18..d64df3a752f8 100644 --- a/core/lib/dal/src/proof_generation_dal.rs +++ b/core/lib/dal/src/proof_generation_dal.rs @@ -19,8 +19,8 @@ pub struct ProofGenerationDal<'a, 'c> { #[derive(Debug, EnumString, Display)] enum ProofGenerationJobStatus { - #[strum(serialize = "ready_to_be_proven")] - ReadyToBeProven, + #[strum(serialize = "unpicked")] + Unpicked, #[strum(serialize = "picked_by_prover")] PickedByProver, #[strum(serialize = "generated")] @@ -48,8 +48,16 @@ impl ProofGenerationDal<'_, '_> { l1_batch_number FROM proof_generation_details + LEFT JOIN l1_batches ON l1_batch_number = l1_batches.number WHERE - status = 'ready_to_be_proven' + ( + vm_run_data_blob_url IS NOT NULL + AND proof_gen_data_blob_url IS NOT NULL + AND l1_batches.hash IS NOT NULL + AND l1_batches.aux_data_hash IS NOT NULL + AND l1_batches.meta_parameters_hash IS NOT NULL + AND status = 'unpicked' + ) OR ( status = 'picked_by_prover' AND prover_taken_at < NOW() - $1::INTERVAL @@ -58,8 +66,6 @@ impl ProofGenerationDal<'_, '_> { l1_batch_number ASC LIMIT 1 - FOR UPDATE - SKIP LOCKED ) RETURNING proof_generation_details.l1_batch_number @@ -112,6 +118,43 @@ impl ProofGenerationDal<'_, '_> { Ok(()) } + pub async fn save_vm_runner_artifacts_metadata( + &mut self, + batch_number: L1BatchNumber, + vm_run_data_blob_url: &str, + ) -> DalResult<()> { + let batch_number = i64::from(batch_number.0); + let query = sqlx::query!( + r#" + UPDATE proof_generation_details + SET + vm_run_data_blob_url = $1, + updated_at = NOW() + WHERE + l1_batch_number = $2 + "#, + vm_run_data_blob_url, + batch_number + ); + let instrumentation = Instrumented::new("save_proof_artifacts_metadata") + .with_arg("vm_run_data_blob_url", &vm_run_data_blob_url) + .with_arg("l1_batch_number", &batch_number); + let result = instrumentation + .clone() + .with(query) + .execute(self.storage) + .await?; + if result.rows_affected() == 0 { + let err = instrumentation.constraint_error(anyhow::anyhow!( + "Cannot save vm_run_data_blob_url for a batch number {} that does not exist", + batch_number + )); + return Err(err); + } + + Ok(()) + } + /// The caller should ensure that `l1_batch_number` exists in the database. pub async fn insert_proof_generation_details( &mut self, @@ -123,7 +166,7 @@ impl ProofGenerationDal<'_, '_> { INSERT INTO proof_generation_details (l1_batch_number, status, proof_gen_data_blob_url, created_at, updated_at) VALUES - ($1, 'ready_to_be_proven', $2, NOW(), NOW()) + ($1, 'unpicked', $2, NOW(), NOW()) ON CONFLICT (l1_batch_number) DO NOTHING "#, i64::from(l1_batch_number.0), @@ -190,7 +233,7 @@ impl ProofGenerationDal<'_, '_> { FROM proof_generation_details WHERE - status = 'ready_to_be_proven' + status = 'unpicked' ORDER BY l1_batch_number ASC LIMIT @@ -231,7 +274,9 @@ impl ProofGenerationDal<'_, '_> { #[cfg(test)] mod tests { - use zksync_types::ProtocolVersion; + use zksync_types::{ + block::L1BatchTreeData, commitment::L1BatchCommitmentArtifacts, ProtocolVersion, H256, + }; use super::*; use crate::{tests::create_l1_batch_header, ConnectionPool, CoreDal}; @@ -274,6 +319,27 @@ mod tests { .insert_proof_generation_details(L1BatchNumber(1), "generation_data") .await .unwrap(); + conn.proof_generation_dal() + .save_vm_runner_artifacts_metadata(L1BatchNumber(1), "vm_run") + .await + .unwrap(); + conn.blocks_dal() + .save_l1_batch_tree_data( + L1BatchNumber(1), + &L1BatchTreeData { + hash: H256::zero(), + rollup_last_leaf_index: 123, + }, + ) + .await + .unwrap(); + conn.blocks_dal() + .save_l1_batch_commitment_artifacts( + L1BatchNumber(1), + &L1BatchCommitmentArtifacts::default(), + ) + .await + .unwrap(); let unpicked_l1_batch = conn .proof_generation_dal() diff --git a/core/lib/dal/src/vm_runner_dal.rs b/core/lib/dal/src/vm_runner_dal.rs index 4c07901c32bc..d1c93824f199 100644 --- a/core/lib/dal/src/vm_runner_dal.rs +++ b/core/lib/dal/src/vm_runner_dal.rs @@ -112,4 +112,76 @@ impl VmRunnerDal<'_, '_> { .await?; Ok(()) } + + pub async fn get_bwip_latest_processed_batch(&mut self) -> DalResult> { + let row = sqlx::query!( + r#" + SELECT + MAX(l1_batch_number) AS "last_processed_l1_batch" + FROM + vm_runner_bwip + "#, + ) + .instrument("get_bwip_latest_processed_batch") + .report_latency() + .fetch_one(self.storage) + .await?; + Ok(row.last_processed_l1_batch.map(|n| L1BatchNumber(n as u32))) + } + + pub async fn get_bwip_last_ready_batch( + &mut self, + default_batch: L1BatchNumber, + window_size: u32, + ) -> DalResult { + let row = sqlx::query!( + r#" + WITH + available_batches AS ( + SELECT + MAX(number) AS "last_batch" + FROM + l1_batches + ), + processed_batches AS ( + SELECT + COALESCE(MAX(l1_batch_number), $1) + $2 AS "last_ready_batch" + FROM + vm_runner_bwip + ) + SELECT + LEAST(last_batch, last_ready_batch) AS "last_ready_batch!" + FROM + available_batches + FULL JOIN processed_batches ON TRUE + "#, + default_batch.0 as i32, + window_size as i32 + ) + .instrument("get_bwip_last_ready_batch") + .report_latency() + .fetch_one(self.storage) + .await?; + Ok(L1BatchNumber(row.last_ready_batch as u32)) + } + + pub async fn mark_bwip_batch_as_completed( + &mut self, + l1_batch_number: L1BatchNumber, + ) -> DalResult<()> { + sqlx::query!( + r#" + INSERT INTO + vm_runner_bwip (l1_batch_number, created_at, updated_at) + VALUES + ($1, NOW(), NOW()) + "#, + i64::from(l1_batch_number.0), + ) + .instrument("mark_bwip_batch_as_completed") + .report_latency() + .execute(self.storage) + .await?; + Ok(()) + } } diff --git a/core/lib/env_config/src/vm_runner.rs b/core/lib/env_config/src/vm_runner.rs index 8a99ea2dc8e2..9973d760a236 100644 --- a/core/lib/env_config/src/vm_runner.rs +++ b/core/lib/env_config/src/vm_runner.rs @@ -1,4 +1,4 @@ -use zksync_config::configs::ProtectiveReadsWriterConfig; +use zksync_config::configs::{BasicWitnessInputProducerConfig, ProtectiveReadsWriterConfig}; use crate::{envy_load, FromEnv}; @@ -7,3 +7,9 @@ impl FromEnv for ProtectiveReadsWriterConfig { envy_load("vm_runner.protective_reads", "VM_RUNNER_PROTECTIVE_READS_") } } + +impl FromEnv for BasicWitnessInputProducerConfig { + fn from_env() -> anyhow::Result { + envy_load("vm_runner.bwip", "VM_RUNNER_BWIP_") + } +} diff --git a/core/lib/merkle_tree/src/domain.rs b/core/lib/merkle_tree/src/domain.rs index 5cb53355765c..37e9e0f23b53 100644 --- a/core/lib/merkle_tree/src/domain.rs +++ b/core/lib/merkle_tree/src/domain.rs @@ -2,7 +2,7 @@ use rayon::{ThreadPool, ThreadPoolBuilder}; use zksync_crypto::hasher::blake2::Blake2Hasher; -use zksync_prover_interface::inputs::{PrepareBasicCircuitsJob, StorageLogMetadata}; +use zksync_prover_interface::inputs::{StorageLogMetadata, WitnessInputMerklePaths}; use zksync_types::{L1BatchNumber, StorageKey}; use crate::{ @@ -37,7 +37,7 @@ pub struct TreeMetadata { /// 1-based index of the next leaf to be inserted in the tree. pub rollup_last_leaf_index: u64, /// Witness information. As with `repeated_writes`, no-op updates will be omitted from Merkle paths. - pub witness: Option, + pub witness: Option, } #[derive(Debug, PartialEq, Eq)] @@ -248,7 +248,7 @@ impl ZkSyncTree { self.tree.extend_with_proofs(instructions.to_vec()) }?; - let mut witness = PrepareBasicCircuitsJob::new(starting_leaf_count + 1); + let mut witness = WitnessInputMerklePaths::new(starting_leaf_count + 1); witness.reserve(output.logs.len()); for (log, instruction) in output.logs.iter().zip(instructions) { let empty_levels_end = TREE_DEPTH - log.merkle_path.len(); diff --git a/core/lib/protobuf_config/src/general.rs b/core/lib/protobuf_config/src/general.rs index 8993adeccb2c..9361c02b18d7 100644 --- a/core/lib/protobuf_config/src/general.rs +++ b/core/lib/protobuf_config/src/general.rs @@ -41,6 +41,10 @@ impl ProtoRepr for proto::GeneralConfig { .context("da_dispatcher")?, protective_reads_writer_config: read_optional_repr(&self.protective_reads_writer) .context("protective_reads_writer")?, + basic_witness_input_producer_config: read_optional_repr( + &self.basic_witness_input_producer, + ) + .context("basic_witness_input_producer")?, core_object_store: read_optional_repr(&self.core_object_store) .context("core_object_store")?, base_token_adjuster: read_optional_repr(&self.base_token_adjuster) @@ -86,6 +90,10 @@ impl ProtoRepr for proto::GeneralConfig { .protective_reads_writer_config .as_ref() .map(ProtoRepr::build), + basic_witness_input_producer: this + .basic_witness_input_producer_config + .as_ref() + .map(ProtoRepr::build), commitment_generator: this.commitment_generator.as_ref().map(ProtoRepr::build), snapshot_recovery: this.snapshot_recovery.as_ref().map(ProtoRepr::build), pruning: this.pruning.as_ref().map(ProtoRepr::build), diff --git a/core/lib/protobuf_config/src/proto/config/general.proto b/core/lib/protobuf_config/src/proto/config/general.proto index 457890158e54..a749fe37b238 100644 --- a/core/lib/protobuf_config/src/proto/config/general.proto +++ b/core/lib/protobuf_config/src/proto/config/general.proto @@ -49,4 +49,5 @@ message GeneralConfig { optional config.commitment_generator.CommitmentGenerator commitment_generator = 37; optional config.da_dispatcher.DataAvailabilityDispatcher da_dispatcher = 38; optional config.base_token_adjuster.BaseTokenAdjuster base_token_adjuster = 39; + optional config.vm_runner.BasicWitnessInputProducer basic_witness_input_producer = 40; } diff --git a/core/lib/protobuf_config/src/proto/config/vm_runner.proto b/core/lib/protobuf_config/src/proto/config/vm_runner.proto index c0c82d4d415f..93521a5fd893 100644 --- a/core/lib/protobuf_config/src/proto/config/vm_runner.proto +++ b/core/lib/protobuf_config/src/proto/config/vm_runner.proto @@ -7,3 +7,9 @@ message ProtectiveReadsWriter { optional uint64 window_size = 2; // required optional uint64 first_processed_batch = 3; // required } + +message BasicWitnessInputProducer { + optional string db_path = 1; // required; fs path + optional uint64 window_size = 2; // required + optional uint64 first_processed_batch = 3; // required +} diff --git a/core/lib/protobuf_config/src/vm_runner.rs b/core/lib/protobuf_config/src/vm_runner.rs index 78bfee750521..cc0d53ad519e 100644 --- a/core/lib/protobuf_config/src/vm_runner.rs +++ b/core/lib/protobuf_config/src/vm_runner.rs @@ -26,3 +26,25 @@ impl ProtoRepr for proto::ProtectiveReadsWriter { } } } + +impl ProtoRepr for proto::BasicWitnessInputProducer { + type Type = configs::BasicWitnessInputProducerConfig; + + fn read(&self) -> anyhow::Result { + Ok(Self::Type { + db_path: required(&self.db_path).context("db_path")?.clone(), + window_size: *required(&self.window_size).context("window_size")? as u32, + first_processed_batch: L1BatchNumber( + *required(&self.first_processed_batch).context("first_batch")? as u32, + ), + }) + } + + fn build(this: &Self::Type) -> Self { + Self { + db_path: Some(this.db_path.clone()), + window_size: Some(this.window_size as u64), + first_processed_batch: Some(this.first_processed_batch.0 as u64), + } + } +} diff --git a/core/lib/prover_interface/Cargo.toml b/core/lib/prover_interface/Cargo.toml index 5c5a9a1bdf14..f61cc3ac9b78 100644 --- a/core/lib/prover_interface/Cargo.toml +++ b/core/lib/prover_interface/Cargo.toml @@ -13,6 +13,7 @@ categories.workspace = true zksync_multivm.workspace = true zksync_object_store.workspace = true zksync_types.workspace = true +zksync_state.workspace = true # We can use the newest api to send proofs to L1. circuit_sequencer_api_1_5_0.workspace = true diff --git a/core/lib/prover_interface/src/api.rs b/core/lib/prover_interface/src/api.rs index 4683fdf21740..00ac85a40739 100644 --- a/core/lib/prover_interface/src/api.rs +++ b/core/lib/prover_interface/src/api.rs @@ -3,13 +3,12 @@ use serde::{Deserialize, Serialize}; use zksync_types::{ - basic_fri_types::Eip4844Blobs, protocol_version::{L1VerifierConfig, ProtocolSemanticVersion}, L1BatchNumber, }; use crate::{ - inputs::{PrepareBasicCircuitsJob, TeeVerifierInput}, + inputs::{TeeVerifierInput, WitnessInputData}, outputs::{L1BatchProofForL1, L1BatchTeeProofForL1}, }; @@ -18,10 +17,9 @@ use crate::{ #[derive(Debug, Serialize, Deserialize)] pub struct ProofGenerationData { pub l1_batch_number: L1BatchNumber, - pub data: PrepareBasicCircuitsJob, + pub witness_input_data: WitnessInputData, pub protocol_version: ProtocolSemanticVersion, pub l1_verifier_config: L1VerifierConfig, - pub eip_4844_blobs: Eip4844Blobs, } #[derive(Debug, Serialize, Deserialize)] diff --git a/core/lib/prover_interface/src/inputs.rs b/core/lib/prover_interface/src/inputs.rs index d9a5b4c2d17f..8f2403d3369a 100644 --- a/core/lib/prover_interface/src/inputs.rs +++ b/core/lib/prover_interface/src/inputs.rs @@ -1,10 +1,14 @@ -use std::{convert::TryInto, fmt::Debug}; +use std::{collections::HashMap, convert::TryInto, fmt::Debug}; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, Bytes}; use zksync_multivm::interface::{L1BatchEnv, SystemEnv}; use zksync_object_store::{serialize_using_bincode, Bucket, StoredObject}; -use zksync_types::{block::L2BlockExecutionData, L1BatchNumber, H256, U256}; +pub use zksync_state::WitnessStorage; +use zksync_types::{ + basic_fri_types::Eip4844Blobs, block::L2BlockExecutionData, + witness_block_state::WitnessStorageState, L1BatchNumber, ProtocolVersionId, H256, U256, +}; const HASH_LEN: usize = H256::len_bytes(); @@ -60,13 +64,13 @@ impl StorageLogMetadata { /// Merkle paths; if this is the case, the starting hashes are skipped and are the same /// as in the first path. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct PrepareBasicCircuitsJob { +pub struct WitnessInputMerklePaths { // Merkle paths and some auxiliary information for each read / write operation in a block. merkle_paths: Vec, next_enumeration_index: u64, } -impl StoredObject for PrepareBasicCircuitsJob { +impl StoredObject for WitnessInputMerklePaths { const BUCKET: Bucket = Bucket::WitnessInput; type Key<'a> = L1BatchNumber; @@ -77,7 +81,7 @@ impl StoredObject for PrepareBasicCircuitsJob { serialize_using_bincode!(); } -impl PrepareBasicCircuitsJob { +impl WitnessInputMerklePaths { /// Creates a new job with the specified leaf index and no included paths. pub fn new(next_enumeration_index: u64) -> Self { Self { @@ -133,22 +137,62 @@ impl PrepareBasicCircuitsJob { } } -/// Enriched `PrepareBasicCircuitsJob`. All the other fields are taken from the `l1_batches` table. -#[derive(Debug, Clone)] -pub struct BasicCircuitWitnessGeneratorInput { - pub block_number: L1BatchNumber, - pub previous_block_hash: H256, - pub previous_block_timestamp: u64, - pub block_timestamp: u64, - pub used_bytecodes_hashes: Vec, +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VMRunWitnessInputData { + pub l1_batch_number: L1BatchNumber, + pub used_bytecodes: HashMap>, pub initial_heap_content: Vec<(usize, U256)>, - pub merkle_paths_input: PrepareBasicCircuitsJob, + pub protocol_version: ProtocolVersionId, + pub bootloader_code: Vec<[u8; 32]>, + pub default_account_code_hash: U256, + pub storage_refunds: Vec, + pub pubdata_costs: Vec, + pub witness_block_state: WitnessStorageState, +} + +impl StoredObject for VMRunWitnessInputData { + const BUCKET: Bucket = Bucket::WitnessInput; + + type Key<'a> = L1BatchNumber; + + fn encode_key(key: Self::Key<'_>) -> String { + format!("vm_run_data_{key}.bin") + } + + serialize_using_bincode!(); +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WitnessInputData { + pub vm_run_data: VMRunWitnessInputData, + pub merkle_paths: WitnessInputMerklePaths, + pub previous_batch_metadata: L1BatchMetadataHashes, + pub eip_4844_blobs: Eip4844Blobs, +} + +impl StoredObject for WitnessInputData { + const BUCKET: Bucket = Bucket::WitnessInput; + + type Key<'a> = L1BatchNumber; + + fn encode_key(key: Self::Key<'_>) -> String { + format!("witness_inputs_{key}.bin") + } + + serialize_using_bincode!(); +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct L1BatchMetadataHashes { + pub root_hash: H256, + pub meta_hash: H256, + pub aux_hash: H256, } /// Version 1 of the data used as input for the TEE verifier. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct V1TeeVerifierInput { - pub prepare_basic_circuits_job: PrepareBasicCircuitsJob, + pub witness_input_merkle_paths: WitnessInputMerklePaths, pub l2_blocks_execution_data: Vec, pub l1_batch_env: L1BatchEnv, pub system_env: SystemEnv, @@ -157,14 +201,14 @@ pub struct V1TeeVerifierInput { impl V1TeeVerifierInput { pub fn new( - prepare_basic_circuits_job: PrepareBasicCircuitsJob, + witness_input_merkle_paths: WitnessInputMerklePaths, l2_blocks_execution_data: Vec, l1_batch_env: L1BatchEnv, system_env: SystemEnv, used_contracts: Vec<(H256, Vec)>, ) -> Self { V1TeeVerifierInput { - prepare_basic_circuits_job, + witness_input_merkle_paths, l2_blocks_execution_data, l1_batch_env, system_env, @@ -223,7 +267,7 @@ mod tests { }); let logs: Vec<_> = logs.collect(); - let mut job = PrepareBasicCircuitsJob::new(4); + let mut job = WitnessInputMerklePaths::new(4); job.reserve(logs.len()); for log in &logs { job.push_merkle_path(log.clone()); diff --git a/core/lib/prover_interface/tests/job_serialization.rs b/core/lib/prover_interface/tests/job_serialization.rs index dd102c322dd0..a2d55a140655 100644 --- a/core/lib/prover_interface/tests/job_serialization.rs +++ b/core/lib/prover_interface/tests/job_serialization.rs @@ -5,7 +5,7 @@ use tokio::fs; use zksync_object_store::{Bucket, MockObjectStore}; use zksync_prover_interface::{ api::{SubmitProofRequest, SubmitTeeProofRequest}, - inputs::{PrepareBasicCircuitsJob, StorageLogMetadata}, + inputs::{StorageLogMetadata, WitnessInputMerklePaths}, outputs::{L1BatchProofForL1, L1BatchTeeProofForL1}, }; use zksync_types::{ @@ -31,7 +31,7 @@ async fn prepare_basic_circuits_job_serialization() { .await .unwrap(); - let job: PrepareBasicCircuitsJob = store.get(L1BatchNumber(1)).await.unwrap(); + let job: WitnessInputMerklePaths = store.get(L1BatchNumber(1)).await.unwrap(); let key = store.put(L1BatchNumber(2), &job).await.unwrap(); let serialized_job = store.get_raw(Bucket::WitnessInput, &key).await.unwrap(); @@ -62,7 +62,7 @@ async fn prepare_basic_circuits_job_compatibility() { let serialized = bincode::serialize(&job_tuple).unwrap(); assert_eq!(serialized, snapshot); - let job: PrepareBasicCircuitsJob = bincode::deserialize(&snapshot).unwrap(); + let job: WitnessInputMerklePaths = bincode::deserialize(&snapshot).unwrap(); assert_eq!(job.next_enumeration_index(), job_tuple.1); let job_merkle_paths: Vec<_> = job.into_merkle_paths().collect(); assert_eq!(job_merkle_paths, job_tuple.0); diff --git a/core/lib/state/src/lib.rs b/core/lib/state/src/lib.rs index b01d4fd35375..66577841fd45 100644 --- a/core/lib/state/src/lib.rs +++ b/core/lib/state/src/lib.rs @@ -17,22 +17,11 @@ use zksync_types::{ H256, }; -mod cache; -mod catchup; -mod in_memory; -mod postgres; -mod rocksdb; -mod shadow_storage; -mod storage_factory; -mod storage_view; -#[cfg(test)] -mod test_utils; - pub use self::{ cache::sequential_cache::SequentialCache, catchup::{AsyncCatchupTask, RocksdbCell}, - in_memory::InMemoryStorage, // Note, that `test_infra` of the bootloader tests relies on this value to be exposed + in_memory::InMemoryStorage, in_memory::IN_MEMORY_STORAGE_DEFAULT_NETWORK_ID, postgres::{PostgresStorage, PostgresStorageCaches, PostgresStorageCachesTask}, rocksdb::{ @@ -40,9 +29,22 @@ pub use self::{ }, shadow_storage::ShadowStorage, storage_factory::{BatchDiff, PgOrRocksdbStorage, ReadStorageFactory, RocksdbWithMemory}, - storage_view::{StorageView, StorageViewMetrics}, + storage_view::{StorageView, StorageViewCache, StorageViewMetrics}, + witness::WitnessStorage, }; +mod cache; +mod catchup; +mod in_memory; +mod postgres; +mod rocksdb; +mod shadow_storage; +mod storage_factory; +mod storage_view; +#[cfg(test)] +mod test_utils; +mod witness; + /// Functionality to read from the VM storage. pub trait ReadStorage: fmt::Debug { /// Read value of the key. diff --git a/core/lib/state/src/storage_view.rs b/core/lib/state/src/storage_view.rs index 03962fdea13c..7dcfda2ba406 100644 --- a/core/lib/state/src/storage_view.rs +++ b/core/lib/state/src/storage_view.rs @@ -45,11 +45,36 @@ pub struct StorageView { storage_handle: S, // Used for caching and to get the list/count of modified keys modified_storage_keys: HashMap, + cache: StorageViewCache, + metrics: StorageViewMetrics, +} + +/// `StorageViewCache` is a struct for caching storage reads and `contains_key()` checks. +#[derive(Debug, Default, Clone)] +pub struct StorageViewCache { // Used purely for caching read_storage_keys: HashMap, // Cache for `contains_key()` checks. The cache is only valid within one L1 batch execution. - initial_writes_cache: HashMap, - metrics: StorageViewMetrics, + initial_writes: HashMap, +} + +impl StorageViewCache { + /// Returns the read storage keys. + pub fn read_storage_keys(&self) -> HashMap { + self.read_storage_keys.clone() + } + + /// Returns the initial writes. + pub fn initial_writes(&self) -> HashMap { + self.initial_writes.clone() + } +} + +impl StorageView { + /// Returns the underlying storage cache. + pub fn cache(&self) -> StorageViewCache { + self.cache.clone() + } } impl ReadStorage for Box @@ -83,8 +108,10 @@ impl StorageView { Self { storage_handle, modified_storage_keys: HashMap::new(), - read_storage_keys: HashMap::new(), - initial_writes_cache: HashMap::new(), + cache: StorageViewCache { + read_storage_keys: HashMap::new(), + initial_writes: HashMap::new(), + }, metrics: StorageViewMetrics::default(), } } @@ -95,10 +122,10 @@ impl StorageView { let cached_value = self .modified_storage_keys .get(key) - .or_else(|| self.read_storage_keys.get(key)); + .or_else(|| self.cache.read_storage_keys.get(key)); cached_value.copied().unwrap_or_else(|| { let value = self.storage_handle.read_value(key); - self.read_storage_keys.insert(*key, value); + self.cache.read_storage_keys.insert(*key, value); self.metrics.time_spent_on_storage_missed += started_at.elapsed(); self.metrics.storage_invocations_missed += 1; value @@ -107,8 +134,8 @@ impl StorageView { fn cache_size(&self) -> usize { self.modified_storage_keys.len() * mem::size_of::<(StorageKey, StorageValue)>() - + self.initial_writes_cache.len() * mem::size_of::<(StorageKey, bool)>() - + self.read_storage_keys.len() * mem::size_of::<(StorageKey, StorageValue)>() + + self.cache.initial_writes.len() * mem::size_of::<(StorageKey, bool)>() + + self.cache.read_storage_keys.len() * mem::size_of::<(StorageKey, StorageValue)>() } /// Returns the current metrics. @@ -146,11 +173,11 @@ impl ReadStorage for StorageView { /// Only keys contained in the underlying storage will return `false`. If a key was /// inserted using [`Self::set_value()`], it will still return `true`. fn is_write_initial(&mut self, key: &StorageKey) -> bool { - if let Some(&is_write_initial) = self.initial_writes_cache.get(key) { + if let Some(&is_write_initial) = self.cache.initial_writes.get(key) { is_write_initial } else { let is_write_initial = self.storage_handle.is_write_initial(key); - self.initial_writes_cache.insert(*key, is_write_initial); + self.cache.initial_writes.insert(*key, is_write_initial); is_write_initial } } @@ -166,7 +193,7 @@ impl ReadStorage for StorageView { impl WriteStorage for StorageView { fn read_storage_keys(&self) -> &HashMap { - &self.read_storage_keys + &self.cache.read_storage_keys } fn set_value(&mut self, key: StorageKey, value: StorageValue) -> StorageValue { diff --git a/core/lib/state/src/witness.rs b/core/lib/state/src/witness.rs new file mode 100644 index 000000000000..5965f3c11884 --- /dev/null +++ b/core/lib/state/src/witness.rs @@ -0,0 +1,44 @@ +use zksync_types::{witness_block_state::WitnessStorageState, StorageKey, StorageValue, H256}; + +use crate::ReadStorage; + +/// [`ReadStorage`] implementation backed by binary serialized [`WitnessHashBlockState`]. +/// Note that `load_factory_deps` is not used. +/// FactoryDeps data is used straight inside witness generator, loaded with the blob. +#[derive(Debug)] +pub struct WitnessStorage { + storage_state: WitnessStorageState, +} + +impl WitnessStorage { + /// Creates a new storage with the provided witness's block state. + pub fn new(storage_state: WitnessStorageState) -> Self { + Self { storage_state } + } +} + +impl ReadStorage for WitnessStorage { + fn read_value(&mut self, key: &StorageKey) -> StorageValue { + self.storage_state + .read_storage_key + .get(key) + .copied() + .unwrap_or_default() + } + + fn is_write_initial(&mut self, key: &StorageKey) -> bool { + self.storage_state + .is_write_initial + .get(key) + .copied() + .unwrap_or_default() + } + + fn load_factory_dep(&mut self, _hash: H256) -> Option> { + unreachable!("Factory deps should not be used in the witness storage") + } + + fn get_enumeration_index(&mut self, _key: &StorageKey) -> Option { + unreachable!("Enumeration index should not be used in the witness storage") + } +} diff --git a/core/lib/tee_verifier/src/lib.rs b/core/lib/tee_verifier/src/lib.rs index 3d47834aa25a..e4adbd37f340 100644 --- a/core/lib/tee_verifier/src/lib.rs +++ b/core/lib/tee_verifier/src/lib.rs @@ -17,7 +17,7 @@ use zksync_multivm::{ VmInstance, }; use zksync_prover_interface::inputs::{ - PrepareBasicCircuitsJob, StorageLogMetadata, V1TeeVerifierInput, + StorageLogMetadata, V1TeeVerifierInput, WitnessInputMerklePaths, }; use zksync_state::{InMemoryStorage, StorageView, WriteStorage}; use zksync_types::{block::L2BlockExecutionData, L1BatchNumber, StorageLog, H256}; @@ -49,7 +49,7 @@ impl Verify for V1TeeVerifierInput { fn verify(self) -> anyhow::Result { let old_root_hash = self.l1_batch_env.previous_batch_hash.unwrap(); let l2_chain_id = self.system_env.chain_id; - let enumeration_index = self.prepare_basic_circuits_job.next_enumeration_index(); + let enumeration_index = self.witness_input_merkle_paths.next_enumeration_index(); let mut raw_storage = InMemoryStorage::with_custom_system_contracts_and_chain_id( l2_chain_id, @@ -63,7 +63,7 @@ impl Verify for V1TeeVerifierInput { } let block_output_with_proofs = - get_bowp_and_set_initial_values(self.prepare_basic_circuits_job, &mut raw_storage); + get_bowp_and_set_initial_values(self.witness_input_merkle_paths, &mut raw_storage); let storage_view = Rc::new(RefCell::new(StorageView::new(&raw_storage))); @@ -88,10 +88,10 @@ impl Verify for V1TeeVerifierInput { /// Sets the initial storage values and returns `BlockOutputWithProofs` fn get_bowp_and_set_initial_values( - prepare_basic_circuits_job: PrepareBasicCircuitsJob, + witness_input_merkle_paths: WitnessInputMerklePaths, raw_storage: &mut InMemoryStorage, ) -> BlockOutputWithProofs { - let logs = prepare_basic_circuits_job + let logs = witness_input_merkle_paths .into_merkle_paths() .map( |StorageLogMetadata { @@ -249,7 +249,7 @@ mod tests { #[test] fn test_v1_serialization() { let tvi = V1TeeVerifierInput::new( - PrepareBasicCircuitsJob::new(0), + WitnessInputMerklePaths::new(0), vec![], L1BatchEnv { previous_batch_hash: Some(H256([1; 32])), diff --git a/core/lib/types/src/commitment/mod.rs b/core/lib/types/src/commitment/mod.rs index 61c2d7b5ea27..63d1bad486f3 100644 --- a/core/lib/types/src/commitment/mod.rs +++ b/core/lib/types/src/commitment/mod.rs @@ -536,7 +536,7 @@ pub struct L1BatchCommitment { pub meta_parameters: L1BatchMetaParameters, } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Default, Debug, Clone, PartialEq, Eq)] #[cfg_attr(test, derive(Serialize, Deserialize))] pub struct L1BatchCommitmentHash { pub pass_through_data: H256, @@ -720,7 +720,7 @@ impl CommitmentInput { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct L1BatchCommitmentArtifacts { pub commitment_hash: L1BatchCommitmentHash, pub l2_l1_merkle_root: H256, diff --git a/core/lib/types/src/storage/mod.rs b/core/lib/types/src/storage/mod.rs index 510ec5b19d12..a30a57bffa51 100644 --- a/core/lib/types/src/storage/mod.rs +++ b/core/lib/types/src/storage/mod.rs @@ -1,18 +1,18 @@ use core::fmt::Debug; use blake2::{Blake2s256, Digest}; +pub use log::*; use serde::{Deserialize, Serialize}; use zksync_basic_types::{web3::keccak256, L2ChainId}; +pub use zksync_system_constants::*; +use zksync_utils::address_to_h256; use crate::{AccountTreeId, Address, H160, H256, U256}; pub mod log; +pub mod witness_block_state; pub mod writes; -pub use log::*; -pub use zksync_system_constants::*; -use zksync_utils::address_to_h256; - /// Typed fully qualified key of the storage slot in global state tree. #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] #[derive(Serialize, Deserialize)] diff --git a/core/lib/types/src/storage/witness_block_state.rs b/core/lib/types/src/storage/witness_block_state.rs index 63ee1ba1c566..bce9cc9034d7 100644 --- a/core/lib/types/src/storage/witness_block_state.rs +++ b/core/lib/types/src/storage/witness_block_state.rs @@ -5,8 +5,43 @@ use serde::{Deserialize, Serialize}; use crate::{StorageKey, StorageValue}; /// Storage data used during Witness Generation. -#[derive(Debug, Default, Serialize, Deserialize)] -pub struct WitnessBlockState { +#[derive(Debug, Default, Clone)] +pub struct WitnessStorageState { pub read_storage_key: HashMap, pub is_write_initial: HashMap, } + +/// A serde schema for serializing/deserializing `WitnessBlockState` +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +struct WitnessStorageStateSerde { + pub read_storage_key: Vec<(StorageKey, StorageValue)>, + pub is_write_initial: Vec<(StorageKey, bool)>, +} + +impl Serialize for WitnessStorageState { + fn serialize(&self, s: S) -> Result { + WitnessStorageStateSerde { + read_storage_key: self + .read_storage_key + .iter() + .map(|(k, v)| (*k, *v)) + .collect(), + is_write_initial: self + .is_write_initial + .iter() + .map(|(k, v)| (*k, *v)) + .collect(), + } + .serialize(s) + } +} + +impl<'de> serde::Deserialize<'de> for WitnessStorageState { + fn deserialize>(d: D) -> Result { + let x = WitnessStorageStateSerde::deserialize(d)?; + Ok(Self { + read_storage_key: x.read_storage_key.into_iter().collect(), + is_write_initial: x.is_write_initial.into_iter().collect(), + }) + } +} diff --git a/core/lib/zksync_core_leftovers/src/lib.rs b/core/lib/zksync_core_leftovers/src/lib.rs index a665c40babd9..4e63a39d6c64 100644 --- a/core/lib/zksync_core_leftovers/src/lib.rs +++ b/core/lib/zksync_core_leftovers/src/lib.rs @@ -92,6 +92,8 @@ pub enum Component { VmRunnerProtectiveReads, /// A component to fetch and persist ETH<->BaseToken conversion ratios for chains with custom base tokens. BaseTokenRatioPersister, + /// VM runner-based component that saves VM execution data for basic witness generation. + VmRunnerBwip, } #[derive(Debug)] @@ -135,6 +137,7 @@ impl FromStr for Components { "base_token_ratio_persister" => { Ok(Components(vec![Component::BaseTokenRatioPersister])) } + "vm_runner_bwip" => Ok(Components(vec![Component::VmRunnerBwip])), other => Err(format!("{} is not a valid component name", other)), } } diff --git a/core/lib/zksync_core_leftovers/src/temp_config_store/mod.rs b/core/lib/zksync_core_leftovers/src/temp_config_store/mod.rs index 3b4c8a53b84f..65b7d1e43200 100644 --- a/core/lib/zksync_core_leftovers/src/temp_config_store/mod.rs +++ b/core/lib/zksync_core_leftovers/src/temp_config_store/mod.rs @@ -10,6 +10,7 @@ use zksync_config::{ }, fri_prover_group::FriProverGroupConfig, house_keeper::HouseKeeperConfig, + vm_runner::BasicWitnessInputProducerConfig, wallets::{AddressWallet, EthSender, StateKeeper, Wallet, Wallets}, CommitmentGeneratorConfig, FriProofCompressorConfig, FriProverConfig, FriProverGatewayConfig, FriWitnessGeneratorConfig, FriWitnessVectorGeneratorConfig, @@ -66,6 +67,7 @@ pub struct TempConfigStore { pub snapshot_creator: Option, pub da_dispatcher_config: Option, pub protective_reads_writer_config: Option, + pub basic_witness_input_producer_config: Option, pub core_object_store: Option, pub base_token_adjuster_config: Option, pub commitment_generator: Option, @@ -98,6 +100,7 @@ impl TempConfigStore { observability: self.observability.clone(), da_dispatcher_config: self.da_dispatcher_config.clone(), protective_reads_writer_config: self.protective_reads_writer_config.clone(), + basic_witness_input_producer_config: self.basic_witness_input_producer_config.clone(), core_object_store: self.core_object_store.clone(), base_token_adjuster: self.base_token_adjuster_config.clone(), commitment_generator: self.commitment_generator.clone(), diff --git a/core/node/metadata_calculator/src/helpers.rs b/core/node/metadata_calculator/src/helpers.rs index 5e3c1f3d9d73..b6989afb179f 100644 --- a/core/node/metadata_calculator/src/helpers.rs +++ b/core/node/metadata_calculator/src/helpers.rs @@ -792,7 +792,7 @@ mod tests { use tempfile::TempDir; use zksync_dal::{ConnectionPool, Core}; use zksync_node_genesis::{insert_genesis_batch, GenesisParams}; - use zksync_prover_interface::inputs::PrepareBasicCircuitsJob; + use zksync_prover_interface::inputs::WitnessInputMerklePaths; use zksync_types::{writes::TreeWrite, StorageKey, StorageLog, U256}; use super::*; @@ -1037,7 +1037,7 @@ mod tests { ); } - fn assert_equivalent_witnesses(lhs: PrepareBasicCircuitsJob, rhs: PrepareBasicCircuitsJob) { + fn assert_equivalent_witnesses(lhs: WitnessInputMerklePaths, rhs: WitnessInputMerklePaths) { assert_eq!(lhs.next_enumeration_index(), rhs.next_enumeration_index()); let lhs_paths = lhs.into_merkle_paths(); let rhs_paths = rhs.into_merkle_paths(); diff --git a/core/node/metadata_calculator/src/tests.rs b/core/node/metadata_calculator/src/tests.rs index c5a00ecd7563..b32af1da722d 100644 --- a/core/node/metadata_calculator/src/tests.rs +++ b/core/node/metadata_calculator/src/tests.rs @@ -17,7 +17,7 @@ use zksync_merkle_tree::domain::ZkSyncTree; use zksync_node_genesis::{insert_genesis_batch, GenesisParams}; use zksync_node_test_utils::{create_l1_batch, create_l2_block}; use zksync_object_store::{MockObjectStore, ObjectStore}; -use zksync_prover_interface::inputs::PrepareBasicCircuitsJob; +use zksync_prover_interface::inputs::WitnessInputMerklePaths; use zksync_storage::RocksDB; use zksync_types::{ block::{L1BatchHeader, L1BatchTreeData}, @@ -248,7 +248,7 @@ async fn basic_workflow(sealed_protective_reads: bool) { let expected_tree_hash = expected_tree_hash(&pool, sealed_protective_reads).await; assert_eq!(merkle_tree_hash, expected_tree_hash); - let job: PrepareBasicCircuitsJob = object_store.get(L1BatchNumber(1)).await.unwrap(); + let job: WitnessInputMerklePaths = object_store.get(L1BatchNumber(1)).await.unwrap(); assert!(job.next_enumeration_index() > 0); let merkle_paths: Vec<_> = job.clone().into_merkle_paths().collect(); assert!(!merkle_paths.is_empty() && merkle_paths.len() <= 100); @@ -370,7 +370,7 @@ async fn multi_l1_batch_workflow(sealed_protective_reads: bool) { let mut prev_index = None; for l1_batch_number in 1..=10 { let l1_batch_number = L1BatchNumber(l1_batch_number); - let job: PrepareBasicCircuitsJob = object_store.get(l1_batch_number).await.unwrap(); + let job: WitnessInputMerklePaths = object_store.get(l1_batch_number).await.unwrap(); let next_enumeration_index = job.next_enumeration_index(); let merkle_paths: Vec<_> = job.into_merkle_paths().collect(); assert!(!merkle_paths.is_empty() && merkle_paths.len() <= 10); diff --git a/core/node/node_framework/src/implementations/layers/vm_runner/bwip.rs b/core/node/node_framework/src/implementations/layers/vm_runner/bwip.rs new file mode 100644 index 000000000000..36ad14b8db5a --- /dev/null +++ b/core/node/node_framework/src/implementations/layers/vm_runner/bwip.rs @@ -0,0 +1,90 @@ +use zksync_config::configs::vm_runner::BasicWitnessInputProducerConfig; +use zksync_types::L2ChainId; +use zksync_vm_runner::BasicWitnessInputProducer; + +use crate::{ + implementations::resources::{ + object_store::ObjectStoreResource, + pools::{MasterPool, PoolResource}, + }, + service::{ServiceContext, StopReceiver}, + task::{Task, TaskId}, + wiring_layer::{WiringError, WiringLayer}, +}; + +#[derive(Debug)] +pub struct BasicWitnessInputProducerLayer { + basic_witness_input_producer_config: BasicWitnessInputProducerConfig, + zksync_network_id: L2ChainId, +} + +impl BasicWitnessInputProducerLayer { + pub fn new( + basic_witness_input_producer_config: BasicWitnessInputProducerConfig, + zksync_network_id: L2ChainId, + ) -> Self { + Self { + basic_witness_input_producer_config, + zksync_network_id, + } + } +} + +#[async_trait::async_trait] +impl WiringLayer for BasicWitnessInputProducerLayer { + fn layer_name(&self) -> &'static str { + "vm_runner_bwip" + } + + async fn wire(self: Box, mut context: ServiceContext<'_>) -> Result<(), WiringError> { + let master_pool = context.get_resource::>()?; + let object_store = context.get_resource::()?; + + let (basic_witness_input_producer, tasks) = BasicWitnessInputProducer::new( + // One for `StorageSyncTask` which can hold a long-term connection in case it needs to + // catch up cache. + // + // One for `ConcurrentOutputHandlerFactoryTask`/`VmRunner` as they need occasional access + // to DB for querying last processed batch and last ready to be loaded batch. + // + // `window_size` connections for `BasicWitnessInputProducer` + // as there can be multiple output handlers holding multi-second connections to process + // BWIP data. + master_pool + .get_custom(self.basic_witness_input_producer_config.window_size + 2) + .await?, + object_store.0, + self.basic_witness_input_producer_config.db_path, + self.zksync_network_id, + self.basic_witness_input_producer_config + .first_processed_batch, + self.basic_witness_input_producer_config.window_size, + ) + .await?; + + context.add_task(tasks.loader_task); + context.add_task(tasks.output_handler_factory_task); + context.add_task(BasicWitnessInputProducerTask { + basic_witness_input_producer, + }); + Ok(()) + } +} + +#[derive(Debug)] +struct BasicWitnessInputProducerTask { + basic_witness_input_producer: BasicWitnessInputProducer, +} + +#[async_trait::async_trait] +impl Task for BasicWitnessInputProducerTask { + fn id(&self) -> TaskId { + "vm_runner/bwip".into() + } + + async fn run(self: Box, stop_receiver: StopReceiver) -> anyhow::Result<()> { + self.basic_witness_input_producer + .run(&stop_receiver.0) + .await + } +} diff --git a/core/node/node_framework/src/implementations/layers/vm_runner/mod.rs b/core/node/node_framework/src/implementations/layers/vm_runner/mod.rs index a105ad81ee60..0b3f611038b2 100644 --- a/core/node/node_framework/src/implementations/layers/vm_runner/mod.rs +++ b/core/node/node_framework/src/implementations/layers/vm_runner/mod.rs @@ -5,6 +5,7 @@ use crate::{ task::{Task, TaskId}, }; +pub mod bwip; pub mod protective_reads; #[async_trait::async_trait] diff --git a/core/node/proof_data_handler/src/request_processor.rs b/core/node/proof_data_handler/src/request_processor.rs index 170b27bb971f..bdb55237c4b6 100644 --- a/core/node/proof_data_handler/src/request_processor.rs +++ b/core/node/proof_data_handler/src/request_processor.rs @@ -4,9 +4,14 @@ use axum::{extract::Path, Json}; use zksync_config::configs::ProofDataHandlerConfig; use zksync_dal::{ConnectionPool, Core, CoreDal}; use zksync_object_store::ObjectStore; -use zksync_prover_interface::api::{ - ProofGenerationData, ProofGenerationDataRequest, ProofGenerationDataResponse, - SubmitProofRequest, SubmitProofResponse, +use zksync_prover_interface::{ + api::{ + ProofGenerationData, ProofGenerationDataRequest, ProofGenerationDataResponse, + SubmitProofRequest, SubmitProofResponse, + }, + inputs::{ + L1BatchMetadataHashes, VMRunWitnessInputData, WitnessInputData, WitnessInputMerklePaths, + }, }; use zksync_types::{ basic_fri_types::Eip4844Blobs, @@ -61,12 +66,28 @@ impl RequestProcessor { None => return Ok(Json(ProofGenerationDataResponse::Success(None))), // no batches pending to be proven }; - let blob = self + let vm_run_data: VMRunWitnessInputData = self + .blob_store + .get(l1_batch_number) + .await + .map_err(RequestProcessorError::ObjectStore)?; + let merkle_paths: WitnessInputMerklePaths = self .blob_store .get(l1_batch_number) .await .map_err(RequestProcessorError::ObjectStore)?; + let previous_batch_metadata = self + .pool + .connection() + .await + .unwrap() + .blocks_dal() + .get_l1_batch_metadata(L1BatchNumber(l1_batch_number.checked_sub(1).unwrap())) + .await + .unwrap() + .expect("No metadata for previous batch"); + let header = self .pool .connection() @@ -115,13 +136,24 @@ impl RequestProcessor { } }; + let blob = WitnessInputData { + vm_run_data, + merkle_paths, + eip_4844_blobs, + previous_batch_metadata: L1BatchMetadataHashes { + root_hash: previous_batch_metadata.metadata.root_hash, + meta_hash: previous_batch_metadata.metadata.meta_parameters_hash, + aux_hash: previous_batch_metadata.metadata.aux_data_hash, + }, + }; + let proof_gen_data = ProofGenerationData { l1_batch_number, - data: blob, + witness_input_data: blob, protocol_version: protocol_version.version, l1_verifier_config: protocol_version.l1_verifier_config, - eip_4844_blobs, }; + Ok(Json(ProofGenerationDataResponse::Success(Some(Box::new( proof_gen_data, ))))) diff --git a/core/node/proof_data_handler/src/tests.rs b/core/node/proof_data_handler/src/tests.rs index a56bc9a59cb8..1fbe563d2d28 100644 --- a/core/node/proof_data_handler/src/tests.rs +++ b/core/node/proof_data_handler/src/tests.rs @@ -16,7 +16,7 @@ use zksync_multivm::interface::{L1BatchEnv, L2BlockEnv, SystemEnv, TxExecutionMo use zksync_object_store::MockObjectStore; use zksync_prover_interface::{ api::SubmitTeeProofRequest, - inputs::{PrepareBasicCircuitsJob, TeeVerifierInput, V1TeeVerifierInput}, + inputs::{TeeVerifierInput, V1TeeVerifierInput, WitnessInputMerklePaths}, }; use zksync_types::{commitment::L1BatchCommitmentMode, L1BatchNumber, H256}; @@ -34,7 +34,7 @@ async fn request_tee_proof_inputs() { let batch_number = L1BatchNumber::from(1); let tvi = V1TeeVerifierInput::new( - PrepareBasicCircuitsJob::new(0), + WitnessInputMerklePaths::new(0), vec![], L1BatchEnv { previous_batch_hash: Some(H256([1; 32])), 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 5bbd9f7c3a52..2434e92e812f 100644 --- a/core/node/state_keeper/src/batch_executor/main_executor.rs +++ b/core/node/state_keeper/src/batch_executor/main_executor.rs @@ -147,6 +147,15 @@ impl CommandReceiver { .observe(metrics.time_spent_on_set_value); return; } + Command::FinishBatchWithCache(resp) => { + 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; + } } } // State keeper can exit because of stop signal, so it's OK to exit mid-batch. diff --git a/core/node/state_keeper/src/batch_executor/mod.rs b/core/node/state_keeper/src/batch_executor/mod.rs index bb3effedbbab..4577ab1b360a 100644 --- a/core/node/state_keeper/src/batch_executor/mod.rs +++ b/core/node/state_keeper/src/batch_executor/mod.rs @@ -9,7 +9,7 @@ use tokio::{ use zksync_multivm::interface::{ FinishedL1Batch, Halt, L1BatchEnv, L2BlockEnv, SystemEnv, VmExecutionResultAndLogs, }; -use zksync_state::ReadStorageFactory; +use zksync_state::{ReadStorageFactory, StorageViewCache}; use zksync_types::{vm_trace::Call, Transaction}; use zksync_utils::bytecode::CompressedBytecodeInfo; @@ -229,6 +229,33 @@ impl BatchExecutorHandle { latency.observe(); Ok(finished_batch) } + + pub async fn finish_batch_with_cache( + mut self, + ) -> anyhow::Result<(FinishedL1Batch, StorageViewCache)> { + let (response_sender, response_receiver) = oneshot::channel(); + let send_failed = self + .commands + .send(Command::FinishBatchWithCache(response_sender)) + .await + .is_err(); + if send_failed { + return Err(self.handle.wait_for_error().await); + } + + let latency = EXECUTOR_METRICS.batch_executor_command_response_time + [&ExecutorCommand::FinishBatchWithCache] + .start(); + let batch_with_cache = match response_receiver.await { + Ok(batch_with_cache) => batch_with_cache, + Err(_) => return Err(self.handle.wait_for_error().await), + }; + + self.handle.wait().await?; + + latency.observe(); + Ok(batch_with_cache) + } } #[derive(Debug)] @@ -237,4 +264,5 @@ pub(super) enum Command { StartNextL2Block(L2BlockEnv, oneshot::Sender<()>), RollbackLastTx(oneshot::Sender<()>), FinishBatch(oneshot::Sender), + FinishBatchWithCache(oneshot::Sender<(FinishedL1Batch, StorageViewCache)>), } diff --git a/core/node/state_keeper/src/metrics.rs b/core/node/state_keeper/src/metrics.rs index 429f4f859c5c..c154719e3900 100644 --- a/core/node/state_keeper/src/metrics.rs +++ b/core/node/state_keeper/src/metrics.rs @@ -444,6 +444,7 @@ pub(super) enum ExecutorCommand { StartNextL2Block, RollbackLastTx, FinishBatch, + FinishBatchWithCache, } const GAS_PER_NANOSECOND_BUCKETS: Buckets = Buckets::values(&[ diff --git a/core/node/state_keeper/src/testonly/mod.rs b/core/node/state_keeper/src/testonly/mod.rs index 940e4c19c4be..c287bc97407f 100644 --- a/core/node/state_keeper/src/testonly/mod.rs +++ b/core/node/state_keeper/src/testonly/mod.rs @@ -15,7 +15,7 @@ use zksync_multivm::{ }, vm_latest::VmExecutionLogs, }; -use zksync_state::ReadStorageFactory; +use zksync_state::{ReadStorageFactory, StorageViewCache}; use zksync_test_account::Account; use zksync_types::{ fee::Fee, utils::storage_key_for_standard_token_balance, AccountTreeId, Address, Execute, @@ -79,6 +79,10 @@ pub(crate) fn successful_exec() -> TxExecutionResult { } } +pub(crate) fn storage_view_cache() -> StorageViewCache { + StorageViewCache::default() +} + /// `BatchExecutor` which doesn't check anything at all. Accepts all transactions. #[derive(Debug)] pub struct MockBatchExecutor; @@ -105,6 +109,9 @@ impl BatchExecutor for MockBatchExecutor { resp.send(default_vm_batch_result()).unwrap(); break; } + Command::FinishBatchWithCache(resp) => resp + .send((default_vm_batch_result(), storage_view_cache())) + .unwrap(), } } anyhow::Ok(()) diff --git a/core/node/state_keeper/src/testonly/test_batch_executor.rs b/core/node/state_keeper/src/testonly/test_batch_executor.rs index 9cb701797483..1be84cfbf54e 100644 --- a/core/node/state_keeper/src/testonly/test_batch_executor.rs +++ b/core/node/state_keeper/src/testonly/test_batch_executor.rs @@ -30,7 +30,9 @@ use crate::{ batch_executor::{BatchExecutor, BatchExecutorHandle, Command, TxExecutionResult}, io::{IoCursor, L1BatchParams, L2BlockParams, PendingBatchData, StateKeeperIO}, seal_criteria::{IoSealCriteria, SequencerSealer, UnexecutableReason}, - testonly::{default_vm_batch_result, successful_exec, BASE_SYSTEM_CONTRACTS}, + testonly::{ + default_vm_batch_result, storage_view_cache, successful_exec, BASE_SYSTEM_CONTRACTS, + }, types::ExecutionMetricsForCriteria, updates::UpdatesManager, OutputHandler, StateKeeperOutputHandler, ZkSyncStateKeeper, @@ -499,6 +501,9 @@ impl TestBatchExecutor { resp.send(default_vm_batch_result()).unwrap(); return; } + Command::FinishBatchWithCache(resp) => resp + .send((default_vm_batch_result(), storage_view_cache())) + .unwrap(), } } } @@ -827,6 +832,9 @@ impl BatchExecutor for MockBatchExecutor { resp.send(default_vm_batch_result()).unwrap(); break; } + Command::FinishBatchWithCache(resp) => resp + .send((default_vm_batch_result(), storage_view_cache())) + .unwrap(), } } anyhow::Ok(()) diff --git a/core/node/state_keeper/src/updates/mod.rs b/core/node/state_keeper/src/updates/mod.rs index 1121af8d72e2..e05432c57b21 100644 --- a/core/node/state_keeper/src/updates/mod.rs +++ b/core/node/state_keeper/src/updates/mod.rs @@ -3,6 +3,7 @@ use zksync_multivm::{ interface::{FinishedL1Batch, L1BatchEnv, SystemEnv, VmExecutionResultAndLogs}, utils::get_batch_base_fee, }; +use zksync_state::StorageViewCache; use zksync_types::{ block::BlockGasCount, fee_model::BatchFeeInput, storage_writes_deduplicator::StorageWritesDeduplicator, @@ -35,6 +36,7 @@ pub struct UpdatesManager { base_fee_per_gas: u64, base_system_contract_hashes: BaseSystemContractsHashes, protocol_version: ProtocolVersionId, + storage_view_cache: Option, pub l1_batch: L1BatchUpdates, pub l2_block: L2BlockUpdates, pub storage_writes_deduplicator: StorageWritesDeduplicator, @@ -59,6 +61,7 @@ impl UpdatesManager { protocol_version, ), storage_writes_deduplicator: StorageWritesDeduplicator::new(), + storage_view_cache: None, } } @@ -66,7 +69,7 @@ impl UpdatesManager { self.batch_timestamp } - pub(crate) fn base_system_contract_hashes(&self) -> BaseSystemContractsHashes { + pub fn base_system_contract_hashes(&self) -> BaseSystemContractsHashes { self.base_system_contract_hashes } @@ -98,7 +101,7 @@ impl UpdatesManager { } } - pub(crate) fn protocol_version(&self) -> ProtocolVersionId { + pub fn protocol_version(&self) -> ProtocolVersionId { self.protocol_version } @@ -153,6 +156,14 @@ impl UpdatesManager { latency.observe(); } + pub fn update_storage_view_cache(&mut self, storage_view_cache: StorageViewCache) { + self.storage_view_cache = Some(storage_view_cache); + } + + pub fn storage_view_cache(&self) -> Option { + self.storage_view_cache.clone() + } + /// Pushes a new L2 block with the specified timestamp into this manager. The previously /// held L2 block is considered sealed and is used to extend the L1 batch data. pub fn push_l2_block(&mut self, l2_block_params: L2BlockParams) { diff --git a/core/node/tee_verifier_input_producer/src/lib.rs b/core/node/tee_verifier_input_producer/src/lib.rs index c45af4cf31b0..ffd613158605 100644 --- a/core/node/tee_verifier_input_producer/src/lib.rs +++ b/core/node/tee_verifier_input_producer/src/lib.rs @@ -15,7 +15,7 @@ use tokio::task::JoinHandle; use zksync_dal::{tee_verifier_input_producer_dal::JOB_MAX_ATTEMPT, ConnectionPool, Core, CoreDal}; use zksync_object_store::ObjectStore; use zksync_prover_interface::inputs::{ - PrepareBasicCircuitsJob, TeeVerifierInput, V1TeeVerifierInput, + TeeVerifierInput, V1TeeVerifierInput, WitnessInputMerklePaths, }; use zksync_queued_job_processor::JobProcessor; use zksync_tee_verifier::Verify; @@ -55,7 +55,7 @@ impl TeeVerifierInputProducer { object_store: Arc, l2_chain_id: L2ChainId, ) -> anyhow::Result { - let prepare_basic_circuits_job: PrepareBasicCircuitsJob = object_store + let prepare_basic_circuits_job: WitnessInputMerklePaths = object_store .get(l1_batch_number) .await .context("failed to get PrepareBasicCircuitsJob from object store")?; diff --git a/core/node/vm_runner/Cargo.toml b/core/node/vm_runner/Cargo.toml index a68cd27f8cb5..f11fdce357c6 100644 --- a/core/node/vm_runner/Cargo.toml +++ b/core/node/vm_runner/Cargo.toml @@ -18,6 +18,8 @@ zksync_state.workspace = true zksync_storage.workspace = true zksync_state_keeper.workspace = true zksync_utils.workspace = true +zksync_prover_interface.workspace = true +zksync_object_store.workspace = true zksync_vm_utils.workspace = true tokio = { workspace = true, features = ["time"] } diff --git a/core/node/vm_runner/src/impls/bwip.rs b/core/node/vm_runner/src/impls/bwip.rs new file mode 100644 index 000000000000..f3bdf55400e6 --- /dev/null +++ b/core/node/vm_runner/src/impls/bwip.rs @@ -0,0 +1,377 @@ +use std::{collections::HashSet, sync::Arc}; + +use anyhow::anyhow; +use async_trait::async_trait; +use tokio::sync::watch; +use zksync_dal::{Connection, ConnectionPool, Core, CoreDal}; +use zksync_object_store::ObjectStore; +use zksync_prover_interface::inputs::VMRunWitnessInputData; +use zksync_state_keeper::{MainBatchExecutor, StateKeeperOutputHandler, UpdatesManager}; +use zksync_types::{ + block::StorageOracleInfo, witness_block_state::WitnessStorageState, L1BatchNumber, L2ChainId, + H256, +}; +use zksync_utils::{bytes_to_chunks, h256_to_u256, u256_to_h256}; + +use crate::{ + storage::StorageSyncTask, ConcurrentOutputHandlerFactory, ConcurrentOutputHandlerFactoryTask, + OutputHandlerFactory, VmRunner, VmRunnerIo, VmRunnerStorage, +}; + +/// A standalone component that retrieves all needed data for basic witness generation and saves it to the bucket +#[derive(Debug)] +pub struct BasicWitnessInputProducer { + vm_runner: VmRunner, +} + +impl BasicWitnessInputProducer { + /// Create a new BWIP from the provided DB parameters and window size which + /// regulates how many batches this component can handle at the same time. + pub async fn new( + pool: ConnectionPool, + object_store: Arc, + rocksdb_path: String, + chain_id: L2ChainId, + first_processed_batch: L1BatchNumber, + window_size: u32, + ) -> anyhow::Result<(Self, BasicWitnessInputProducerTasks)> { + let io = BasicWitnessInputProducerIo { + first_processed_batch, + window_size, + }; + let (loader, loader_task) = + VmRunnerStorage::new(pool.clone(), rocksdb_path, io.clone(), chain_id).await?; + let output_handler_factory = BasicWitnessInputProducerOutputHandlerFactory { + pool: pool.clone(), + object_store, + }; + let (output_handler_factory, output_handler_factory_task) = + ConcurrentOutputHandlerFactory::new(pool.clone(), io.clone(), output_handler_factory); + let batch_processor = MainBatchExecutor::new(false, false); + let vm_runner = VmRunner::new( + pool, + Box::new(io), + Arc::new(loader), + Box::new(output_handler_factory), + Box::new(batch_processor), + ); + Ok(( + Self { vm_runner }, + BasicWitnessInputProducerTasks { + loader_task, + output_handler_factory_task, + }, + )) + } + + /// Continuously loads new available batches and writes the corresponding data + /// produced by that batch. + /// + /// # Errors + /// + /// Propagates RocksDB and Postgres errors. + pub async fn run(self, stop_receiver: &watch::Receiver) -> anyhow::Result<()> { + self.vm_runner.run(stop_receiver).await + } +} + +/// A collections of tasks that need to be run in order for BWIP to work as +/// intended. +#[derive(Debug)] +pub struct BasicWitnessInputProducerTasks { + /// Task that synchronizes storage with new available batches. + pub loader_task: StorageSyncTask, + /// Task that handles output from processed batches. + pub output_handler_factory_task: + ConcurrentOutputHandlerFactoryTask, +} + +#[derive(Debug, Clone)] +pub struct BasicWitnessInputProducerIo { + first_processed_batch: L1BatchNumber, + window_size: u32, +} + +#[async_trait] +impl VmRunnerIo for BasicWitnessInputProducerIo { + fn name(&self) -> &'static str { + "basic_witness_input_producer" + } + + async fn latest_processed_batch( + &self, + conn: &mut Connection<'_, Core>, + ) -> anyhow::Result { + Ok(conn + .vm_runner_dal() + .get_bwip_latest_processed_batch() + .await? + .unwrap_or(self.first_processed_batch)) + } + + async fn last_ready_to_be_loaded_batch( + &self, + conn: &mut Connection<'_, Core>, + ) -> anyhow::Result { + Ok(conn + .vm_runner_dal() + .get_bwip_last_ready_batch(self.first_processed_batch, self.window_size) + .await?) + } + + async fn mark_l1_batch_as_completed( + &self, + conn: &mut Connection<'_, Core>, + l1_batch_number: L1BatchNumber, + ) -> anyhow::Result<()> { + Ok(conn + .vm_runner_dal() + .mark_bwip_batch_as_completed(l1_batch_number) + .await?) + } +} + +#[derive(Debug)] +struct BasicWitnessInputProducerOutputHandler { + pool: ConnectionPool, + object_store: Arc, +} + +#[async_trait] +impl StateKeeperOutputHandler for BasicWitnessInputProducerOutputHandler { + async fn handle_l2_block(&mut self, _updates_manager: &UpdatesManager) -> anyhow::Result<()> { + Ok(()) + } + + async fn handle_l1_batch( + &mut self, + updates_manager: Arc, + ) -> anyhow::Result<()> { + let l1_batch_number = updates_manager.l1_batch.number; + let mut connection = self.pool.connection().await?; + + tracing::info!(%l1_batch_number, "Started saving VM run data"); + + let result = + get_updates_manager_witness_input_data(&mut connection, updates_manager).await?; + + assert_database_witness_input_data(&mut connection, l1_batch_number, &result).await; + + let blob_url = self.object_store.put(l1_batch_number, &result).await?; + + tracing::info!(%l1_batch_number, "Saved VM run data"); + + connection + .proof_generation_dal() + .save_vm_runner_artifacts_metadata(l1_batch_number, &blob_url) + .await?; + + Ok(()) + } +} + +async fn get_updates_manager_witness_input_data( + connection: &mut Connection<'_, Core>, + updates_manager: Arc, +) -> anyhow::Result { + let l1_batch_number = updates_manager.l1_batch.number; + let finished_batch = updates_manager + .l1_batch + .finished + .clone() + .ok_or_else(|| anyhow!("L1 batch {l1_batch_number:?} is not finished"))?; + + let initial_heap_content = finished_batch.final_bootloader_memory.unwrap(); // might be just empty + let default_aa = updates_manager.base_system_contract_hashes().default_aa; + let bootloader = updates_manager.base_system_contract_hashes().bootloader; + let bootloader_code_bytes = connection + .factory_deps_dal() + .get_sealed_factory_dep(bootloader) + .await? + .ok_or_else(|| anyhow!("Failed fetching bootloader bytecode from DB"))?; + let bootloader_code = bytes_to_chunks(&bootloader_code_bytes); + + let account_code_hash = h256_to_u256(default_aa); + let account_bytecode_bytes = connection + .factory_deps_dal() + .get_sealed_factory_dep(default_aa) + .await? + .ok_or_else(|| anyhow!("Default account bytecode should exist"))?; + let account_bytecode = bytes_to_chunks(&account_bytecode_bytes); + + let hashes: HashSet = finished_batch + .final_execution_state + .used_contract_hashes + .iter() + // SMA-1555: remove this hack once updated to the latest version of `zkevm_test_harness` + .filter(|&&hash| hash != h256_to_u256(bootloader)) + .map(|hash| u256_to_h256(*hash)) + .collect(); + let mut used_bytecodes = connection + .factory_deps_dal() + .get_factory_deps(&hashes) + .await; + if finished_batch + .final_execution_state + .used_contract_hashes + .contains(&account_code_hash) + { + used_bytecodes.insert(account_code_hash, account_bytecode); + } + + let storage_refunds = finished_batch.final_execution_state.storage_refunds; + let pubdata_costs = finished_batch.final_execution_state.pubdata_costs; + + let storage_view_cache = updates_manager + .storage_view_cache() + .expect("Storage view cache was not initialized"); + + let witness_block_state = WitnessStorageState { + read_storage_key: storage_view_cache.read_storage_keys(), + is_write_initial: storage_view_cache.initial_writes(), + }; + + Ok(VMRunWitnessInputData { + l1_batch_number, + used_bytecodes, + initial_heap_content, + + protocol_version: updates_manager.protocol_version(), + + bootloader_code, + default_account_code_hash: account_code_hash, + storage_refunds, + pubdata_costs, + witness_block_state, + }) +} + +async fn assert_database_witness_input_data( + connection: &mut Connection<'_, Core>, + l1_batch_number: L1BatchNumber, + result: &VMRunWitnessInputData, +) { + let block_header = connection + .blocks_dal() + .get_l1_batch_header(l1_batch_number) + .await + .expect("Failed fetching L1 block from DB") + .expect("L1 block header should exist"); + + let initial_heap_content = connection + .blocks_dal() + .get_initial_bootloader_heap(l1_batch_number) + .await + .expect("Failed fetching initial heap content from DB") + .expect("Initial bootloader heap should exist"); + + let account_code_hash = h256_to_u256(block_header.base_system_contracts_hashes.default_aa); + let account_bytecode_bytes = connection + .factory_deps_dal() + .get_sealed_factory_dep(block_header.base_system_contracts_hashes.default_aa) + .await + .expect("Failed fetching default account bytecode from DB") + .expect("Default account bytecode should exist"); + let account_bytecode = bytes_to_chunks(&account_bytecode_bytes); + + let hashes: HashSet = block_header + .used_contract_hashes + .iter() + // SMA-1555: remove this hack once updated to the latest version of `zkevm_test_harness` + .filter(|&&hash| hash != h256_to_u256(block_header.base_system_contracts_hashes.bootloader)) + .map(|hash| u256_to_h256(*hash)) + .collect(); + let mut used_bytecodes = connection + .factory_deps_dal() + .get_factory_deps(&hashes) + .await; + if block_header + .used_contract_hashes + .contains(&account_code_hash) + { + used_bytecodes.insert(account_code_hash, account_bytecode); + } + + assert_eq!( + hashes.len(), + used_bytecodes.len(), + "{} factory deps are not found in DB", + hashes.len() - used_bytecodes.len() + ); + + let StorageOracleInfo { + storage_refunds, + pubdata_costs, + } = connection + .blocks_dal() + .get_storage_oracle_info(block_header.number) + .await + .expect("Failed fetching L1 block from DB") + .expect("Storage oracle info should exist"); + let pubdata_costs = pubdata_costs.unwrap(); + + let bootloader_code_bytes = connection + .factory_deps_dal() + .get_sealed_factory_dep(block_header.base_system_contracts_hashes.bootloader) + .await + .expect("Failed fetching bootloader bytecode from DB") + .expect("Bootloader bytecode should exist"); + let bootloader_code = bytes_to_chunks(&bootloader_code_bytes); + + assert_eq!( + block_header.protocol_version.unwrap(), + result.protocol_version, + "Protocol version mismatch in basic witness input producer: DB: {:?}, UpdatesManager: {:?}", + block_header.protocol_version, + result.protocol_version + ); + assert_eq!( + used_bytecodes, result.used_bytecodes, + "Used bytecodes mismatch in basic witness input producer: DB: {:?}, UpdatesManager: {:?}", + used_bytecodes, result.used_bytecodes + ); + assert_eq!( + storage_refunds, result.storage_refunds, + "Storage refunds mismatch in basic witness input producer: DB: {:?}, UpdatesManager: {:?}", + storage_refunds, result.storage_refunds + ); + assert_eq!( + pubdata_costs, result.pubdata_costs, + "Pubdata costs mismatch in basic witness input producer: DB: {:?}, UpdatesManager: {:?}", + pubdata_costs, result.pubdata_costs + ); + assert_eq!( + initial_heap_content, result.initial_heap_content, + "Initial heap content mismatch in basic witness input producer: DB: {:?}, UpdatesManager: {:?}", + initial_heap_content, result.initial_heap_content + ); + assert_eq!( + bootloader_code, result.bootloader_code, + "Bootloader code mismatch in basic witness input producer: DB: {:?}, UpdatesManager: {:?}", + bootloader_code, result.bootloader_code + ); + assert_eq!( + account_code_hash, result.default_account_code_hash, + "Default account code hash mismatch in basic witness input producer: DB: {:?}, UpdatesManager: {:?}", + account_code_hash, result.default_account_code_hash + ); +} + +#[derive(Debug)] +struct BasicWitnessInputProducerOutputHandlerFactory { + pool: ConnectionPool, + object_store: Arc, +} + +#[async_trait] +impl OutputHandlerFactory for BasicWitnessInputProducerOutputHandlerFactory { + async fn create_handler( + &mut self, + _l1_batch_number: L1BatchNumber, + ) -> anyhow::Result> { + Ok(Box::new(BasicWitnessInputProducerOutputHandler { + pool: self.pool.clone(), + object_store: self.object_store.clone(), + })) + } +} diff --git a/core/node/vm_runner/src/impls/mod.rs b/core/node/vm_runner/src/impls/mod.rs index 70d01f6932ef..5bae7e03f568 100644 --- a/core/node/vm_runner/src/impls/mod.rs +++ b/core/node/vm_runner/src/impls/mod.rs @@ -1,3 +1,5 @@ +mod bwip; mod protective_reads; +pub use bwip::{BasicWitnessInputProducer, BasicWitnessInputProducerTasks}; pub use protective_reads::{ProtectiveReadsWriter, ProtectiveReadsWriterTasks}; diff --git a/core/node/vm_runner/src/lib.rs b/core/node/vm_runner/src/lib.rs index 50cf2a4433c1..d6c9a88185ee 100644 --- a/core/node/vm_runner/src/lib.rs +++ b/core/node/vm_runner/src/lib.rs @@ -13,7 +13,10 @@ mod metrics; #[cfg(test)] mod tests; -pub use impls::{ProtectiveReadsWriter, ProtectiveReadsWriterTasks}; +pub use impls::{ + BasicWitnessInputProducer, BasicWitnessInputProducerTasks, ProtectiveReadsWriter, + ProtectiveReadsWriterTasks, +}; pub use io::VmRunnerIo; pub use output_handler::{ ConcurrentOutputHandlerFactory, ConcurrentOutputHandlerFactoryTask, OutputHandlerFactory, diff --git a/core/node/vm_runner/src/process.rs b/core/node/vm_runner/src/process.rs index 8a9ebb4e3dc9..b300915cef64 100644 --- a/core/node/vm_runner/src/process.rs +++ b/core/node/vm_runner/src/process.rs @@ -110,11 +110,15 @@ impl VmRunner { .await .context("VM runner failed to handle L2 block")?; } - let finished_batch = batch_executor - .finish_batch() + + let (finished_batch, storage_view_cache) = batch_executor + .finish_batch_with_cache() .await - .context("failed finishing L1 batch in executor")?; + .context("Failed getting storage view cache")?; updates_manager.finish_batch(finished_batch); + // this is needed for Basic Witness Input Producer to use in memory reads, but not database queries + updates_manager.update_storage_view_cache(storage_view_cache); + latency.observe(); output_handler .handle_l1_batch(Arc::new(updates_manager)) diff --git a/etc/env/base/vm_runner.toml b/etc/env/base/vm_runner.toml index c8f259efc3b7..dd8e9915280b 100644 --- a/etc/env/base/vm_runner.toml +++ b/etc/env/base/vm_runner.toml @@ -9,3 +9,11 @@ db_path = "./db/main/protective_reads" window_size = 3 # All batches before this one (inclusive) are always considered to be processed. first_processed_batch = 0 + +[vm_runner.bwip] +# Path to the directory that contains RocksDB with bwip writer cache. +db_path = "./db/main/basic_witness_input_producer" +# Amount of batches that can be processed in parallel. +window_size = 3 +# All batches before this one (inclusive) are always considered to be processed. +first_processed_batch = 0 diff --git a/etc/env/file_based/general.yaml b/etc/env/file_based/general.yaml index 4a258a7cd99d..fbd7c816b1bb 100644 --- a/etc/env/file_based/general.yaml +++ b/etc/env/file_based/general.yaml @@ -336,6 +336,11 @@ protective_reads_writer: window_size: 3 first_processed_batch: 0 +basic_witness_input_producer: + db_path: "./db/main/basic_witness_input_producer" + window_size: 3 + first_processed_batch: 0 + snapshot_recovery: enabled: false postgres: diff --git a/prover/Cargo.lock b/prover/Cargo.lock index 6f544e4c6c84..93e157a56825 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -8453,6 +8453,7 @@ dependencies = [ "strum", "zksync_multivm", "zksync_object_store", + "zksync_state", "zksync_types", ] @@ -8641,7 +8642,6 @@ dependencies = [ "zkevm_test_harness 1.5.0", "zksync_config", "zksync_core_leftovers", - "zksync_dal", "zksync_env_config", "zksync_multivm", "zksync_object_store", diff --git a/prover/config/src/lib.rs b/prover/config/src/lib.rs index 9b6ee308b62c..99e3ddbee8fa 100644 --- a/prover/config/src/lib.rs +++ b/prover/config/src/lib.rs @@ -8,10 +8,11 @@ use zksync_config::{ }, fri_prover_group::FriProverGroupConfig, house_keeper::HouseKeeperConfig, - BaseTokenAdjusterConfig, DADispatcherConfig, DatabaseSecrets, FriProofCompressorConfig, - FriProverConfig, FriProverGatewayConfig, FriWitnessGeneratorConfig, - FriWitnessVectorGeneratorConfig, GeneralConfig, ObjectStoreConfig, ObservabilityConfig, - PrometheusConfig, ProofDataHandlerConfig, ProtectiveReadsWriterConfig, + BaseTokenAdjusterConfig, BasicWitnessInputProducerConfig, DADispatcherConfig, + DatabaseSecrets, FriProofCompressorConfig, FriProverConfig, FriProverGatewayConfig, + FriWitnessGeneratorConfig, FriWitnessVectorGeneratorConfig, GeneralConfig, + ObjectStoreConfig, ObservabilityConfig, PrometheusConfig, ProofDataHandlerConfig, + ProtectiveReadsWriterConfig, }, ApiConfig, ContractVerifierConfig, DBConfig, EthConfig, EthWatchConfig, GasAdjusterConfig, PostgresConfig, SnapshotsCreatorConfig, @@ -50,6 +51,7 @@ fn load_env_config() -> anyhow::Result { snapshot_creator: SnapshotsCreatorConfig::from_env().ok(), da_dispatcher_config: DADispatcherConfig::from_env().ok(), protective_reads_writer_config: ProtectiveReadsWriterConfig::from_env().ok(), + basic_witness_input_producer_config: BasicWitnessInputProducerConfig::from_env().ok(), core_object_store: ObjectStoreConfig::from_env().ok(), base_token_adjuster_config: BaseTokenAdjusterConfig::from_env().ok(), commitment_generator: None, diff --git a/prover/prover_dal/.sqlx/query-5354ed86960505fe6b159ce859656f870f8bbd15666fec5cc9f398306eeb6136.json b/prover/prover_dal/.sqlx/query-5354ed86960505fe6b159ce859656f870f8bbd15666fec5cc9f398306eeb6136.json deleted file mode 100644 index 298f7bb30aa3..000000000000 --- a/prover/prover_dal/.sqlx/query-5354ed86960505fe6b159ce859656f870f8bbd15666fec5cc9f398306eeb6136.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO\n witness_inputs_fri (\n l1_batch_number,\n merkle_tree_paths_blob_url,\n protocol_version,\n eip_4844_blobs,\n status,\n created_at,\n updated_at,\n protocol_version_patch\n )\n VALUES\n ($1, $2, $3, $4, 'queued', NOW(), NOW(), $5)\n ON CONFLICT (l1_batch_number) DO NOTHING\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int8", - "Text", - "Int4", - "Bytea", - "Int4" - ] - }, - "nullable": [] - }, - "hash": "5354ed86960505fe6b159ce859656f870f8bbd15666fec5cc9f398306eeb6136" -} diff --git a/prover/prover_dal/.sqlx/query-adaa3126792aac4e3afb805068f01ab8ae3f32526d9b5eadcfe52d139f7d6e66.json b/prover/prover_dal/.sqlx/query-adaa3126792aac4e3afb805068f01ab8ae3f32526d9b5eadcfe52d139f7d6e66.json new file mode 100644 index 000000000000..1af0943a3dd8 --- /dev/null +++ b/prover/prover_dal/.sqlx/query-adaa3126792aac4e3afb805068f01ab8ae3f32526d9b5eadcfe52d139f7d6e66.json @@ -0,0 +1,19 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO\n witness_inputs_fri (\n l1_batch_number,\n merkle_tree_paths_blob_url,\n witness_inputs_blob_url,\n protocol_version,\n eip_4844_blobs,\n status,\n created_at,\n updated_at,\n protocol_version_patch\n )\n VALUES\n ($1, $2, $3, $4, $5, 'queued', NOW(), NOW(), $6)\n ON CONFLICT (l1_batch_number) DO NOTHING\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8", + "Text", + "Text", + "Int4", + "Bytea", + "Int4" + ] + }, + "nullable": [] + }, + "hash": "adaa3126792aac4e3afb805068f01ab8ae3f32526d9b5eadcfe52d139f7d6e66" +} diff --git a/prover/prover_dal/.sqlx/query-d91c931e2a14cf1183a608d041fc6fadb8e12a9218399d189b4d95e2ca4fcc48.json b/prover/prover_dal/.sqlx/query-d91c931e2a14cf1183a608d041fc6fadb8e12a9218399d189b4d95e2ca4fcc48.json new file mode 100644 index 000000000000..c353ecf1bad3 --- /dev/null +++ b/prover/prover_dal/.sqlx/query-d91c931e2a14cf1183a608d041fc6fadb8e12a9218399d189b4d95e2ca4fcc48.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE witness_inputs_fri\n SET\n status = 'in_progress',\n attempts = attempts + 1,\n updated_at = NOW(),\n processing_started_at = NOW(),\n picked_by = $3\n WHERE\n l1_batch_number = (\n SELECT\n l1_batch_number\n FROM\n witness_inputs_fri\n WHERE\n l1_batch_number <= $1\n AND status = 'queued'\n AND protocol_version = $2\n AND protocol_version_patch = $4\n ORDER BY\n l1_batch_number ASC\n LIMIT\n 1\n FOR UPDATE\n SKIP LOCKED\n )\n RETURNING\n witness_inputs_fri.l1_batch_number\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "l1_batch_number", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int4", + "Text", + "Int4" + ] + }, + "nullable": [ + false + ] + }, + "hash": "d91c931e2a14cf1183a608d041fc6fadb8e12a9218399d189b4d95e2ca4fcc48" +} diff --git a/prover/prover_dal/.sqlx/query-e0a6cc885e437aa7ded9def71f3e118cabc67b6e507efefb7b69e102f1b43c58.json b/prover/prover_dal/.sqlx/query-e0a6cc885e437aa7ded9def71f3e118cabc67b6e507efefb7b69e102f1b43c58.json index 738a8b54a0b3..79f12689194f 100644 --- a/prover/prover_dal/.sqlx/query-e0a6cc885e437aa7ded9def71f3e118cabc67b6e507efefb7b69e102f1b43c58.json +++ b/prover/prover_dal/.sqlx/query-e0a6cc885e437aa7ded9def71f3e118cabc67b6e507efefb7b69e102f1b43c58.json @@ -72,6 +72,11 @@ "ordinal": 13, "name": "protocol_version_patch", "type_info": "Int4" + }, + { + "ordinal": 14, + "name": "witness_inputs_blob_url", + "type_info": "Text" } ], "parameters": { @@ -93,7 +98,8 @@ true, true, true, - false + false, + true ] }, "hash": "e0a6cc885e437aa7ded9def71f3e118cabc67b6e507efefb7b69e102f1b43c58" diff --git a/prover/prover_dal/.sqlx/query-e8412d5ad1b17269da02f9a5c201ed762158a27449f61d3b1bb80069ca446727.json b/prover/prover_dal/.sqlx/query-e8412d5ad1b17269da02f9a5c201ed762158a27449f61d3b1bb80069ca446727.json deleted file mode 100644 index 4ab8c324ff58..000000000000 --- a/prover/prover_dal/.sqlx/query-e8412d5ad1b17269da02f9a5c201ed762158a27449f61d3b1bb80069ca446727.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE witness_inputs_fri\n SET\n status = 'in_progress',\n attempts = attempts + 1,\n updated_at = NOW(),\n processing_started_at = NOW(),\n picked_by = $3\n WHERE\n l1_batch_number = (\n SELECT\n l1_batch_number\n FROM\n witness_inputs_fri\n WHERE\n l1_batch_number <= $1\n AND status = 'queued'\n AND protocol_version = $2\n AND protocol_version_patch = $4\n ORDER BY\n l1_batch_number ASC\n LIMIT\n 1\n FOR UPDATE\n SKIP LOCKED\n )\n RETURNING\n witness_inputs_fri.*\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "l1_batch_number", - "type_info": "Int8" - }, - { - "ordinal": 1, - "name": "merkle_tree_paths_blob_url", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "attempts", - "type_info": "Int2" - }, - { - "ordinal": 3, - "name": "status", - "type_info": "Text" - }, - { - "ordinal": 4, - "name": "error", - "type_info": "Text" - }, - { - "ordinal": 5, - "name": "created_at", - "type_info": "Timestamp" - }, - { - "ordinal": 6, - "name": "updated_at", - "type_info": "Timestamp" - }, - { - "ordinal": 7, - "name": "processing_started_at", - "type_info": "Timestamp" - }, - { - "ordinal": 8, - "name": "time_taken", - "type_info": "Time" - }, - { - "ordinal": 9, - "name": "is_blob_cleaned", - "type_info": "Bool" - }, - { - "ordinal": 10, - "name": "protocol_version", - "type_info": "Int4" - }, - { - "ordinal": 11, - "name": "picked_by", - "type_info": "Text" - }, - { - "ordinal": 12, - "name": "eip_4844_blobs", - "type_info": "Bytea" - }, - { - "ordinal": 13, - "name": "protocol_version_patch", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Int8", - "Int4", - "Text", - "Int4" - ] - }, - "nullable": [ - false, - true, - false, - false, - true, - false, - false, - true, - true, - true, - true, - true, - true, - false - ] - }, - "hash": "e8412d5ad1b17269da02f9a5c201ed762158a27449f61d3b1bb80069ca446727" -} diff --git a/prover/prover_dal/migrations/20240703113903_add-vm_run_data-column.down.sql b/prover/prover_dal/migrations/20240703113903_add-vm_run_data-column.down.sql new file mode 100644 index 000000000000..2d62a594cc73 --- /dev/null +++ b/prover/prover_dal/migrations/20240703113903_add-vm_run_data-column.down.sql @@ -0,0 +1 @@ +ALTER TABLE witness_inputs_fri DROP COLUMN IF EXISTS witness_inputs_blob_url; diff --git a/prover/prover_dal/migrations/20240703113903_add-vm_run_data-column.up.sql b/prover/prover_dal/migrations/20240703113903_add-vm_run_data-column.up.sql new file mode 100644 index 000000000000..311244337ca7 --- /dev/null +++ b/prover/prover_dal/migrations/20240703113903_add-vm_run_data-column.up.sql @@ -0,0 +1 @@ +ALTER TABLE witness_inputs_fri ADD COLUMN IF NOT EXISTS witness_inputs_blob_url TEXT DEFAULT NULL; diff --git a/prover/prover_dal/src/fri_witness_generator_dal.rs b/prover/prover_dal/src/fri_witness_generator_dal.rs index d884ce05aa16..d56d18550e50 100644 --- a/prover/prover_dal/src/fri_witness_generator_dal.rs +++ b/prover/prover_dal/src/fri_witness_generator_dal.rs @@ -43,7 +43,8 @@ impl FriWitnessGeneratorDal<'_, '_> { pub async fn save_witness_inputs( &mut self, block_number: L1BatchNumber, - object_key: &str, + merkle_paths_blob_url: &str, + witness_inputs_blob_url: &str, protocol_version: ProtocolSemanticVersion, eip_4844_blobs: Eip4844Blobs, ) { @@ -54,6 +55,7 @@ impl FriWitnessGeneratorDal<'_, '_> { witness_inputs_fri ( l1_batch_number, merkle_tree_paths_blob_url, + witness_inputs_blob_url, protocol_version, eip_4844_blobs, status, @@ -62,11 +64,12 @@ impl FriWitnessGeneratorDal<'_, '_> { protocol_version_patch ) VALUES - ($1, $2, $3, $4, 'queued', NOW(), NOW(), $5) + ($1, $2, $3, $4, $5, 'queued', NOW(), NOW(), $6) ON CONFLICT (l1_batch_number) DO NOTHING "#, i64::from(block_number.0), - object_key, + merkle_paths_blob_url, + witness_inputs_blob_url, protocol_version.minor as i32, blobs_raw, protocol_version.patch.0 as i32, @@ -83,7 +86,7 @@ impl FriWitnessGeneratorDal<'_, '_> { last_l1_batch_to_process: u32, protocol_version: ProtocolSemanticVersion, picked_by: &str, - ) -> Option<(L1BatchNumber, Eip4844Blobs)> { + ) -> Option { sqlx::query!( r#" UPDATE witness_inputs_fri @@ -112,7 +115,7 @@ impl FriWitnessGeneratorDal<'_, '_> { SKIP LOCKED ) RETURNING - witness_inputs_fri.* + witness_inputs_fri.l1_batch_number "#, i64::from(last_l1_batch_to_process), protocol_version.minor as i32, @@ -122,21 +125,7 @@ impl FriWitnessGeneratorDal<'_, '_> { .fetch_optional(self.storage.conn()) .await .unwrap() - .map(|row| { - // Blobs can be `None` if we are using an `off-chain DA` - let blobs = if row.eip_4844_blobs.is_none() { - Eip4844Blobs::empty() - } else { - Eip4844Blobs::decode(&row.eip_4844_blobs.unwrap_or_else(|| { - panic!( - "missing eip 4844 blobs from the database for batch {}", - row.l1_batch_number - ) - })) - .expect("failed to decode EIP4844 blobs") - }; - (L1BatchNumber(row.l1_batch_number as u32), blobs) - }) + .map(|row| L1BatchNumber(row.l1_batch_number as u32)) } pub async fn get_basic_circuit_witness_job_attempts( @@ -1476,6 +1465,7 @@ impl FriWitnessGeneratorDal<'_, '_> { .map(|row| BasicWitnessGeneratorJobInfo { l1_batch_number, merkle_tree_paths_blob_url: row.merkle_tree_paths_blob_url, + witness_inputs_blob_url: row.witness_inputs_blob_url, attempts: row.attempts as u32, status: row.status.parse::().unwrap(), error: row.error, diff --git a/prover/prover_fri_gateway/src/proof_gen_data_fetcher.rs b/prover/prover_fri_gateway/src/proof_gen_data_fetcher.rs index a2e213a4e24a..9dcc93a4be77 100644 --- a/prover/prover_fri_gateway/src/proof_gen_data_fetcher.rs +++ b/prover/prover_fri_gateway/src/proof_gen_data_fetcher.rs @@ -9,22 +9,29 @@ use crate::api_data_fetcher::{PeriodicApi, PeriodicApiStruct}; impl PeriodicApiStruct { async fn save_proof_gen_data(&self, data: ProofGenerationData) { let store = &*self.blob_store; - let blob_url = store - .put(data.l1_batch_number, &data.data) + let merkle_paths = store + .put(data.l1_batch_number, &data.witness_input_data.merkle_paths) + .await + .expect("Failed to save proof generation data to GCS"); + let witness_inputs = store + .put(data.l1_batch_number, &data.witness_input_data) .await .expect("Failed to save proof generation data to GCS"); let mut connection = self.pool.connection().await.unwrap(); + connection .fri_protocol_versions_dal() .save_prover_protocol_version(data.protocol_version, data.l1_verifier_config) .await; + connection .fri_witness_generator_dal() .save_witness_inputs( data.l1_batch_number, - &blob_url, + &merkle_paths, + &witness_inputs, data.protocol_version, - data.eip_4844_blobs, + data.witness_input_data.eip_4844_blobs, ) .await; } diff --git a/prover/witness_generator/Cargo.toml b/prover/witness_generator/Cargo.toml index 5c42343f60b9..c31e1662d733 100644 --- a/prover/witness_generator/Cargo.toml +++ b/prover/witness_generator/Cargo.toml @@ -12,7 +12,6 @@ categories.workspace = true [dependencies] vise.workspace = true zksync_prover_dal.workspace = true -zksync_dal.workspace = true zksync_config.workspace = true zksync_prover_interface.workspace = true zksync_prover_config.workspace = true diff --git a/prover/witness_generator/src/basic_circuits.rs b/prover/witness_generator/src/basic_circuits.rs index af21fe909712..c17458ab4338 100644 --- a/prover/witness_generator/src/basic_circuits.rs +++ b/prover/witness_generator/src/basic_circuits.rs @@ -15,7 +15,6 @@ use circuit_definitions::{ use tracing::Instrument; use zkevm_test_harness::geometry_config::get_geometry_config; use zksync_config::configs::FriWitnessGeneratorConfig; -use zksync_dal::{Core, CoreDal}; use zksync_multivm::vm_latest::{ constants::MAX_CYCLES_FOR_TX, HistoryDisabled, StorageOracle as VmStorageOracle, }; @@ -36,16 +35,13 @@ use zksync_prover_fri_types::{ AuxOutputWitnessWrapper, }; use zksync_prover_fri_utils::get_recursive_layer_circuit_id_for_base_layer; -use zksync_prover_interface::inputs::{BasicCircuitWitnessGeneratorInput, PrepareBasicCircuitsJob}; +use zksync_prover_interface::inputs::WitnessInputData; use zksync_queued_job_processor::JobProcessor; -use zksync_state::{PostgresStorage, StorageView}; +use zksync_state::{StorageView, WitnessStorage}; use zksync_types::{ - basic_fri_types::{AggregationRound, Eip4844Blobs}, - block::StorageOracleInfo, - protocol_version::ProtocolSemanticVersion, - Address, L1BatchNumber, ProtocolVersionId, BOOTLOADER_ADDRESS, H256, + basic_fri_types::AggregationRound, protocol_version::ProtocolSemanticVersion, Address, + L1BatchNumber, BOOTLOADER_ADDRESS, }; -use zksync_utils::{bytes_to_chunks, h256_to_u256, u256_to_h256}; use crate::{ metrics::WITNESS_GENERATOR_METRICS, @@ -78,8 +74,7 @@ struct BlobUrls { #[derive(Clone)] pub struct BasicWitnessGeneratorJob { block_number: L1BatchNumber, - job: PrepareBasicCircuitsJob, - eip_4844_blobs: Eip4844Blobs, + job: WitnessInputData, } #[derive(Debug)] @@ -87,7 +82,6 @@ pub struct BasicWitnessGenerator { config: Arc, object_store: Arc, public_blob_store: Option>, - connection_pool: ConnectionPool, prover_connection_pool: ConnectionPool, protocol_version: ProtocolSemanticVersion, } @@ -97,7 +91,6 @@ impl BasicWitnessGenerator { config: FriWitnessGeneratorConfig, object_store: Arc, public_blob_store: Option>, - connection_pool: ConnectionPool, prover_connection_pool: ConnectionPool, protocol_version: ProtocolSemanticVersion, ) -> Self { @@ -105,7 +98,6 @@ impl BasicWitnessGenerator { config: Arc::new(config), object_store, public_blob_store, - connection_pool, prover_connection_pool, protocol_version, } @@ -113,15 +105,10 @@ impl BasicWitnessGenerator { async fn process_job_impl( object_store: Arc, - connection_pool: ConnectionPool, basic_job: BasicWitnessGeneratorJob, started_at: Instant, ) -> Option { - let BasicWitnessGeneratorJob { - block_number, - job, - eip_4844_blobs, - } = basic_job; + let BasicWitnessGeneratorJob { block_number, job } = basic_job; tracing::info!( "Starting witness generation of type {:?} for block {}", @@ -129,17 +116,7 @@ impl BasicWitnessGenerator { block_number.0 ); - Some( - process_basic_circuits_job( - &*object_store, - connection_pool, - started_at, - block_number, - job, - eip_4844_blobs, - ) - .await, - ) + Some(process_basic_circuits_job(&*object_store, started_at, block_number, job).await) } } @@ -165,13 +142,13 @@ impl JobProcessor for BasicWitnessGenerator { ) .await { - Some((block_number, eip_4844_blobs)) => { + Some(block_number) => { tracing::info!( "Processing FRI basic witness-gen for block {}", block_number ); let started_at = Instant::now(); - let job = get_artifacts(block_number, &*self.object_store, eip_4844_blobs).await; + let job = get_artifacts(block_number, &*self.object_store).await; WITNESS_GENERATOR_METRICS.blob_fetch_time[&AggregationRound::BasicCircuits.into()] .observe(started_at.elapsed()); @@ -200,14 +177,11 @@ impl JobProcessor for BasicWitnessGenerator { started_at: Instant, ) -> tokio::task::JoinHandle>> { let object_store = Arc::clone(&self.object_store); - let connection_pool = self.connection_pool.clone(); tokio::spawn(async move { let block_number = job.block_number; - Ok( - Self::process_job_impl(object_store, connection_pool, job, started_at) - .instrument(tracing::info_span!("basic_circuit", %block_number)) - .await, - ) + Ok(Self::process_job_impl(object_store, job, started_at) + .instrument(tracing::info_span!("basic_circuit", %block_number)) + .await) }) } @@ -272,22 +246,12 @@ impl JobProcessor for BasicWitnessGenerator { #[allow(clippy::too_many_arguments)] async fn process_basic_circuits_job( object_store: &dyn ObjectStore, - connection_pool: ConnectionPool, started_at: Instant, block_number: L1BatchNumber, - job: PrepareBasicCircuitsJob, - eip_4844_blobs: Eip4844Blobs, + job: WitnessInputData, ) -> BasicCircuitArtifacts { - let witness_gen_input = - build_basic_circuits_witness_generator_input(&connection_pool, job, block_number).await; - let (circuit_urls, queue_urls, scheduler_witness, aux_output_witness) = generate_witness( - block_number, - object_store, - connection_pool, - witness_gen_input, - eip_4844_blobs, - ) - .await; + let (circuit_urls, queue_urls, scheduler_witness, aux_output_witness) = + generate_witness(block_number, object_store, job).await; WITNESS_GENERATOR_METRICS.witness_generation_time[&AggregationRound::BasicCircuits.into()] .observe(started_at.elapsed()); tracing::info!( @@ -344,14 +308,9 @@ async fn update_database( async fn get_artifacts( block_number: L1BatchNumber, object_store: &dyn ObjectStore, - eip_4844_blobs: Eip4844Blobs, ) -> BasicWitnessGeneratorJob { let job = object_store.get(block_number).await.unwrap(); - BasicWitnessGeneratorJob { - block_number, - job, - eip_4844_blobs, - } + BasicWitnessGeneratorJob { block_number, job } } async fn save_scheduler_artifacts( @@ -403,55 +362,10 @@ async fn save_recursion_queue( (circuit_id, blob_url, basic_circuit_count) } -// If making changes to this method, consider moving this logic to the DAL layer and make -// `PrepareBasicCircuitsJob` have all fields of `BasicCircuitWitnessGeneratorInput`. -async fn build_basic_circuits_witness_generator_input( - connection_pool: &ConnectionPool, - witness_merkle_input: PrepareBasicCircuitsJob, - l1_batch_number: L1BatchNumber, -) -> BasicCircuitWitnessGeneratorInput { - let mut connection = connection_pool.connection().await.unwrap(); - let block_header = connection - .blocks_dal() - .get_l1_batch_header(l1_batch_number) - .await - .unwrap() - .unwrap(); - let initial_heap_content = connection - .blocks_dal() - .get_initial_bootloader_heap(l1_batch_number) - .await - .unwrap() - .unwrap(); - let (_, previous_block_timestamp) = connection - .blocks_dal() - .get_l1_batch_state_root_and_timestamp(l1_batch_number - 1) - .await - .unwrap() - .unwrap(); - let previous_block_hash = connection - .blocks_dal() - .get_l1_batch_state_root(l1_batch_number - 1) - .await - .unwrap() - .expect("cannot generate witness before the root hash is computed"); - BasicCircuitWitnessGeneratorInput { - block_number: l1_batch_number, - previous_block_timestamp, - previous_block_hash, - block_timestamp: block_header.timestamp, - used_bytecodes_hashes: block_header.used_contract_hashes, - initial_heap_content, - merkle_paths_input: witness_merkle_input, - } -} - async fn generate_witness( block_number: L1BatchNumber, object_store: &dyn ObjectStore, - connection_pool: ConnectionPool, - input: BasicCircuitWitnessGeneratorInput, - eip_4844_blobs: Eip4844Blobs, + input: WitnessInputData, ) -> ( Vec<(u8, String)>, Vec<(u8, String, usize)>, @@ -462,119 +376,37 @@ async fn generate_witness( >, BlockAuxilaryOutputWitness, ) { - let mut connection = connection_pool.connection().await.unwrap(); - let header = connection - .blocks_dal() - .get_l1_batch_header(input.block_number) - .await - .unwrap() - .unwrap(); - - let protocol_version = header - .protocol_version - .unwrap_or(ProtocolVersionId::last_potentially_undefined()); - - let previous_batch_with_metadata = connection - .blocks_dal() - .get_l1_batch_metadata(zksync_types::L1BatchNumber( - input.block_number.checked_sub(1).unwrap(), - )) - .await - .unwrap() - .unwrap(); - - let bootloader_code_bytes = connection - .factory_deps_dal() - .get_sealed_factory_dep(header.base_system_contracts_hashes.bootloader) - .await - .expect("Failed fetching bootloader bytecode from DB") - .expect("Bootloader bytecode should exist"); - let bootloader_code = bytes_to_chunks(&bootloader_code_bytes); - let account_bytecode_bytes = connection - .factory_deps_dal() - .get_sealed_factory_dep(header.base_system_contracts_hashes.default_aa) - .await - .expect("Failed fetching default account bytecode from DB") - .expect("Default account bytecode should exist"); - let account_bytecode = bytes_to_chunks(&account_bytecode_bytes); - let bootloader_contents = - expand_bootloader_contents(&input.initial_heap_content, protocol_version); - let account_code_hash = h256_to_u256(header.base_system_contracts_hashes.default_aa); - - let hashes: HashSet = input - .used_bytecodes_hashes - .iter() - // SMA-1555: remove this hack once updated to the latest version of `zkevm_test_harness` - .filter(|&&hash| hash != h256_to_u256(header.base_system_contracts_hashes.bootloader)) - .map(|hash| u256_to_h256(*hash)) - .collect(); - - let StorageOracleInfo { - storage_refunds, - pubdata_costs, - } = connection - .blocks_dal() - .get_storage_oracle_info(input.block_number) - .await - .unwrap() - .unwrap(); - - let mut used_bytecodes = connection - .factory_deps_dal() - .get_factory_deps(&hashes) - .await; - if input.used_bytecodes_hashes.contains(&account_code_hash) { - used_bytecodes.insert(account_code_hash, account_bytecode); - } - - assert_eq!( - hashes.len(), - used_bytecodes.len(), - "{} factory deps are not found in DB", - hashes.len() - used_bytecodes.len() + let bootloader_contents = expand_bootloader_contents( + &input.vm_run_data.initial_heap_content, + input.vm_run_data.protocol_version, ); - // `DbStorageProvider` was designed to be used in API, so it accepts miniblock numbers. - // Probably, we should make it work with L1 batch numbers too. - let (_, last_miniblock_number) = connection - .blocks_dal() - .get_l2_block_range_of_l1_batch(input.block_number - 1) - .await - .unwrap() - .expect("L1 batch should contain at least one miniblock"); - drop(connection); - let mut tree = PrecalculatedMerklePathsProvider::new( - input.merkle_paths_input, - input.previous_block_hash.0, + input.merkle_paths, + input.previous_batch_metadata.root_hash.0, ); let geometry_config = get_geometry_config(); let mut hasher = DefaultHasher::new(); geometry_config.hash(&mut hasher); tracing::info!( "generating witness for block {} using geometry config hash: {}", - input.block_number.0, + input.vm_run_data.l1_batch_number.0, hasher.finish() ); - // The following part is CPU-heavy, so we move it to a separate thread. - let rt_handle = tokio::runtime::Handle::current(); - let (circuit_sender, mut circuit_receiver) = tokio::sync::mpsc::channel(1); let (queue_sender, mut queue_receiver) = tokio::sync::mpsc::channel(1); let make_circuits = tokio::task::spawn_blocking(move || { - let connection = rt_handle.block_on(connection_pool.connection()).unwrap(); - - let storage = PostgresStorage::new(rt_handle, connection, last_miniblock_number, true); - let storage_view = StorageView::new(storage).to_rc_ptr(); + let witness_storage = WitnessStorage::new(input.vm_run_data.witness_block_state); + let storage_view = StorageView::new(witness_storage).to_rc_ptr(); - let vm_storage_oracle: VmStorageOracle>, HistoryDisabled> = + let vm_storage_oracle: VmStorageOracle, HistoryDisabled> = VmStorageOracle::new(storage_view.clone()); let storage_oracle = StorageOracle::new( vm_storage_oracle, - storage_refunds, - pubdata_costs.expect("pubdata costs should be present"), + input.vm_run_data.storage_refunds, + input.vm_run_data.pubdata_costs, ); let path = KZG_TRUSTED_SETUP_FILE @@ -585,20 +417,20 @@ async fn generate_witness( let (scheduler_witness, block_witness) = zkevm_test_harness::external_calls::run( Address::zero(), BOOTLOADER_ADDRESS, - bootloader_code, + input.vm_run_data.bootloader_code, bootloader_contents, false, - account_code_hash, + input.vm_run_data.default_account_code_hash, // NOTE: this will be evm_simulator_code_hash in future releases - account_code_hash, - used_bytecodes, + input.vm_run_data.default_account_code_hash, + input.vm_run_data.used_bytecodes, Vec::default(), MAX_CYCLES_FOR_TX as usize, geometry_config, storage_oracle, &mut tree, path, - eip_4844_blobs.blobs(), + input.eip_4844_blobs.blobs(), |circuit| { circuit_sender.blocking_send(circuit).unwrap(); }, @@ -635,10 +467,8 @@ async fn generate_witness( recursion_urls.retain(|(circuit_id, _, _)| circuits_present.contains(circuit_id)); - scheduler_witness.previous_block_meta_hash = - previous_batch_with_metadata.metadata.meta_parameters_hash.0; - scheduler_witness.previous_block_aux_hash = - previous_batch_with_metadata.metadata.aux_data_hash.0; + 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; ( circuit_urls, diff --git a/prover/witness_generator/src/leaf_aggregation.rs b/prover/witness_generator/src/leaf_aggregation.rs index 112d07498837..76703d0d874d 100644 --- a/prover/witness_generator/src/leaf_aggregation.rs +++ b/prover/witness_generator/src/leaf_aggregation.rs @@ -8,9 +8,8 @@ use zkevm_test_harness::{ zkevm_circuits::scheduler::aux::BaseLayerCircuitType, }; use zksync_config::configs::FriWitnessGeneratorConfig; -use zksync_dal::ConnectionPool; use zksync_object_store::ObjectStore; -use zksync_prover_dal::{Prover, ProverDal}; +use zksync_prover_dal::{ConnectionPool, Prover, ProverDal}; use zksync_prover_fri_types::{ circuit_definitions::{ boojum::field::goldilocks::GoldilocksField, diff --git a/prover/witness_generator/src/main.rs b/prover/witness_generator/src/main.rs index 661965b75061..f26d445999db 100644 --- a/prover/witness_generator/src/main.rs +++ b/prover/witness_generator/src/main.rs @@ -35,7 +35,6 @@ mod utils; #[cfg(not(target_env = "msvc"))] use jemallocator::Jemalloc; -use zksync_dal::Core; use zksync_prover_fri_types::PROVER_PROTOCOL_SEMANTIC_VERSION; #[cfg(not(target_env = "msvc"))] @@ -125,14 +124,6 @@ async fn main() -> anyhow::Result<()> { let prometheus_config = general_config .prometheus_config .context("prometheus config")?; - let postgres_config = general_config.postgres_config.context("postgres config")?; - let connection_pool = ConnectionPool::::builder( - database_secrets.master_url()?, - postgres_config.max_connections()?, - ) - .build() - .await - .context("failed to build a connection_pool")?; let prover_connection_pool = ConnectionPool::::singleton(database_secrets.prover_url()?) .build() @@ -225,7 +216,6 @@ async fn main() -> anyhow::Result<()> { config.clone(), store_factory.create_store().await?, public_blob_store, - connection_pool.clone(), prover_connection_pool.clone(), protocol_version, ); diff --git a/prover/witness_generator/src/node_aggregation.rs b/prover/witness_generator/src/node_aggregation.rs index 0af59890504d..36b13d4357a9 100644 --- a/prover/witness_generator/src/node_aggregation.rs +++ b/prover/witness_generator/src/node_aggregation.rs @@ -6,9 +6,8 @@ use zkevm_test_harness::witness::recursive_aggregation::{ compute_node_vk_commitment, create_node_witnesses, }; use zksync_config::configs::FriWitnessGeneratorConfig; -use zksync_dal::ConnectionPool; use zksync_object_store::ObjectStore; -use zksync_prover_dal::{Prover, ProverDal}; +use zksync_prover_dal::{ConnectionPool, Prover, ProverDal}; use zksync_prover_fri_types::{ circuit_definitions::{ boojum::field::goldilocks::GoldilocksField, diff --git a/prover/witness_generator/src/precalculated_merkle_paths_provider.rs b/prover/witness_generator/src/precalculated_merkle_paths_provider.rs index 2cfadc93fc6a..52c8688cfb42 100644 --- a/prover/witness_generator/src/precalculated_merkle_paths_provider.rs +++ b/prover/witness_generator/src/precalculated_merkle_paths_provider.rs @@ -3,7 +3,7 @@ use zk_evm::blake2::Blake2s256; use zkevm_test_harness::witness::tree::{ BinaryHasher, BinarySparseStorageTree, EnumeratedBinaryLeaf, LeafQuery, ZkSyncStorageLeaf, }; -use zksync_prover_interface::inputs::{PrepareBasicCircuitsJob, StorageLogMetadata}; +use zksync_prover_interface::inputs::{StorageLogMetadata, WitnessInputMerklePaths}; #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] pub struct PrecalculatedMerklePathsProvider { @@ -19,7 +19,7 @@ pub struct PrecalculatedMerklePathsProvider { } impl PrecalculatedMerklePathsProvider { - pub fn new(input: PrepareBasicCircuitsJob, root_hash: [u8; 32]) -> Self { + pub fn new(input: WitnessInputMerklePaths, root_hash: [u8; 32]) -> Self { let next_enumeration_index = input.next_enumeration_index(); tracing::debug!("Initializing PrecalculatedMerklePathsProvider. Initial root_hash: {:?}, initial next_enumeration_index: {:?}", root_hash, next_enumeration_index); Self { diff --git a/prover/witness_generator/src/recursion_tip.rs b/prover/witness_generator/src/recursion_tip.rs index b6c9cd7173dd..2f55621fecaf 100644 --- a/prover/witness_generator/src/recursion_tip.rs +++ b/prover/witness_generator/src/recursion_tip.rs @@ -36,9 +36,8 @@ use zkevm_test_harness::{ }, }; use zksync_config::configs::FriWitnessGeneratorConfig; -use zksync_dal::ConnectionPool; use zksync_object_store::ObjectStore; -use zksync_prover_dal::{Prover, ProverDal}; +use zksync_prover_dal::{ConnectionPool, Prover, ProverDal}; use zksync_prover_fri_types::{ get_current_pod_name, keys::{ClosedFormInputKey, FriCircuitKey}, diff --git a/prover/witness_generator/src/scheduler.rs b/prover/witness_generator/src/scheduler.rs index a6173c813586..80c4322e644e 100644 --- a/prover/witness_generator/src/scheduler.rs +++ b/prover/witness_generator/src/scheduler.rs @@ -6,9 +6,8 @@ use zkevm_test_harness::zkevm_circuits::recursion::{ leaf_layer::input::RecursionLeafParametersWitness, NUM_BASE_LAYER_CIRCUITS, }; use zksync_config::configs::FriWitnessGeneratorConfig; -use zksync_dal::ConnectionPool; use zksync_object_store::ObjectStore; -use zksync_prover_dal::{Prover, ProverDal}; +use zksync_prover_dal::{ConnectionPool, Prover, ProverDal}; use zksync_prover_fri_types::{ circuit_definitions::{ boojum::{ diff --git a/prover/witness_generator/src/tests.rs b/prover/witness_generator/src/tests.rs index 5163368d66d2..d6b00d2ccb4b 100644 --- a/prover/witness_generator/src/tests.rs +++ b/prover/witness_generator/src/tests.rs @@ -5,7 +5,7 @@ use zkevm_test_harness::{ kzg::KzgSettings, witness::tree::{BinarySparseStorageTree, ZkSyncStorageLeaf}, }; -use zksync_prover_interface::inputs::{PrepareBasicCircuitsJob, StorageLogMetadata}; +use zksync_prover_interface::inputs::{StorageLogMetadata, WitnessInputMerklePaths}; use zksync_types::U256; use super::precalculated_merkle_paths_provider::PrecalculatedMerklePathsProvider; @@ -81,7 +81,7 @@ const fn generate_storage_log_metadata( } fn create_provider() -> PrecalculatedMerklePathsProvider { - let mut job = PrepareBasicCircuitsJob::new(4); + let mut job = WitnessInputMerklePaths::new(4); for (mut log, merkle_path) in LOGS_AND_PATHS { log.merkle_paths = vec![merkle_path]; job.push_merkle_path(log);