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

feat: Protocol semantic version #2059

Merged
merged 25 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions checks-config/era.dic
Original file line number Diff line number Diff line change
Expand Up @@ -970,3 +970,4 @@ e2e
upcasting
foundryup
UNNEST
semver
5 changes: 2 additions & 3 deletions core/bin/external_node/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ fn mock_eth_client(diamond_proxy_addr: Address) -> MockClient<L1> {
let mock = MockEthereum::builder().with_call_handler(move |call, _| {
tracing::info!("L1 call: {call:?}");
if call.to == Some(diamond_proxy_addr) {
let packed_semver = ProtocolVersionId::latest().into_packed_semver_with_patch(0);
let call_signature = &call.data.as_ref().unwrap().0[..4];
let contract = zksync_contracts::hyperchain_contract();
let pricing_mode_sig = contract
Expand All @@ -117,9 +118,7 @@ fn mock_eth_client(diamond_proxy_addr: Address) -> MockClient<L1> {
sig if sig == pricing_mode_sig => {
return ethabi::Token::Uint(0.into()); // "rollup" mode encoding
}
sig if sig == protocol_version_sig => {
return ethabi::Token::Uint((ProtocolVersionId::latest() as u16).into())
}
sig if sig == protocol_version_sig => return ethabi::Token::Uint(packed_semver),
_ => { /* unknown call; panic below */ }
}
}
Expand Down
9 changes: 7 additions & 2 deletions core/bin/genesis_generator/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ use zksync_protobuf::{
ProtoRepr,
};
use zksync_protobuf_config::proto::genesis::Genesis;
use zksync_types::{url::SensitiveUrl, ProtocolVersionId};
use zksync_types::{
protocol_version::ProtocolSemanticVersion, url::SensitiveUrl, ProtocolVersionId,
};

const DEFAULT_GENESIS_FILE_PATH: &str = "./etc/env/file_based/genesis.yaml";

Expand Down Expand Up @@ -80,7 +82,10 @@ async fn generate_new_config(

let base_system_contracts = BaseSystemContracts::load_from_disk().hashes();
let mut updated_genesis = GenesisConfig {
protocol_version: Some(ProtocolVersionId::latest() as u16),
protocol_version: Some(ProtocolSemanticVersion {
minor: ProtocolVersionId::latest(),
patch: 0.into(), // genesis generator proposes some new valid config, so patch 0 works here.
}),
genesis_root_hash: None,
rollup_last_leaf_index: None,
genesis_commitment: None,
Expand Down
1 change: 1 addition & 0 deletions core/lib/basic_types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ strum = { workspace = true, features = ["derive"] }
num_enum.workspace = true
anyhow.workspace = true
url = { workspace = true, features = ["serde"] }
serde_with.workspace = true

[dev-dependencies]
bincode.workspace = true
161 changes: 160 additions & 1 deletion core/lib/basic_types/src/protocol_version.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
use std::{
convert::{TryFrom, TryInto},
fmt,
num::ParseIntError,
ops::{Add, Deref, DerefMut, Sub},
str::FromStr,
};

use num_enum::TryFromPrimitive;
use serde::{Deserialize, Serialize};
use serde_with::{DeserializeFromStr, SerializeDisplay};

use crate::{
ethabi::Token,
Expand All @@ -13,6 +17,24 @@ use crate::{
H256, U256,
};

pub const PACKED_SEMVER_MINOR_OFFSET: u32 = 32;
pub const PACKED_SEMVER_MINOR_MASK: u32 = 0xFFFF;

// These values should be manually updated for every protocol upgrade
// Otherwise, the prover will not be able to work with new versions.
// TODO(PLA-954): Move to prover workspace
pub const PROVER_PROTOCOL_VERSION: ProtocolVersionId = ProtocolVersionId::Version24;
pub const PROVER_PROTOCOL_PATCH: VersionPatch = VersionPatch(0);
pub const PROVER_PROTOCOL_SEMANTIC_VERSION: ProtocolSemanticVersion = ProtocolSemanticVersion {
minor: PROVER_PROTOCOL_VERSION,
patch: PROVER_PROTOCOL_PATCH,
};

/// `ProtocolVersionId` is a unique identifier of the protocol version.
/// Note, that it is an identifier of the `minor` semver version of the protocol, with
/// the `major` version being `0`. Also, the protocol version on the contracts may contain
/// potential minor versions, that may have different contract behavior (e.g. Verifier), but it should not
/// impact the users.
#[repr(u16)]
#[derive(
Debug,
Expand Down Expand Up @@ -64,13 +86,24 @@ impl ProtocolVersionId {
}

pub fn current_prover_version() -> Self {
Self::Version24
PROVER_PROTOCOL_VERSION
}

pub fn next() -> Self {
Self::Version25
}

pub fn try_from_packed_semver(packed_semver: U256) -> Result<Self, String> {
ProtocolSemanticVersion::try_from_packed(packed_semver).map(|p| p.minor)
}

pub fn into_packed_semver_with_patch(self, patch: usize) -> U256 {
let minor = U256::from(self as u16);
let patch = U256::from(patch as u32);

(minor << U256::from(PACKED_SEMVER_MINOR_OFFSET)) | patch
}

/// Returns VM version to be used by API for this protocol version.
/// We temporary support only two latest VM versions for API.
pub fn into_api_vm_version(self) -> VmVersion {
Expand Down Expand Up @@ -255,3 +288,129 @@ impl From<ProtocolVersionId> for VmVersion {
}
}
}

basic_type!(
/// Patch part of semantic protocol version.
VersionPatch,
u32
);

/// Semantic protocol version.
#[derive(
Debug, Clone, Copy, PartialEq, Eq, SerializeDisplay, DeserializeFromStr, Hash, PartialOrd, Ord,
)]
pub struct ProtocolSemanticVersion {
pub minor: ProtocolVersionId,
pub patch: VersionPatch,
}

impl ProtocolSemanticVersion {
const MAJOR_VERSION: u8 = 0;

pub fn new(minor: ProtocolVersionId, patch: VersionPatch) -> Self {
Self { minor, patch }
}

pub fn current_prover_version() -> Self {
PROVER_PROTOCOL_SEMANTIC_VERSION
}

pub fn try_from_packed(packed: U256) -> Result<Self, String> {
let minor = ((packed >> U256::from(PACKED_SEMVER_MINOR_OFFSET))
& U256::from(PACKED_SEMVER_MINOR_MASK))
.try_into()?;
let patch = packed.0[0] as u32;

Ok(Self {
minor,
patch: VersionPatch(patch),
})
}

pub fn pack(&self) -> U256 {
(U256::from(self.minor as u16) << U256::from(PACKED_SEMVER_MINOR_OFFSET))
| U256::from(self.patch.0)
}
}

impl fmt::Display for ProtocolSemanticVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}.{}.{}",
Self::MAJOR_VERSION,
self.minor as u16,
self.patch
)
}
}

