diff --git a/core/lib/types/src/commitment.rs b/core/lib/types/src/commitment.rs index d4f5fec30cd..296d3ea87b6 100644 --- a/core/lib/types/src/commitment.rs +++ b/core/lib/types/src/commitment.rs @@ -22,7 +22,7 @@ use crate::{ compress_state_diffs, InitialStorageWrite, RepeatedStorageWrite, StateDiffRecord, PADDED_ENCODED_STORAGE_DIFF_LEN_BYTES, }, - H256, KNOWN_CODES_STORAGE_ADDRESS, U256, + ProtocolVersionId, H256, KNOWN_CODES_STORAGE_ADDRESS, U256, }; /// Type that can be serialized for commitment. @@ -34,6 +34,21 @@ pub trait SerializeCommitment { fn serialize_commitment(&self, buffer: &mut [u8]); } +/// Serialize elements for commitment. The results consist of: +/// 1. Number of elements (4 bytes) +/// 2. Serialized elements +pub(crate) fn pre_boojum_serialize_commitments(values: &[I]) -> Vec { + let final_len = values.len() * I::SERIALIZED_SIZE + 4; + let mut input = vec![0_u8; final_len]; + input[0..4].copy_from_slice(&(values.len() as u32).to_be_bytes()); + + let chunks = input[4..].chunks_mut(I::SERIALIZED_SIZE); + for (value, chunk) in values.iter().zip(chunks) { + value.serialize_commitment(chunk); + } + input +} + /// Serialize elements for commitment. The result consists of packed serialized elements. pub fn serialize_commitments(values: &[I]) -> Vec { let final_len = values.len() * I::SERIALIZED_SIZE; @@ -323,9 +338,11 @@ struct L1BatchAuxiliaryOutput { bootloader_heap_hash: H256, #[allow(dead_code)] events_state_queue_hash: H256, + protocol_version: ProtocolVersionId, } impl L1BatchAuxiliaryOutput { + #[allow(clippy::too_many_arguments)] fn new( l2_l1_logs: Vec, initial_writes: Vec, @@ -334,12 +351,31 @@ impl L1BatchAuxiliaryOutput { state_diffs: Vec, bootloader_heap_hash: H256, events_state_queue_hash: H256, + protocol_version: ProtocolVersionId, ) -> Self { - let l2_l1_logs_compressed = serialize_commitments(&l2_l1_logs); - let initial_writes_compressed = serialize_commitments(&initial_writes); - let repeated_writes_compressed = serialize_commitments(&repeated_writes); - let system_logs_compressed = serialize_commitments(&system_logs); - let state_diffs_packed = serialize_commitments(&state_diffs); + let ( + l2_l1_logs_compressed, + initial_writes_compressed, + repeated_writes_compressed, + system_logs_compressed, + state_diffs_packed, + ) = if protocol_version.is_pre_boojum() { + ( + pre_boojum_serialize_commitments(&l2_l1_logs), + pre_boojum_serialize_commitments(&initial_writes), + pre_boojum_serialize_commitments(&repeated_writes), + pre_boojum_serialize_commitments(&system_logs), + pre_boojum_serialize_commitments(&state_diffs), + ) + } else { + ( + serialize_commitments(&l2_l1_logs), + serialize_commitments(&initial_writes), + serialize_commitments(&repeated_writes), + serialize_commitments(&system_logs), + serialize_commitments(&state_diffs), + ) + }; let state_diffs_compressed = compress_state_diffs(state_diffs.clone()); @@ -349,13 +385,23 @@ impl L1BatchAuxiliaryOutput { let repeated_writes_hash = H256::from(keccak256(&repeated_writes_compressed)); let state_diffs_hash = H256::from(keccak256(&(state_diffs_packed))); - let merkle_tree_leaves = l2_l1_logs_compressed + let serialized_logs = if protocol_version.is_pre_boojum() { + &l2_l1_logs_compressed[4..] + } else { + &l2_l1_logs_compressed + }; + + let merkle_tree_leaves = serialized_logs .chunks(UserL2ToL1Log::SERIALIZED_SIZE) .map(|chunk| <[u8; UserL2ToL1Log::SERIALIZED_SIZE]>::try_from(chunk).unwrap()); // ^ Skip first 4 bytes of the serialized logs (i.e., the number of logs). - let min_tree_size = Some(L2ToL1Log::MIN_L2_L1_LOGS_TREE_SIZE); + let min_tree_size = if protocol_version.is_pre_boojum() { + L2ToL1Log::PRE_BOOJUM_MIN_L2_L1_LOGS_TREE_SIZE + } else { + L2ToL1Log::MIN_L2_L1_LOGS_TREE_SIZE + }; let l2_l1_logs_merkle_root = - MiniMerkleTree::new(merkle_tree_leaves, min_tree_size).merkle_root(); + MiniMerkleTree::new(merkle_tree_leaves, Some(min_tree_size)).merkle_root(); Self { l2_l1_logs_compressed, @@ -375,6 +421,7 @@ impl L1BatchAuxiliaryOutput { bootloader_heap_hash, events_state_queue_hash, + protocol_version, } } @@ -382,10 +429,18 @@ impl L1BatchAuxiliaryOutput { // 4 H256 values const SERIALIZED_SIZE: usize = 128; let mut result = Vec::with_capacity(SERIALIZED_SIZE); - result.extend(self.system_logs_linear_hash.as_bytes()); - result.extend(self.state_diffs_hash.as_bytes()); - result.extend(self.bootloader_heap_hash.as_bytes()); - result.extend(self.events_state_queue_hash.as_bytes()); + + if self.protocol_version.is_pre_boojum() { + result.extend(self.l2_l1_logs_merkle_root.as_bytes()); + result.extend(self.l2_l1_logs_linear_hash.as_bytes()); + result.extend(self.initial_writes_hash.as_bytes()); + result.extend(self.repeated_writes_hash.as_bytes()); + } else { + result.extend(self.system_logs_linear_hash.as_bytes()); + result.extend(self.state_diffs_hash.as_bytes()); + result.extend(self.bootloader_heap_hash.as_bytes()); + result.extend(self.events_state_queue_hash.as_bytes()); + } result } @@ -479,6 +534,7 @@ impl L1BatchCommitment { state_diffs: Vec, bootloader_heap_hash: H256, events_state_queue_hash: H256, + protocol_version: ProtocolVersionId, ) -> Self { let meta_parameters = L1BatchMetaParameters { zkporter_is_available: ZKPORTER_IS_AVAILABLE, @@ -508,6 +564,7 @@ impl L1BatchCommitment { state_diffs, bootloader_heap_hash, events_state_queue_hash, + protocol_version, ), meta_parameters, } @@ -582,7 +639,7 @@ mod tests { }; use crate::l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}; use crate::writes::{InitialStorageWrite, RepeatedStorageWrite}; - use crate::{H256, U256}; + use crate::{ProtocolVersionId, H256, U256}; #[serde_as] #[derive(Debug, Serialize, Deserialize)] @@ -665,6 +722,7 @@ mod tests { vec![], H256::zero(), H256::zero(), + ProtocolVersionId::latest(), ); let commitment = L1BatchCommitment { diff --git a/core/lib/types/src/l2_to_l1_log.rs b/core/lib/types/src/l2_to_l1_log.rs index 3e8cfb40241..670a2b22e81 100644 --- a/core/lib/types/src/l2_to_l1_log.rs +++ b/core/lib/types/src/l2_to_l1_log.rs @@ -31,6 +31,10 @@ impl L2ToL1Log { /// for a certain batch. pub const MIN_L2_L1_LOGS_TREE_SIZE: usize = 2048; + /// Determines the minimum number of items in the Merkle tree built from L2-to-L1 logs + /// for a pre-boojum batch. + pub const PRE_BOOJUM_MIN_L2_L1_LOGS_TREE_SIZE: usize = 512; + pub fn from_slice(data: &[u8]) -> Self { assert_eq!(data.len(), Self::SERIALIZED_SIZE); Self { diff --git a/core/lib/zksync_core/src/genesis.rs b/core/lib/zksync_core/src/genesis.rs index ae1c737cb44..231ed4c88a9 100644 --- a/core/lib/zksync_core/src/genesis.rs +++ b/core/lib/zksync_core/src/genesis.rs @@ -111,6 +111,7 @@ pub async fn ensure_genesis_state( vec![], H256::zero(), H256::zero(), + *protocol_version, ); save_genesis_l1_batch_metadata( diff --git a/core/lib/zksync_core/src/metadata_calculator/mod.rs b/core/lib/zksync_core/src/metadata_calculator/mod.rs index 0f50b2bb09b..8392511858a 100644 --- a/core/lib/zksync_core/src/metadata_calculator/mod.rs +++ b/core/lib/zksync_core/src/metadata_calculator/mod.rs @@ -199,10 +199,16 @@ impl MetadataCalculator { tree_metadata.state_diffs, bootloader_initial_content_commitment.unwrap_or_default(), events_queue_commitment.unwrap_or_default(), + header.protocol_version.unwrap(), ); let commitment_hash = commitment.hash(); tracing::trace!("L1 batch commitment: {commitment:?}"); + let l2_l1_messages_compressed = if header.protocol_version.unwrap().is_pre_boojum() { + commitment.l2_l1_logs_compressed().to_vec() + } else { + commitment.system_logs_compressed().to_vec() + }; let metadata = L1BatchMetadata { root_hash: merkle_root_hash, rollup_last_leaf_index: tree_metadata.rollup_last_leaf_index, @@ -210,7 +216,7 @@ impl MetadataCalculator { initial_writes_compressed: commitment.initial_writes_compressed().to_vec(), repeated_writes_compressed: commitment.repeated_writes_compressed().to_vec(), commitment: commitment_hash.commitment, - l2_l1_messages_compressed: commitment.system_logs_compressed().to_vec(), + l2_l1_messages_compressed, l2_l1_merkle_root: commitment.l2_l1_logs_merkle_root(), block_meta_params: commitment.meta_parameters(), aux_data_hash: commitment_hash.aux_output,