Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Versioned L1 batch metadata #450

Merged
merged 1 commit into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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