From 175741211ec21e71936ac1b6a23e70a18924240c Mon Sep 17 00:00:00 2001 From: perekopskiy <53865202+perekopskiy@users.noreply.github.com> Date: Wed, 20 Mar 2024 12:44:27 +0200 Subject: [PATCH] feat!: Use protocol version v22 as latest (#1432) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ - Use protocol version v22 as latest - Migration for syncing v22 batches/miniblock for ENs. ## Why ❔ - There was a protocol upgrade v22 - We had to update protocol version for already sealed batches in main node DB. ENs should resync their data. ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zk fmt` and `zk lint`. - [ ] Spellcheck has been run via `zk spellcheck`. - [ ] Linkcheck has been run via `zk linkcheck`. --- core/bin/external_node/src/main.rs | 25 +++- .../external_node/src/version_sync_task.rs | 132 ++++++++++++++++++ core/lib/basic_types/src/protocol_version.rs | 13 +- ...a7db7673811ac181e801a16a7aefc984d60b0.json | 16 +++ ...edac2f0db60c263c1564b64c717f8ae53e44d.json | 22 +++ ...f3f9dd37b771cf76261762ae18fd14cf0efc5.json | 16 +++ core/lib/dal/src/blocks_dal.rs | 66 +++++++++ .../src/api_server/tx_sender/mod.rs | 4 +- 8 files changed, 284 insertions(+), 10 deletions(-) create mode 100644 core/bin/external_node/src/version_sync_task.rs create mode 100644 core/lib/dal/.sqlx/query-00220170d8f9e577321a0522337a7db7673811ac181e801a16a7aefc984d60b0.json create mode 100644 core/lib/dal/.sqlx/query-86cbe509988c8775bcf738d5cb1edac2f0db60c263c1564b64c717f8ae53e44d.json create mode 100644 core/lib/dal/.sqlx/query-e673789d5b4a7aae11feccb085bf3f9dd37b771cf76261762ae18fd14cf0efc5.json diff --git a/core/bin/external_node/src/main.rs b/core/bin/external_node/src/main.rs index a6d14851fac..2205abeb077 100644 --- a/core/bin/external_node/src/main.rs +++ b/core/bin/external_node/src/main.rs @@ -1,4 +1,4 @@ -use std::{sync::Arc, time::Duration}; +use std::{future, sync::Arc, time::Duration}; use anyhow::Context as _; use clap::Parser; @@ -51,6 +51,7 @@ mod config; mod helpers; mod init; mod metrics; +mod version_sync_task; const RELEASE_MANIFEST: &str = include_str!("../../../../.github/release-please/manifest.json"); @@ -540,10 +541,24 @@ async fn main() -> anyhow::Result<()> { ); // Start scraping Postgres metrics before store initialization as well. let metrics_pool = connection_pool.clone(); - let mut task_handles = vec![tokio::spawn(async move { - PostgresMetrics::run_scraping(metrics_pool, Duration::from_secs(60)).await; - Ok(()) - })]; + let version_sync_task_pool = connection_pool.clone(); + let version_sync_task_main_node_client = main_node_client.clone(); + let mut task_handles = vec![ + tokio::spawn(async move { + PostgresMetrics::run_scraping(metrics_pool, Duration::from_secs(60)).await; + Ok(()) + }), + tokio::spawn(async move { + version_sync_task::sync_versions( + version_sync_task_pool, + version_sync_task_main_node_client, + ) + .await?; + future::pending::<()>().await; + // ^ Since this is run as a task, we don't want it to exit on success (this would shut down the node). + Ok(()) + }), + ]; // Make sure that the node storage is initialized either via genesis or snapshot recovery. ensure_storage_initialized( diff --git a/core/bin/external_node/src/version_sync_task.rs b/core/bin/external_node/src/version_sync_task.rs new file mode 100644 index 00000000000..ffb2c6eba37 --- /dev/null +++ b/core/bin/external_node/src/version_sync_task.rs @@ -0,0 +1,132 @@ +use std::cmp::Ordering; + +use anyhow::Context; +use zksync_basic_types::{L1BatchNumber, MiniblockNumber}; +use zksync_dal::{ConnectionPool, Server, ServerDals}; +use zksync_types::ProtocolVersionId; +use zksync_web3_decl::{ + jsonrpsee::http_client::HttpClient, + namespaces::{EnNamespaceClient, ZksNamespaceClient}, +}; + +pub async fn get_l1_batch_remote_protocol_version( + main_node_client: &HttpClient, + l1_batch_number: L1BatchNumber, +) -> anyhow::Result> { + let Some((miniblock, _)) = main_node_client + .get_miniblock_range(l1_batch_number) + .await? + else { + return Ok(None); + }; + let sync_block = main_node_client + .sync_l2_block(MiniblockNumber(miniblock.as_u32()), false) + .await?; + Ok(sync_block.map(|b| b.protocol_version)) +} + +// Synchronizes protocol version in `l1_batches` and `miniblocks` tables between EN and main node. +pub async fn sync_versions( + connection_pool: ConnectionPool, + main_node_client: HttpClient, +) -> anyhow::Result<()> { + tracing::info!("Starting syncing protocol version of blocks"); + + let mut connection = connection_pool.access_storage().await?; + + // Load the first local batch number with version 22. + let Some(local_first_v22_l1_batch) = connection + .blocks_dal() + .get_first_l1_batch_number_for_version(ProtocolVersionId::Version22) + .await? + else { + return Ok(()); + }; + tracing::info!("First local v22 batch is #{local_first_v22_l1_batch}"); + + // Find the first remote batch with version 22, assuming it's less then or equal than local one. + // Uses binary search. + + let mut left_bound = L1BatchNumber(0); + let mut right_bound = local_first_v22_l1_batch; + + let right_bound_remote_version = + get_l1_batch_remote_protocol_version(&main_node_client, right_bound).await?; + if right_bound_remote_version != Some(ProtocolVersionId::Version22) { + anyhow::bail!("Remote protocol versions should be v22 for the first local v22 batch, got {right_bound_remote_version:?}"); + } + + while left_bound < right_bound { + let mid_batch = L1BatchNumber((left_bound.0 + right_bound.0) / 2); + let (mid_miniblock, _) = connection + .blocks_dal() + .get_miniblock_range_of_l1_batch(mid_batch) + .await + .with_context(|| format!("Failed to get miniblock range for L1 batch #{mid_batch}"))? + .with_context(|| { + format!("Postgres is inconsistent: missing miniblocks for L1 batch #{mid_batch}") + })?; + let mid_protocol_version = main_node_client + .sync_l2_block(mid_miniblock, false) + .await? + .with_context(|| format!("Main node missing data about miniblock #{mid_miniblock}"))? + .protocol_version; + + match mid_protocol_version.cmp(&ProtocolVersionId::Version22) { + Ordering::Less => { + left_bound = mid_batch + 1; + } + Ordering::Equal => { + right_bound = mid_batch; + } + Ordering::Greater => { + anyhow::bail!("Unexpected remote protocol version: {mid_protocol_version:?} for miniblock #{mid_miniblock}"); + } + } + } + + let remote_first_v22_l1_batch = left_bound; + let (remote_first_v22_miniblock, _) = connection + .blocks_dal() + .get_miniblock_range_of_l1_batch(remote_first_v22_l1_batch) + .await + .with_context(|| format!("Failed to get miniblock range for L1 batch #{remote_first_v22_l1_batch}"))? + .with_context(|| { + format!("Postgres is inconsistent: missing miniblocks for L1 batch #{remote_first_v22_l1_batch}") + })?; + + let mut transaction = connection.start_transaction().await?; + + tracing::info!( + "Setting version 22 for batches {remote_first_v22_l1_batch}..={local_first_v22_l1_batch}" + ); + transaction + .blocks_dal() + .reset_protocol_version_for_l1_batches( + remote_first_v22_l1_batch..=local_first_v22_l1_batch, + ProtocolVersionId::Version22, + ) + .await?; + + let (local_first_v22_miniblock, _) = transaction + .blocks_dal() + .get_miniblock_range_of_l1_batch(local_first_v22_l1_batch) + .await + .with_context(|| format!("Failed to get miniblock range for L1 batch #{local_first_v22_l1_batch}"))? + .with_context(|| { + format!("Postgres is inconsistent: missing miniblocks for L1 batch #{local_first_v22_l1_batch}") + })?; + + tracing::info!("Setting version 22 for miniblocks {remote_first_v22_miniblock}..={local_first_v22_miniblock}"); + transaction + .blocks_dal() + .reset_protocol_version_for_miniblocks( + remote_first_v22_miniblock..=local_first_v22_miniblock, + ProtocolVersionId::Version22, + ) + .await?; + + transaction.commit().await?; + + Ok(()) +} diff --git a/core/lib/basic_types/src/protocol_version.rs b/core/lib/basic_types/src/protocol_version.rs index a4b4e94fb8b..d45fc3850c3 100644 --- a/core/lib/basic_types/src/protocol_version.rs +++ b/core/lib/basic_types/src/protocol_version.rs @@ -44,15 +44,16 @@ pub enum ProtocolVersionId { Version20, Version21, Version22, + Version23, } impl ProtocolVersionId { pub fn latest() -> Self { - Self::Version21 + Self::Version22 } pub fn next() -> Self { - Self::Version22 + Self::Version23 } /// Returns VM version to be used by API for this protocol version. @@ -82,6 +83,7 @@ impl ProtocolVersionId { ProtocolVersionId::Version20 => VmVersion::Vm1_4_1, ProtocolVersionId::Version21 => VmVersion::Vm1_4_2, ProtocolVersionId::Version22 => VmVersion::Vm1_4_2, + ProtocolVersionId::Version23 => VmVersion::Vm1_4_2, } } @@ -170,15 +172,16 @@ pub enum FriProtocolVersionId { Version20, Version21, Version22, + Version23, } impl FriProtocolVersionId { pub fn latest() -> Self { - Self::Version21 + Self::Version22 } pub fn next() -> Self { - Self::Version22 + Self::Version23 } } @@ -214,6 +217,7 @@ impl From for FriProtocolVersionId { ProtocolVersionId::Version20 => FriProtocolVersionId::Version20, ProtocolVersionId::Version21 => FriProtocolVersionId::Version21, ProtocolVersionId::Version22 => FriProtocolVersionId::Version22, + ProtocolVersionId::Version23 => FriProtocolVersionId::Version23, } } } @@ -280,6 +284,7 @@ impl From for VmVersion { ProtocolVersionId::Version20 => VmVersion::Vm1_4_1, ProtocolVersionId::Version21 => VmVersion::Vm1_4_2, ProtocolVersionId::Version22 => VmVersion::Vm1_4_2, + ProtocolVersionId::Version23 => VmVersion::Vm1_4_2, } } } diff --git a/core/lib/dal/.sqlx/query-00220170d8f9e577321a0522337a7db7673811ac181e801a16a7aefc984d60b0.json b/core/lib/dal/.sqlx/query-00220170d8f9e577321a0522337a7db7673811ac181e801a16a7aefc984d60b0.json new file mode 100644 index 00000000000..7676cdc2501 --- /dev/null +++ b/core/lib/dal/.sqlx/query-00220170d8f9e577321a0522337a7db7673811ac181e801a16a7aefc984d60b0.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE l1_batches\n SET\n protocol_version = $1\n WHERE\n number BETWEEN $2 AND $3\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int4", + "Int8", + "Int8" + ] + }, + "nullable": [] + }, + "hash": "00220170d8f9e577321a0522337a7db7673811ac181e801a16a7aefc984d60b0" +} diff --git a/core/lib/dal/.sqlx/query-86cbe509988c8775bcf738d5cb1edac2f0db60c263c1564b64c717f8ae53e44d.json b/core/lib/dal/.sqlx/query-86cbe509988c8775bcf738d5cb1edac2f0db60c263c1564b64c717f8ae53e44d.json new file mode 100644 index 00000000000..f9799079442 --- /dev/null +++ b/core/lib/dal/.sqlx/query-86cbe509988c8775bcf738d5cb1edac2f0db60c263c1564b64c717f8ae53e44d.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n MIN(number) AS \"min?\"\n FROM\n l1_batches\n WHERE\n protocol_version = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "min?", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int4" + ] + }, + "nullable": [ + null + ] + }, + "hash": "86cbe509988c8775bcf738d5cb1edac2f0db60c263c1564b64c717f8ae53e44d" +} diff --git a/core/lib/dal/.sqlx/query-e673789d5b4a7aae11feccb085bf3f9dd37b771cf76261762ae18fd14cf0efc5.json b/core/lib/dal/.sqlx/query-e673789d5b4a7aae11feccb085bf3f9dd37b771cf76261762ae18fd14cf0efc5.json new file mode 100644 index 00000000000..65803f089a8 --- /dev/null +++ b/core/lib/dal/.sqlx/query-e673789d5b4a7aae11feccb085bf3f9dd37b771cf76261762ae18fd14cf0efc5.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE miniblocks\n SET\n protocol_version = $1\n WHERE\n number BETWEEN $2 AND $3\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int4", + "Int8", + "Int8" + ] + }, + "nullable": [] + }, + "hash": "e673789d5b4a7aae11feccb085bf3f9dd37b771cf76261762ae18fd14cf0efc5" +} diff --git a/core/lib/dal/src/blocks_dal.rs b/core/lib/dal/src/blocks_dal.rs index 71c78d63189..5e8a0dc43d1 100644 --- a/core/lib/dal/src/blocks_dal.rs +++ b/core/lib/dal/src/blocks_dal.rs @@ -2,6 +2,7 @@ use std::{ collections::HashMap, convert::{Into, TryInto}, ops, + ops::RangeInclusive, }; use anyhow::Context as _; @@ -2146,6 +2147,71 @@ impl BlocksDal<'_, '_> { .await? .map(|row| row.virtual_blocks as u32)) } + + pub async fn get_first_l1_batch_number_for_version( + &mut self, + protocol_version: ProtocolVersionId, + ) -> sqlx::Result> { + Ok(sqlx::query!( + r#" + SELECT + MIN(number) AS "min?" + FROM + l1_batches + WHERE + protocol_version = $1 + "#, + protocol_version as i32 + ) + .fetch_optional(self.storage.conn()) + .await? + .and_then(|row| row.min) + .map(|min| L1BatchNumber(min as u32))) + } + + pub async fn reset_protocol_version_for_l1_batches( + &mut self, + l1_batch_range: RangeInclusive, + protocol_version: ProtocolVersionId, + ) -> sqlx::Result<()> { + sqlx::query!( + r#" + UPDATE l1_batches + SET + protocol_version = $1 + WHERE + number BETWEEN $2 AND $3 + "#, + protocol_version as i32, + i64::from(l1_batch_range.start().0), + i64::from(l1_batch_range.end().0), + ) + .execute(self.storage.conn()) + .await?; + Ok(()) + } + + pub async fn reset_protocol_version_for_miniblocks( + &mut self, + miniblock_range: RangeInclusive, + protocol_version: ProtocolVersionId, + ) -> sqlx::Result<()> { + sqlx::query!( + r#" + UPDATE miniblocks + SET + protocol_version = $1 + WHERE + number BETWEEN $2 AND $3 + "#, + protocol_version as i32, + i64::from(miniblock_range.start().0), + i64::from(miniblock_range.end().0), + ) + .execute(self.storage.conn()) + .await?; + Ok(()) + } } /// Temporary methods for migrating `fee_account_address`. diff --git a/core/lib/zksync_core/src/api_server/tx_sender/mod.rs b/core/lib/zksync_core/src/api_server/tx_sender/mod.rs index 2634e5c00b8..2ecc982afab 100644 --- a/core/lib/zksync_core/src/api_server/tx_sender/mod.rs +++ b/core/lib/zksync_core/src/api_server/tx_sender/mod.rs @@ -92,7 +92,9 @@ impl MultiVMBaseSystemContracts { ProtocolVersionId::Version18 => self.post_boojum, ProtocolVersionId::Version19 => self.post_allowlist_removal, ProtocolVersionId::Version20 => self.post_1_4_1, - ProtocolVersionId::Version21 | ProtocolVersionId::Version22 => self.post_1_4_2, + ProtocolVersionId::Version21 + | ProtocolVersionId::Version22 + | ProtocolVersionId::Version23 => self.post_1_4_2, } } }