From d5e8e9bc66ff38b828730b62d8a7b8794cb1758a Mon Sep 17 00:00:00 2001 From: AnastasiiaVashchuk <72273339+AnastasiiaVashchuk@users.noreply.github.com> Date: Thu, 20 Jun 2024 13:04:22 +0300 Subject: [PATCH] feat(api): Add new `l1_committed` block tag (#2282) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Follow-ups: - add `l1_committed` tag to zksync-ethers codebase. - update docs. ## What ❔ - added the new tag `L1Committed` to `BlockNumber` enum. - added a new query to db to fetch l2 block, that was included into committed on L1 `l1_batch` inside `resolve_block_id ` dal method. - added unit test to check new feature `resolving_l1_committed_block_id `. - updated integration test. - refactored `dal`'s tests - created `create_l1_batch_header` for tests to avoid duplication. ## Why ❔ Chainlink needs this feature. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `zk spellcheck`. --- Cargo.lock | 11 ++- ...ae1cee8eeca1c74529adee78157dcf8930c44.json | 20 ++++ core/lib/dal/src/blocks_dal.rs | 12 +-- core/lib/dal/src/blocks_web3_dal.rs | 96 ++++++++++++++++++- core/lib/dal/src/helpers.rs | 15 +-- core/lib/dal/src/pruning_dal/tests.rs | 16 +--- core/lib/dal/src/tests/mod.rs | 13 ++- core/lib/types/src/api/mod.rs | 4 + core/node/api_server/src/web3/metrics.rs | 2 + .../ts-integration/tests/api/web3.test.ts | 2 + 10 files changed, 151 insertions(+), 40 deletions(-) create mode 100644 core/lib/dal/.sqlx/query-01cc22f19b61145b0dbed96ec84ae1cee8eeca1c74529adee78157dcf8930c44.json diff --git a/Cargo.lock b/Cargo.lock index b013517e0cc2..3582fbe51319 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1509,15 +1509,16 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.3" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" dependencies = [ "cfg-if 1.0.0", "cpufeatures", "curve25519-dalek-derive", "digest 0.10.7", "fiat-crypto", + "platforms", "rustc_version", "subtle", "zeroize", @@ -4386,6 +4387,12 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "platforms" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0" + [[package]] name = "plotters" version = "0.3.5" diff --git a/core/lib/dal/.sqlx/query-01cc22f19b61145b0dbed96ec84ae1cee8eeca1c74529adee78157dcf8930c44.json b/core/lib/dal/.sqlx/query-01cc22f19b61145b0dbed96ec84ae1cee8eeca1c74529adee78157dcf8930c44.json new file mode 100644 index 000000000000..d8af3cae95b9 --- /dev/null +++ b/core/lib/dal/.sqlx/query-01cc22f19b61145b0dbed96ec84ae1cee8eeca1c74529adee78157dcf8930c44.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT COALESCE(\n (\n SELECT MAX(number) FROM miniblocks\n WHERE l1_batch_number = (\n SELECT number FROM l1_batches\n JOIN eth_txs ON\n l1_batches.eth_commit_tx_id = eth_txs.id\n WHERE\n eth_txs.confirmed_eth_tx_history_id IS NOT NULL\n ORDER BY number DESC LIMIT 1\n )\n ),\n 0\n ) AS number\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "number", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "01cc22f19b61145b0dbed96ec84ae1cee8eeca1c74529adee78157dcf8930c44" +} diff --git a/core/lib/dal/src/blocks_dal.rs b/core/lib/dal/src/blocks_dal.rs index 94d3b3372d93..2e59c2db50e4 100644 --- a/core/lib/dal/src/blocks_dal.rs +++ b/core/lib/dal/src/blocks_dal.rs @@ -2366,7 +2366,7 @@ mod tests { }; use super::*; - use crate::{ConnectionPool, Core, CoreDal}; + use crate::{tests::create_l1_batch_header, ConnectionPool, Core, CoreDal}; async fn save_mock_eth_tx(action_type: AggregatedActionType, conn: &mut Connection<'_, Core>) { conn.eth_sender_dal() @@ -2376,15 +2376,7 @@ mod tests { } fn mock_l1_batch_header() -> L1BatchHeader { - let mut header = L1BatchHeader::new( - L1BatchNumber(1), - 100, - BaseSystemContractsHashes { - bootloader: H256::repeat_byte(1), - default_aa: H256::repeat_byte(42), - }, - ProtocolVersionId::latest(), - ); + let mut header = create_l1_batch_header(1); header.l1_tx_count = 3; header.l2_tx_count = 5; header.l2_to_l1_logs.push(UserL2ToL1Log(L2ToL1Log { diff --git a/core/lib/dal/src/blocks_web3_dal.rs b/core/lib/dal/src/blocks_web3_dal.rs index 1c7f912728cc..b1637d2124b5 100644 --- a/core/lib/dal/src/blocks_web3_dal.rs +++ b/core/lib/dal/src/blocks_web3_dal.rs @@ -271,6 +271,24 @@ impl BlocksWeb3Dal<'_, '_> { api::BlockId::Number(api::BlockNumber::Latest | api::BlockNumber::Committed) => ( "SELECT MAX(number) AS number FROM miniblocks"; ), + api::BlockId::Number(api::BlockNumber::L1Committed) => ( + " + SELECT COALESCE( + ( + SELECT MAX(number) FROM miniblocks + WHERE l1_batch_number = ( + SELECT number FROM l1_batches + JOIN eth_txs ON + l1_batches.eth_commit_tx_id = eth_txs.id + WHERE + eth_txs.confirmed_eth_tx_history_id IS NOT NULL + ORDER BY number DESC LIMIT 1 + ) + ), + 0 + ) AS number + "; + ), api::BlockId::Number(api::BlockNumber::Finalized) => ( " SELECT COALESCE( @@ -733,6 +751,7 @@ impl BlocksWeb3Dal<'_, '_> { #[cfg(test)] mod tests { use zksync_types::{ + aggregated_operations::AggregatedActionType, block::{L2BlockHasher, L2BlockHeader}, fee::TransactionExecutionMetrics, Address, L2BlockNumber, ProtocolVersion, ProtocolVersionId, @@ -741,8 +760,8 @@ mod tests { use super::*; use crate::{ tests::{ - create_l2_block_header, create_snapshot_recovery, mock_execution_result, - mock_l2_transaction, + create_l1_batch_header, create_l2_block_header, create_snapshot_recovery, + mock_execution_result, mock_l2_transaction, }, ConnectionPool, Core, CoreDal, }; @@ -902,6 +921,79 @@ mod tests { assert_eq!(l2_block_number, Some(L2BlockNumber(43))); } + #[tokio::test] + async fn resolving_l1_committed_block_id() { + let connection_pool = ConnectionPool::::test_pool().await; + let mut conn = connection_pool.connection().await.unwrap(); + conn.protocol_versions_dal() + .save_protocol_version_with_tx(&ProtocolVersion::default()) + .await + .unwrap(); + + let l2_block_header = create_l2_block_header(1); + conn.blocks_dal() + .insert_l2_block(&l2_block_header) + .await + .unwrap(); + + let l1_batch_header = create_l1_batch_header(0); + + conn.blocks_dal() + .insert_mock_l1_batch(&l1_batch_header) + .await + .unwrap(); + conn.blocks_dal() + .mark_l2_blocks_as_executed_in_l1_batch(l1_batch_header.number) + .await + .unwrap(); + + let resolved_l2_block_number = conn + .blocks_web3_dal() + .resolve_block_id(api::BlockId::Number(api::BlockNumber::L1Committed)) + .await + .unwrap(); + assert_eq!(resolved_l2_block_number, Some(L2BlockNumber(0))); + + let mocked_commit_eth_tx = conn + .eth_sender_dal() + .save_eth_tx( + 0, + vec![], + AggregatedActionType::Commit, + Address::default(), + 0, + None, + None, + ) + .await + .unwrap(); + let tx_hash = H256::random(); + conn.eth_sender_dal() + .insert_tx_history(mocked_commit_eth_tx.id, 0, 0, None, tx_hash, &[], 0) + .await + .unwrap(); + conn.eth_sender_dal() + .confirm_tx(tx_hash, U256::zero()) + .await + .unwrap(); + conn.blocks_dal() + .set_eth_tx_id( + l1_batch_header.number..=l1_batch_header.number, + mocked_commit_eth_tx.id, + AggregatedActionType::Commit, + ) + .await + .unwrap(); + + let resolved_l2_block_number = conn + .blocks_web3_dal() + .resolve_block_id(api::BlockId::Number(api::BlockNumber::L1Committed)) + .await + .unwrap(); + + assert_eq!(resolved_l2_block_number, Some(l2_block_header.number)); + } + #[tokio::test] async fn resolving_block_by_hash() { let connection_pool = ConnectionPool::::test_pool().await; diff --git a/core/lib/dal/src/helpers.rs b/core/lib/dal/src/helpers.rs index e8e11f1cc5f3..65e9161bd047 100644 --- a/core/lib/dal/src/helpers.rs +++ b/core/lib/dal/src/helpers.rs @@ -40,11 +40,10 @@ pub async fn wait_for_l1_batch( #[cfg(test)] mod tests { - use zksync_contracts::BaseSystemContractsHashes; - use zksync_types::{block::L1BatchHeader, ProtocolVersion, ProtocolVersionId, H256}; + use zksync_types::ProtocolVersion; use super::*; - use crate::{ConnectionPool, Core, CoreDal}; + use crate::{tests::create_l1_batch_header, ConnectionPool, Core, CoreDal}; #[tokio::test] async fn waiting_for_l1_batch_success() { @@ -59,15 +58,7 @@ mod tests { .save_protocol_version_with_tx(&ProtocolVersion::default()) .await .unwrap(); - let header = L1BatchHeader::new( - L1BatchNumber(0), - 100, - BaseSystemContractsHashes { - bootloader: H256::repeat_byte(1), - default_aa: H256::repeat_byte(42), - }, - ProtocolVersionId::latest(), - ); + let header = create_l1_batch_header(0); conn.blocks_dal() .insert_mock_l1_batch(&header) .await diff --git a/core/lib/dal/src/pruning_dal/tests.rs b/core/lib/dal/src/pruning_dal/tests.rs index 61b5766b93e7..1c3b1edcbd42 100644 --- a/core/lib/dal/src/pruning_dal/tests.rs +++ b/core/lib/dal/src/pruning_dal/tests.rs @@ -1,9 +1,7 @@ use std::ops; -use zksync_contracts::BaseSystemContractsHashes; use zksync_db_connection::connection::Connection; use zksync_types::{ - block::L1BatchHeader, fee::TransactionExecutionMetrics, l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}, tx::IncludedTxLocation, @@ -15,8 +13,8 @@ use super::*; use crate::{ storage_logs_dal::DbStorageLog, tests::{ - create_l2_block_header, mock_execution_result, mock_l2_to_l1_log, mock_l2_transaction, - mock_vm_event, + create_l1_batch_header, create_l2_block_header, mock_execution_result, mock_l2_to_l1_log, + mock_l2_transaction, mock_vm_event, }, ConnectionPool, Core, CoreDal, }; @@ -89,15 +87,7 @@ async fn insert_events(conn: &mut Connection<'_, Core>, l2_block_number: L2Block } async fn insert_l1_batch(conn: &mut Connection<'_, Core>, l1_batch_number: L1BatchNumber) { - let mut header = L1BatchHeader::new( - l1_batch_number, - 100, - BaseSystemContractsHashes { - bootloader: H256::repeat_byte(1), - default_aa: H256::repeat_byte(42), - }, - ProtocolVersionId::latest(), - ); + let mut header = create_l1_batch_header(*l1_batch_number); header.l1_tx_count = 3; header.l2_tx_count = 5; header.l2_to_l1_logs.push(UserL2ToL1Log(L2ToL1Log { diff --git a/core/lib/dal/src/tests/mod.rs b/core/lib/dal/src/tests/mod.rs index c4dab1246552..d6ffde594323 100644 --- a/core/lib/dal/src/tests/mod.rs +++ b/core/lib/dal/src/tests/mod.rs @@ -3,7 +3,7 @@ use std::time::Duration; use zksync_contracts::BaseSystemContractsHashes; use zksync_db_connection::connection_pool::ConnectionPool; use zksync_types::{ - block::{L2BlockHasher, L2BlockHeader}, + block::{L1BatchHeader, L2BlockHasher, L2BlockHeader}, fee::{Fee, TransactionExecutionMetrics}, fee_model::BatchFeeInput, helpers::unix_timestamp_ms, @@ -50,6 +50,17 @@ pub(crate) fn create_l2_block_header(number: u32) -> L2BlockHeader { gas_limit: 0, } } +pub(crate) fn create_l1_batch_header(number: u32) -> L1BatchHeader { + L1BatchHeader::new( + L1BatchNumber(number), + 100, + BaseSystemContractsHashes { + bootloader: H256::repeat_byte(1), + default_aa: H256::repeat_byte(42), + }, + ProtocolVersionId::latest(), + ) +} pub(crate) fn mock_l2_transaction() -> L2Tx { let fee = Fee { diff --git a/core/lib/types/src/api/mod.rs b/core/lib/types/src/api/mod.rs index ce21a754c7aa..0617f47268aa 100644 --- a/core/lib/types/src/api/mod.rs +++ b/core/lib/types/src/api/mod.rs @@ -27,6 +27,8 @@ pub enum BlockNumber { Finalized, /// Latest sealed block Latest, + /// Last block that was committed on L1 + L1Committed, /// Earliest block (genesis) Earliest, /// Latest block (may be the block that is currently open). @@ -51,6 +53,7 @@ impl Serialize for BlockNumber { BlockNumber::Committed => serializer.serialize_str("committed"), BlockNumber::Finalized => serializer.serialize_str("finalized"), BlockNumber::Latest => serializer.serialize_str("latest"), + BlockNumber::L1Committed => serializer.serialize_str("l1_committed"), BlockNumber::Earliest => serializer.serialize_str("earliest"), BlockNumber::Pending => serializer.serialize_str("pending"), } @@ -73,6 +76,7 @@ impl<'de> Deserialize<'de> for BlockNumber { "committed" => BlockNumber::Committed, "finalized" => BlockNumber::Finalized, "latest" => BlockNumber::Latest, + "l1_committed" => BlockNumber::L1Committed, "earliest" => BlockNumber::Earliest, "pending" => BlockNumber::Pending, num => { diff --git a/core/node/api_server/src/web3/metrics.rs b/core/node/api_server/src/web3/metrics.rs index af6e1bf63ad8..9d8cbf813b03 100644 --- a/core/node/api_server/src/web3/metrics.rs +++ b/core/node/api_server/src/web3/metrics.rs @@ -102,6 +102,7 @@ enum BlockIdLabel { Committed, Finalized, Latest, + L1Committed, Earliest, Pending, Number, @@ -139,6 +140,7 @@ impl From<&MethodMetadata> for MethodLabels { api::BlockId::Number(api::BlockNumber::Committed) => BlockIdLabel::Committed, api::BlockId::Number(api::BlockNumber::Finalized) => BlockIdLabel::Finalized, api::BlockId::Number(api::BlockNumber::Latest) => BlockIdLabel::Latest, + api::BlockId::Number(api::BlockNumber::L1Committed) => BlockIdLabel::L1Committed, api::BlockId::Number(api::BlockNumber::Earliest) => BlockIdLabel::Earliest, api::BlockId::Number(api::BlockNumber::Pending) => BlockIdLabel::Pending, }); diff --git a/core/tests/ts-integration/tests/api/web3.test.ts b/core/tests/ts-integration/tests/api/web3.test.ts index 09f78ce75059..3eb4afb3977e 100644 --- a/core/tests/ts-integration/tests/api/web3.test.ts +++ b/core/tests/ts-integration/tests/api/web3.test.ts @@ -690,6 +690,8 @@ describe('web3 API compatibility tests', () => { expect(+finalizedBlock.number!).toEqual(expect.any(Number)); const latestBlock = await alice.provider.send('eth_getBlockByNumber', ['latest', true]); expect(+latestBlock.number!).toEqual(expect.any(Number)); + const l1CommittedBlock = await alice.provider.send('eth_getBlockByNumber', ['l1_committed', true]); + expect(+l1CommittedBlock.number!).toEqual(expect.any(Number)); const pendingBlock = await alice.provider.send('eth_getBlockByNumber', ['pending', true]); expect(pendingBlock).toEqual(null); });