Skip to content

Commit

Permalink
fix: Versioned L1 batch metadata (#450)
Browse files Browse the repository at this point in the history
## What ❔

L1 batch metadata is calculated differently for pre-boojum and
post-boojum batches.

## Why ❔

L1 batch metadata backward compatibility

## Checklist

<!-- Check your PR fulfills the following items. -->
<!-- For draft PRs check the boxes as you complete them. -->

- [x] PR title corresponds to the body of PR (we generate changelog
entries from PRs).
- [ ] Tests for the changes have been added / updated.
- [x] Documentation comments have been added / updated.
- [x] Code has been formatted via `zk fmt` and `zk lint`.
  • Loading branch information
perekopskiy committed Nov 8, 2023
1 parent fe3b2cf commit 8a40dc3
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 15 deletions.
86 changes: 72 additions & 14 deletions core/lib/types/src/commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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<I: SerializeCommitment>(values: &[I]) -> Vec<u8> {
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<I: SerializeCommitment>(values: &[I]) -> Vec<u8> {
let final_len = values.len() * I::SERIALIZED_SIZE;
Expand Down Expand Up @@ -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<UserL2ToL1Log>,
initial_writes: Vec<InitialStorageWrite>,
Expand All @@ -334,12 +351,31 @@ impl L1BatchAuxiliaryOutput {
state_diffs: Vec<StateDiffRecord>,
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());

Expand All @@ -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,
Expand All @@ -375,17 +421,26 @@ impl L1BatchAuxiliaryOutput {

bootloader_heap_hash,
events_state_queue_hash,
protocol_version,
}
}

pub fn to_bytes(&self) -> Vec<u8> {
// 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
}

Expand Down Expand Up @@ -479,6 +534,7 @@ impl L1BatchCommitment {
state_diffs: Vec<StateDiffRecord>,
bootloader_heap_hash: H256,
events_state_queue_hash: H256,
protocol_version: ProtocolVersionId,
) -> Self {
let meta_parameters = L1BatchMetaParameters {
zkporter_is_available: ZKPORTER_IS_AVAILABLE,
Expand Down Expand Up @@ -508,6 +564,7 @@ impl L1BatchCommitment {
state_diffs,
bootloader_heap_hash,
events_state_queue_hash,
protocol_version,
),
meta_parameters,
}
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -665,6 +722,7 @@ mod tests {
vec![],
H256::zero(),
H256::zero(),
ProtocolVersionId::latest(),
);

let commitment = L1BatchCommitment {
Expand Down
4 changes: 4 additions & 0 deletions core/lib/types/src/l2_to_l1_log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions core/lib/zksync_core/src/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ pub async fn ensure_genesis_state(
vec![],
H256::zero(),
H256::zero(),
*protocol_version,
);

save_genesis_l1_batch_metadata(
Expand Down
8 changes: 7 additions & 1 deletion core/lib/zksync_core/src/metadata_calculator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,18 +199,24 @@ 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,
merkle_root_hash,
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,
Expand Down

0 comments on commit 8a40dc3

Please sign in to comment.