From 227e10180396fbb54a2e99cab775f13bc93745f3 Mon Sep 17 00:00:00 2001 From: pompon0 Date: Thu, 13 Jun 2024 12:53:27 +0200 Subject: [PATCH] revert: verification of L1Batch witness (BFT-471) (#2230) Execute::factory_deps is set explicitly to null is storage, which breaks parsing --- Cargo.lock | 9 - .../src/intrinsic_costs.rs | 6 +- .../system-constants-generator/src/utils.rs | 8 +- .../src/eip712_signature/typed_structure.rs | 2 +- .../src/eip712_signature/utils.rs | 2 +- ...43b7b722e0e467ad03978e9efe652c92a975.json} | 5 +- ...bcc9e940c555a8629afa0960d99ca177f220.json} | 5 +- core/lib/dal/src/consensus/mod.rs | 10 +- core/lib/dal/src/consensus_dal.rs | 61 ++-- core/lib/dal/src/models/tests.rs | 2 +- core/lib/dal/src/sync_dal.rs | 43 +-- core/lib/dal/src/tests/mod.rs | 6 +- core/lib/dal/src/transactions_web3_dal.rs | 62 +--- core/lib/mempool/src/tests.rs | 4 +- core/lib/merkle_tree/src/getters.rs | 8 +- core/lib/merkle_tree/src/hasher/proofs.rs | 18 +- .../tests/integration/merkle_tree.rs | 10 +- .../types/outputs/execution_result.rs | 7 +- .../src/versions/vm_1_3_2/test_utils.rs | 2 +- .../src/versions/vm_1_3_2/transaction_data.rs | 6 +- core/lib/multivm/src/versions/vm_1_3_2/vm.rs | 2 +- .../types/internals/transaction_data.rs | 9 +- .../types/internals/transaction_data.rs | 9 +- .../types/internals/transaction_data.rs | 9 +- .../src/versions/vm_latest/tests/block_tip.rs | 2 +- .../versions/vm_latest/tests/call_tracer.rs | 4 +- .../src/versions/vm_latest/tests/circuits.rs | 2 +- .../versions/vm_latest/tests/code_oracle.rs | 6 +- .../src/versions/vm_latest/tests/gas_limit.rs | 10 +- .../vm_latest/tests/get_used_contracts.rs | 4 +- .../vm_latest/tests/l1_tx_execution.rs | 2 +- .../src/versions/vm_latest/tests/l2_blocks.rs | 7 +- .../versions/vm_latest/tests/nonce_holder.rs | 2 +- .../versions/vm_latest/tests/precompiles.rs | 6 +- .../vm_latest/tests/prestate_tracer.rs | 4 +- .../vm_latest/tests/require_eip712.rs | 4 +- .../src/versions/vm_latest/tests/rollbacks.rs | 4 +- .../src/versions/vm_latest/tests/sekp256r1.rs | 2 +- .../src/versions/vm_latest/tests/storage.rs | 5 +- .../tests/tracing_execution_error.rs | 2 +- .../src/versions/vm_latest/tests/transfer.rs | 6 +- .../src/versions/vm_latest/tests/upgrade.rs | 4 +- .../types/internals/transaction_data.rs | 9 +- .../multivm/src/versions/vm_m5/test_utils.rs | 2 +- .../src/versions/vm_m5/transaction_data.rs | 4 +- .../multivm/src/versions/vm_m6/test_utils.rs | 2 +- .../src/versions/vm_m6/transaction_data.rs | 6 +- core/lib/multivm/src/versions/vm_m6/vm.rs | 2 +- .../types/internals/transaction_data.rs | 9 +- .../types/internals/transaction_data.rs | 9 +- core/lib/types/src/abi.rs | 1 + core/lib/types/src/l1/mod.rs | 4 +- core/lib/types/src/l2/mod.rs | 52 +-- core/lib/types/src/lib.rs | 22 +- core/lib/types/src/protocol_upgrade.rs | 6 +- core/lib/types/src/transaction_request.rs | 109 ++++--- core/lib/types/src/tx/execute.rs | 19 +- .../src/execution_sandbox/execute.rs | 6 +- core/node/api_server/src/tx_sender/mod.rs | 4 +- core/node/consensus/Cargo.toml | 8 - core/node/consensus/src/batch.rs | 275 ---------------- core/node/consensus/src/lib.rs | 4 - core/node/consensus/src/storage/mod.rs | 26 +- core/node/consensus/src/storage/testonly.rs | 23 -- core/node/consensus/src/testonly.rs | 299 ++---------------- core/node/consensus/src/tests.rs | 44 --- core/node/eth_watch/src/tests.rs | 5 +- core/node/metadata_calculator/Cargo.toml | 1 - .../metadata_calculator/src/api_server/mod.rs | 18 +- core/node/state_keeper/Cargo.toml | 6 +- .../state_keeper/src/batch_executor/mod.rs | 3 +- .../src/batch_executor/tests/tester.rs | 52 ++- core/node/state_keeper/src/testonly/mod.rs | 81 ----- .../src/updates/l2_block_updates.rs | 2 +- core/node/test_utils/src/lib.rs | 2 +- core/node/vm_runner/src/tests/mod.rs | 2 +- .../src/sdk/operations/deploy_contract.rs | 4 +- .../src/sdk/operations/execute_contract.rs | 4 +- .../loadnext/src/sdk/operations/transfer.rs | 4 +- core/tests/loadnext/src/sdk/signer.rs | 8 +- core/tests/test_account/src/lib.rs | 84 +++-- core/tests/vm-benchmark/harness/src/lib.rs | 2 +- prover/Cargo.lock | 23 -- 83 files changed, 439 insertions(+), 1188 deletions(-) rename core/lib/dal/.sqlx/{query-0f1856e55a370280a078d048f09e2d457914c737660b37e9f66b576bbc9a7904.json => query-a1829ef4532c8db6c1c907026e8643b7b722e0e467ad03978e9efe652c92a975.json} (95%) rename core/lib/dal/.sqlx/{query-778f92b1ac91e1ae279f588053d75a9ac877fdd28bda99661e423405e695223d.json => query-d0636ad46d8978f18292b3e66209bcc9e940c555a8629afa0960d99ca177f220.json} (95%) delete mode 100644 core/node/consensus/src/batch.rs diff --git a/Cargo.lock b/Cargo.lock index cfe47a2a4b1..ffea732c3be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8747,7 +8747,6 @@ dependencies = [ "tracing", "vise", "zksync_config", - "zksync_crypto", "zksync_dal", "zksync_health_check", "zksync_merkle_tree", @@ -8828,7 +8827,6 @@ dependencies = [ "async-trait", "rand 0.8.5", "secrecy", - "tempfile", "test-casing", "tokio", "tracing", @@ -8842,20 +8840,13 @@ dependencies = [ "zksync_consensus_storage", "zksync_consensus_utils", "zksync_dal", - "zksync_l1_contract_interface", - "zksync_merkle_tree", - "zksync_metadata_calculator", "zksync_node_api_server", "zksync_node_genesis", "zksync_node_sync", "zksync_node_test_utils", "zksync_protobuf", - "zksync_state", "zksync_state_keeper", - "zksync_system_constants", - "zksync_test_account", "zksync_types", - "zksync_utils", "zksync_web3_decl", ] diff --git a/core/bin/system-constants-generator/src/intrinsic_costs.rs b/core/bin/system-constants-generator/src/intrinsic_costs.rs index c94592defee..4f5e988e7b1 100644 --- a/core/bin/system-constants-generator/src/intrinsic_costs.rs +++ b/core/bin/system-constants-generator/src/intrinsic_costs.rs @@ -74,7 +74,7 @@ pub(crate) fn l2_gas_constants() -> IntrinsicSystemGasConstants { 0, Some(U256::zero()), None, - vec![], + None, ) .into(), ], @@ -99,7 +99,7 @@ pub(crate) fn l2_gas_constants() -> IntrinsicSystemGasConstants { 0, Some(U256::zero()), Some(vec![0u8; DELTA_IN_TX_SIZE]), - vec![], + None, ) .into()], true, @@ -117,7 +117,7 @@ pub(crate) fn l2_gas_constants() -> IntrinsicSystemGasConstants { 0, Some(U256::zero()), None, - vec![vec![0u8; 32]], + Some(vec![vec![0u8; 32]]), ) .into()], true, diff --git a/core/bin/system-constants-generator/src/utils.rs b/core/bin/system-constants-generator/src/utils.rs index 329ff77738c..d6f1ea85eff 100644 --- a/core/bin/system-constants-generator/src/utils.rs +++ b/core/bin/system-constants-generator/src/utils.rs @@ -99,7 +99,7 @@ pub(super) fn get_l2_tx( U256::from(0), L2ChainId::from(270), signer, - vec![], + None, Default::default(), ) .unwrap() @@ -128,7 +128,7 @@ pub(super) fn get_l1_tx( pubdata_price: u32, custom_gas_limit: Option, custom_calldata: Option>, - factory_deps: Vec>, + factory_deps: Option>>, ) -> L1Tx { L1Tx { execute: Execute { @@ -157,10 +157,10 @@ pub(super) fn get_l1_txs(number_of_txs: usize) -> (Vec, Vec StructMember for TypedStructure { } /// Interface for defining the structure for the EIP712 signature. -pub trait EIP712TypedStructure { +pub trait EIP712TypedStructure: Serialize { const TYPE_NAME: &'static str; fn build_structure(&self, builder: &mut BUILDER); diff --git a/core/lib/crypto_primitives/src/eip712_signature/utils.rs b/core/lib/crypto_primitives/src/eip712_signature/utils.rs index 526bb3b6b22..743d646ec58 100644 --- a/core/lib/crypto_primitives/src/eip712_signature/utils.rs +++ b/core/lib/crypto_primitives/src/eip712_signature/utils.rs @@ -4,7 +4,7 @@ use crate::eip712_signature::typed_structure::{EIP712TypedStructure, Eip712Domai /// Formats the data that needs to be signed in json according to the standard eip-712. /// Compatible with `eth_signTypedData` RPC call. -pub fn get_eip712_json( +pub fn get_eip712_json( eip712_domain: &Eip712Domain, typed_struct: &T, ) -> Value { diff --git a/core/lib/dal/.sqlx/query-0f1856e55a370280a078d048f09e2d457914c737660b37e9f66b576bbc9a7904.json b/core/lib/dal/.sqlx/query-a1829ef4532c8db6c1c907026e8643b7b722e0e467ad03978e9efe652c92a975.json similarity index 95% rename from core/lib/dal/.sqlx/query-0f1856e55a370280a078d048f09e2d457914c737660b37e9f66b576bbc9a7904.json rename to core/lib/dal/.sqlx/query-a1829ef4532c8db6c1c907026e8643b7b722e0e467ad03978e9efe652c92a975.json index 498e839a63d..605b6c1f025 100644 --- a/core/lib/dal/.sqlx/query-0f1856e55a370280a078d048f09e2d457914c737660b37e9f66b576bbc9a7904.json +++ b/core/lib/dal/.sqlx/query-a1829ef4532c8db6c1c907026e8643b7b722e0e467ad03978e9efe652c92a975.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n transactions.*\n FROM\n transactions\n INNER JOIN miniblocks ON miniblocks.number = transactions.miniblock_number\n WHERE\n miniblocks.number BETWEEN $1 AND $2\n ORDER BY\n miniblock_number,\n index_in_block\n ", + "query": "\n SELECT\n transactions.*\n FROM\n transactions\n INNER JOIN miniblocks ON miniblocks.number = transactions.miniblock_number\n WHERE\n miniblocks.number = $1\n ORDER BY\n index_in_block\n ", "describe": { "columns": [ { @@ -186,7 +186,6 @@ ], "parameters": { "Left": [ - "Int8", "Int8" ] }, @@ -229,5 +228,5 @@ true ] }, - "hash": "0f1856e55a370280a078d048f09e2d457914c737660b37e9f66b576bbc9a7904" + "hash": "a1829ef4532c8db6c1c907026e8643b7b722e0e467ad03978e9efe652c92a975" } diff --git a/core/lib/dal/.sqlx/query-778f92b1ac91e1ae279f588053d75a9ac877fdd28bda99661e423405e695223d.json b/core/lib/dal/.sqlx/query-d0636ad46d8978f18292b3e66209bcc9e940c555a8629afa0960d99ca177f220.json similarity index 95% rename from core/lib/dal/.sqlx/query-778f92b1ac91e1ae279f588053d75a9ac877fdd28bda99661e423405e695223d.json rename to core/lib/dal/.sqlx/query-d0636ad46d8978f18292b3e66209bcc9e940c555a8629afa0960d99ca177f220.json index aa7d4c65a39..c9f08e92810 100644 --- a/core/lib/dal/.sqlx/query-778f92b1ac91e1ae279f588053d75a9ac877fdd28bda99661e423405e695223d.json +++ b/core/lib/dal/.sqlx/query-d0636ad46d8978f18292b3e66209bcc9e940c555a8629afa0960d99ca177f220.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n miniblocks.number,\n COALESCE(\n miniblocks.l1_batch_number,\n (\n SELECT\n (MAX(number) + 1)\n FROM\n l1_batches\n ),\n (\n SELECT\n MAX(l1_batch_number) + 1\n FROM\n snapshot_recovery\n )\n ) AS \"l1_batch_number!\",\n (miniblocks.l1_tx_count + miniblocks.l2_tx_count) AS \"tx_count!\",\n miniblocks.timestamp,\n miniblocks.l1_gas_price,\n miniblocks.l2_fair_gas_price,\n miniblocks.fair_pubdata_price,\n miniblocks.bootloader_code_hash,\n miniblocks.default_aa_code_hash,\n miniblocks.virtual_blocks,\n miniblocks.hash,\n miniblocks.protocol_version AS \"protocol_version!\",\n miniblocks.fee_account_address AS \"fee_account_address!\"\n FROM\n miniblocks\n WHERE\n miniblocks.number BETWEEN $1 AND $2\n ", + "query": "\n SELECT\n miniblocks.number,\n COALESCE(\n miniblocks.l1_batch_number,\n (\n SELECT\n (MAX(number) + 1)\n FROM\n l1_batches\n ),\n (\n SELECT\n MAX(l1_batch_number) + 1\n FROM\n snapshot_recovery\n )\n ) AS \"l1_batch_number!\",\n (miniblocks.l1_tx_count + miniblocks.l2_tx_count) AS \"tx_count!\",\n miniblocks.timestamp,\n miniblocks.l1_gas_price,\n miniblocks.l2_fair_gas_price,\n miniblocks.fair_pubdata_price,\n miniblocks.bootloader_code_hash,\n miniblocks.default_aa_code_hash,\n miniblocks.virtual_blocks,\n miniblocks.hash,\n miniblocks.protocol_version AS \"protocol_version!\",\n miniblocks.fee_account_address AS \"fee_account_address!\"\n FROM\n miniblocks\n WHERE\n miniblocks.number = $1\n ", "describe": { "columns": [ { @@ -71,7 +71,6 @@ ], "parameters": { "Left": [ - "Int8", "Int8" ] }, @@ -91,5 +90,5 @@ false ] }, - "hash": "778f92b1ac91e1ae279f588053d75a9ac877fdd28bda99661e423405e695223d" + "hash": "d0636ad46d8978f18292b3e66209bcc9e940c555a8629afa0960d99ca177f220" } diff --git a/core/lib/dal/src/consensus/mod.rs b/core/lib/dal/src/consensus/mod.rs index 8e1f246b657..f7a3b066624 100644 --- a/core/lib/dal/src/consensus/mod.rs +++ b/core/lib/dal/src/consensus/mod.rs @@ -277,7 +277,10 @@ impl ProtoRepr for proto::Transaction { .and_then(|x| parse_h256(x)) .map(h256_to_u256) .context("execute.value")?, - factory_deps: execute.factory_deps.clone(), + factory_deps: match execute.factory_deps.is_empty() { + true => None, + false => Some(execute.factory_deps.clone()), + }, }, received_timestamp_ms: 0, // This timestamp is local to the node raw_bytes: self.raw_bytes.as_ref().map(|x| x.clone().into()), @@ -358,7 +361,10 @@ impl ProtoRepr for proto::Transaction { contract_address: Some(this.execute.contract_address.as_bytes().into()), calldata: Some(this.execute.calldata.clone()), value: Some(u256_to_h256(this.execute.value).as_bytes().into()), - factory_deps: this.execute.factory_deps.clone(), + factory_deps: match &this.execute.factory_deps { + Some(inner) => inner.clone(), + None => vec![], + }, }; Self { common_data: Some(common_data), diff --git a/core/lib/dal/src/consensus_dal.rs b/core/lib/dal/src/consensus_dal.rs index f2742cbedd8..041bd5c39a8 100644 --- a/core/lib/dal/src/consensus_dal.rs +++ b/core/lib/dal/src/consensus_dal.rs @@ -279,54 +279,33 @@ impl ConsensusDal<'_, '_> { .await } - /// Fetches a range of L2 blocks from storage and converts them to `Payload`s. - pub async fn block_payloads( + /// Converts the L2 block `block_number` into consensus payload. `Payload` is an + /// opaque format for the L2 block that consensus understands and generates a + /// certificate for it. + pub async fn block_payload( &mut self, - numbers: std::ops::Range, - ) -> DalResult> { - let numbers = (|| { - anyhow::Ok(std::ops::Range { - start: L2BlockNumber(numbers.start.0.try_into().context("start")?), - end: L2BlockNumber(numbers.end.0.try_into().context("end")?), - }) - })() - .map_err(|err| { - Instrumented::new("block_payloads") - .with_arg("numbers", &numbers) - .arg_error("numbers", err) - })?; + block_number: validator::BlockNumber, + ) -> DalResult> { + let instrumentation = + Instrumented::new("block_payload").with_arg("block_number", &block_number); + let block_number = u32::try_from(block_number.0) + .map_err(|err| instrumentation.arg_error("block_number", err))?; + let block_number = L2BlockNumber(block_number); - let blocks = self + let Some(block) = self .storage .sync_dal() - .sync_blocks_inner(numbers.clone()) - .await?; - let mut transactions = self + .sync_block_inner(block_number) + .await? + else { + return Ok(None); + }; + let transactions = self .storage .transactions_web3_dal() - .get_raw_l2_blocks_transactions(numbers) + .get_raw_l2_block_transactions(block_number) .await?; - Ok(blocks - .into_iter() - .map(|b| { - let txs = transactions.remove(&b.number).unwrap_or_default(); - b.into_payload(txs) - }) - .collect()) - } - - /// Fetches an L2 block from storage and converts it to `Payload`. `Payload` is an - /// opaque format for the L2 block that consensus understands and generates a - /// certificate for it. - pub async fn block_payload( - &mut self, - number: validator::BlockNumber, - ) -> DalResult> { - Ok(self - .block_payloads(number..number + 1) - .await? - .into_iter() - .next()) + Ok(Some(block.into_payload(transactions))) } /// Inserts a certificate for the L2 block `cert.header().number`. It verifies that diff --git a/core/lib/dal/src/models/tests.rs b/core/lib/dal/src/models/tests.rs index 34cfde108f1..373fbf3a7b4 100644 --- a/core/lib/dal/src/models/tests.rs +++ b/core/lib/dal/src/models/tests.rs @@ -20,7 +20,7 @@ fn default_execute() -> Execute { 8cdfd0000000000000000000000000000000000000000000000000000000157d600d0", ) .unwrap(), - factory_deps: vec![], + factory_deps: None, } } diff --git a/core/lib/dal/src/sync_dal.rs b/core/lib/dal/src/sync_dal.rs index 898770c38f5..1296cb6e24a 100644 --- a/core/lib/dal/src/sync_dal.rs +++ b/core/lib/dal/src/sync_dal.rs @@ -15,15 +15,11 @@ pub struct SyncDal<'a, 'c> { } impl SyncDal<'_, '_> { - pub(super) async fn sync_blocks_inner( + pub(super) async fn sync_block_inner( &mut self, - numbers: std::ops::Range, - ) -> DalResult> { - // Check if range is non-empty, because BETWEEN in SQL in `unordered`. - if numbers.is_empty() { - return Ok(vec![]); - } - let blocks = sqlx::query_as!( + block_number: L2BlockNumber, + ) -> DalResult> { + let block = sqlx::query_as!( StorageSyncBlock, r#" SELECT @@ -57,44 +53,35 @@ impl SyncDal<'_, '_> { FROM miniblocks WHERE - miniblocks.number BETWEEN $1 AND $2 + miniblocks.number = $1 "#, - i64::from(numbers.start.0), - i64::from(numbers.end.0 - 1), + i64::from(block_number.0) ) .try_map(SyncBlock::try_from) - .instrument("sync_dal_sync_blocks.block") - .with_arg("numbers", &numbers) - .fetch_all(self.storage) + .instrument("sync_dal_sync_block.block") + .with_arg("block_number", &block_number) + .fetch_optional(self.storage) .await?; - Ok(blocks) + Ok(block) } pub async fn sync_block( &mut self, - number: L2BlockNumber, + block_number: L2BlockNumber, include_transactions: bool, ) -> DalResult> { let _latency = MethodLatency::new("sync_dal_sync_block"); - let numbers = number..number + 1; - let Some(block) = self - .sync_blocks_inner(numbers.clone()) - .await? - .into_iter() - .next() - else { + let Some(block) = self.sync_block_inner(block_number).await? else { return Ok(None); }; let transactions = if include_transactions { - let mut transactions = self + let transactions = self .storage .transactions_web3_dal() - .get_raw_l2_blocks_transactions(numbers) + .get_raw_l2_block_transactions(block_number) .await?; - // If there are no transactions in the block, - // return `Some(vec![])`. - Some(transactions.remove(&number).unwrap_or_default()) + Some(transactions) } else { None }; diff --git a/core/lib/dal/src/tests/mod.rs b/core/lib/dal/src/tests/mod.rs index c4dab124655..500da25ace8 100644 --- a/core/lib/dal/src/tests/mod.rs +++ b/core/lib/dal/src/tests/mod.rs @@ -66,7 +66,7 @@ pub(crate) fn mock_l2_transaction() -> L2Tx { Default::default(), L2ChainId::from(270), &K256PrivateKey::random(), - vec![], + None, Default::default(), ) .unwrap(); @@ -98,7 +98,7 @@ pub(crate) fn mock_l1_execute() -> L1Tx { contract_address: H160::random(), value: Default::default(), calldata: vec![], - factory_deps: vec![], + factory_deps: None, }; L1Tx { @@ -126,7 +126,7 @@ pub(crate) fn mock_protocol_upgrade_transaction() -> ProtocolUpgradeTx { contract_address: H160::random(), value: Default::default(), calldata: vec![], - factory_deps: vec![], + factory_deps: None, }; ProtocolUpgradeTx { diff --git a/core/lib/dal/src/transactions_web3_dal.rs b/core/lib/dal/src/transactions_web3_dal.rs index 2d380a8059a..b7cbf16c89c 100644 --- a/core/lib/dal/src/transactions_web3_dal.rs +++ b/core/lib/dal/src/transactions_web3_dal.rs @@ -1,12 +1,7 @@ -use std::collections::HashMap; - -use anyhow::Context as _; use sqlx::types::chrono::NaiveDateTime; use zksync_db_connection::{ - connection::Connection, - error::{DalResult, SqlxContext as _}, - instrument::InstrumentExt, - interpolate_query, match_query_as, + connection::Connection, error::DalResult, instrument::InstrumentExt, interpolate_query, + match_query_as, }; use zksync_types::{ api, api::TransactionReceipt, event::DEPLOY_EVENT_SIGNATURE, Address, L2BlockNumber, L2ChainId, @@ -384,17 +379,12 @@ impl TransactionsWeb3Dal<'_, '_> { Ok(U256::from(pending_nonce)) } - /// Returns the server transactions (not API ones) from a L2 block range. - pub async fn get_raw_l2_blocks_transactions( + /// Returns the server transactions (not API ones) from a certain L2 block. + /// Returns an empty list if the L2 block doesn't exist. + pub async fn get_raw_l2_block_transactions( &mut self, - blocks: std::ops::Range, - ) -> DalResult>> { - // Check if range is non-empty, because BETWEEN in SQL in `unordered`. - if blocks.is_empty() { - return Ok(HashMap::default()); - } - // We do an inner join with `miniblocks.number`, because - // transaction insertions are not atomic with miniblock insertion. + l2_block: L2BlockNumber, + ) -> DalResult> { let rows = sqlx::query_as!( StorageTransaction, r#" @@ -404,46 +394,18 @@ impl TransactionsWeb3Dal<'_, '_> { transactions INNER JOIN miniblocks ON miniblocks.number = transactions.miniblock_number WHERE - miniblocks.number BETWEEN $1 AND $2 + miniblocks.number = $1 ORDER BY - miniblock_number, index_in_block "#, - i64::from(blocks.start.0), - i64::from(blocks.end.0 - 1), + i64::from(l2_block.0) ) - .try_map(|row| { - let to_block_number = |n: Option| { - anyhow::Ok(L2BlockNumber( - n.context("missing")?.try_into().context("overflow")?, - )) - }; - Ok(( - to_block_number(row.miniblock_number).decode_column("miniblock_number")?, - Transaction::from(row), - )) - }) - .instrument("get_raw_l2_blocks_transactions") - .with_arg("blocks", &blocks) + .instrument("get_raw_l2_block_transactions") + .with_arg("l2_block", &l2_block) .fetch_all(self.storage) .await?; - let mut txs: HashMap> = HashMap::new(); - for (n, tx) in rows { - txs.entry(n).or_default().push(tx); - } - Ok(txs) - } - /// Returns the server transactions (not API ones) from an L2 block. - pub async fn get_raw_l2_block_transactions( - &mut self, - block: L2BlockNumber, - ) -> DalResult> { - Ok(self - .get_raw_l2_blocks_transactions(block..block + 1) - .await? - .remove(&block) - .unwrap_or_default()) + Ok(rows.into_iter().map(Into::into).collect()) } } diff --git a/core/lib/mempool/src/tests.rs b/core/lib/mempool/src/tests.rs index 6ea1be3b514..a8c7128baa9 100644 --- a/core/lib/mempool/src/tests.rs +++ b/core/lib/mempool/src/tests.rs @@ -377,7 +377,7 @@ fn gen_l2_tx_with_timestamp(address: Address, nonce: Nonce, received_at_ms: u64) Fee::default(), address, U256::zero(), - vec![], + None, Default::default(), ); txn.received_timestamp_ms = received_at_ms; @@ -388,7 +388,7 @@ fn gen_l1_tx(priority_id: PriorityOpId) -> Transaction { let execute = Execute { contract_address: Address::repeat_byte(0x11), calldata: vec![1, 2, 3], - factory_deps: vec![], + factory_deps: None, value: U256::zero(), }; let op_data = L1TxCommonData { diff --git a/core/lib/merkle_tree/src/getters.rs b/core/lib/merkle_tree/src/getters.rs index 34978f5dc6a..c20c182adef 100644 --- a/core/lib/merkle_tree/src/getters.rs +++ b/core/lib/merkle_tree/src/getters.rs @@ -131,9 +131,7 @@ mod tests { let entries = tree.entries_with_proofs(0, &[missing_key]).unwrap(); assert_eq!(entries.len(), 1); assert!(entries[0].base.is_empty()); - entries[0] - .verify(&tree.hasher, tree.hasher.empty_tree_hash()) - .unwrap(); + entries[0].verify(&tree.hasher, tree.hasher.empty_tree_hash()); } #[test] @@ -153,8 +151,8 @@ mod tests { let entries = tree.entries_with_proofs(0, &[key, missing_key]).unwrap(); assert_eq!(entries.len(), 2); assert!(!entries[0].base.is_empty()); - entries[0].verify(&tree.hasher, output.root_hash).unwrap(); + entries[0].verify(&tree.hasher, output.root_hash); assert!(entries[1].base.is_empty()); - entries[1].verify(&tree.hasher, output.root_hash).unwrap(); + entries[1].verify(&tree.hasher, output.root_hash); } } diff --git a/core/lib/merkle_tree/src/hasher/proofs.rs b/core/lib/merkle_tree/src/hasher/proofs.rs index 9af732af489..3e61c9e1d86 100644 --- a/core/lib/merkle_tree/src/hasher/proofs.rs +++ b/core/lib/merkle_tree/src/hasher/proofs.rs @@ -81,26 +81,18 @@ impl BlockOutputWithProofs { impl TreeEntryWithProof { /// Verifies this proof. /// - /// # Errors + /// # Panics /// - /// Returns an error <=> proof is invalid. - pub fn verify( - &self, - hasher: &dyn HashTree, - trusted_root_hash: ValueHash, - ) -> anyhow::Result<()> { + /// Panics if the proof doesn't verify. + pub fn verify(&self, hasher: &dyn HashTree, trusted_root_hash: ValueHash) { if self.base.leaf_index == 0 { - ensure!( + assert!( self.base.value.is_zero(), "Invalid missing value specification: leaf index is zero, but value is non-default" ); } let root_hash = hasher.fold_merkle_path(&self.merkle_path, self.base); - ensure!( - root_hash == trusted_root_hash, - "Root hash mismatch: got {root_hash}, want {trusted_root_hash}" - ); - Ok(()) + assert_eq!(root_hash, trusted_root_hash, "Root hash mismatch"); } } diff --git a/core/lib/merkle_tree/tests/integration/merkle_tree.rs b/core/lib/merkle_tree/tests/integration/merkle_tree.rs index a83b982cc49..f778862720d 100644 --- a/core/lib/merkle_tree/tests/integration/merkle_tree.rs +++ b/core/lib/merkle_tree/tests/integration/merkle_tree.rs @@ -86,7 +86,7 @@ fn entry_proofs_are_computed_correctly_on_empty_tree(kv_count: u64) { let entries = tree.entries_with_proofs(0, &existing_keys).unwrap(); assert_eq!(entries.len(), existing_keys.len()); for (input_entry, entry) in kvs.iter().zip(entries) { - entry.verify(&Blake2Hasher, expected_hash).unwrap(); + entry.verify(&Blake2Hasher, expected_hash); assert_eq!(entry.base, *input_entry); } @@ -110,7 +110,7 @@ fn entry_proofs_are_computed_correctly_on_empty_tree(kv_count: u64) { for (key, entry) in missing_keys.iter().zip(entries) { assert!(entry.base.is_empty()); assert_eq!(entry.base.key, *key); - entry.verify(&Blake2Hasher, expected_hash).unwrap(); + entry.verify(&Blake2Hasher, expected_hash); } } @@ -228,7 +228,7 @@ fn entry_proofs_are_computed_correctly_with_intermediate_commits(chunk_size: usi for (i, (key, entry)) in all_keys.iter().zip(entries).enumerate() { assert_eq!(entry.base.key, *key); assert_eq!(entry.base.is_empty(), i >= (version + 1) * chunk_size); - entry.verify(&Blake2Hasher, output.root_hash).unwrap(); + entry.verify(&Blake2Hasher, output.root_hash); } } @@ -239,7 +239,7 @@ fn entry_proofs_are_computed_correctly_with_intermediate_commits(chunk_size: usi for (i, (key, entry)) in all_keys.iter().zip(entries).enumerate() { assert_eq!(entry.base.key, *key); assert_eq!(entry.base.is_empty(), i >= (version + 1) * chunk_size); - entry.verify(&Blake2Hasher, root_hash).unwrap(); + entry.verify(&Blake2Hasher, root_hash); } } } @@ -415,7 +415,7 @@ fn proofs_are_computed_correctly_with_key_updates(updated_keys: usize) { let proofs = tree.entries_with_proofs(1, &keys).unwrap(); for (entry, proof) in kvs.iter().zip(proofs) { assert_eq!(proof.base, *entry); - proof.verify(&Blake2Hasher, *expected_hash).unwrap(); + proof.verify(&Blake2Hasher, *expected_hash); } } diff --git a/core/lib/multivm/src/interface/types/outputs/execution_result.rs b/core/lib/multivm/src/interface/types/outputs/execution_result.rs index faa702f411b..3ce7d31f212 100644 --- a/core/lib/multivm/src/interface/types/outputs/execution_result.rs +++ b/core/lib/multivm/src/interface/types/outputs/execution_result.rs @@ -64,7 +64,12 @@ impl ExecutionResult { impl VmExecutionResultAndLogs { pub fn get_execution_metrics(&self, tx: Option<&Transaction>) -> ExecutionMetrics { let contracts_deployed = tx - .map(|tx| tx.execute.factory_deps.len() as u16) + .map(|tx| { + tx.execute + .factory_deps + .as_ref() + .map_or(0, |deps| deps.len() as u16) + }) .unwrap_or(0); // We published the data as ABI-encoded `bytes`, so the total length is: diff --git a/core/lib/multivm/src/versions/vm_1_3_2/test_utils.rs b/core/lib/multivm/src/versions/vm_1_3_2/test_utils.rs index 603725790f8..375a8bdb7ad 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/test_utils.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/test_utils.rs @@ -155,7 +155,7 @@ pub fn get_create_execute(code: &[u8], calldata: &[u8]) -> Execute { Execute { contract_address: CONTRACT_DEPLOYER_ADDRESS, calldata, - factory_deps: vec![code.to_vec()], + factory_deps: Some(vec![code.to_vec()]), value: U256::zero(), } } diff --git a/core/lib/multivm/src/versions/vm_1_3_2/transaction_data.rs b/core/lib/multivm/src/versions/vm_1_3_2/transaction_data.rs index 788a52206e8..896af8d84f4 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/transaction_data.rs @@ -89,7 +89,7 @@ impl From for TransactionData { ], data: execute_tx.execute.calldata, signature: common_data.signature, - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: common_data.paymaster_params.paymaster_input, reserved_dynamic: vec![], } @@ -118,7 +118,7 @@ impl From for TransactionData { data: execute_tx.execute.calldata, // The signature isn't checked for L1 transactions so we don't care signature: vec![], - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: vec![], reserved_dynamic: vec![], } @@ -147,7 +147,7 @@ impl From for TransactionData { data: execute_tx.execute.calldata, // The signature isn't checked for L1 transactions so we don't care signature: vec![], - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: vec![], reserved_dynamic: vec![], } diff --git a/core/lib/multivm/src/versions/vm_1_3_2/vm.rs b/core/lib/multivm/src/versions/vm_1_3_2/vm.rs index 36ba32a8120..d76704f892b 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/vm.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/vm.rs @@ -196,7 +196,7 @@ impl VmInterface for Vm { } self.last_tx_compressed_bytecodes = vec![]; let bytecodes = if with_compression { - let deps = &tx.execute.factory_deps; + let deps = tx.execute.factory_deps.as_deref().unwrap_or_default(); let mut deps_hashes = HashSet::with_capacity(deps.len()); let mut bytecode_hashes = vec![]; let filtered_deps = deps.iter().filter_map(|bytecode| { diff --git a/core/lib/multivm/src/versions/vm_1_4_1/types/internals/transaction_data.rs b/core/lib/multivm/src/versions/vm_1_4_1/types/internals/transaction_data.rs index 1379b853a54..61c14156dfb 100644 --- a/core/lib/multivm/src/versions/vm_1_4_1/types/internals/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_1_4_1/types/internals/transaction_data.rs @@ -91,7 +91,7 @@ impl From for TransactionData { ], data: execute_tx.execute.calldata, signature: common_data.signature, - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: common_data.paymaster_params.paymaster_input, reserved_dynamic: vec![], raw_bytes: execute_tx.raw_bytes.map(|a| a.0), @@ -121,7 +121,7 @@ impl From for TransactionData { data: execute_tx.execute.calldata, // The signature isn't checked for L1 transactions so we don't care signature: vec![], - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: vec![], reserved_dynamic: vec![], raw_bytes: None, @@ -151,7 +151,7 @@ impl From for TransactionData { data: execute_tx.execute.calldata, // The signature isn't checked for L1 transactions so we don't care signature: vec![], - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: vec![], reserved_dynamic: vec![], raw_bytes: None, @@ -284,11 +284,12 @@ impl TryInto for TransactionData { paymaster_input: self.paymaster_input, }, }; + let factory_deps = (!self.factory_deps.is_empty()).then_some(self.factory_deps); let execute = Execute { contract_address: self.to, value: self.value, calldata: self.data, - factory_deps: self.factory_deps, + factory_deps, }; Ok(L2Tx { diff --git a/core/lib/multivm/src/versions/vm_1_4_2/types/internals/transaction_data.rs b/core/lib/multivm/src/versions/vm_1_4_2/types/internals/transaction_data.rs index 3498e51ec30..a201df01af6 100644 --- a/core/lib/multivm/src/versions/vm_1_4_2/types/internals/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_1_4_2/types/internals/transaction_data.rs @@ -91,7 +91,7 @@ impl From for TransactionData { ], data: execute_tx.execute.calldata, signature: common_data.signature, - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: common_data.paymaster_params.paymaster_input, reserved_dynamic: vec![], raw_bytes: execute_tx.raw_bytes.map(|a| a.0), @@ -121,7 +121,7 @@ impl From for TransactionData { data: execute_tx.execute.calldata, // The signature isn't checked for L1 transactions so we don't care signature: vec![], - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: vec![], reserved_dynamic: vec![], raw_bytes: None, @@ -151,7 +151,7 @@ impl From for TransactionData { data: execute_tx.execute.calldata, // The signature isn't checked for L1 transactions so we don't care signature: vec![], - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: vec![], reserved_dynamic: vec![], raw_bytes: None, @@ -284,11 +284,12 @@ impl TryInto for TransactionData { paymaster_input: self.paymaster_input, }, }; + let factory_deps = (!self.factory_deps.is_empty()).then_some(self.factory_deps); let execute = Execute { contract_address: self.to, value: self.value, calldata: self.data, - factory_deps: self.factory_deps, + factory_deps, }; Ok(L2Tx { diff --git a/core/lib/multivm/src/versions/vm_boojum_integration/types/internals/transaction_data.rs b/core/lib/multivm/src/versions/vm_boojum_integration/types/internals/transaction_data.rs index ad740a279dc..8cc4e256740 100644 --- a/core/lib/multivm/src/versions/vm_boojum_integration/types/internals/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_boojum_integration/types/internals/transaction_data.rs @@ -91,7 +91,7 @@ impl From for TransactionData { ], data: execute_tx.execute.calldata, signature: common_data.signature, - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: common_data.paymaster_params.paymaster_input, reserved_dynamic: vec![], raw_bytes: execute_tx.raw_bytes.map(|a| a.0), @@ -121,7 +121,7 @@ impl From for TransactionData { data: execute_tx.execute.calldata, // The signature isn't checked for L1 transactions so we don't care signature: vec![], - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: vec![], reserved_dynamic: vec![], raw_bytes: None, @@ -151,7 +151,7 @@ impl From for TransactionData { data: execute_tx.execute.calldata, // The signature isn't checked for L1 transactions so we don't care signature: vec![], - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: vec![], reserved_dynamic: vec![], raw_bytes: None, @@ -298,11 +298,12 @@ impl TryInto for TransactionData { paymaster_input: self.paymaster_input, }, }; + let factory_deps = (!self.factory_deps.is_empty()).then_some(self.factory_deps); let execute = Execute { contract_address: self.to, value: self.value, calldata: self.data, - factory_deps: self.factory_deps, + factory_deps, }; Ok(L2Tx { diff --git a/core/lib/multivm/src/versions/vm_latest/tests/block_tip.rs b/core/lib/multivm/src/versions/vm_latest/tests/block_tip.rs index 78136602dae..bf1acb981f3 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/block_tip.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/block_tip.rs @@ -167,7 +167,7 @@ fn execute_test(test_data: L1MessengerTestData) -> TestStatistics { contract_address: CONTRACT_FORCE_DEPLOYER_ADDRESS, calldata: data, value: U256::zero(), - factory_deps: vec![], + factory_deps: None, }, None, ); diff --git a/core/lib/multivm/src/versions/vm_latest/tests/call_tracer.rs b/core/lib/multivm/src/versions/vm_latest/tests/call_tracer.rs index a4d0eb2d17e..c97b38b6afc 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/call_tracer.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/call_tracer.rs @@ -37,7 +37,7 @@ fn test_max_depth() { contract_address: address, calldata: vec![], value: Default::default(), - factory_deps: vec![], + factory_deps: None, }, None, ); @@ -72,7 +72,7 @@ fn test_basic_behavior() { contract_address: address, calldata: hex::decode(increment_by_6_calldata).unwrap(), value: Default::default(), - factory_deps: vec![], + factory_deps: None, }, None, ); diff --git a/core/lib/multivm/src/versions/vm_latest/tests/circuits.rs b/core/lib/multivm/src/versions/vm_latest/tests/circuits.rs index 02ec2dc58aa..c582bd28c88 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/circuits.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/circuits.rs @@ -25,7 +25,7 @@ fn test_circuits() { contract_address: Address::random(), calldata: Vec::new(), value: U256::from(1u8), - factory_deps: vec![], + factory_deps: None, }, None, ); diff --git a/core/lib/multivm/src/versions/vm_latest/tests/code_oracle.rs b/core/lib/multivm/src/versions/vm_latest/tests/code_oracle.rs index 8c8c6e2d097..feb60f93a23 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/code_oracle.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/code_oracle.rs @@ -72,7 +72,7 @@ fn test_code_oracle() { ]) .unwrap(), value: U256::zero(), - factory_deps: vec![], + factory_deps: None, }, None, ); @@ -93,7 +93,7 @@ fn test_code_oracle() { ]) .unwrap(), value: U256::zero(), - factory_deps: vec![], + factory_deps: None, }, None, ); @@ -155,7 +155,7 @@ fn test_code_oracle_big_bytecode() { ]) .unwrap(), value: U256::zero(), - factory_deps: vec![], + factory_deps: None, }, None, ); diff --git a/core/lib/multivm/src/versions/vm_latest/tests/gas_limit.rs b/core/lib/multivm/src/versions/vm_latest/tests/gas_limit.rs index 34e1e2d25f3..533d9ec660e 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/gas_limit.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/gas_limit.rs @@ -1,4 +1,3 @@ -use zksync_test_account::Account; use zksync_types::{fee::Fee, Execute}; use crate::{ @@ -21,10 +20,15 @@ fn test_tx_gas_limit_offset() { let gas_limit = 9999.into(); let tx = vm.rich_accounts[0].get_l2_tx_for_execute( - Execute::default(), + Execute { + contract_address: Default::default(), + calldata: vec![], + value: Default::default(), + factory_deps: None, + }, Some(Fee { gas_limit, - ..Account::default_fee() + ..Default::default() }), ); diff --git a/core/lib/multivm/src/versions/vm_latest/tests/get_used_contracts.rs b/core/lib/multivm/src/versions/vm_latest/tests/get_used_contracts.rs index 7bc08b6fb49..38a4d7cbb43 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/get_used_contracts.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/get_used_contracts.rs @@ -70,7 +70,7 @@ fn test_get_used_contracts() { contract_address: CONTRACT_DEPLOYER_ADDRESS, calldata: big_calldata, value: Default::default(), - factory_deps: vec![vec![1; 32]], + factory_deps: Some(vec![vec![1; 32]]), }, 1, ); @@ -81,7 +81,7 @@ fn test_get_used_contracts() { assert!(res2.result.is_failed()); - for factory_dep in tx2.execute.factory_deps { + for factory_dep in tx2.execute.factory_deps.unwrap() { let hash = hash_bytecode(&factory_dep); let hash_to_u256 = h256_to_u256(hash); assert!(known_bytecodes_without_aa_code(&vm.vm) diff --git a/core/lib/multivm/src/versions/vm_latest/tests/l1_tx_execution.rs b/core/lib/multivm/src/versions/vm_latest/tests/l1_tx_execution.rs index 5a87ce59be2..2144ad9812d 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/l1_tx_execution.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/l1_tx_execution.rs @@ -172,7 +172,7 @@ fn test_l1_tx_execution_high_gas_limit() { Execute { contract_address: L1_MESSENGER_ADDRESS, value: 0.into(), - factory_deps: vec![], + factory_deps: None, calldata, }, 0, diff --git a/core/lib/multivm/src/versions/vm_latest/tests/l2_blocks.rs b/core/lib/multivm/src/versions/vm_latest/tests/l2_blocks.rs index e62786bb55e..59b161019f7 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/l2_blocks.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/l2_blocks.rs @@ -37,7 +37,12 @@ fn get_l1_noop() -> Transaction { gas_per_pubdata_limit: REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_BYTE.into(), ..Default::default() }), - execute: Execute::default(), + execute: Execute { + contract_address: H160::zero(), + calldata: vec![], + value: U256::zero(), + factory_deps: None, + }, received_timestamp_ms: 0, raw_bytes: None, } diff --git a/core/lib/multivm/src/versions/vm_latest/tests/nonce_holder.rs b/core/lib/multivm/src/versions/vm_latest/tests/nonce_holder.rs index 076ecb52361..309e26120af 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/nonce_holder.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/nonce_holder.rs @@ -67,7 +67,7 @@ fn test_nonce_holder() { contract_address: account.address, calldata: vec![12], value: Default::default(), - factory_deps: vec![], + factory_deps: None, }, None, Nonce(nonce), diff --git a/core/lib/multivm/src/versions/vm_latest/tests/precompiles.rs b/core/lib/multivm/src/versions/vm_latest/tests/precompiles.rs index 2ab40faf22c..652f9c0c03f 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/precompiles.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/precompiles.rs @@ -34,7 +34,7 @@ fn test_keccak() { contract_address: address, calldata: hex::decode(keccak1000_calldata).unwrap(), value: Default::default(), - factory_deps: vec![], + factory_deps: None, }, None, ); @@ -78,7 +78,7 @@ fn test_sha256() { contract_address: address, calldata: hex::decode(sha1000_calldata).unwrap(), value: Default::default(), - factory_deps: vec![], + factory_deps: None, }, None, ); @@ -115,7 +115,7 @@ fn test_ecrecover() { contract_address: account.address, calldata: Vec::new(), value: Default::default(), - factory_deps: vec![], + factory_deps: None, }, None, ); diff --git a/core/lib/multivm/src/versions/vm_latest/tests/prestate_tracer.rs b/core/lib/multivm/src/versions/vm_latest/tests/prestate_tracer.rs index 893ca57bc4d..63620c7d9ff 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/prestate_tracer.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/prestate_tracer.rs @@ -91,7 +91,7 @@ fn test_prestate_tracer_diff_mode() { contract_address: vm.test_contract.unwrap(), calldata: Default::default(), value: U256::from(100000), - factory_deps: vec![], + factory_deps: None, }; vm.vm @@ -101,7 +101,7 @@ fn test_prestate_tracer_diff_mode() { contract_address: deployed_address2, calldata: Default::default(), value: U256::from(200000), - factory_deps: vec![], + factory_deps: None, }; vm.vm diff --git a/core/lib/multivm/src/versions/vm_latest/tests/require_eip712.rs b/core/lib/multivm/src/versions/vm_latest/tests/require_eip712.rs index 5178c5dc29c..f4d6051272e 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/require_eip712.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/require_eip712.rs @@ -66,7 +66,7 @@ async fn test_require_eip712() { contract_address: account_abstraction.address, calldata: encoded_input, value: Default::default(), - factory_deps: vec![], + factory_deps: None, }, None, ); @@ -131,7 +131,7 @@ async fn test_require_eip712() { }, account_abstraction.address, U256::from(28374938), - vec![], + None, Default::default(), ); diff --git a/core/lib/multivm/src/versions/vm_latest/tests/rollbacks.rs b/core/lib/multivm/src/versions/vm_latest/tests/rollbacks.rs index e0c3ec4157d..436981dd158 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/rollbacks.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/rollbacks.rs @@ -103,7 +103,7 @@ fn test_vm_loadnext_rollbacks() { } .to_bytes(), value: Default::default(), - factory_deps: vec![], + factory_deps: None, }, None, ); @@ -121,7 +121,7 @@ fn test_vm_loadnext_rollbacks() { } .to_bytes(), value: Default::default(), - factory_deps: vec![], + factory_deps: None, }, None, ); diff --git a/core/lib/multivm/src/versions/vm_latest/tests/sekp256r1.rs b/core/lib/multivm/src/versions/vm_latest/tests/sekp256r1.rs index 07b25eb0a8b..18917456888 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/sekp256r1.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/sekp256r1.rs @@ -51,7 +51,7 @@ fn test_sekp256r1() { contract_address: P256VERIFY_PRECOMPILE_ADDRESS, calldata: [digest, encoded_r, encoded_s, x, y].concat(), value: U256::zero(), - factory_deps: vec![], + factory_deps: None, }, None, ); diff --git a/core/lib/multivm/src/versions/vm_latest/tests/storage.rs b/core/lib/multivm/src/versions/vm_latest/tests/storage.rs index b7c14c54f6d..b39c0dc53b7 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/storage.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/storage.rs @@ -1,6 +1,5 @@ use ethabi::Token; use zksync_contracts::{load_contract, read_bytecode}; -use zksync_test_account::Account; use zksync_types::{fee::Fee, Address, Execute, U256}; use crate::{ @@ -51,7 +50,7 @@ fn test_storage(txs: Vec) -> u32 { contract_address: test_contract_address, calldata, value: 0.into(), - factory_deps: vec![], + factory_deps: None, }, fee_overrides, ); @@ -165,7 +164,7 @@ fn test_transient_storage_behavior_panic() { let small_fee = Fee { // Something very-very small to make the validation fail gas_limit: 10_000.into(), - ..Account::default_fee() + ..Default::default() }; test_storage(vec![ diff --git a/core/lib/multivm/src/versions/vm_latest/tests/tracing_execution_error.rs b/core/lib/multivm/src/versions/vm_latest/tests/tracing_execution_error.rs index 58c5ef77dc4..f02de899b03 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/tracing_execution_error.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/tracing_execution_error.rs @@ -30,7 +30,7 @@ fn test_tracing_of_execution_errors() { contract_address, calldata: get_execute_error_calldata(), value: Default::default(), - factory_deps: vec![], + factory_deps: Some(vec![]), }, None, ); diff --git a/core/lib/multivm/src/versions/vm_latest/tests/transfer.rs b/core/lib/multivm/src/versions/vm_latest/tests/transfer.rs index f4198d541f7..6351c216f3a 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/transfer.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/transfer.rs @@ -76,7 +76,7 @@ fn test_send_or_transfer(test_option: TestOptions) { contract_address: test_contract_address, calldata, value: U256::zero(), - factory_deps: vec![], + factory_deps: None, }, None, ); @@ -176,7 +176,7 @@ fn test_reentrancy_protection_send_or_transfer(test_option: TestOptions) { .encode_input(&[]) .unwrap(), value: U256::from(1), - factory_deps: vec![], + factory_deps: None, }, None, ); @@ -193,7 +193,7 @@ fn test_reentrancy_protection_send_or_transfer(test_option: TestOptions) { contract_address: test_contract_address, calldata, value, - factory_deps: vec![], + factory_deps: None, }, None, ); diff --git a/core/lib/multivm/src/versions/vm_latest/tests/upgrade.rs b/core/lib/multivm/src/versions/vm_latest/tests/upgrade.rs index 80e16248fb2..559cf588453 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/upgrade.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/upgrade.rs @@ -279,7 +279,7 @@ fn get_forced_deploy_tx(deployment: &[ForceDeployment]) -> Transaction { let execute = Execute { contract_address: CONTRACT_DEPLOYER_ADDRESS, calldata, - factory_deps: vec![], + factory_deps: None, value: U256::zero(), }; @@ -329,7 +329,7 @@ fn get_complex_upgrade_tx( let execute = Execute { contract_address: COMPLEX_UPGRADER_ADDRESS, calldata: complex_upgrader_calldata, - factory_deps: vec![], + factory_deps: None, value: U256::zero(), }; diff --git a/core/lib/multivm/src/versions/vm_latest/types/internals/transaction_data.rs b/core/lib/multivm/src/versions/vm_latest/types/internals/transaction_data.rs index 502be0dc22c..2bc77ca0f73 100644 --- a/core/lib/multivm/src/versions/vm_latest/types/internals/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_latest/types/internals/transaction_data.rs @@ -91,7 +91,7 @@ impl From for TransactionData { ], data: execute_tx.execute.calldata, signature: common_data.signature, - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: common_data.paymaster_params.paymaster_input, reserved_dynamic: vec![], raw_bytes: execute_tx.raw_bytes.map(|a| a.0), @@ -121,7 +121,7 @@ impl From for TransactionData { data: execute_tx.execute.calldata, // The signature isn't checked for L1 transactions so we don't care signature: vec![], - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: vec![], reserved_dynamic: vec![], raw_bytes: None, @@ -151,7 +151,7 @@ impl From for TransactionData { data: execute_tx.execute.calldata, // The signature isn't checked for L1 transactions so we don't care signature: vec![], - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: vec![], reserved_dynamic: vec![], raw_bytes: None, @@ -278,11 +278,12 @@ impl TryInto for TransactionData { paymaster_input: self.paymaster_input, }, }; + let factory_deps = (!self.factory_deps.is_empty()).then_some(self.factory_deps); let execute = Execute { contract_address: self.to, value: self.value, calldata: self.data, - factory_deps: self.factory_deps, + factory_deps, }; Ok(L2Tx { diff --git a/core/lib/multivm/src/versions/vm_m5/test_utils.rs b/core/lib/multivm/src/versions/vm_m5/test_utils.rs index 785eb49835f..e91b365d534 100644 --- a/core/lib/multivm/src/versions/vm_m5/test_utils.rs +++ b/core/lib/multivm/src/versions/vm_m5/test_utils.rs @@ -153,7 +153,7 @@ pub fn get_create_execute(code: &[u8], calldata: &[u8]) -> Execute { Execute { contract_address: CONTRACT_DEPLOYER_ADDRESS, calldata, - factory_deps: vec![code.to_vec()], + factory_deps: Some(vec![code.to_vec()]), value: U256::zero(), } } diff --git a/core/lib/multivm/src/versions/vm_m5/transaction_data.rs b/core/lib/multivm/src/versions/vm_m5/transaction_data.rs index 7ef739fd5bf..0a093462c1f 100644 --- a/core/lib/multivm/src/versions/vm_m5/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_m5/transaction_data.rs @@ -89,7 +89,7 @@ impl From for TransactionData { ], data: execute_tx.execute.calldata, signature: common_data.signature.clone(), - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: common_data.paymaster_params.paymaster_input.clone(), reserved_dynamic: vec![], } @@ -118,7 +118,7 @@ impl From for TransactionData { data: execute_tx.execute.calldata, // The signature isn't checked for L1 transactions so we don't care signature: vec![], - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: vec![], reserved_dynamic: vec![], } diff --git a/core/lib/multivm/src/versions/vm_m6/test_utils.rs b/core/lib/multivm/src/versions/vm_m6/test_utils.rs index ecad7d911b4..bd724dca5ca 100644 --- a/core/lib/multivm/src/versions/vm_m6/test_utils.rs +++ b/core/lib/multivm/src/versions/vm_m6/test_utils.rs @@ -153,7 +153,7 @@ pub fn get_create_execute(code: &[u8], calldata: &[u8]) -> Execute { Execute { contract_address: CONTRACT_DEPLOYER_ADDRESS, calldata, - factory_deps: vec![code.to_vec()], + factory_deps: Some(vec![code.to_vec()]), value: U256::zero(), } } diff --git a/core/lib/multivm/src/versions/vm_m6/transaction_data.rs b/core/lib/multivm/src/versions/vm_m6/transaction_data.rs index 99ce4671c29..0abac18e5ed 100644 --- a/core/lib/multivm/src/versions/vm_m6/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_m6/transaction_data.rs @@ -90,7 +90,7 @@ impl From for TransactionData { ], data: execute_tx.execute.calldata, signature: common_data.signature.clone(), - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: common_data.paymaster_params.paymaster_input.clone(), reserved_dynamic: vec![], } @@ -119,7 +119,7 @@ impl From for TransactionData { data: execute_tx.execute.calldata, // The signature isn't checked for L1 transactions so we don't care signature: vec![], - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: vec![], reserved_dynamic: vec![], } @@ -148,7 +148,7 @@ impl From for TransactionData { data: execute_tx.execute.calldata, // The signature isn't checked for L1 transactions so we don't care signature: vec![], - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: vec![], reserved_dynamic: vec![], } diff --git a/core/lib/multivm/src/versions/vm_m6/vm.rs b/core/lib/multivm/src/versions/vm_m6/vm.rs index 8fd512ef575..36303c57744 100644 --- a/core/lib/multivm/src/versions/vm_m6/vm.rs +++ b/core/lib/multivm/src/versions/vm_m6/vm.rs @@ -224,7 +224,7 @@ impl VmInterface for Vm { self.last_tx_compressed_bytecodes = vec![]; let bytecodes = if with_compression { - let deps = &tx.execute.factory_deps; + let deps = tx.execute.factory_deps.as_deref().unwrap_or_default(); let mut deps_hashes = HashSet::with_capacity(deps.len()); let mut bytecode_hashes = vec![]; let filtered_deps = deps.iter().filter_map(|bytecode| { diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/types/internals/transaction_data.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/types/internals/transaction_data.rs index 205090ba633..b7ad5e64094 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/types/internals/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/types/internals/transaction_data.rs @@ -91,7 +91,7 @@ impl From for TransactionData { ], data: execute_tx.execute.calldata, signature: common_data.signature, - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: common_data.paymaster_params.paymaster_input, reserved_dynamic: vec![], raw_bytes: execute_tx.raw_bytes.map(|a| a.0), @@ -121,7 +121,7 @@ impl From for TransactionData { data: execute_tx.execute.calldata, // The signature isn't checked for L1 transactions so we don't care signature: vec![], - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: vec![], reserved_dynamic: vec![], raw_bytes: None, @@ -151,7 +151,7 @@ impl From for TransactionData { data: execute_tx.execute.calldata, // The signature isn't checked for L1 transactions so we don't care signature: vec![], - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: vec![], reserved_dynamic: vec![], raw_bytes: None, @@ -298,11 +298,12 @@ impl TryInto for TransactionData { paymaster_input: self.paymaster_input, }, }; + let factory_deps = (!self.factory_deps.is_empty()).then_some(self.factory_deps); let execute = Execute { contract_address: self.to, value: self.value, calldata: self.data, - factory_deps: self.factory_deps, + factory_deps, }; Ok(L2Tx { diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/types/internals/transaction_data.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/types/internals/transaction_data.rs index b42950399f6..a62b96ca92f 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/types/internals/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/types/internals/transaction_data.rs @@ -91,7 +91,7 @@ impl From for TransactionData { ], data: execute_tx.execute.calldata, signature: common_data.signature, - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: common_data.paymaster_params.paymaster_input, reserved_dynamic: vec![], raw_bytes: execute_tx.raw_bytes.map(|a| a.0), @@ -121,7 +121,7 @@ impl From for TransactionData { data: execute_tx.execute.calldata, // The signature isn't checked for L1 transactions so we don't care signature: vec![], - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: vec![], reserved_dynamic: vec![], raw_bytes: None, @@ -151,7 +151,7 @@ impl From for TransactionData { data: execute_tx.execute.calldata, // The signature isn't checked for L1 transactions so we don't care signature: vec![], - factory_deps: execute_tx.execute.factory_deps, + factory_deps: execute_tx.execute.factory_deps.unwrap_or_default(), paymaster_input: vec![], reserved_dynamic: vec![], raw_bytes: None, @@ -298,11 +298,12 @@ impl TryInto for TransactionData { paymaster_input: self.paymaster_input, }, }; + let factory_deps = (!self.factory_deps.is_empty()).then_some(self.factory_deps); let execute = Execute { contract_address: self.to, value: self.value, calldata: self.data, - factory_deps: self.factory_deps, + factory_deps, }; Ok(L2Tx { diff --git a/core/lib/types/src/abi.rs b/core/lib/types/src/abi.rs index 84f8aba6486..5778c4d8d40 100644 --- a/core/lib/types/src/abi.rs +++ b/core/lib/types/src/abi.rs @@ -338,6 +338,7 @@ pub enum Transaction { factory_deps: Vec>, /// Auxiliary data, not hashed. eth_block: u64, + received_timestamp_ms: u64, }, /// RLP encoding of a L2 transaction. L2(Vec), diff --git a/core/lib/types/src/l1/mod.rs b/core/lib/types/src/l1/mod.rs index 348600b6ee8..796a8621c39 100644 --- a/core/lib/types/src/l1/mod.rs +++ b/core/lib/types/src/l1/mod.rs @@ -266,7 +266,7 @@ impl L1Tx { impl From for abi::NewPriorityRequest { fn from(t: L1Tx) -> Self { - let factory_deps = t.execute.factory_deps; + let factory_deps = t.execute.factory_deps.unwrap_or_default(); Self { tx_id: t.common_data.serial_id.0.into(), tx_hash: t.common_data.canonical_tx_hash.to_fixed_bytes(), @@ -347,7 +347,7 @@ impl TryFrom for L1Tx { let execute = Execute { contract_address: u256_to_account_address(&req.transaction.to), calldata: req.transaction.data, - factory_deps: req.factory_deps, + factory_deps: Some(req.factory_deps), value: req.transaction.value, }; Ok(Self { diff --git a/core/lib/types/src/l2/mod.rs b/core/lib/types/src/l2/mod.rs index 57edc6181c8..38d26cf0232 100644 --- a/core/lib/types/src/l2/mod.rs +++ b/core/lib/types/src/l2/mod.rs @@ -15,8 +15,8 @@ use crate::{ transaction_request::PaymasterParams, tx::Execute, web3::Bytes, - Address, EIP712TypedStructure, ExecuteTransactionCommon, InputData, L2ChainId, Nonce, - PackedEthSignature, StructBuilder, Transaction, EIP_1559_TX_TYPE, EIP_2930_TX_TYPE, + Address, EIP712TypedStructure, Eip712Domain, ExecuteTransactionCommon, InputData, L2ChainId, + Nonce, PackedEthSignature, StructBuilder, Transaction, EIP_1559_TX_TYPE, EIP_2930_TX_TYPE, EIP_712_TX_TYPE, H256, LEGACY_TX_TYPE, PRIORITY_OPERATION_L2_TX_TYPE, PROTOCOL_UPGRADE_TX_TYPE, U256, U64, }; @@ -159,7 +159,7 @@ impl L2Tx { fee: Fee, initiator_address: Address, value: U256, - factory_deps: Vec>, + factory_deps: Option>>, paymaster_params: PaymasterParams, ) -> Self { Self { @@ -192,11 +192,11 @@ impl L2Tx { value: U256, chain_id: L2ChainId, private_key: &K256PrivateKey, - factory_deps: Vec>, + factory_deps: Option>>, paymaster_params: PaymasterParams, ) -> Result { let initiator_address = private_key.address(); - let tx = Self::new( + let mut res = Self::new( contract_address, calldata, nonce, @@ -206,19 +206,10 @@ impl L2Tx { factory_deps, paymaster_params, ); - // We do a whole dance to reconstruct missing data: RLP encoding, hash and signature. - let mut req: TransactionRequest = tx.into(); - req.chain_id = Some(chain_id.as_u64()); - let data = req - .get_default_signed_message() - .context("get_default_signed_message()")?; - let sig = PackedEthSignature::sign_raw(private_key, &data).context("sign_raw")?; - let raw = req.get_signed_bytes(&sig).context("get_signed_bytes")?; - let (req, hash) = - TransactionRequest::from_bytes_unverified(&raw).context("from_bytes_unverified()")?; - let mut tx = L2Tx::from_request_unverified(req).context("from_request_unverified()")?; - tx.set_input(raw, hash); - Ok(tx) + + let data = res.get_signed_bytes(chain_id); + res.set_signature(PackedEthSignature::sign_raw(private_key, &data).context("sign_raw")?); + Ok(res) } /// Returns the hash of the transaction. @@ -246,10 +237,18 @@ impl L2Tx { } pub fn get_signed_bytes(&self, chain_id: L2ChainId) -> H256 { - let mut req: TransactionRequest = self.clone().into(); - req.chain_id = Some(chain_id.as_u64()); - // It is ok to unwrap, because the `chain_id` is set. - req.get_default_signed_message().unwrap() + let mut tx: TransactionRequest = self.clone().into(); + tx.chain_id = Some(chain_id.as_u64()); + if tx.is_eip712_tx() { + PackedEthSignature::typed_data_to_signed_bytes(&Eip712Domain::new(chain_id), &tx) + } else { + // It is ok to unwrap, because the `chain_id` is set. + let mut data = tx.get_rlp().unwrap(); + if let Some(tx_type) = tx.transaction_type { + data.insert(0, tx_type.as_u32() as u8); + } + PackedEthSignature::message_to_signed_bytes(&data) + } } pub fn set_signature(&mut self, signature: PackedEthSignature) { @@ -267,7 +266,7 @@ impl L2Tx { pub fn abi_encoding_len(&self) -> usize { let data_len = self.execute.calldata.len(); let signature_len = self.common_data.signature.len(); - let factory_deps_len = self.execute.factory_deps.len(); + let factory_deps_len = self.execute.factory_deps_length(); let paymaster_input_len = self.common_data.paymaster_params.paymaster_input.len(); encoding_len( @@ -290,8 +289,9 @@ impl L2Tx { pub fn factory_deps_len(&self) -> u32 { self.execute .factory_deps - .iter() - .fold(0u32, |len, item| len + item.len() as u32) + .as_ref() + .map(|deps| deps.iter().fold(0u32, |len, item| len + item.len() as u32)) + .unwrap_or_default() } } @@ -486,7 +486,7 @@ mod tests { contract_address: Default::default(), calldata: vec![], value: U256::zero(), - factory_deps: vec![], + factory_deps: None, }, common_data: L2TxCommonData { nonce: Nonce(0), diff --git a/core/lib/types/src/lib.rs b/core/lib/types/src/lib.rs index 2617bf0e498..fd5af40e35f 100644 --- a/core/lib/types/src/lib.rs +++ b/core/lib/types/src/lib.rs @@ -192,7 +192,12 @@ impl Transaction { // Returns how many slots it takes to encode the transaction pub fn encoding_len(&self) -> usize { let data_len = self.execute.calldata.len(); - let factory_deps_len = self.execute.factory_deps.len(); + let factory_deps_len = self + .execute + .factory_deps + .as_ref() + .map(|deps| deps.len()) + .unwrap_or_default(); let (signature_len, paymaster_input_len) = match &self.common_data { ExecuteTransactionCommon::L1(_) => (0, 0), ExecuteTransactionCommon::L2(l2_common_data) => ( @@ -246,7 +251,7 @@ impl TryFrom for abi::Transaction { fn try_from(tx: Transaction) -> anyhow::Result { use ExecuteTransactionCommon as E; - let factory_deps = tx.execute.factory_deps; + let factory_deps = tx.execute.factory_deps.unwrap_or_default(); Ok(match tx.common_data { E::L2(data) => Self::L2( data.input @@ -283,6 +288,7 @@ impl TryFrom for abi::Transaction { .into(), factory_deps, eth_block: data.eth_block, + received_timestamp_ms: tx.received_timestamp_ms, }, E::ProtocolUpgrade(data) => Self::L1 { tx: abi::L2CanonicalTransaction { @@ -314,6 +320,7 @@ impl TryFrom for abi::Transaction { .into(), factory_deps, eth_block: data.eth_block, + received_timestamp_ms: tx.received_timestamp_ms, }, }) } @@ -327,6 +334,7 @@ impl TryFrom for Transaction { tx, factory_deps, eth_block, + received_timestamp_ms, } => { let factory_deps_hashes: Vec<_> = factory_deps .iter() @@ -383,19 +391,17 @@ impl TryFrom for Transaction { execute: Execute { contract_address: u256_to_account_address(&tx.to), calldata: tx.data, - factory_deps, + factory_deps: Some(factory_deps), value: tx.value, }, raw_bytes: None, - received_timestamp_ms: helpers::unix_timestamp_ms(), + received_timestamp_ms, } } abi::Transaction::L2(raw) => { - let (req, hash) = + let (req, _) = transaction_request::TransactionRequest::from_bytes_unverified(&raw)?; - let mut tx = L2Tx::from_request_unverified(req)?; - tx.set_input(raw, hash); - tx.into() + L2Tx::from_request_unverified(req)?.into() } }) } diff --git a/core/lib/types/src/protocol_upgrade.rs b/core/lib/types/src/protocol_upgrade.rs index c1bcc2f5cac..d3951f44962 100644 --- a/core/lib/types/src/protocol_upgrade.rs +++ b/core/lib/types/src/protocol_upgrade.rs @@ -15,8 +15,8 @@ use zksync_contracts::{ use zksync_utils::h256_to_u256; use crate::{ - abi, ethabi::ParamType, web3::Log, Address, Execute, ExecuteTransactionCommon, Transaction, - TransactionType, H256, U256, + abi, ethabi::ParamType, helpers, web3::Log, Address, Execute, ExecuteTransactionCommon, + Transaction, TransactionType, H256, U256, }; /// Represents a call to be made during governance operation. @@ -125,6 +125,7 @@ impl ProtocolUpgrade { tx: upgrade.l2_protocol_upgrade_tx, factory_deps: upgrade.factory_deps, eth_block, + received_timestamp_ms: helpers::unix_timestamp_ms(), }) .context("Transaction::try_from()")? .try_into() @@ -153,6 +154,7 @@ pub fn decode_set_chain_id_event( .expect("Event block number is missing") .as_u64(), factory_deps: vec![], + received_timestamp_ms: helpers::unix_timestamp_ms(), }) .unwrap() .try_into() diff --git a/core/lib/types/src/transaction_request.rs b/core/lib/types/src/transaction_request.rs index a59b21409cd..7cf2d9f432b 100644 --- a/core/lib/types/src/transaction_request.rs +++ b/core/lib/types/src/transaction_request.rs @@ -223,11 +223,13 @@ pub enum SerializationTransactionError { GasPerPubDataLimitZero, } -#[derive(Clone, Debug, PartialEq, Default)] /// Description of a Transaction, pending or in the chain. +#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Default)] +#[serde(rename_all = "camelCase")] pub struct TransactionRequest { /// Nonce pub nonce: U256, + #[serde(default, skip_serializing_if = "Option::is_none")] pub from: Option
, /// Recipient (None when contract creation) pub to: Option
, @@ -238,23 +240,32 @@ pub struct TransactionRequest { /// Gas amount pub gas: U256, /// EIP-1559 part of gas price that goes to miners + #[serde(default, skip_serializing_if = "Option::is_none")] pub max_priority_fee_per_gas: Option, /// Input data pub input: Bytes, /// ECDSA recovery id + #[serde(default, skip_serializing_if = "Option::is_none")] pub v: Option, /// ECDSA signature r, 32 bytes + #[serde(default, skip_serializing_if = "Option::is_none")] pub r: Option, /// ECDSA signature s, 32 bytes + #[serde(default, skip_serializing_if = "Option::is_none")] pub s: Option, /// Raw transaction data + #[serde(default, skip_serializing_if = "Option::is_none")] pub raw: Option, /// Transaction type, Some(1) for AccessList transaction, None for Legacy + #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")] pub transaction_type: Option, /// Access list + #[serde(default, skip_serializing_if = "Option::is_none")] pub access_list: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] pub eip712_meta: Option, /// Chain ID + #[serde(default, skip_serializing_if = "Option::is_none")] pub chain_id: Option, } @@ -288,7 +299,7 @@ impl PaymasterParams { pub struct Eip712Meta { pub gas_per_pubdata: U256, #[serde(default)] - pub factory_deps: Vec>, + pub factory_deps: Option>>, pub custom_signature: Option>, pub paymaster_params: Option, } @@ -296,9 +307,13 @@ pub struct Eip712Meta { impl Eip712Meta { pub fn rlp_append(&self, rlp: &mut RlpStream) { rlp.append(&self.gas_per_pubdata); - rlp.begin_list(self.factory_deps.len()); - for dep in &self.factory_deps { - rlp.append(&dep.as_slice()); + if let Some(factory_deps) = &self.factory_deps { + rlp.begin_list(factory_deps.len()); + for dep in factory_deps.iter() { + rlp.append(&dep.as_slice()); + } + } else { + rlp.begin_list(0); } rlp_opt(rlp, &self.custom_signature); @@ -368,34 +383,30 @@ impl EIP712TypedStructure for TransactionRequest { impl TransactionRequest { pub fn get_custom_signature(&self) -> Option> { - self.eip712_meta.as_ref()?.custom_signature.clone() + self.eip712_meta + .as_ref() + .and_then(|meta| meta.custom_signature.as_ref()) + .cloned() } pub fn get_paymaster(&self) -> Option
{ - Some( - self.eip712_meta - .as_ref()? - .paymaster_params - .as_ref()? - .paymaster, - ) + self.eip712_meta + .clone() + .and_then(|meta| meta.paymaster_params) + .map(|params| params.paymaster) } pub fn get_paymaster_input(&self) -> Option> { - Some( - self.eip712_meta - .as_ref()? - .paymaster_params - .as_ref()? - .paymaster_input - .clone(), - ) + self.eip712_meta + .clone() + .and_then(|meta| meta.paymaster_params) + .map(|params| params.paymaster_input) } pub fn get_factory_deps(&self) -> Vec> { self.eip712_meta - .as_ref() - .map(|meta| meta.factory_deps.clone()) + .clone() + .and_then(|meta| meta.factory_deps) .unwrap_or_default() } @@ -465,7 +476,7 @@ impl TransactionRequest { /// Encodes `TransactionRequest` to RLP. /// It may fail if `chain_id` is `None` while required. - pub fn get_rlp(&self) -> Result, SerializationTransactionError> { + pub fn get_rlp(&self) -> anyhow::Result> { let mut rlp_stream = RlpStream::new(); self.rlp(&mut rlp_stream, None)?; Ok(rlp_stream.as_raw().into()) @@ -659,7 +670,7 @@ impl TransactionRequest { s: Some(rlp.val_at(9)?), eip712_meta: Some(Eip712Meta { gas_per_pubdata: rlp.val_at(12)?, - factory_deps: rlp.list_at(13)?, + factory_deps: rlp.list_at(13).ok(), custom_signature: rlp.val_at(14).ok(), paymaster_params: if let Ok(params) = rlp.list_at(15) { PaymasterParams::from_vector(params)? @@ -678,16 +689,21 @@ impl TransactionRequest { } _ => return Err(SerializationTransactionError::UnknownTransactionFormat), }; - if let Some(meta) = &tx.eip712_meta { - validate_factory_deps(&meta.factory_deps)?; + let factory_deps_ref = tx + .eip712_meta + .as_ref() + .and_then(|m| m.factory_deps.as_ref()); + if let Some(deps) = factory_deps_ref { + validate_factory_deps(deps)?; } tx.raw = Some(Bytes(bytes.to_vec())); let default_signed_message = tx.get_default_signed_message()?; - if tx.from.is_none() { - tx.from = tx.recover_default_signer(default_signed_message).ok(); - } + tx.from = match tx.from { + Some(_) => tx.from, + None => tx.recover_default_signer(default_signed_message).ok(), + }; // `tx.raw` is set, so unwrap is safe here. let hash = tx @@ -707,7 +723,7 @@ impl TransactionRequest { Ok((tx, hash)) } - pub fn get_default_signed_message(&self) -> Result { + fn get_default_signed_message(&self) -> Result { if self.is_eip712_tx() { let chain_id = self .chain_id @@ -717,7 +733,9 @@ impl TransactionRequest { self, )) } else { - let mut data = self.get_rlp()?; + let mut rlp_stream = RlpStream::new(); + self.rlp(&mut rlp_stream, None)?; + let mut data = rlp_stream.out().to_vec(); if let Some(tx_type) = self.transaction_type { data.insert(0, tx_type.as_u64() as u8); } @@ -806,14 +824,21 @@ impl TransactionRequest { impl L2Tx { pub(crate) fn from_request_unverified( - mut value: TransactionRequest, + value: TransactionRequest, ) -> Result { let fee = value.get_fee_data_checked()?; let nonce = value.get_nonce_checked()?; let raw_signature = value.get_signature().unwrap_or_default(); - let meta = value.eip712_meta.take().unwrap_or_default(); - validate_factory_deps(&meta.factory_deps)?; + // Destruct `eip712_meta` in one go to avoid cloning. + let (factory_deps, paymaster_params) = value + .eip712_meta + .map(|eip712_meta| (eip712_meta.factory_deps, eip712_meta.paymaster_params)) + .unwrap_or_default(); + + if let Some(deps) = factory_deps.as_ref() { + validate_factory_deps(deps)?; + } let mut tx = L2Tx::new( value @@ -824,8 +849,8 @@ impl L2Tx { fee, value.from.unwrap_or_default(), value.value, - meta.factory_deps, - meta.paymaster_params.unwrap_or_default(), + factory_deps, + paymaster_params.unwrap_or_default(), ); tx.common_data.transaction_type = match value.transaction_type.map(|t| t.as_u64() as u8) { @@ -870,7 +895,7 @@ impl From for CallRequest { fn from(tx: L2Tx) -> Self { let mut meta = Eip712Meta { gas_per_pubdata: tx.common_data.fee.gas_per_pubdata_limit, - factory_deps: vec![], + factory_deps: None, custom_signature: Some(tx.common_data.signature.clone()), paymaster_params: Some(tx.common_data.paymaster_params.clone()), }; @@ -1035,7 +1060,7 @@ mod tests { transaction_type: Some(U64::from(EIP_712_TX_TYPE)), eip712_meta: Some(Eip712Meta { gas_per_pubdata: U256::from(4u32), - factory_deps: vec![vec![2; 32]], + factory_deps: Some(vec![vec![2; 32]]), custom_signature: Some(vec![1, 2, 3]), paymaster_params: Some(PaymasterParams { paymaster: Default::default(), @@ -1083,7 +1108,7 @@ mod tests { transaction_type: Some(U64::from(EIP_712_TX_TYPE)), eip712_meta: Some(Eip712Meta { gas_per_pubdata: U256::from(4u32), - factory_deps: vec![vec![2; 32]], + factory_deps: Some(vec![vec![2; 32]]), custom_signature: Some(vec![]), paymaster_params: None, }), @@ -1120,7 +1145,7 @@ mod tests { transaction_type: Some(U64::from(EIP_712_TX_TYPE)), eip712_meta: Some(Eip712Meta { gas_per_pubdata: U256::from(4u32), - factory_deps: vec![vec![2; 32]], + factory_deps: Some(vec![vec![2; 32]]), custom_signature: Some(vec![1, 2, 3]), paymaster_params: Some(PaymasterParams { paymaster: Default::default(), @@ -1398,7 +1423,7 @@ mod tests { transaction_type: Some(U64::from(EIP_712_TX_TYPE)), eip712_meta: Some(Eip712Meta { gas_per_pubdata: U256::from(4u32), - factory_deps, + factory_deps: Some(factory_deps), custom_signature: Some(vec![1, 2, 3]), paymaster_params: Some(PaymasterParams { paymaster: Default::default(), diff --git a/core/lib/types/src/tx/execute.rs b/core/lib/types/src/tx/execute.rs index 22546df99cb..e54f469b135 100644 --- a/core/lib/types/src/tx/execute.rs +++ b/core/lib/types/src/tx/execute.rs @@ -16,13 +16,18 @@ pub struct Execute { pub value: U256, /// Factory dependencies: list of contract bytecodes associated with the deploy transaction. - #[serde(default)] - pub factory_deps: Vec>, + /// This field is always `None` for all the transaction that do not cause the contract deployment. + /// For the deployment transactions, this field is always `Some`, even if there s no "dependencies" for the + /// contract being deployed, since the bytecode of the contract itself is also included into this list. + pub factory_deps: Option>>, } impl std::fmt::Debug for Execute { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let factory_deps = format!("<{} factory deps>", self.factory_deps.len()); + let factory_deps = match &self.factory_deps { + Some(deps) => format!("Some(<{} factory deps>)", deps.len()), + None => "None".to_string(), + }; f.debug_struct("Execute") .field("contract_address", &self.contract_address) .field("calldata", &hex::encode(&self.calldata)) @@ -78,4 +83,12 @@ impl Execute { FUNCTION_SIGNATURE.iter().copied().chain(params).collect() } + + /// Number of new factory dependencies in this transaction + pub fn factory_deps_length(&self) -> usize { + self.factory_deps + .as_ref() + .map(|deps| deps.len()) + .unwrap_or_default() + } } diff --git a/core/node/api_server/src/execution_sandbox/execute.rs b/core/node/api_server/src/execution_sandbox/execute.rs index 9a844df2867..72c94e2a428 100644 --- a/core/node/api_server/src/execution_sandbox/execute.rs +++ b/core/node/api_server/src/execution_sandbox/execute.rs @@ -117,7 +117,11 @@ impl TransactionExecutor { return mock_executor.execute_tx(&tx, &block_args); } - let total_factory_deps = tx.execute.factory_deps.len() as u16; + let total_factory_deps = tx + .execute + .factory_deps + .as_ref() + .map_or(0, |deps| deps.len() as u16); let (published_bytecodes, execution_result) = tokio::task::spawn_blocking(move || { let span = span!(Level::DEBUG, "execute_in_sandbox").entered(); diff --git a/core/node/api_server/src/tx_sender/mod.rs b/core/node/api_server/src/tx_sender/mod.rs index 1dd3f4c6e94..c4fd6dff692 100644 --- a/core/node/api_server/src/tx_sender/mod.rs +++ b/core/node/api_server/src/tx_sender/mod.rs @@ -531,9 +531,9 @@ impl TxSender { ); return Err(SubmitTxError::MaxPriorityFeeGreaterThanMaxFee); } - if tx.execute.factory_deps.len() > MAX_NEW_FACTORY_DEPS { + if tx.execute.factory_deps_length() > MAX_NEW_FACTORY_DEPS { return Err(SubmitTxError::TooManyFactoryDependencies( - tx.execute.factory_deps.len(), + tx.execute.factory_deps_length(), MAX_NEW_FACTORY_DEPS, )); } diff --git a/core/node/consensus/Cargo.toml b/core/node/consensus/Cargo.toml index b22fde34e7c..9cfb3c86b0b 100644 --- a/core/node/consensus/Cargo.toml +++ b/core/node/consensus/Cargo.toml @@ -21,28 +21,20 @@ zksync_consensus_bft.workspace = true zksync_consensus_utils.workspace = true zksync_protobuf.workspace = true zksync_dal.workspace = true -zksync_state.workspace = true -zksync_l1_contract_interface.workspace = true -zksync_metadata_calculator.workspace = true -zksync_merkle_tree.workspace = true zksync_state_keeper.workspace = true zksync_node_sync.workspace = true -zksync_system_constants.workspace = true zksync_types.workspace = true -zksync_utils.workspace = true zksync_web3_decl.workspace = true anyhow.workspace = true async-trait.workspace = true secrecy.workspace = true -tempfile.workspace = true tracing.workspace = true [dev-dependencies] zksync_node_genesis.workspace = true zksync_node_test_utils.workspace = true zksync_node_api_server.workspace = true -zksync_test_account.workspace = true tokio.workspace = true test-casing.workspace = true diff --git a/core/node/consensus/src/batch.rs b/core/node/consensus/src/batch.rs deleted file mode 100644 index d393a845ec6..00000000000 --- a/core/node/consensus/src/batch.rs +++ /dev/null @@ -1,275 +0,0 @@ -//! L1 Batch representation for sending over p2p network. -use anyhow::Context as _; -use zksync_concurrency::{ctx, error::Wrap as _}; -use zksync_consensus_roles::validator; -use zksync_dal::consensus_dal::Payload; -use zksync_l1_contract_interface::i_executor; -use zksync_metadata_calculator::api_server::{TreeApiClient, TreeEntryWithProof}; -use zksync_system_constants as constants; -use zksync_types::{ - abi, - block::{unpack_block_info, L2BlockHasher}, - AccountTreeId, L1BatchNumber, L2BlockNumber, ProtocolVersionId, StorageKey, Transaction, H256, - U256, -}; -use zksync_utils::{h256_to_u256, u256_to_h256}; - -use crate::ConnectionPool; - -/// Commitment to the last block of a batch. -pub(crate) struct LastBlockCommit { - /// Hash of the `StoredBatchInfo` which is stored on L1. - /// The hashed `StoredBatchInfo` contains a `root_hash` of the L2 state, - /// which contains state of the `SystemContext` contract, - /// which contains enough data to reconstruct the hash - /// of the last L2 block of the batch. - pub(crate) info: H256, -} - -/// Witness proving what is the last block of a batch. -/// Contains the hash and the number of the last block. -pub(crate) struct LastBlockWitness { - info: i_executor::structures::StoredBatchInfo, - protocol_version: ProtocolVersionId, - - current_l2_block_info: TreeEntryWithProof, - tx_rolling_hash: TreeEntryWithProof, - l2_block_hash_entry: TreeEntryWithProof, -} - -/// Commitment to an L1 batch. -pub(crate) struct L1BatchCommit { - pub(crate) number: L1BatchNumber, - pub(crate) this_batch: LastBlockCommit, - pub(crate) prev_batch: LastBlockCommit, -} - -/// L1Batch with witness that can be -/// verified against `L1BatchCommit`. -pub struct L1BatchWithWitness { - pub(crate) blocks: Vec, - pub(crate) this_batch: LastBlockWitness, - pub(crate) prev_batch: LastBlockWitness, -} - -impl LastBlockWitness { - /// Address of the SystemContext contract. - fn system_context_addr() -> AccountTreeId { - AccountTreeId::new(constants::SYSTEM_CONTEXT_ADDRESS) - } - - /// Storage key of the `SystemContext.current_l2_block_info` field. - fn current_l2_block_info_key() -> U256 { - StorageKey::new( - Self::system_context_addr(), - constants::SYSTEM_CONTEXT_CURRENT_L2_BLOCK_INFO_POSITION, - ) - .hashed_key_u256() - } - - /// Storage key of the `SystemContext.tx_rolling_hash` field. - fn tx_rolling_hash_key() -> U256 { - StorageKey::new( - Self::system_context_addr(), - constants::SYSTEM_CONTEXT_CURRENT_TX_ROLLING_HASH_POSITION, - ) - .hashed_key_u256() - } - - /// Storage key of the entry of the `SystemContext.l2BlockHash[]` array, corresponding to l2 - /// block with number i. - fn l2_block_hash_entry_key(i: L2BlockNumber) -> U256 { - let key = h256_to_u256(constants::SYSTEM_CONTEXT_CURRENT_L2_BLOCK_HASHES_POSITION) - + U256::from(i.0) % U256::from(constants::SYSTEM_CONTEXT_STORED_L2_BLOCK_HASHES); - StorageKey::new(Self::system_context_addr(), u256_to_h256(key)).hashed_key_u256() - } - - /// Loads a `LastBlockWitness` from storage. - async fn load( - ctx: &ctx::Ctx, - n: L1BatchNumber, - pool: &ConnectionPool, - tree: &dyn TreeApiClient, - ) -> ctx::Result { - let mut conn = pool.connection(ctx).await.wrap("pool.connection()")?; - let batch = conn - .batch(ctx, n) - .await - .wrap("batch()")? - .context("batch not in storage")?; - - let proofs = tree - .get_proofs( - n, - vec![ - Self::current_l2_block_info_key(), - Self::tx_rolling_hash_key(), - ], - ) - .await - .context("get_proofs()")?; - if proofs.len() != 2 { - return Err(anyhow::format_err!("proofs.len()!=2").into()); - } - let current_l2_block_info = proofs[0].clone(); - let tx_rolling_hash = proofs[1].clone(); - let (block_number, _) = unpack_block_info(current_l2_block_info.value.as_bytes().into()); - let prev = L2BlockNumber( - block_number - .checked_sub(1) - .context("L2BlockNumber underflow")? - .try_into() - .context("L2BlockNumber overflow")?, - ); - let proofs = tree - .get_proofs(n, vec![Self::l2_block_hash_entry_key(prev)]) - .await - .context("get_proofs()")?; - if proofs.len() != 1 { - return Err(anyhow::format_err!("proofs.len()!=1").into()); - } - let l2_block_hash_entry = proofs[0].clone(); - Ok(Self { - info: i_executor::structures::StoredBatchInfo::from(&batch), - protocol_version: batch - .header - .protocol_version - .context("missing protocol_version")?, - - current_l2_block_info, - tx_rolling_hash, - l2_block_hash_entry, - }) - } - - /// Verifies the proof against the commit and returns the hash - /// of the last L2 block. - pub(crate) fn verify(&self, comm: &LastBlockCommit) -> anyhow::Result<(L2BlockNumber, H256)> { - // Verify info. - anyhow::ensure!(comm.info == self.info.hash()); - - // Check the protocol version. - anyhow::ensure!( - self.protocol_version >= ProtocolVersionId::Version13, - "unsupported protocol version" - ); - - let (block_number, block_timestamp) = - unpack_block_info(self.current_l2_block_info.value.as_bytes().into()); - let prev = L2BlockNumber( - block_number - .checked_sub(1) - .context("L2BlockNumber underflow")? - .try_into() - .context("L2BlockNumber overflow")?, - ); - - // Verify merkle paths. - self.current_l2_block_info - .verify(Self::current_l2_block_info_key(), self.info.batch_hash) - .context("invalid merkle path for current_l2_block_info")?; - self.tx_rolling_hash - .verify(Self::tx_rolling_hash_key(), self.info.batch_hash) - .context("invalid merkle path for tx_rolling_hash")?; - self.l2_block_hash_entry - .verify(Self::l2_block_hash_entry_key(prev), self.info.batch_hash) - .context("invalid merkle path for l2_block_hash entry")?; - - let block_number = L2BlockNumber(block_number.try_into().context("block_number overflow")?); - // Derive hash of the last block - Ok(( - block_number, - L2BlockHasher::hash( - block_number, - block_timestamp, - self.l2_block_hash_entry.value, - self.tx_rolling_hash.value, - self.protocol_version, - ), - )) - } - - /// Last L2 block of the batch. - pub fn last_block(&self) -> validator::BlockNumber { - let (n, _) = unpack_block_info(self.current_l2_block_info.value.as_bytes().into()); - validator::BlockNumber(n) - } -} - -impl L1BatchWithWitness { - /// Loads an `L1BatchWithWitness` from storage. - pub(crate) async fn load( - ctx: &ctx::Ctx, - number: L1BatchNumber, - pool: &ConnectionPool, - tree: &dyn TreeApiClient, - ) -> ctx::Result { - let prev_batch = LastBlockWitness::load(ctx, number - 1, pool, tree) - .await - .with_wrap(|| format!("LastBlockWitness::make({})", number - 1))?; - let this_batch = LastBlockWitness::load(ctx, number, pool, tree) - .await - .with_wrap(|| format!("LastBlockWitness::make({number})"))?; - let mut conn = pool.connection(ctx).await.wrap("connection()")?; - let this = Self { - blocks: conn - .payloads( - ctx, - std::ops::Range { - start: prev_batch.last_block() + 1, - end: this_batch.last_block() + 1, - }, - ) - .await - .wrap("payloads()")?, - prev_batch, - this_batch, - }; - Ok(this) - } - - /// Verifies the L1Batch and witness against the commitment. - /// WARNING: the following fields of the payload are not currently verified: - /// * `l1_gas_price` - /// * `l2_fair_gas_price` - /// * `fair_pubdata_price` - /// * `virtual_blocks` - /// * `operator_address` - /// * `protocol_version` (present both in payload and witness, but neither has a commitment) - pub(crate) fn verify(&self, comm: &L1BatchCommit) -> anyhow::Result<()> { - let (last_number, last_hash) = self.this_batch.verify(&comm.this_batch)?; - let (mut prev_number, mut prev_hash) = self.prev_batch.verify(&comm.prev_batch)?; - anyhow::ensure!( - self.prev_batch - .info - .batch_number - .checked_add(1) - .context("batch_number overflow")? - == u64::from(comm.number.0) - ); - anyhow::ensure!(self.this_batch.info.batch_number == u64::from(comm.number.0)); - for (i, b) in self.blocks.iter().enumerate() { - anyhow::ensure!(b.l1_batch_number == comm.number); - anyhow::ensure!(b.protocol_version == self.this_batch.protocol_version); - anyhow::ensure!(b.last_in_batch == (i + 1 == self.blocks.len())); - prev_number += 1; - let mut hasher = L2BlockHasher::new(prev_number, b.timestamp, prev_hash); - for t in &b.transactions { - // Reconstruct transaction by converting it back and forth to `abi::Transaction`. - // This allows us to verify that the transaction actually matches the transaction - // hash. - // TODO: make consensus payload contain `abi::Transaction` instead. - // TODO: currently the payload doesn't contain the block number, which is - // annoying. Consider adding it to payload. - let t2: Transaction = abi::Transaction::try_from(t.clone())?.try_into()?; - anyhow::ensure!(t == &t2); - hasher.push_tx_hash(t.hash()); - } - prev_hash = hasher.finalize(self.this_batch.protocol_version); - anyhow::ensure!(prev_hash == b.hash); - } - anyhow::ensure!(prev_hash == last_hash); - anyhow::ensure!(prev_number == last_number); - Ok(()) - } -} diff --git a/core/node/consensus/src/lib.rs b/core/node/consensus/src/lib.rs index bc9776c42df..b076b26e274 100644 --- a/core/node/consensus/src/lib.rs +++ b/core/node/consensus/src/lib.rs @@ -11,10 +11,6 @@ use zksync_consensus_storage::BlockStore; use crate::storage::{ConnectionPool, Store}; -// Currently `batch` module is only used in tests, -// but will be used in production once batch syncing is implemented in consensus. -#[allow(unused)] -mod batch; mod config; mod en; pub mod era; diff --git a/core/node/consensus/src/storage/mod.rs b/core/node/consensus/src/storage/mod.rs index cf45f89ad11..658c7a887d5 100644 --- a/core/node/consensus/src/storage/mod.rs +++ b/core/node/consensus/src/storage/mod.rs @@ -13,7 +13,7 @@ use zksync_node_sync::{ SyncState, }; use zksync_state_keeper::io::common::IoCursor; -use zksync_types::{commitment::L1BatchWithMetadata, L1BatchNumber, L2BlockNumber}; +use zksync_types::L2BlockNumber; use super::config; @@ -101,18 +101,6 @@ impl<'a> Connection<'a> { .map_err(DalError::generalize)?) } - /// Wrapper for `consensus_dal().block_payloads()`. - pub async fn payloads( - &mut self, - ctx: &ctx::Ctx, - numbers: std::ops::Range, - ) -> ctx::Result> { - Ok(ctx - .wait(self.0.consensus_dal().block_payloads(numbers)) - .await? - .map_err(DalError::generalize)?) - } - /// Wrapper for `consensus_dal().first_certificate()`. pub async fn first_certificate( &mut self, @@ -178,18 +166,6 @@ impl<'a> Connection<'a> { .context("sqlx")?) } - /// Wrapper for `consensus_dal().get_l1_batch_metadata()`. - pub async fn batch( - &mut self, - ctx: &ctx::Ctx, - number: L1BatchNumber, - ) -> ctx::Result> { - Ok(ctx - .wait(self.0.blocks_dal().get_l1_batch_metadata(number)) - .await? - .context("get_l1_batch_metadata()")?) - } - /// Wrapper for `FetcherCursor::new()`. pub async fn new_payload_queue( &mut self, diff --git a/core/node/consensus/src/storage/testonly.rs b/core/node/consensus/src/storage/testonly.rs index ccac1f7e45a..48feba61e15 100644 --- a/core/node/consensus/src/storage/testonly.rs +++ b/core/node/consensus/src/storage/testonly.rs @@ -5,7 +5,6 @@ use zksync_concurrency::{ctx, error::Wrap as _, time}; use zksync_consensus_roles::validator; use zksync_node_genesis::{insert_genesis_batch, GenesisParams}; use zksync_node_test_utils::{recover, snapshot, Snapshot}; -use zksync_types::{commitment::L1BatchWithMetadata, L1BatchNumber}; use super::ConnectionPool; @@ -31,28 +30,6 @@ impl ConnectionPool { Ok(()) } - /// Waits for the `number` L1 batch. - pub async fn wait_for_batch( - &self, - ctx: &ctx::Ctx, - number: L1BatchNumber, - ) -> ctx::Result { - const POLL_INTERVAL: time::Duration = time::Duration::milliseconds(50); - loop { - if let Some(payload) = self - .connection(ctx) - .await - .wrap("connection()")? - .batch(ctx, number) - .await - .wrap("batch()")? - { - return Ok(payload); - } - ctx.sleep(POLL_INTERVAL).await?; - } - } - /// Takes a storage snapshot at the last sealed L1 batch. pub(crate) async fn snapshot(&self, ctx: &ctx::Ctx) -> ctx::Result { let mut conn = self.connection(ctx).await.wrap("connection()")?; diff --git a/core/node/consensus/src/testonly.rs b/core/node/consensus/src/testonly.rs index 5baa1c7b1ee..3b990bf088f 100644 --- a/core/node/consensus/src/testonly.rs +++ b/core/node/consensus/src/testonly.rs @@ -1,25 +1,15 @@ //! Utilities for testing the consensus module. + use std::sync::Arc; use anyhow::Context as _; use rand::Rng; use zksync_concurrency::{ctx, error::Wrap as _, scope, sync, time}; -use zksync_config::{ - configs, - configs::{ - chain::OperationsManagerConfig, - consensus as config, - database::{MerkleTreeConfig, MerkleTreeMode}, - }, -}; +use zksync_config::{configs, configs::consensus as config}; use zksync_consensus_crypto::TextFmt as _; use zksync_consensus_network as network; use zksync_consensus_roles::validator; use zksync_dal::{CoreDal, DalError}; -use zksync_l1_contract_interface::i_executor::structures::StoredBatchInfo; -use zksync_metadata_calculator::{ - LazyAsyncTreeReader, MetadataCalculator, MetadataCalculatorConfig, -}; use zksync_node_api_server::web3::{state::InternalApiConfig, testonly::spawn_http_server}; use zksync_node_genesis::GenesisParams; use zksync_node_sync::{ @@ -28,29 +18,17 @@ use zksync_node_sync::{ testonly::MockMainNodeClient, ExternalIO, MainNodeClient, SyncState, }; -use zksync_node_test_utils::{create_l1_batch_metadata, l1_batch_metadata_to_commitment_artifacts}; -use zksync_state::RocksdbStorageOptions; +use zksync_node_test_utils::{create_l1_batch_metadata, create_l2_transaction}; use zksync_state_keeper::{ io::{IoCursor, L1BatchParams, L2BlockParams}, seal_criteria::NoopSealer, - testonly::{ - fund, l1_transaction, l2_transaction, test_batch_executor::MockReadStorageFactory, - MockBatchExecutor, - }, - AsyncRocksdbCache, MainBatchExecutor, OutputHandler, StateKeeperPersistence, - TreeWritesPersistence, ZkSyncStateKeeper, -}; -use zksync_test_account::Account; -use zksync_types::{ - fee_model::{BatchFeeInput, L1PeggedBatchFeeModelInput}, - Address, L1BatchNumber, L2BlockNumber, L2ChainId, PriorityOpId, ProtocolVersionId, + testonly::{test_batch_executor::MockReadStorageFactory, MockBatchExecutor}, + OutputHandler, StateKeeperPersistence, TreeWritesPersistence, ZkSyncStateKeeper, }; +use zksync_types::{Address, L1BatchNumber, L2BlockNumber, L2ChainId, ProtocolVersionId}; use zksync_web3_decl::client::{Client, DynClient, L2}; -use crate::{ - batch::{L1BatchCommit, L1BatchWithWitness, LastBlockCommit}, - en, ConnectionPool, -}; +use crate::{en, ConnectionPool}; /// Fake StateKeeper for tests. pub(super) struct StateKeeper { @@ -60,15 +38,14 @@ pub(super) struct StateKeeper { // timestamp of the last block. last_timestamp: u64, batch_sealed: bool, - // test L2 account - account: Account, - next_priority_op: PriorityOpId, + + fee_per_gas: u64, + gas_per_pubdata: u64, actions_sender: ActionQueueSender, sync_state: SyncState, addr: sync::watch::Receiver>, pool: ConnectionPool, - tree_reader: LazyAsyncTreeReader, } pub(super) fn config(cfg: &network::Config) -> (config::ConsensusConfig, config::ConsensusSecrets) { @@ -115,11 +92,7 @@ pub(super) struct StateKeeperRunner { actions_queue: ActionQueue, sync_state: SyncState, pool: ConnectionPool, - addr: sync::watch::Sender>, - rocksdb_dir: tempfile::TempDir, - metadata_calculator: MetadataCalculator, - account: Account, } impl StateKeeper { @@ -141,49 +114,24 @@ impl StateKeeper { let (actions_sender, actions_queue) = ActionQueue::new(); let addr = sync::watch::channel(None).0; let sync_state = SyncState::default(); - - let rocksdb_dir = tempfile::tempdir().context("tempdir()")?; - let merkle_tree_config = MerkleTreeConfig { - path: rocksdb_dir - .path() - .join("merkle_tree") - .to_string_lossy() - .into(), - mode: MerkleTreeMode::Lightweight, - ..Default::default() - }; - let operation_manager_config = OperationsManagerConfig { - delay_interval: 100, //`100ms` - }; - let config = - MetadataCalculatorConfig::for_main_node(&merkle_tree_config, &operation_manager_config); - let metadata_calculator = MetadataCalculator::new(config, None, pool.0.clone()) - .await - .context("MetadataCalculator::new()")?; - let tree_reader = metadata_calculator.tree_reader(); - let account = Account::random(); Ok(( Self { last_batch: cursor.l1_batch, last_block: cursor.next_l2_block - 1, last_timestamp: cursor.prev_l2_block_timestamp, batch_sealed: !pending_batch, - next_priority_op: PriorityOpId(1), + fee_per_gas: 10, + gas_per_pubdata: 100, actions_sender, sync_state: sync_state.clone(), addr: addr.subscribe(), pool: pool.clone(), - tree_reader, - account: account.clone(), }, StateKeeperRunner { actions_queue, sync_state, pool: pool.clone(), addr, - rocksdb_dir, - metadata_calculator, - account, }, )) } @@ -199,10 +147,7 @@ impl StateKeeper { protocol_version: ProtocolVersionId::latest(), validation_computational_gas_limit: u32::MAX, operator_address: GenesisParams::mock().config().fee_account, - fee_input: BatchFeeInput::L1Pegged(L1PeggedBatchFeeModelInput { - fair_l2_gas_price: 10, - l1_gas_price: 100, - }), + fee_input: Default::default(), first_l2_block: L2BlockParams { timestamp: self.last_timestamp, virtual_blocks: 1, @@ -225,18 +170,12 @@ impl StateKeeper { } /// Pushes a new L2 block with `transactions` transactions to the `StateKeeper`. - pub async fn push_random_block(&mut self, rng: &mut impl Rng) { + pub async fn push_block(&mut self, transactions: usize) { + assert!(transactions > 0); let mut actions = vec![self.open_block()]; - for _ in 0..rng.gen_range(3..8) { - let tx = match rng.gen() { - true => l2_transaction(&mut self.account, 1_000_000), - false => { - let tx = l1_transaction(&mut self.account, self.next_priority_op); - self.next_priority_op += 1; - tx - } - }; - actions.push(FetchedTransaction::new(tx).into()); + for _ in 0..transactions { + let tx = create_l2_transaction(self.fee_per_gas, self.gas_per_pubdata); + actions.push(FetchedTransaction::new(tx.into()).into()); } actions.push(SyncAction::SealL2Block); self.actions_sender.push_actions(actions).await; @@ -259,7 +198,7 @@ impl StateKeeper { if rng.gen_range(0..100) < 20 { self.seal_batch().await; } else { - self.push_random_block(rng).await; + self.push_block(rng.gen_range(3..8)).await; } } } @@ -270,49 +209,6 @@ impl StateKeeper { validator::BlockNumber(self.last_block.0.into()) } - /// Last L1 batch that has been sealed and will have - /// metadata computed eventually. - pub fn last_sealed_batch(&self) -> L1BatchNumber { - self.last_batch - (!self.batch_sealed) as u32 - } - - /// Loads a commitment to L1 batch directly from the database. - // TODO: ideally, we should rather fake fetching it from Ethereum. - // We can use `zksync_eth_client::clients::MockEthereum` for that, - // which implements `EthInterface`. It should be enough to use - // `MockEthereum.with_call_handler()`. - pub async fn load_batch_commit( - &self, - ctx: &ctx::Ctx, - number: L1BatchNumber, - ) -> ctx::Result { - // TODO: we should mock the `eth_sender` as well. - let mut conn = self.pool.connection(ctx).await?; - let this = conn.batch(ctx, number).await?.context("missing batch")?; - let prev = conn - .batch(ctx, number - 1) - .await? - .context("missing batch")?; - Ok(L1BatchCommit { - number, - this_batch: LastBlockCommit { - info: StoredBatchInfo::from(&this).hash(), - }, - prev_batch: LastBlockCommit { - info: StoredBatchInfo::from(&prev).hash(), - }, - }) - } - - /// Loads an `L1BatchWithWitness`. - pub async fn load_batch_with_witness( - &self, - ctx: &ctx::Ctx, - n: L1BatchNumber, - ) -> ctx::Result { - L1BatchWithWitness::load(ctx, n, &self.pool, &self.tree_reader).await - } - /// Connects to the json RPC endpoint exposed by the state keeper. pub async fn connect(&self, ctx: &ctx::Ctx) -> ctx::Result>> { let addr = sync::wait_for(ctx, &mut self.addr.clone(), Option::is_some) @@ -370,43 +266,7 @@ impl StateKeeper { } } -async fn mock_commitment_generator_step(ctx: &ctx::Ctx, pool: &ConnectionPool) -> ctx::Result<()> { - let mut conn = pool.connection(ctx).await.wrap("connection()")?; - let Some(first) = ctx - .wait( - conn.0 - .blocks_dal() - .get_next_l1_batch_ready_for_commitment_generation(), - ) - .await? - .map_err(|e| e.generalize())? - else { - return Ok(()); - }; - let last = ctx - .wait( - conn.0 - .blocks_dal() - .get_last_l1_batch_ready_for_commitment_generation(), - ) - .await? - .map_err(|e| e.generalize())? - .context("batch disappeared")?; - // Create artificial `L1BatchCommitmentArtifacts`. - for i in (first.0..=last.0).map(L1BatchNumber) { - let metadata = create_l1_batch_metadata(i.0); - let artifacts = l1_batch_metadata_to_commitment_artifacts(&metadata); - ctx.wait( - conn.0 - .blocks_dal() - .save_l1_batch_commitment_artifacts(i, &artifacts), - ) - .await??; - } - Ok(()) -} - -async fn mock_metadata_calculator_step(ctx: &ctx::Ctx, pool: &ConnectionPool) -> ctx::Result<()> { +async fn calculate_mock_metadata(ctx: &ctx::Ctx, pool: &ConnectionPool) -> ctx::Result<()> { let mut conn = pool.connection(ctx).await.wrap("connection()")?; let Some(last) = ctx .wait(conn.0.blocks_dal().get_sealed_l1_batch_number()) @@ -446,122 +306,6 @@ async fn mock_metadata_calculator_step(ctx: &ctx::Ctx, pool: &ConnectionPool) -> } impl StateKeeperRunner { - // Executes the state keeper task with real metadata calculator task - // and fake commitment generator (because real one is too slow). - pub async fn run_real(self, ctx: &ctx::Ctx) -> anyhow::Result<()> { - let res = scope::run!(ctx, |ctx, s| async { - // Fund the test account. Required for L2 transactions to succeed. - fund(&self.pool.0, &[self.account.address]).await; - - let (stop_send, stop_recv) = sync::watch::channel(false); - let (persistence, l2_block_sealer) = - StateKeeperPersistence::new(self.pool.0.clone(), Address::repeat_byte(11), 5); - - let io = ExternalIO::new( - self.pool.0.clone(), - self.actions_queue, - Box::::default(), - L2ChainId::default(), - ) - .await?; - - s.spawn_bg(async { - Ok(l2_block_sealer - .run() - .await - .context("l2_block_sealer.run()")?) - }); - - s.spawn_bg({ - let stop_recv = stop_recv.clone(); - async { - self.metadata_calculator.run(stop_recv).await?; - Ok(()) - } - }); - - // TODO: should be replaceable with `PostgresFactory`. - // Caching shouldn't be needed for tests. - let (async_cache, async_catchup_task) = AsyncRocksdbCache::new( - self.pool.0.clone(), - self.rocksdb_dir - .path() - .join("cache") - .to_string_lossy() - .into(), - RocksdbStorageOptions { - block_cache_capacity: (1 << 20), // `1MB` - max_open_files: None, - }, - ); - s.spawn_bg({ - let stop_recv = stop_recv.clone(); - async { - async_catchup_task.run(stop_recv).await?; - Ok(()) - } - }); - s.spawn_bg::<()>(async { - loop { - mock_commitment_generator_step(ctx, &self.pool).await?; - // Sleep real time. - ctx.wait(tokio::time::sleep(tokio::time::Duration::from_millis(100))) - .await?; - } - }); - - s.spawn_bg({ - let stop_recv = stop_recv.clone(); - async { - ZkSyncStateKeeper::new( - stop_recv, - Box::new(io), - Box::new(MainBatchExecutor::new(false, false)), - OutputHandler::new(Box::new(persistence.with_tx_insertion())) - .with_handler(Box::new(self.sync_state.clone())), - Arc::new(NoopSealer), - Arc::new(async_cache), - ) - .run() - .await - .context("ZkSyncStateKeeper::run()")?; - Ok(()) - } - }); - s.spawn_bg(async { - // Spawn HTTP server. - let cfg = InternalApiConfig::new( - &configs::api::Web3JsonRpcConfig::for_tests(), - &configs::contracts::ContractsConfig::for_tests(), - &configs::GenesisConfig::for_tests(), - ); - let mut server = spawn_http_server( - cfg, - self.pool.0.clone(), - Default::default(), - Arc::default(), - stop_recv, - ) - .await; - if let Ok(addr) = ctx.wait(server.wait_until_ready()).await { - self.addr.send_replace(Some(addr)); - tracing::info!("API server ready!"); - } - ctx.canceled().await; - server.shutdown().await; - Ok(()) - }); - ctx.canceled().await; - stop_send.send_replace(true); - Ok(()) - }) - .await; - match res { - Ok(()) | Err(ctx::Error::Canceled(_)) => Ok(()), - Err(ctx::Error::Internal(err)) => Err(err), - } - } - /// Executes the StateKeeper task. pub async fn run(self, ctx: &ctx::Ctx) -> anyhow::Result<()> { let res = scope::run!(ctx, |ctx, s| async { @@ -585,8 +329,7 @@ impl StateKeeperRunner { }); s.spawn_bg::<()>(async { loop { - mock_metadata_calculator_step(ctx, &self.pool).await?; - mock_commitment_generator_step(ctx, &self.pool).await?; + calculate_mock_metadata(ctx, &self.pool).await?; // Sleep real time. ctx.wait(tokio::time::sleep(tokio::time::Duration::from_millis(100))) .await?; diff --git a/core/node/consensus/src/tests.rs b/core/node/consensus/src/tests.rs index 79784f0fbb5..6ed65161362 100644 --- a/core/node/consensus/src/tests.rs +++ b/core/node/consensus/src/tests.rs @@ -1,4 +1,3 @@ -#![allow(unused)] use anyhow::Context as _; use test_casing::test_casing; use tracing::Instrument as _; @@ -10,7 +9,6 @@ use zksync_consensus_roles::{ validator, validator::testonly::{Setup, SetupSpec}, }; -use zksync_dal::CoreDal; use zksync_node_test_utils::Snapshot; use zksync_types::{L1BatchNumber, L2BlockNumber}; @@ -517,45 +515,3 @@ async fn test_centralized_fetcher(from_snapshot: bool) { .await .unwrap(); } - -/// Tests that generated L1 batch witnesses can be verified successfully. -/// TODO: add tests for verification failures. -#[tokio::test] -async fn test_batch_witness() { - zksync_concurrency::testonly::abort_on_panic(); - let ctx = &ctx::test_root(&ctx::RealClock); - let rng = &mut ctx.rng(); - - scope::run!(ctx, |ctx, s| async { - let pool = ConnectionPool::from_genesis().await; - let (mut node, runner) = testonly::StateKeeper::new(ctx, pool.clone()).await?; - s.spawn_bg(runner.run_real(ctx)); - - tracing::info!("analyzing storage"); - { - let mut conn = pool.connection(ctx).await.unwrap(); - let mut n = validator::BlockNumber(0); - while let Some(p) = conn.payload(ctx, n).await? { - tracing::info!("block[{n}] = {p:?}"); - n = n + 1; - } - } - - // Seal a bunch of batches. - node.push_random_blocks(rng, 10).await; - node.seal_batch().await; - pool.wait_for_batch(ctx, node.last_sealed_batch()).await?; - // We can verify only 2nd batch onward, because - // batch witness verifies parent of the last block of the - // previous batch (and 0th batch contains only 1 block). - for n in 2..=node.last_sealed_batch().0 { - let n = L1BatchNumber(n); - let batch_with_witness = node.load_batch_with_witness(ctx, n).await?; - let commit = node.load_batch_commit(ctx, n).await?; - batch_with_witness.verify(&commit)?; - } - Ok(()) - }) - .await - .unwrap(); -} diff --git a/core/node/eth_watch/src/tests.rs b/core/node/eth_watch/src/tests.rs index 6b15c71bd14..71d33f5c973 100644 --- a/core/node/eth_watch/src/tests.rs +++ b/core/node/eth_watch/src/tests.rs @@ -142,7 +142,7 @@ fn build_l1_tx(serial_id: u64, eth_block: u64) -> L1Tx { execute: Execute { contract_address: Address::repeat_byte(0x11), calldata: vec![1, 2, 3], - factory_deps: vec![], + factory_deps: Some(vec![]), value: U256::zero(), }, common_data: L1TxCommonData { @@ -173,7 +173,7 @@ fn build_upgrade_tx(id: ProtocolVersionId, eth_block: u64) -> ProtocolUpgradeTx execute: Execute { contract_address: Address::repeat_byte(0x11), calldata: vec![1, 2, 3], - factory_deps: vec![], + factory_deps: None, value: U256::zero(), }, common_data: ProtocolUpgradeTxCommonData { @@ -562,6 +562,7 @@ fn upgrade_into_diamond_cut(upgrade: ProtocolUpgrade) -> Token { tx: Default::default(), factory_deps: vec![], eth_block: 0, + received_timestamp_ms: 0, }) else { unreachable!() diff --git a/core/node/metadata_calculator/Cargo.toml b/core/node/metadata_calculator/Cargo.toml index b694c1d198c..5f336bb11d4 100644 --- a/core/node/metadata_calculator/Cargo.toml +++ b/core/node/metadata_calculator/Cargo.toml @@ -10,7 +10,6 @@ keywords.workspace = true categories.workspace = true [dependencies] -zksync_crypto.workspace = true zksync_dal.workspace = true zksync_health_check.workspace = true zksync_merkle_tree.workspace = true diff --git a/core/node/metadata_calculator/src/api_server/mod.rs b/core/node/metadata_calculator/src/api_server/mod.rs index c90b889df91..77773ffa37c 100644 --- a/core/node/metadata_calculator/src/api_server/mod.rs +++ b/core/node/metadata_calculator/src/api_server/mod.rs @@ -12,7 +12,6 @@ use axum::{ }; use serde::{Deserialize, Serialize}; use tokio::sync::watch; -use zksync_crypto::hasher::blake2::Blake2Hasher; use zksync_health_check::{CheckHealth, Health, HealthStatus}; use zksync_merkle_tree::NoVersionError; use zksync_types::{L1BatchNumber, H256, U256}; @@ -35,7 +34,7 @@ struct TreeProofsResponse { entries: Vec, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct TreeEntryWithProof { #[serde(default, skip_serializing_if = "H256::is_zero")] pub value: H256, @@ -60,21 +59,6 @@ impl TreeEntryWithProof { merkle_path, } } - - /// Verifies the entry. - pub fn verify(&self, key: U256, trusted_root_hash: H256) -> anyhow::Result<()> { - let mut merkle_path = self.merkle_path.clone(); - merkle_path.reverse(); - zksync_merkle_tree::TreeEntryWithProof { - base: zksync_merkle_tree::TreeEntry { - value: self.value, - leaf_index: self.index, - key, - }, - merkle_path, - } - .verify(&Blake2Hasher, trusted_root_hash) - } } /// Server-side tree API error. diff --git a/core/node/state_keeper/Cargo.toml b/core/node/state_keeper/Cargo.toml index c2ac940eef3..afc2d6ed826 100644 --- a/core/node/state_keeper/Cargo.toml +++ b/core/node/state_keeper/Cargo.toml @@ -24,8 +24,6 @@ zksync_node_fee_model.workspace = true zksync_utils.workspace = true zksync_contracts.workspace = true zksync_protobuf.workspace = true -zksync_test_account.workspace = true -zksync_node_genesis.workspace = true zksync_node_test_utils.workspace = true vm_utils.workspace = true @@ -42,8 +40,10 @@ hex.workspace = true [dev-dependencies] assert_matches.workspace = true test-casing.workspace = true -futures.workspace = true tempfile.workspace = true +futures.workspace = true +zksync_test_account.workspace = true +zksync_node_genesis.workspace = true zksync_eth_client.workspace = true zksync_system_constants.workspace = true diff --git a/core/node/state_keeper/src/batch_executor/mod.rs b/core/node/state_keeper/src/batch_executor/mod.rs index 8703831f395..eb6292ee1da 100644 --- a/core/node/state_keeper/src/batch_executor/mod.rs +++ b/core/node/state_keeper/src/batch_executor/mod.rs @@ -18,10 +18,11 @@ use crate::{ types::ExecutionMetricsForCriteria, }; -pub mod main_executor; #[cfg(test)] mod tests; +pub mod main_executor; + /// Representation of a transaction executed in the virtual machine. #[derive(Debug, Clone)] pub enum TxExecutionResult { diff --git a/core/node/state_keeper/src/batch_executor/tests/tester.rs b/core/node/state_keeper/src/batch_executor/tests/tester.rs index 39f860b752e..d091520e652 100644 --- a/core/node/state_keeper/src/batch_executor/tests/tester.rs +++ b/core/node/state_keeper/src/batch_executor/tests/tester.rs @@ -17,11 +17,12 @@ use zksync_node_test_utils::prepare_recovery_snapshot; use zksync_state::{ReadStorageFactory, RocksdbStorageOptions}; use zksync_test_account::{Account, DeployContractsTx, TxType}; use zksync_types::{ - block::L2BlockHasher, ethabi::Token, protocol_version::ProtocolSemanticVersion, + block::L2BlockHasher, ethabi::Token, fee::Fee, protocol_version::ProtocolSemanticVersion, snapshots::SnapshotRecoveryStatus, storage_writes_deduplicator::StorageWritesDeduplicator, system_contracts::get_system_smart_contracts, utils::storage_key_for_standard_token_balance, AccountTreeId, Address, Execute, L1BatchNumber, L2BlockNumber, PriorityOpId, ProtocolVersionId, - StorageKey, StorageLog, Transaction, H256, L2_BASE_TOKEN_ADDRESS, U256, + StorageKey, StorageLog, Transaction, H256, L2_BASE_TOKEN_ADDRESS, + SYSTEM_CONTEXT_MINIMAL_BASE_FEE, U256, }; use zksync_utils::u256_to_h256; @@ -31,12 +32,13 @@ use super::{ }; use crate::{ batch_executor::{BatchExecutorHandle, TxExecutionResult}, - testonly, testonly::BASE_SYSTEM_CONTRACTS, tests::{default_l1_batch_env, default_system_env}, AsyncRocksdbCache, BatchExecutor, MainBatchExecutor, }; +const DEFAULT_GAS_PER_PUBDATA: u32 = 10000; + /// Representation of configuration parameters used by the state keeper. /// Has sensible defaults for most tests, each of which can be overridden. #[derive(Debug)] @@ -344,7 +346,15 @@ impl AccountLoadNextExecutable for Account { ) } fn l1_execute(&mut self, serial_id: PriorityOpId) -> Transaction { - testonly::l1_transaction(self, serial_id) + self.get_l1_tx( + Execute { + contract_address: Address::random(), + value: Default::default(), + calldata: vec![], + factory_deps: None, + }, + serial_id.0, + ) } /// Returns a valid `execute` transaction. @@ -363,12 +373,10 @@ impl AccountLoadNextExecutable for Account { ) -> Transaction { // For each iteration of the expensive contract, there are two slots that are updated: // the length of the vector and the new slot with the element itself. - let minimal_fee = 2 - * testonly::DEFAULT_GAS_PER_PUBDATA - * writes - * INITIAL_STORAGE_WRITE_PUBDATA_BYTES as u32; + let minimal_fee = + 2 * DEFAULT_GAS_PER_PUBDATA * writes * INITIAL_STORAGE_WRITE_PUBDATA_BYTES as u32; - let fee = testonly::fee(minimal_fee + gas_limit); + let fee = fee(minimal_fee + gas_limit); self.get_l2_tx_for_execute( Execute { @@ -383,7 +391,7 @@ impl AccountLoadNextExecutable for Account { } .to_bytes(), value: Default::default(), - factory_deps: vec![], + factory_deps: None, }, Some(fee), ) @@ -392,7 +400,16 @@ impl AccountLoadNextExecutable for Account { /// Returns a valid `execute` transaction. /// Automatically increments nonce of the account. fn execute_with_gas_limit(&mut self, gas_limit: u32) -> Transaction { - testonly::l2_transaction(self, gas_limit) + let fee = fee(gas_limit); + self.get_l2_tx_for_execute( + Execute { + contract_address: Address::random(), + calldata: vec![], + value: Default::default(), + factory_deps: None, + }, + Some(fee), + ) } /// Returns a transaction to the loadnext contract with custom gas limit and expected burned gas amount. @@ -403,7 +420,7 @@ impl AccountLoadNextExecutable for Account { gas_to_burn: u32, gas_limit: u32, ) -> Transaction { - let fee = testonly::fee(gas_limit); + let fee = fee(gas_limit); let calldata = mock_loadnext_gas_burn_calldata(gas_to_burn); self.get_l2_tx_for_execute( @@ -411,13 +428,22 @@ impl AccountLoadNextExecutable for Account { contract_address: address, calldata, value: Default::default(), - factory_deps: vec![], + factory_deps: None, }, Some(fee), ) } } +fn fee(gas_limit: u32) -> Fee { + Fee { + gas_limit: U256::from(gas_limit), + max_fee_per_gas: SYSTEM_CONTEXT_MINIMAL_BASE_FEE.into(), + max_priority_fee_per_gas: U256::zero(), + gas_per_pubdata_limit: U256::from(DEFAULT_GAS_PER_PUBDATA), + } +} + pub fn mock_loadnext_gas_burn_calldata(gas: u32) -> Vec { let loadnext_contract = get_loadnext_contract(); let contract_function = loadnext_contract.contract.function("burnGas").unwrap(); diff --git a/core/node/state_keeper/src/testonly/mod.rs b/core/node/state_keeper/src/testonly/mod.rs index 3ba61949516..b50cd483fc5 100644 --- a/core/node/state_keeper/src/testonly/mod.rs +++ b/core/node/state_keeper/src/testonly/mod.rs @@ -14,15 +14,7 @@ use multivm::{ use once_cell::sync::Lazy; use tokio::sync::{mpsc, watch}; use zksync_contracts::BaseSystemContracts; -use zksync_dal::{ConnectionPool, Core, CoreDal as _}; use zksync_state::ReadStorageFactory; -use zksync_test_account::Account; -use zksync_types::{ - fee::Fee, utils::storage_key_for_standard_token_balance, AccountTreeId, Address, Execute, - L1BatchNumber, L2BlockNumber, PriorityOpId, StorageLog, Transaction, H256, - L2_BASE_TOKEN_ADDRESS, SYSTEM_CONTEXT_MINIMAL_BASE_FEE, U256, -}; -use zksync_utils::u256_to_h256; use crate::{ batch_executor::{BatchExecutor, BatchExecutorHandle, Command, TxExecutionResult}, @@ -112,76 +104,3 @@ impl BatchExecutor for MockBatchExecutor { Some(BatchExecutorHandle::from_raw(handle, send)) } } - -/// Adds funds for specified account list. -/// Expects genesis to be performed (i.e. `setup_storage` called beforehand). -pub async fn fund(pool: &ConnectionPool, addresses: &[Address]) { - let mut storage = pool.connection().await.unwrap(); - - let eth_amount = U256::from(10u32).pow(U256::from(32)); //10^32 wei - - for address in addresses { - let key = storage_key_for_standard_token_balance( - AccountTreeId::new(L2_BASE_TOKEN_ADDRESS), - address, - ); - let value = u256_to_h256(eth_amount); - let storage_log = StorageLog::new_write_log(key, value); - - storage - .storage_logs_dal() - .append_storage_logs(L2BlockNumber(0), &[(H256::zero(), vec![storage_log])]) - .await - .unwrap(); - if storage - .storage_logs_dedup_dal() - .filter_written_slots(&[storage_log.key.hashed_key()]) - .await - .unwrap() - .is_empty() - { - storage - .storage_logs_dedup_dal() - .insert_initial_writes(L1BatchNumber(0), &[storage_log.key]) - .await - .unwrap(); - } - } -} - -pub(crate) const DEFAULT_GAS_PER_PUBDATA: u32 = 10000; - -pub(crate) fn fee(gas_limit: u32) -> Fee { - Fee { - gas_limit: U256::from(gas_limit), - max_fee_per_gas: SYSTEM_CONTEXT_MINIMAL_BASE_FEE.into(), - max_priority_fee_per_gas: U256::zero(), - gas_per_pubdata_limit: U256::from(DEFAULT_GAS_PER_PUBDATA), - } -} - -/// Returns a valid L2 transaction. -/// Automatically increments nonce of the account. -pub fn l2_transaction(account: &mut Account, gas_limit: u32) -> Transaction { - account.get_l2_tx_for_execute( - Execute { - contract_address: Address::random(), - calldata: vec![], - value: Default::default(), - factory_deps: vec![], - }, - Some(fee(gas_limit)), - ) -} - -pub fn l1_transaction(account: &mut Account, serial_id: PriorityOpId) -> Transaction { - account.get_l1_tx( - Execute { - contract_address: Address::random(), - value: Default::default(), - calldata: vec![], - factory_deps: vec![], - }, - serial_id.0, - ) -} diff --git a/core/node/state_keeper/src/updates/l2_block_updates.rs b/core/node/state_keeper/src/updates/l2_block_updates.rs index 34cfad44f93..efc09472fb0 100644 --- a/core/node/state_keeper/src/updates/l2_block_updates.rs +++ b/core/node/state_keeper/src/updates/l2_block_updates.rs @@ -120,7 +120,7 @@ impl L2BlockUpdates { }; // Get transaction factory deps - let factory_deps = &tx.execute.factory_deps; + let factory_deps = tx.execute.factory_deps.as_deref().unwrap_or_default(); let tx_factory_deps: HashMap<_, _> = factory_deps .iter() .map(|bytecode| (hash_bytecode(bytecode), bytecode)) diff --git a/core/node/test_utils/src/lib.rs b/core/node/test_utils/src/lib.rs index 566eab9c3d2..9abd968acb1 100644 --- a/core/node/test_utils/src/lib.rs +++ b/core/node/test_utils/src/lib.rs @@ -123,7 +123,7 @@ pub fn create_l2_transaction(fee_per_gas: u64, gas_per_pubdata: u64) -> L2Tx { U256::zero(), L2ChainId::from(271), &K256PrivateKey::random(), - vec![], + None, PaymasterParams::default(), ) .unwrap(); diff --git a/core/node/vm_runner/src/tests/mod.rs b/core/node/vm_runner/src/tests/mod.rs index 0d106235f71..d0374e0d5fa 100644 --- a/core/node/vm_runner/src/tests/mod.rs +++ b/core/node/vm_runner/src/tests/mod.rs @@ -189,7 +189,7 @@ pub fn create_l2_transaction( contract_address: Address::random(), calldata: vec![], value: Default::default(), - factory_deps: vec![], + factory_deps: None, }, Some(fee), ); diff --git a/core/tests/loadnext/src/sdk/operations/deploy_contract.rs b/core/tests/loadnext/src/sdk/operations/deploy_contract.rs index af621249ed8..adf1fe09ee7 100644 --- a/core/tests/loadnext/src/sdk/operations/deploy_contract.rs +++ b/core/tests/loadnext/src/sdk/operations/deploy_contract.rs @@ -73,7 +73,7 @@ where execute_calldata, fee, nonce, - vec![bytecode.clone()], + Some(vec![bytecode.clone()]), paymaster_params, ) .await @@ -150,7 +150,7 @@ where Default::default(), self.wallet.address(), self.value.unwrap_or_default(), - factory_deps, + Some(factory_deps), paymaster_params, ); self.wallet diff --git a/core/tests/loadnext/src/sdk/operations/execute_contract.rs b/core/tests/loadnext/src/sdk/operations/execute_contract.rs index 18b93008a73..3572d24a8b5 100644 --- a/core/tests/loadnext/src/sdk/operations/execute_contract.rs +++ b/core/tests/loadnext/src/sdk/operations/execute_contract.rs @@ -67,7 +67,7 @@ where calldata, fee, nonce, - self.factory_deps.unwrap_or_default(), + self.factory_deps, paymaster_params, ) .await @@ -150,7 +150,7 @@ where Default::default(), self.wallet.address(), self.value.unwrap_or_default(), - self.factory_deps.clone().unwrap_or_default(), + self.factory_deps.clone(), paymaster_params, ); self.wallet diff --git a/core/tests/loadnext/src/sdk/operations/transfer.rs b/core/tests/loadnext/src/sdk/operations/transfer.rs index 34bab615c7c..8fe35fae92e 100644 --- a/core/tests/loadnext/src/sdk/operations/transfer.rs +++ b/core/tests/loadnext/src/sdk/operations/transfer.rs @@ -155,7 +155,7 @@ where Execute { contract_address: to, calldata: Default::default(), - factory_deps: vec![], + factory_deps: None, value: amount, } } else { @@ -163,7 +163,7 @@ where Execute { contract_address: token, calldata: create_transfer_calldata(to, amount), - factory_deps: vec![], + factory_deps: None, value: Default::default(), } }; diff --git a/core/tests/loadnext/src/sdk/signer.rs b/core/tests/loadnext/src/sdk/signer.rs index 0f4b1cf2971..a992772909b 100644 --- a/core/tests/loadnext/src/sdk/signer.rs +++ b/core/tests/loadnext/src/sdk/signer.rs @@ -57,7 +57,7 @@ impl Signer { fee, self.eth_signer.get_address().await?, amount, - vec![], + None, Default::default(), ); @@ -79,7 +79,7 @@ impl Signer { fee, self.eth_signer.get_address().await?, U256::zero(), - vec![], + None, paymaster_params, ); @@ -98,7 +98,7 @@ impl Signer { calldata: Vec, fee: Fee, nonce: Nonce, - factory_deps: Vec>, + factory_deps: Option>>, paymaster_params: PaymasterParams, ) -> Result { self.sign_execute_contract_for_deploy( @@ -118,7 +118,7 @@ impl Signer { calldata: Vec, fee: Fee, nonce: Nonce, - factory_deps: Vec>, + factory_deps: Option>>, paymaster_params: PaymasterParams, ) -> Result { let mut execute_contract = L2Tx::new( diff --git a/core/tests/test_account/src/lib.rs b/core/tests/test_account/src/lib.rs index 619caeb1ebd..9574c47b9ab 100644 --- a/core/tests/test_account/src/lib.rs +++ b/core/tests/test_account/src/lib.rs @@ -8,10 +8,15 @@ use zksync_system_constants::{ REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_BYTE, }; use zksync_types::{ - abi, fee::Fee, l2::L2Tx, utils::deployed_address_create, Address, Execute, K256PrivateKey, - L2ChainId, Nonce, Transaction, H256, PRIORITY_OPERATION_L2_TX_TYPE, U256, + api, + fee::Fee, + l1::{OpProcessingType, PriorityQueueType}, + l2::L2Tx, + utils::deployed_address_create, + Address, Execute, ExecuteTransactionCommon, K256PrivateKey, L1TxCommonData, L2ChainId, Nonce, + PriorityOpId, Transaction, H256, U256, }; -use zksync_utils::{address_to_u256, bytecode::hash_bytecode, h256_to_u256}; +use zksync_utils::bytecode::hash_bytecode; pub const L1_TEST_GAS_PER_PUBDATA_BYTE: u32 = 800; const BASE_FEE: u64 = 2_000_000_000; @@ -68,22 +73,28 @@ impl Account { value, factory_deps, } = execute; - L2Tx::new_signed( + let mut tx = L2Tx::new_signed( contract_address, calldata, nonce, - fee.unwrap_or_else(Self::default_fee), + fee.unwrap_or_else(|| self.default_fee()), value, L2ChainId::default(), &self.private_key, factory_deps, Default::default(), ) - .expect("should create a signed execute transaction") - .into() + .expect("should create a signed execute transaction"); + + // Set the real transaction hash, which is necessary for transaction execution in VM to function properly. + let mut tx_request = api::TransactionRequest::from(tx.clone()); + tx_request.chain_id = Some(L2ChainId::default().as_u64()); + let tx_hash = tx_request.get_tx_hash().unwrap(); + tx.set_input(H256::random().0.to_vec(), tx_hash); + tx.into() } - pub fn default_fee() -> Fee { + fn default_fee(&self) -> Fee { Fee { gas_limit: U256::from(2000000000u32), max_fee_per_gas: U256::from(BASE_FEE), @@ -127,7 +138,7 @@ impl Account { let execute = Execute { contract_address: CONTRACT_DEPLOYER_ADDRESS, calldata, - factory_deps, + factory_deps: Some(factory_deps), value: U256::zero(), }; @@ -149,42 +160,27 @@ impl Account { pub fn get_l1_tx(&self, execute: Execute, serial_id: u64) -> Transaction { let max_fee_per_gas = U256::from(0u32); let gas_limit = U256::from(20_000_000); - let factory_deps = execute.factory_deps; - abi::Transaction::L1 { - tx: abi::L2CanonicalTransaction { - tx_type: PRIORITY_OPERATION_L2_TX_TYPE.into(), - from: address_to_u256(&self.address), - to: address_to_u256(&execute.contract_address), + + Transaction { + common_data: ExecuteTransactionCommon::L1(L1TxCommonData { + sender: self.address, gas_limit, - gas_per_pubdata_byte_limit: REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_BYTE.into(), + gas_per_pubdata_limit: REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_BYTE.into(), + to_mint: gas_limit * max_fee_per_gas + execute.value, + serial_id: PriorityOpId(serial_id), max_fee_per_gas, - max_priority_fee_per_gas: 0.into(), - paymaster: 0.into(), - nonce: serial_id.into(), - value: execute.value, - reserved: [ - // `to_mint` - gas_limit * max_fee_per_gas + execute.value, - // `refund_recipient` - address_to_u256(&self.address), - 0.into(), - 0.into(), - ], - data: execute.calldata, - signature: vec![], - factory_deps: factory_deps - .iter() - .map(|b| h256_to_u256(hash_bytecode(b))) - .collect(), - paymaster_input: vec![], - reserved_dynamic: vec![], - } - .into(), - factory_deps, - eth_block: 0, + canonical_tx_hash: H256::from_low_u64_be(serial_id), + layer_2_tip_fee: Default::default(), + op_processing_type: OpProcessingType::Common, + priority_queue_type: PriorityQueueType::Deque, + eth_block: 0, + refund_recipient: self.address, + full_fee: Default::default(), + }), + execute, + received_timestamp_ms: 0, + raw_bytes: None, } - .try_into() - .unwrap() } pub fn get_test_contract_transaction( @@ -215,7 +211,7 @@ impl Account { contract_address: address, calldata, value: value.unwrap_or_default(), - factory_deps: vec![], + factory_deps: None, }; match tx_type { TxType::L2 => self.get_l2_tx_for_execute(execute, None), @@ -234,7 +230,7 @@ impl Account { contract_address: address, calldata, value: U256::zero(), - factory_deps: vec![], + factory_deps: None, }; match tx_type { diff --git a/core/tests/vm-benchmark/harness/src/lib.rs b/core/tests/vm-benchmark/harness/src/lib.rs index 137a3b654cb..83750d2e2a2 100644 --- a/core/tests/vm-benchmark/harness/src/lib.rs +++ b/core/tests/vm-benchmark/harness/src/lib.rs @@ -147,7 +147,7 @@ pub fn get_deploy_tx(code: &[u8]) -> Transaction { U256::zero(), L2ChainId::from(270), &PRIVATE_KEY, - vec![code.to_vec()], // maybe not needed? + Some(vec![code.to_vec()]), // maybe not needed? Default::default(), ) .expect("should create a signed execute transaction"); diff --git a/prover/Cargo.lock b/prover/Cargo.lock index 44c2a8b8395..7f30f6be590 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -8974,7 +8974,6 @@ dependencies = [ "tracing", "vise", "zksync_config", - "zksync_crypto", "zksync_dal", "zksync_health_check", "zksync_merkle_tree", @@ -9046,7 +9045,6 @@ dependencies = [ "anyhow", "async-trait", "secrecy", - "tempfile", "tracing", "zksync_concurrency", "zksync_config", @@ -9058,16 +9056,10 @@ dependencies = [ "zksync_consensus_storage", "zksync_consensus_utils", "zksync_dal", - "zksync_l1_contract_interface", - "zksync_merkle_tree", - "zksync_metadata_calculator", "zksync_node_sync", "zksync_protobuf", - "zksync_state", "zksync_state_keeper", - "zksync_system_constants", "zksync_types", - "zksync_utils", "zksync_web3_decl", ] @@ -9455,13 +9447,11 @@ dependencies = [ "zksync_dal", "zksync_mempool", "zksync_node_fee_model", - "zksync_node_genesis", "zksync_node_test_utils", "zksync_protobuf", "zksync_shared_metrics", "zksync_state", "zksync_storage", - "zksync_test_account", "zksync_types", "zksync_utils", ] @@ -9530,19 +9520,6 @@ dependencies = [ "zksync_utils", ] -[[package]] -name = "zksync_test_account" -version = "0.1.0" -dependencies = [ - "ethabi", - "hex", - "zksync_contracts", - "zksync_eth_signer", - "zksync_system_constants", - "zksync_types", - "zksync_utils", -] - [[package]] name = "zksync_types" version = "0.1.0"