diff --git a/core/bin/system-constants-generator/src/utils.rs b/core/bin/system-constants-generator/src/utils.rs index b138c5261a8..cb874545def 100644 --- a/core/bin/system-constants-generator/src/utils.rs +++ b/core/bin/system-constants-generator/src/utils.rs @@ -18,7 +18,7 @@ use zksync_contracts::{ }; use zksync_state::{InMemoryStorage, StorageView, WriteStorage}; use zksync_types::{ - block::legacy_miniblock_hash, ethabi::Token, fee::Fee, l1::L1Tx, l2::L2Tx, + block::MiniblockHasher, ethabi::Token, fee::Fee, l1::L1Tx, l2::L2Tx, utils::storage_key_for_eth_balance, AccountTreeId, Address, Execute, L1BatchNumber, L1TxCommonData, L2ChainId, MiniblockNumber, Nonce, ProtocolVersionId, StorageKey, Timestamp, Transaction, BOOTLOADER_ADDRESS, H256, SYSTEM_CONTEXT_ADDRESS, @@ -181,7 +181,7 @@ fn default_l1_batch() -> L1BatchEnv { first_l2_block: L2BlockEnv { number: 1, timestamp: 100, - prev_block_hash: legacy_miniblock_hash(MiniblockNumber(0)), + prev_block_hash: MiniblockHasher::legacy_hash(MiniblockNumber(0)), max_virtual_blocks_to_create: 100, }, } diff --git a/core/lib/dal/src/blocks_web3_dal.rs b/core/lib/dal/src/blocks_web3_dal.rs index 87f6fca1eb2..a8ef6b056e6 100644 --- a/core/lib/dal/src/blocks_web3_dal.rs +++ b/core/lib/dal/src/blocks_web3_dal.rs @@ -588,7 +588,7 @@ impl BlocksWeb3Dal<'_, '_> { mod tests { use zksync_contracts::BaseSystemContractsHashes; use zksync_types::{ - block::{miniblock_hash, MiniblockHeader}, + block::{MiniblockHasher, MiniblockHeader}, MiniblockNumber, ProtocolVersion, ProtocolVersionId, }; @@ -613,16 +613,13 @@ mod tests { }; conn.blocks_dal().insert_miniblock(&header).await.unwrap(); + let block_hash = MiniblockHasher::new(MiniblockNumber(0), 0, H256::zero()) + .finalize(ProtocolVersionId::latest()); let block_ids = [ api::BlockId::Number(api::BlockNumber::Earliest), api::BlockId::Number(api::BlockNumber::Latest), api::BlockId::Number(api::BlockNumber::Number(0.into())), - api::BlockId::Hash(miniblock_hash( - MiniblockNumber(0), - 0, - H256::zero(), - H256::zero(), - )), + api::BlockId::Hash(block_hash), ]; for block_id in block_ids { let block = conn @@ -632,24 +629,18 @@ mod tests { let block = block.unwrap().unwrap(); assert!(block.transactions.is_empty()); assert_eq!(block.number, U64::zero()); - assert_eq!( - block.hash, - miniblock_hash(MiniblockNumber(0), 0, H256::zero(), H256::zero()) - ); + assert_eq!(block.hash, block_hash); let tx_count = conn.blocks_web3_dal().get_block_tx_count(block_id).await; assert_eq!(tx_count.unwrap(), Some((MiniblockNumber(0), 8.into()))); } + let non_existing_block_hash = MiniblockHasher::new(MiniblockNumber(1), 1, H256::zero()) + .finalize(ProtocolVersionId::latest()); let non_existing_block_ids = [ api::BlockId::Number(api::BlockNumber::Pending), api::BlockId::Number(api::BlockNumber::Number(1.into())), - api::BlockId::Hash(miniblock_hash( - MiniblockNumber(1), - 1, - H256::zero(), - H256::zero(), - )), + api::BlockId::Hash(non_existing_block_hash), ]; for block_id in non_existing_block_ids { let block = conn @@ -751,14 +742,16 @@ mod tests { .await .unwrap(); - let hash = miniblock_hash(MiniblockNumber(0), 0, H256::zero(), H256::zero()); + let hash = MiniblockHasher::new(MiniblockNumber(0), 0, H256::zero()) + .finalize(ProtocolVersionId::latest()); let miniblock_number = conn .blocks_web3_dal() .resolve_block_id(api::BlockId::Hash(hash)) .await; assert_eq!(miniblock_number.unwrap(), Some(MiniblockNumber(0))); - let hash = miniblock_hash(MiniblockNumber(1), 1, H256::zero(), H256::zero()); + let hash = MiniblockHasher::new(MiniblockNumber(1), 1, H256::zero()) + .finalize(ProtocolVersionId::latest()); let miniblock_number = conn .blocks_web3_dal() .resolve_block_id(api::BlockId::Hash(hash)) @@ -778,7 +771,8 @@ mod tests { let mut header = MiniblockHeader { number: MiniblockNumber(0), timestamp: 0, - hash: miniblock_hash(MiniblockNumber(0), 0, H256::zero(), H256::zero()), + hash: MiniblockHasher::new(MiniblockNumber(0), 0, H256::zero()) + .finalize(ProtocolVersionId::latest()), l1_tx_count: 0, l2_tx_count: 0, base_fee_per_gas: 100, diff --git a/core/lib/dal/src/tests/mod.rs b/core/lib/dal/src/tests/mod.rs index 94fb6e9ebf6..ce47d35e6ea 100644 --- a/core/lib/dal/src/tests/mod.rs +++ b/core/lib/dal/src/tests/mod.rs @@ -2,7 +2,7 @@ use std::{fs, time::Duration}; use zksync_contracts::BaseSystemContractsHashes; use zksync_types::{ - block::{miniblock_hash, L1BatchHeader, MiniblockHeader}, + block::{L1BatchHeader, MiniblockHasher, MiniblockHeader}, fee::{Fee, TransactionExecutionMetrics}, helpers::unix_timestamp_ms, l1::{L1Tx, OpProcessingType, PriorityQueueType}, @@ -30,17 +30,19 @@ fn mock_tx_execution_metrics() -> TransactionExecutionMetrics { } pub(crate) fn create_miniblock_header(number: u32) -> MiniblockHeader { + let number = MiniblockNumber(number); + let protocol_version = ProtocolVersionId::default(); MiniblockHeader { - number: MiniblockNumber(number), + number, timestamp: 0, - hash: miniblock_hash(MiniblockNumber(number), 0, H256::zero(), H256::zero()), + hash: MiniblockHasher::new(number, 0, H256::zero()).finalize(protocol_version), l1_tx_count: 0, l2_tx_count: 0, base_fee_per_gas: 100, l1_gas_price: 100, l2_fair_gas_price: 100, base_system_contracts_hashes: BaseSystemContractsHashes::default(), - protocol_version: Some(ProtocolVersionId::default()), + protocol_version: Some(protocol_version), virtual_blocks: 1, } } diff --git a/core/lib/dal/src/transactions_web3_dal.rs b/core/lib/dal/src/transactions_web3_dal.rs index d87ddc9a517..0fc2c09f597 100644 --- a/core/lib/dal/src/transactions_web3_dal.rs +++ b/core/lib/dal/src/transactions_web3_dal.rs @@ -356,7 +356,8 @@ impl TransactionsWeb3Dal<'_, '_> { #[cfg(test)] mod tests { use zksync_types::{ - block::miniblock_hash, fee::TransactionExecutionMetrics, l2::L2Tx, ProtocolVersion, + block::MiniblockHasher, fee::TransactionExecutionMetrics, l2::L2Tx, ProtocolVersion, + ProtocolVersionId, }; use super::*; @@ -401,15 +402,12 @@ mod tests { let tx_hash = tx.hash(); prepare_transaction(&mut conn, tx).await; + let block_hash = MiniblockHasher::new(MiniblockNumber(1), 0, H256::zero()) + .finalize(ProtocolVersionId::latest()); let block_ids = [ api::BlockId::Number(api::BlockNumber::Latest), api::BlockId::Number(api::BlockNumber::Number(1.into())), - api::BlockId::Hash(miniblock_hash( - MiniblockNumber(1), - 0, - H256::zero(), - H256::zero(), - )), + api::BlockId::Hash(block_hash), ]; let transaction_ids = block_ids .iter() 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 81939d402ff..1faeba9652f 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 @@ -7,11 +7,12 @@ use zk_evm_1_4_0::aux_structures::Timestamp; use zksync_state::WriteStorage; use zksync_system_constants::REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_BYTE; use zksync_types::{ - block::{legacy_miniblock_hash, miniblock_hash, pack_block_info}, + block::{pack_block_info, MiniblockHasher}, AccountTreeId, Execute, ExecuteTransactionCommon, L1BatchNumber, L1TxCommonData, - MiniblockNumber, StorageKey, Transaction, H160, H256, SYSTEM_CONTEXT_ADDRESS, - SYSTEM_CONTEXT_BLOCK_INFO_POSITION, SYSTEM_CONTEXT_CURRENT_L2_BLOCK_INFO_POSITION, - SYSTEM_CONTEXT_CURRENT_TX_ROLLING_HASH_POSITION, U256, + MiniblockNumber, ProtocolVersionId, StorageKey, Transaction, H160, H256, + SYSTEM_CONTEXT_ADDRESS, SYSTEM_CONTEXT_BLOCK_INFO_POSITION, + SYSTEM_CONTEXT_CURRENT_L2_BLOCK_INFO_POSITION, SYSTEM_CONTEXT_CURRENT_TX_ROLLING_HASH_POSITION, + U256, }; use zksync_utils::{h256_to_u256, u256_to_h256}; @@ -64,7 +65,7 @@ fn test_l2_block_initialization_timestamp() { vm.vm.bootloader_state.push_l2_block(L2BlockEnv { number: 1, timestamp: 0, - prev_block_hash: legacy_miniblock_hash(MiniblockNumber(0)), + prev_block_hash: MiniblockHasher::legacy_hash(MiniblockNumber(0)), max_virtual_blocks_to_create: 1, }); let l1_tx = get_l1_noop(); @@ -87,7 +88,7 @@ fn test_l2_block_initialization_number_non_zero() { let first_l2_block = L2BlockEnv { number: 0, timestamp: l1_batch.timestamp, - prev_block_hash: legacy_miniblock_hash(MiniblockNumber(0)), + prev_block_hash: MiniblockHasher::legacy_hash(MiniblockNumber(0)), max_virtual_blocks_to_create: 1, }; @@ -246,7 +247,7 @@ fn test_l2_block_new_l2_block() { let correct_first_block = L2BlockEnv { number: 1, timestamp: 1, - prev_block_hash: legacy_miniblock_hash(MiniblockNumber(0)), + prev_block_hash: MiniblockHasher::legacy_hash(MiniblockNumber(0)), max_virtual_blocks_to_create: 1, }; @@ -340,7 +341,7 @@ fn test_first_in_batch( ); storage_ptr.borrow_mut().set_value( prev_block_hash_position, - legacy_miniblock_hash(MiniblockNumber(miniblock_number - 1)), + MiniblockHasher::legacy_hash(MiniblockNumber(miniblock_number - 1)), ); // In order to skip checks from the Rust side of the VM, we firstly use some definitely correct L2 block info. @@ -369,6 +370,9 @@ fn test_first_in_batch( #[test] fn test_l2_block_first_in_batch() { + let prev_block_hash = MiniblockHasher::legacy_hash(MiniblockNumber(0)); + let prev_block_hash = MiniblockHasher::new(MiniblockNumber(1), 1, prev_block_hash) + .finalize(ProtocolVersionId::latest()); test_first_in_batch( 1, 1, @@ -379,17 +383,15 @@ fn test_l2_block_first_in_batch() { L2BlockEnv { number: 2, timestamp: 2, - prev_block_hash: miniblock_hash( - MiniblockNumber(1), - 1, - legacy_miniblock_hash(MiniblockNumber(0)), - H256::zero(), - ), + prev_block_hash, max_virtual_blocks_to_create: 1, }, None, ); + let prev_block_hash = MiniblockHasher::legacy_hash(MiniblockNumber(0)); + let prev_block_hash = MiniblockHasher::new(MiniblockNumber(1), 8, prev_block_hash) + .finalize(ProtocolVersionId::latest()); test_first_in_batch( 8, 1, @@ -400,8 +402,8 @@ fn test_l2_block_first_in_batch() { L2BlockEnv { number: 2, timestamp: 9, - prev_block_hash: miniblock_hash(MiniblockNumber(1), 8, legacy_miniblock_hash(MiniblockNumber(0)), H256::zero()), - max_virtual_blocks_to_create: 1 + prev_block_hash, + max_virtual_blocks_to_create: 1, }, Some(Halt::FailedToSetL2Block("The timestamp of the L2 block must be greater than or equal to the timestamp of the current batch".to_string())), ); diff --git a/core/lib/multivm/src/versions/vm_latest/tests/tester/vm_tester.rs b/core/lib/multivm/src/versions/vm_latest/tests/tester/vm_tester.rs index 6218a391824..25f1361f14d 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/tester/vm_tester.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/tester/vm_tester.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use zksync_contracts::BaseSystemContracts; use zksync_state::{InMemoryStorage, StoragePtr, StorageView, WriteStorage}; use zksync_types::{ - block::legacy_miniblock_hash, + block::MiniblockHasher, get_code_key, get_is_account_key, helpers::unix_timestamp_ms, utils::{deployed_address_create, storage_key_for_eth_balance}, @@ -84,7 +84,7 @@ impl VmTester { let last_l2_block = load_last_l2_block(self.storage.clone()).unwrap_or(L2Block { number: 0, timestamp: 0, - hash: legacy_miniblock_hash(MiniblockNumber(0)), + hash: MiniblockHasher::legacy_hash(MiniblockNumber(0)), }); l1_batch.first_l2_block = L2BlockEnv { number: last_l2_block.number + 1, @@ -258,7 +258,7 @@ pub(crate) fn default_l1_batch(number: L1BatchNumber) -> L1BatchEnv { first_l2_block: L2BlockEnv { number: 1, timestamp, - prev_block_hash: legacy_miniblock_hash(MiniblockNumber(0)), + prev_block_hash: MiniblockHasher::legacy_hash(MiniblockNumber(0)), max_virtual_blocks_to_create: 100, }, } diff --git a/core/lib/multivm/src/versions/vm_latest/types/internals/vm_state.rs b/core/lib/multivm/src/versions/vm_latest/types/internals/vm_state.rs index f65785dcfe5..da9c8132144 100644 --- a/core/lib/multivm/src/versions/vm_latest/types/internals/vm_state.rs +++ b/core/lib/multivm/src/versions/vm_latest/types/internals/vm_state.rs @@ -12,7 +12,7 @@ use zk_evm_1_4_0::{ use zksync_state::{StoragePtr, WriteStorage}; use zksync_system_constants::BOOTLOADER_ADDRESS; use zksync_types::{ - block::legacy_miniblock_hash, zkevm_test_harness::INITIAL_MONOTONIC_CYCLE_COUNTER, Address, + block::MiniblockHasher, zkevm_test_harness::INITIAL_MONOTONIC_CYCLE_COUNTER, Address, MiniblockNumber, }; use zksync_utils::h256_to_u256; @@ -73,7 +73,9 @@ pub(crate) fn new_vm_state( L2Block { number: l1_batch_env.first_l2_block.number.saturating_sub(1), timestamp: 0, - hash: legacy_miniblock_hash(MiniblockNumber(l1_batch_env.first_l2_block.number) - 1), + hash: MiniblockHasher::legacy_hash( + MiniblockNumber(l1_batch_env.first_l2_block.number) - 1, + ), } }; diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/types/internals/vm_state.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/types/internals/vm_state.rs index adeef89466f..48c1e1f082f 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/types/internals/vm_state.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/types/internals/vm_state.rs @@ -12,7 +12,7 @@ use zk_evm_1_3_3::{ use zksync_state::{StoragePtr, WriteStorage}; use zksync_system_constants::BOOTLOADER_ADDRESS; use zksync_types::{ - block::legacy_miniblock_hash, zkevm_test_harness::INITIAL_MONOTONIC_CYCLE_COUNTER, Address, + block::MiniblockHasher, zkevm_test_harness::INITIAL_MONOTONIC_CYCLE_COUNTER, Address, MiniblockNumber, }; use zksync_utils::h256_to_u256; @@ -73,7 +73,9 @@ pub(crate) fn new_vm_state( L2Block { number: l1_batch_env.first_l2_block.number.saturating_sub(1), timestamp: 0, - hash: legacy_miniblock_hash(MiniblockNumber(l1_batch_env.first_l2_block.number) - 1), + hash: MiniblockHasher::legacy_hash( + MiniblockNumber(l1_batch_env.first_l2_block.number) - 1, + ), } }; diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/types/internals/vm_state.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/types/internals/vm_state.rs index 5d67982e7b4..c2dc400439d 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/types/internals/vm_state.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/types/internals/vm_state.rs @@ -12,7 +12,7 @@ use zk_evm_1_3_3::{ use zksync_state::{StoragePtr, WriteStorage}; use zksync_system_constants::BOOTLOADER_ADDRESS; use zksync_types::{ - block::legacy_miniblock_hash, zkevm_test_harness::INITIAL_MONOTONIC_CYCLE_COUNTER, Address, + block::MiniblockHasher, zkevm_test_harness::INITIAL_MONOTONIC_CYCLE_COUNTER, Address, MiniblockNumber, }; use zksync_utils::h256_to_u256; @@ -73,7 +73,9 @@ pub(crate) fn new_vm_state( L2Block { number: l1_batch_env.first_l2_block.number.saturating_sub(1), timestamp: 0, - hash: legacy_miniblock_hash(MiniblockNumber(l1_batch_env.first_l2_block.number) - 1), + hash: MiniblockHasher::legacy_hash( + MiniblockNumber(l1_batch_env.first_l2_block.number) - 1, + ), } }; diff --git a/core/lib/types/src/block.rs b/core/lib/types/src/block.rs index 0aa9d06711c..896bfabe138 100644 --- a/core/lib/types/src/block.rs +++ b/core/lib/types/src/block.rs @@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize}; use zksync_basic_types::{H2048, H256, U256}; use zksync_contracts::BaseSystemContractsHashes; use zksync_system_constants::SYSTEM_BLOCK_INFO_BLOCK_NUMBER_MULTIPLIER; +use zksync_utils::concat_and_hash; use crate::{ l2_to_l1_log::{SystemL2ToL1Log, UserL2ToL1Log}, @@ -187,30 +188,65 @@ impl ops::AddAssign for BlockGasCount { } } -/// Returns the hash of the miniblock. -/// `txs_rolling_hash` of the miniblock is calculated the following way: -/// If the miniblock has 0 transactions, then `txs_rolling_hash` is equal to `H256::zero()`. -/// If the miniblock has i transactions, then `txs_rolling_hash` is equal to `H(H_{i-1}, H(tx_i))`, where -/// `H_{i-1}` is the `txs_rolling_hash` of the first i-1 transactions. -pub fn miniblock_hash( - miniblock_number: MiniblockNumber, - miniblock_timestamp: u64, +/// Hasher of miniblock contents used by the VM. +#[derive(Debug)] +pub struct MiniblockHasher { + number: MiniblockNumber, + timestamp: u64, prev_miniblock_hash: H256, txs_rolling_hash: H256, -) -> H256 { - let mut digest: [u8; 128] = [0u8; 128]; - U256::from(miniblock_number.0).to_big_endian(&mut digest[0..32]); - U256::from(miniblock_timestamp).to_big_endian(&mut digest[32..64]); - digest[64..96].copy_from_slice(prev_miniblock_hash.as_bytes()); - digest[96..128].copy_from_slice(txs_rolling_hash.as_bytes()); - - H256(keccak256(&digest)) } -/// At the beginning of the zkSync, the hashes of the blocks could be calculated as the hash of their number. -/// This method returns the hash of such miniblocks. -pub fn legacy_miniblock_hash(miniblock_number: MiniblockNumber) -> H256 { - H256(keccak256(&miniblock_number.0.to_be_bytes())) +impl MiniblockHasher { + /// At the beginning of the zkSync, the hashes of the blocks could be calculated as the hash of their number. + /// This method returns the hash of such miniblocks. + pub fn legacy_hash(miniblock_number: MiniblockNumber) -> H256 { + H256(keccak256(&miniblock_number.0.to_be_bytes())) + } + + /// Creates a new hasher with the specified params. This assumes a miniblock without transactions; + /// transaction hashes can be supplied using [`Self::push_tx_hash()`]. + pub fn new(number: MiniblockNumber, timestamp: u64, prev_miniblock_hash: H256) -> Self { + Self { + number, + timestamp, + prev_miniblock_hash, + txs_rolling_hash: H256::zero(), + } + } + + /// Updates this hasher with a transaction hash. This should be called for all transactions in the block + /// in the order of their execution. + pub fn push_tx_hash(&mut self, tx_hash: H256) { + self.txs_rolling_hash = concat_and_hash(self.txs_rolling_hash, tx_hash); + } + + /// Returns the hash of the miniblock. + /// + /// For newer protocol versions, the hash is computed as + /// + /// ```text + /// keccak256(u256_be(number) ++ u256_be(timestamp) ++ prev_miniblock_hash ++ txs_rolling_hash) + /// ``` + /// + /// Here, `u256_be` is the big-endian 256-bit serialization of a number, and `txs_rolling_hash` + /// is *the rolling hash* of miniblock transactions. `txs_rolling_hash` is calculated the following way: + /// + /// - If the miniblock has 0 transactions, then `txs_rolling_hash` is equal to `H256::zero()`. + /// - If the miniblock has i transactions, then `txs_rolling_hash` is equal to `H(H_{i-1}, H(tx_i))`, where + /// `H_{i-1}` is the `txs_rolling_hash` of the first i-1 transactions. + pub fn finalize(self, protocol_version: ProtocolVersionId) -> H256 { + if protocol_version >= ProtocolVersionId::Version13 { + let mut digest = [0_u8; 128]; + U256::from(self.number.0).to_big_endian(&mut digest[0..32]); + U256::from(self.timestamp).to_big_endian(&mut digest[32..64]); + digest[64..96].copy_from_slice(self.prev_miniblock_hash.as_bytes()); + digest[96..128].copy_from_slice(self.txs_rolling_hash.as_bytes()); + H256(keccak256(&digest)) + } else { + Self::legacy_hash(self.number) + } + } } /// Returns block.number/timestamp based on the block's information @@ -236,9 +272,7 @@ pub fn unpack_block_upgrade_info(info: U256) -> (u64, u64) { #[cfg(test)] mod tests { - use zksync_basic_types::{MiniblockNumber, H256}; - - use crate::block::{legacy_miniblock_hash, miniblock_hash, pack_block_info, unpack_block_info}; + use super::*; #[test] fn test_legacy_miniblock_hashes() { @@ -247,7 +281,7 @@ mod tests { .parse() .unwrap(); assert_eq!( - legacy_miniblock_hash(MiniblockNumber(11470850)), + MiniblockHasher::legacy_hash(MiniblockNumber(11470850)), expected_hash ) } @@ -268,12 +302,13 @@ mod tests { .unwrap(); assert_eq!( expected_hash, - miniblock_hash( - MiniblockNumber(1), - 12, + MiniblockHasher { + number: MiniblockNumber(1), + timestamp: 12, prev_miniblock_hash, - txs_rolling_hash - ) + txs_rolling_hash, + } + .finalize(ProtocolVersionId::latest()) ) } diff --git a/core/lib/zksync_core/src/api_server/execution_sandbox/apply.rs b/core/lib/zksync_core/src/api_server/execution_sandbox/apply.rs index f0488f71190..4360519d1e5 100644 --- a/core/lib/zksync_core/src/api_server/execution_sandbox/apply.rs +++ b/core/lib/zksync_core/src/api_server/execution_sandbox/apply.rs @@ -21,7 +21,7 @@ use zksync_system_constants::{ }; use zksync_types::{ api, - block::{legacy_miniblock_hash, pack_block_info, unpack_block_info}, + block::{pack_block_info, unpack_block_info, MiniblockHasher}, get_nonce_key, utils::{decompose_full_nonce, nonces_to_full_nonce, storage_key_for_eth_balance}, AccountTreeId, L1BatchNumber, MiniblockNumber, Nonce, ProtocolVersionId, StorageKey, @@ -107,7 +107,7 @@ pub(super) fn apply_vm_in_sandbox( L2BlockEnv { number: 1, timestamp: 0, - prev_block_hash: legacy_miniblock_hash(MiniblockNumber(0)), + prev_block_hash: MiniblockHasher::legacy_hash(MiniblockNumber(0)), max_virtual_blocks_to_create: 1, } } else { diff --git a/core/lib/zksync_core/src/genesis.rs b/core/lib/zksync_core/src/genesis.rs index 01d4628caac..bba6a2d6744 100644 --- a/core/lib/zksync_core/src/genesis.rs +++ b/core/lib/zksync_core/src/genesis.rs @@ -7,9 +7,7 @@ use zksync_contracts::BaseSystemContracts; use zksync_dal::StorageProcessor; use zksync_merkle_tree::domain::ZkSyncTree; use zksync_types::{ - block::{ - legacy_miniblock_hash, BlockGasCount, DeployedContract, L1BatchHeader, MiniblockHeader, - }, + block::{BlockGasCount, DeployedContract, L1BatchHeader, MiniblockHasher, MiniblockHeader}, commitment::{L1BatchCommitment, L1BatchMetadata}, get_code_key, get_system_context_init_logs, protocol_version::{L1VerifierConfig, ProtocolVersion}, @@ -292,7 +290,7 @@ pub(crate) async fn create_genesis_l1_batch( let genesis_miniblock_header = MiniblockHeader { number: MiniblockNumber(0), timestamp: 0, - hash: legacy_miniblock_hash(MiniblockNumber(0)), + hash: MiniblockHasher::legacy_hash(MiniblockNumber(0)), l1_tx_count: 0, l2_tx_count: 0, base_fee_per_gas: 0, diff --git a/core/lib/zksync_core/src/metadata_calculator/tests.rs b/core/lib/zksync_core/src/metadata_calculator/tests.rs index 85d179fe3b0..e609ed6972c 100644 --- a/core/lib/zksync_core/src/metadata_calculator/tests.rs +++ b/core/lib/zksync_core/src/metadata_calculator/tests.rs @@ -11,10 +11,10 @@ use zksync_health_check::{CheckHealth, HealthStatus}; use zksync_merkle_tree::domain::ZkSyncTree; use zksync_object_store::{ObjectStore, ObjectStoreFactory}; use zksync_types::{ - block::{miniblock_hash, BlockGasCount, L1BatchHeader, MiniblockHeader}, + block::{BlockGasCount, L1BatchHeader, MiniblockHasher, MiniblockHeader}, proofs::PrepareBasicCircuitsJob, - AccountTreeId, Address, L1BatchNumber, L2ChainId, MiniblockNumber, StorageKey, StorageLog, - H256, + AccountTreeId, Address, L1BatchNumber, L2ChainId, MiniblockNumber, ProtocolVersionId, + StorageKey, StorageLog, H256, }; use zksync_utils::u32_to_h256; @@ -502,19 +502,15 @@ pub(super) async fn extend_db_state( let miniblock_header = MiniblockHeader { number: miniblock_number, timestamp: header.timestamp, - hash: miniblock_hash( - miniblock_number, - header.timestamp, - H256::zero(), - H256::zero(), - ), + hash: MiniblockHasher::new(miniblock_number, header.timestamp, H256::zero()) + .finalize(ProtocolVersionId::latest()), l1_tx_count: header.l1_tx_count, l2_tx_count: header.l2_tx_count, base_fee_per_gas: header.base_fee_per_gas, l1_gas_price: 0, l2_fair_gas_price: 0, base_system_contracts_hashes: base_system_contracts.hashes(), - protocol_version: Some(Default::default()), + protocol_version: Some(ProtocolVersionId::latest()), virtual_blocks: 0, }; diff --git a/core/lib/zksync_core/src/state_keeper/io/tests/mod.rs b/core/lib/zksync_core/src/state_keeper/io/tests/mod.rs index ef8c5424854..70466ae4843 100644 --- a/core/lib/zksync_core/src/state_keeper/io/tests/mod.rs +++ b/core/lib/zksync_core/src/state_keeper/io/tests/mod.rs @@ -185,8 +185,7 @@ async fn l1_batch_timestamp_respects_prev_miniblock_with_clock_skew() { #[tokio::test] async fn processing_storage_logs_when_sealing_miniblock() { let connection_pool = ConnectionPool::test_pool().await; - let mut miniblock = - MiniblockUpdates::new(0, 1, H256::zero(), 1, Some(ProtocolVersionId::latest())); + let mut miniblock = MiniblockUpdates::new(0, 1, H256::zero(), 1, ProtocolVersionId::latest()); let tx = create_transaction(10, 100); let storage_logs = [ @@ -283,8 +282,7 @@ async fn processing_storage_logs_when_sealing_miniblock() { async fn processing_events_when_sealing_miniblock() { let pool = ConnectionPool::test_pool().await; let l1_batch_number = L1BatchNumber(2); - let mut miniblock = - MiniblockUpdates::new(0, 1, H256::zero(), 1, Some(ProtocolVersionId::latest())); + let mut miniblock = MiniblockUpdates::new(0, 1, H256::zero(), 1, ProtocolVersionId::latest()); let events = (0_u8..10).map(|i| VmEvent { location: (l1_batch_number, u32::from(i / 4)), diff --git a/core/lib/zksync_core/src/state_keeper/tests/mod.rs b/core/lib/zksync_core/src/state_keeper/tests/mod.rs index 06411ecaa55..7ad0116078c 100644 --- a/core/lib/zksync_core/src/state_keeper/tests/mod.rs +++ b/core/lib/zksync_core/src/state_keeper/tests/mod.rs @@ -19,7 +19,7 @@ use zksync_contracts::{BaseSystemContracts, BaseSystemContractsHashes}; use zksync_system_constants::ZKPORTER_IS_AVAILABLE; use zksync_types::{ aggregated_operations::AggregatedActionType, - block::{legacy_miniblock_hash, miniblock_hash, BlockGasCount, MiniblockExecutionData}, + block::{BlockGasCount, MiniblockExecutionData, MiniblockHasher}, commitment::{L1BatchMetaParameters, L1BatchMetadata}, fee::Fee, l2::L2Tx, @@ -80,7 +80,7 @@ pub(super) fn default_l1_batch_env( first_l2_block: L2BlockEnv { number, timestamp, - prev_block_hash: legacy_miniblock_hash(MiniblockNumber(number - 1)), + prev_block_hash: MiniblockHasher::legacy_hash(MiniblockNumber(number - 1)), max_virtual_blocks_to_create: 1, }, } @@ -447,14 +447,16 @@ async fn pending_batch_is_applied() { MiniblockExecutionData { number: MiniblockNumber(1), timestamp: 1, - prev_block_hash: miniblock_hash(MiniblockNumber(0), 0, H256::zero(), H256::zero()), + prev_block_hash: MiniblockHasher::new(MiniblockNumber(0), 0, H256::zero()) + .finalize(ProtocolVersionId::latest()), virtual_blocks: 1, txs: vec![random_tx(1)], }, MiniblockExecutionData { number: MiniblockNumber(2), timestamp: 2, - prev_block_hash: miniblock_hash(MiniblockNumber(1), 1, H256::zero(), H256::zero()), + prev_block_hash: MiniblockHasher::new(MiniblockNumber(1), 1, H256::zero()) + .finalize(ProtocolVersionId::latest()), virtual_blocks: 1, txs: vec![random_tx(2)], }, @@ -532,7 +534,8 @@ async fn miniblock_timestamp_after_pending_batch() { let pending_batch = pending_batch_data(vec![MiniblockExecutionData { number: MiniblockNumber(1), timestamp: 1, - prev_block_hash: miniblock_hash(MiniblockNumber(0), 0, H256::zero(), H256::zero()), + prev_block_hash: MiniblockHasher::new(MiniblockNumber(0), 0, H256::zero()) + .finalize(ProtocolVersionId::latest()), virtual_blocks: 1, txs: vec![random_tx(1)], }]); diff --git a/core/lib/zksync_core/src/state_keeper/updates/l1_batch_updates.rs b/core/lib/zksync_core/src/state_keeper/updates/l1_batch_updates.rs index 584a0c835e7..7f18edb3320 100644 --- a/core/lib/zksync_core/src/state_keeper/updates/l1_batch_updates.rs +++ b/core/lib/zksync_core/src/state_keeper/updates/l1_batch_updates.rs @@ -59,7 +59,7 @@ mod tests { #[test] fn apply_miniblock_with_empty_tx() { let mut miniblock_accumulator = - MiniblockUpdates::new(0, 0, H256::zero(), 1, Some(ProtocolVersionId::latest())); + MiniblockUpdates::new(0, 0, H256::zero(), 1, ProtocolVersionId::latest()); let tx = create_transaction(10, 100); let expected_tx_size = tx.bootloader_encoding_size(); diff --git a/core/lib/zksync_core/src/state_keeper/updates/miniblock_updates.rs b/core/lib/zksync_core/src/state_keeper/updates/miniblock_updates.rs index 0c8591a2898..5d001fd7689 100644 --- a/core/lib/zksync_core/src/state_keeper/updates/miniblock_updates.rs +++ b/core/lib/zksync_core/src/state_keeper/updates/miniblock_updates.rs @@ -5,17 +5,14 @@ use multivm::{ vm_latest::TransactionVmExt, }; use zksync_types::{ - block::{legacy_miniblock_hash, miniblock_hash, BlockGasCount}, + block::{BlockGasCount, MiniblockHasher}, event::extract_bytecodes_marked_as_known, l2_to_l1_log::{SystemL2ToL1Log, UserL2ToL1Log}, tx::{tx_execution_info::TxExecutionStatus, ExecutionMetrics, TransactionExecutionResult}, vm_trace::Call, MiniblockNumber, ProtocolVersionId, StorageLogQuery, Transaction, VmEvent, H256, }; -use zksync_utils::{ - bytecode::{hash_bytecode, CompressedBytecodeInfo}, - concat_and_hash, -}; +use zksync_utils::bytecode::{hash_bytecode, CompressedBytecodeInfo}; #[derive(Debug, Clone, PartialEq)] pub struct MiniblockUpdates { @@ -32,9 +29,8 @@ pub struct MiniblockUpdates { pub timestamp: u64, pub number: u32, pub prev_block_hash: H256, - pub txs_rolling_hash: H256, pub virtual_blocks: u32, - pub protocol_version: Option, + pub protocol_version: ProtocolVersionId, } impl MiniblockUpdates { @@ -43,7 +39,7 @@ impl MiniblockUpdates { number: u32, prev_block_hash: H256, virtual_blocks: u32, - protocol_version: Option, + protocol_version: ProtocolVersionId, ) -> Self { Self { executed_transactions: vec![], @@ -58,7 +54,6 @@ impl MiniblockUpdates { timestamp, number, prev_block_hash, - txs_rolling_hash: H256::zero(), virtual_blocks, protocol_version, } @@ -127,12 +122,9 @@ impl MiniblockUpdates { self.l1_gas_count += tx_l1_gas_this_tx; self.block_execution_metrics += execution_metrics; self.txs_encoding_size += tx.bootloader_encoding_size(); - self.storage_logs .extend(tx_execution_result.logs.storage_logs); - self.txs_rolling_hash = concat_and_hash(self.txs_rolling_hash, tx.hash()); - self.executed_transactions.push(TransactionExecutionResult { hash: tx.hash(), transaction: tx, @@ -148,15 +140,15 @@ impl MiniblockUpdates { /// Calculates miniblock hash based on the protocol version. pub(crate) fn get_miniblock_hash(&self) -> H256 { - match self.protocol_version { - Some(id) if id >= ProtocolVersionId::Version13 => miniblock_hash( - MiniblockNumber(self.number), - self.timestamp, - self.prev_block_hash, - self.txs_rolling_hash, - ), - _ => legacy_miniblock_hash(MiniblockNumber(self.number)), + let mut digest = MiniblockHasher::new( + MiniblockNumber(self.number), + self.timestamp, + self.prev_block_hash, + ); + for tx in &self.executed_transactions { + digest.push_tx_hash(tx.hash); } + digest.finalize(self.protocol_version) } pub(crate) fn get_miniblock_env(&self) -> L2BlockEnv { @@ -179,7 +171,7 @@ mod tests { #[test] fn apply_empty_l2_tx() { let mut accumulator = - MiniblockUpdates::new(0, 0, H256::random(), 0, Some(ProtocolVersionId::latest())); + MiniblockUpdates::new(0, 0, H256::random(), 0, ProtocolVersionId::latest()); let tx = create_transaction(10, 100); let bootloader_encoding_size = tx.bootloader_encoding_size(); accumulator.extend_from_executed_transaction( diff --git a/core/lib/zksync_core/src/state_keeper/updates/mod.rs b/core/lib/zksync_core/src/state_keeper/updates/mod.rs index c34164557b5..ad5e9e432d4 100644 --- a/core/lib/zksync_core/src/state_keeper/updates/mod.rs +++ b/core/lib/zksync_core/src/state_keeper/updates/mod.rs @@ -52,7 +52,7 @@ impl UpdatesManager { l1_batch_env.first_l2_block.number, l1_batch_env.first_l2_block.prev_block_hash, l1_batch_env.first_l2_block.max_virtual_blocks_to_create, - Some(protocol_version), + protocol_version, ), storage_writes_deduplicator: StorageWritesDeduplicator::new(), } @@ -137,7 +137,7 @@ impl UpdatesManager { self.miniblock.number + 1, self.miniblock.get_miniblock_hash(), miniblock_params.virtual_blocks, - Some(self.protocol_version), + self.protocol_version, ); let old_miniblock_updates = std::mem::replace(&mut self.miniblock, new_miniblock_updates); self.l1_batch diff --git a/core/lib/zksync_core/src/sync_layer/fetcher.rs b/core/lib/zksync_core/src/sync_layer/fetcher.rs index 2989b6b70a3..4321a5d3533 100644 --- a/core/lib/zksync_core/src/sync_layer/fetcher.rs +++ b/core/lib/zksync_core/src/sync_layer/fetcher.rs @@ -4,7 +4,8 @@ use anyhow::Context as _; use tokio::sync::watch; use zksync_dal::{blocks_dal::ConsensusBlockFields, StorageProcessor}; use zksync_types::{ - api::en::SyncBlock, Address, L1BatchNumber, MiniblockNumber, ProtocolVersionId, + api::en::SyncBlock, block::MiniblockHasher, Address, L1BatchNumber, MiniblockNumber, + ProtocolVersionId, H256, }; use zksync_web3_decl::jsonrpsee::core::Error as RpcError; @@ -27,6 +28,7 @@ pub(super) struct FetchedBlock { pub last_in_batch: bool, pub protocol_version: ProtocolVersionId, pub timestamp: u64, + pub reference_hash: Option, pub l1_gas_price: u64, pub l2_fair_gas_price: u64, pub virtual_blocks: u32, @@ -35,8 +37,19 @@ pub(super) struct FetchedBlock { pub consensus: Option, } +impl FetchedBlock { + fn compute_hash(&self, prev_miniblock_hash: H256) -> H256 { + let mut hasher = MiniblockHasher::new(self.number, self.timestamp, prev_miniblock_hash); + for tx in &self.transactions { + hasher.push_tx_hash(tx.hash()); + } + hasher.finalize(self.protocol_version) + } +} + impl TryFrom for FetchedBlock { type Error = anyhow::Error; + fn try_from(block: SyncBlock) -> anyhow::Result { Ok(Self { number: block.number, @@ -44,6 +57,7 @@ impl TryFrom for FetchedBlock { last_in_batch: block.last_in_batch, protocol_version: block.protocol_version, timestamp: block.timestamp, + reference_hash: block.hash, l1_gas_price: block.l1_gas_price, l2_fair_gas_price: block.l2_fair_gas_price, virtual_blocks: block.virtual_blocks.unwrap_or(0), @@ -66,6 +80,7 @@ impl TryFrom for FetchedBlock { pub struct FetcherCursor { // Fields are public for testing purposes. pub(super) next_miniblock: MiniblockNumber, + pub(super) prev_miniblock_hash: H256, pub(super) l1_batch: L1BatchNumber, } @@ -94,6 +109,7 @@ impl FetcherCursor { // Miniblocks are always fully processed. let next_miniblock = last_miniblock_header.number + 1; + let prev_miniblock_hash = last_miniblock_header.hash; // Decide whether the next batch should be explicitly opened or not. let l1_batch = if was_new_batch_open { // No `OpenBatch` action needed. @@ -105,12 +121,20 @@ impl FetcherCursor { Ok(Self { next_miniblock, + prev_miniblock_hash, l1_batch, }) } pub(super) fn advance(&mut self, block: FetchedBlock) -> Vec { assert_eq!(block.number, self.next_miniblock); + let local_block_hash = block.compute_hash(self.prev_miniblock_hash); + if let Some(reference_hash) = block.reference_hash { + assert_eq!( + local_block_hash, reference_hash, + "Mismatch between the locally computed and received miniblock hash for {block:?}" + ); + } let mut new_actions = Vec::new(); if block.l1_batch_number != self.l1_batch { @@ -166,6 +190,7 @@ impl FetcherCursor { new_actions.push(SyncAction::SealMiniblock(block.consensus)); } self.next_miniblock += 1; + self.prev_miniblock_hash = local_block_hash; new_actions } diff --git a/core/lib/zksync_core/src/sync_layer/gossip/conversions.rs b/core/lib/zksync_core/src/sync_layer/gossip/conversions.rs index 616a4283c73..44baa9e8058 100644 --- a/core/lib/zksync_core/src/sync_layer/gossip/conversions.rs +++ b/core/lib/zksync_core/src/sync_layer/gossip/conversions.rs @@ -59,6 +59,7 @@ impl FetchedBlock { last_in_batch, protocol_version, timestamp: payload.timestamp, + reference_hash: Some(payload.hash), l1_gas_price: payload.l1_gas_price, l2_fair_gas_price: payload.l2_fair_gas_price, virtual_blocks: payload.virtual_blocks, diff --git a/core/lib/zksync_core/src/sync_layer/tests.rs b/core/lib/zksync_core/src/sync_layer/tests.rs index 3c76e05d93f..d3ae864dcf5 100644 --- a/core/lib/zksync_core/src/sync_layer/tests.rs +++ b/core/lib/zksync_core/src/sync_layer/tests.rs @@ -12,7 +12,8 @@ use zksync_config::configs::chain::NetworkConfig; use zksync_contracts::{BaseSystemContractsHashes, SystemContractCode}; use zksync_dal::{ConnectionPool, StorageProcessor}; use zksync_types::{ - api, Address, L1BatchNumber, L2ChainId, MiniblockNumber, ProtocolVersionId, Transaction, H256, + api, block::MiniblockHasher, Address, L1BatchNumber, L2ChainId, MiniblockNumber, + ProtocolVersionId, Transaction, H256, }; use super::{fetcher::FetcherCursor, sync_action::SyncAction, *}; @@ -30,6 +31,7 @@ const POLL_INTERVAL: Duration = Duration::from_millis(50); #[derive(Debug, Default)] struct MockMainNodeClient { + prev_miniblock_hash: H256, l2_blocks: Vec, } @@ -45,14 +47,27 @@ impl MockMainNodeClient { let mut tx_hashes = vec![]; let l2_blocks = (0..=miniblock_count).map(|number| { let is_fictive = number == miniblock_count; + let number = number + number_offset; + let mut hasher = MiniblockHasher::new( + MiniblockNumber(number), + number.into(), + self.prev_miniblock_hash, + ); + let transactions = if is_fictive { vec![] } else { let transaction = create_l2_transaction(10, 100); tx_hashes.push(transaction.hash()); + hasher.push_tx_hash(transaction.hash()); vec![transaction.into()] }; - let number = number + number_offset; + let miniblock_hash = hasher.finalize(if number == 0 { + ProtocolVersionId::Version0 // The genesis block always uses the legacy hashing mode + } else { + ProtocolVersionId::latest() + }); + self.prev_miniblock_hash = miniblock_hash; api::en::SyncBlock { number: MiniblockNumber(number), @@ -66,7 +81,7 @@ impl MockMainNodeClient { operator_address: Address::repeat_byte(2), transactions: Some(transactions), virtual_blocks: Some(!is_fictive as u32), - hash: Some(H256::repeat_byte(1)), + hash: Some(miniblock_hash), protocol_version: ProtocolVersionId::latest(), consensus: None, } @@ -576,6 +591,15 @@ async fn fetcher_with_real_server() { // Fill in transactions grouped in multiple miniblocks in the storage. let tx_hashes = run_state_keeper_with_multiple_miniblocks(pool.clone()).await; let mut tx_hashes = VecDeque::from(tx_hashes); + let mut connection = pool.access_storage().await.unwrap(); + let genesis_miniblock_hash = connection + .blocks_dal() + .get_miniblock_header(MiniblockNumber(0)) + .await + .unwrap() + .expect("No genesis miniblock") + .hash; + drop(connection); // Start the API server. let network_config = NetworkConfig::for_tests(); @@ -591,6 +615,7 @@ async fn fetcher_with_real_server() { let client = ::json_rpc(&format!("http://{server_addr}/")).unwrap(); let fetcher_cursor = FetcherCursor { next_miniblock: MiniblockNumber(1), + prev_miniblock_hash: genesis_miniblock_hash, l1_batch: L1BatchNumber(0), }; let fetcher = fetcher_cursor.into_fetcher( diff --git a/core/tests/vm-benchmark/harness/src/lib.rs b/core/tests/vm-benchmark/harness/src/lib.rs index 00da5bcca9f..33042adaaeb 100644 --- a/core/tests/vm-benchmark/harness/src/lib.rs +++ b/core/tests/vm-benchmark/harness/src/lib.rs @@ -11,7 +11,7 @@ use zksync_contracts::{deployer_contract, BaseSystemContracts}; use zksync_state::{InMemoryStorage, StorageView}; use zksync_system_constants::ethereum::MAX_GAS_PER_PUBDATA_BYTE; use zksync_types::{ - block::legacy_miniblock_hash, + block::MiniblockHasher, ethabi::{encode, Token}, fee::Fee, helpers::unix_timestamp_ms, @@ -76,7 +76,7 @@ impl BenchmarkingVm { first_l2_block: L2BlockEnv { number: 1, timestamp, - prev_block_hash: legacy_miniblock_hash(MiniblockNumber(0)), + prev_block_hash: MiniblockHasher::legacy_hash(MiniblockNumber(0)), max_virtual_blocks_to_create: 100, }, },