#[derive(Debug, thiserror::Error)]
pub enum ParseProtocolSemanticVersionError {
#[error("invalid format")]
InvalidFormat,
#[error("non zero major version")]
NonZeroMajorVersion,
#[error("{0}")]
ParseIntError(ParseIntError),
}

impl FromStr for ProtocolSemanticVersion {
type Err = ParseProtocolSemanticVersionError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = s.split('.').collect();
if parts.len() != 3 {
return Err(ParseProtocolSemanticVersionError::InvalidFormat);
}

let major = parts[0]
.parse::<u16>()
.map_err(ParseProtocolSemanticVersionError::ParseIntError)?;
if major != 0 {
return Err(ParseProtocolSemanticVersionError::NonZeroMajorVersion);
}

let minor = parts[1]
.parse::<u16>()
.map_err(ParseProtocolSemanticVersionError::ParseIntError)?;
let minor = ProtocolVersionId::try_from(minor)
.map_err(|_| ParseProtocolSemanticVersionError::InvalidFormat)?;

let patch = parts[2]
.parse::<u32>()
.map_err(ParseProtocolSemanticVersionError::ParseIntError)?;

Ok(ProtocolSemanticVersion {
minor,
patch: patch.into(),
})
}
}

impl Default for ProtocolSemanticVersion {
fn default() -> Self {
Self {
minor: Default::default(),
patch: 0.into(),
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_protocol_version_packing() {
let version = ProtocolSemanticVersion {
minor: ProtocolVersionId::latest(),
patch: 10.into(),
};

let packed = version.pack();
let unpacked = ProtocolSemanticVersion::try_from_packed(packed).unwrap();

assert_eq!(version, unpacked);
}
}
13 changes: 10 additions & 3 deletions core/lib/config/src/configs/genesis.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
use serde::{Deserialize, Serialize};
use zksync_basic_types::{commitment::L1BatchCommitmentMode, Address, L1ChainId, L2ChainId, H256};
use zksync_basic_types::{
commitment::L1BatchCommitmentMode,
protocol_version::{ProtocolSemanticVersion, ProtocolVersionId},
Address, L1ChainId, L2ChainId, H256,
};

/// This config represents the genesis state of the chain.
/// Each chain has this config immutable and we update it only during the protocol upgrade
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct GenesisConfig {
// TODO make fields non optional, once we fully moved to file based configs.
// Now for backward compatibility we keep it optional
pub protocol_version: Option<u16>,
pub protocol_version: Option<ProtocolSemanticVersion>,
pub genesis_root_hash: Option<H256>,
pub rollup_last_leaf_index: Option<u64>,
pub genesis_commitment: Option<H256>,
Expand Down Expand Up @@ -38,7 +42,10 @@ impl GenesisConfig {
bootloader_hash: Default::default(),
default_aa_hash: Default::default(),
l1_chain_id: L1ChainId(9),
protocol_version: Some(22),
protocol_version: Some(ProtocolSemanticVersion {
minor: ProtocolVersionId::latest(),
patch: 0.into(),
}),
l2_chain_id: L2ChainId::default(),
dummy_verifier: false,
l1_batch_commit_data_generator_mode: L1BatchCommitmentMode::Rollup,
Expand Down
13 changes: 11 additions & 2 deletions core/lib/config/src/testonly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use std::num::NonZeroUsize;

use rand::{distributions::Distribution, Rng};
use zksync_basic_types::{
basic_fri_types::CircuitIdRoundTuple, commitment::L1BatchCommitmentMode, network::Network,
basic_fri_types::CircuitIdRoundTuple,
commitment::L1BatchCommitmentMode,
network::Network,
protocol_version::{ProtocolSemanticVersion, ProtocolVersionId, VersionPatch},
L1ChainId, L2ChainId,
};
use zksync_consensus_utils::EncodeDist;
Expand Down Expand Up @@ -659,7 +662,13 @@ impl Distribution<configs::OpentelemetryConfig> for EncodeDist {
impl Distribution<configs::GenesisConfig> for EncodeDist {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> configs::GenesisConfig {
configs::GenesisConfig {
protocol_version: self.sample(rng),
protocol_version: Some(ProtocolSemanticVersion {
minor: ProtocolVersionId::try_from(
rng.gen_range(0..(ProtocolVersionId::latest() as u16)),
)
.unwrap(),
patch: VersionPatch(rng.gen()),
}),
genesis_root_hash: rng.gen(),
rollup_last_leaf_index: self.sample(rng),
genesis_commitment: rng.gen(),
Expand Down

This file was deleted.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading