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(eth-watch): Change protocol upgrade schema #3435

Merged
merged 2 commits into from
Jan 7, 2025
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
2 changes: 2 additions & 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 core/lib/types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ zksync_mini_merkle_tree.workspace = true
zksync_protobuf.workspace = true
zksync_crypto_primitives.workspace = true

async-trait.workspace = true
anyhow.workspace = true
chrono = { workspace = true, features = ["serde"] }
derive_more = { workspace = true, features = ["debug"] }
Expand Down
276 changes: 247 additions & 29 deletions core/lib/types/src/abi.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use anyhow::Context as _;
use zksync_basic_types::protocol_version::ProtocolSemanticVersion;

use crate::{
bytecode::BytecodeHash,
Expand Down Expand Up @@ -185,17 +186,19 @@ impl NewPriorityRequest {
}

/// `VerifierParams` from `l1-contracts/contracts/state-transition/chain-interfaces/IVerifier.sol`.
#[derive(Default, PartialEq)]
#[derive(Debug, Default, PartialEq)]
pub struct VerifierParams {
pub recursion_node_level_vk_hash: [u8; 32],
pub recursion_leaf_level_vk_hash: [u8; 32],
pub recursion_circuits_set_vks_hash: [u8; 32],
}

/// `ProposedUpgrade` from, `l1-contracts/contracts/upgrades/BazeZkSyncUpgrade.sol`.
#[derive(Debug)]
pub struct ProposedUpgrade {
pub l2_protocol_upgrade_tx: Box<L2CanonicalTransaction>,
pub factory_deps: Vec<Vec<u8>>,
// Factory deps are set only pre-gateway upgrades.
pub factory_deps: Option<Vec<Vec<u8>>>,
perekopskiy marked this conversation as resolved.
Show resolved Hide resolved
pub bootloader_hash: [u8; 32],
pub default_account_hash: [u8; 32],
pub verifier: Address,
Expand Down Expand Up @@ -250,8 +253,8 @@ impl VerifierParams {
}

impl ProposedUpgrade {
/// RLP schema of the `ProposedUpgrade`.
pub fn schema() -> ParamType {
/// Pre-gateway RLP schema of the `ProposedUpgrade`.
pub fn schema_pre_gateway() -> ParamType {
ParamType::Tuple(vec![
L2CanonicalTransaction::schema(), // transaction data
ParamType::Array(ParamType::Bytes.into()), // factory deps
Expand All @@ -266,16 +269,39 @@ impl ProposedUpgrade {
])
}

/// Post-gateway RLP schema of the `ProposedUpgrade`.
pub fn schema_post_gateway() -> ParamType {
ParamType::Tuple(vec![
L2CanonicalTransaction::schema(), // transaction data
ParamType::FixedBytes(32), // bootloader code hash
ParamType::FixedBytes(32), // default account code hash
ParamType::Address, // verifier address
VerifierParams::schema(), // verifier params
ParamType::Bytes, // l1 custom data
ParamType::Bytes, // l1 post-upgrade custom data
ParamType::Uint(256), // timestamp
ParamType::Uint(256), // version id
])
}

/// Encodes `ProposedUpgrade` to a RLP token.
pub fn encode(&self) -> Token {
Token::Tuple(vec![
self.l2_protocol_upgrade_tx.encode(),
Token::Array(
let mut tokens = vec![self.l2_protocol_upgrade_tx.encode()];

let protocol_version = ProtocolSemanticVersion::try_from_packed(self.new_protocol_version)
.expect("Version is not supported")
.minor;
if protocol_version.is_pre_gateway() {
tokens.push(Token::Array(
self.factory_deps
.iter()
.map(|b| Token::Bytes(b.clone()))
.clone()
.expect("Factory deps should be present in pre-gateway upgrade data")
.into_iter()
.map(Token::Bytes)
.collect(),
),
));
}
tokens.extend([
Token::FixedBytes(self.bootloader_hash.into()),
Token::FixedBytes(self.default_account_hash.into()),
Token::Address(self.verifier),
Expand All @@ -284,32 +310,52 @@ impl ProposedUpgrade {
Token::Bytes(self.post_upgrade_calldata.clone()),
Token::Uint(self.upgrade_timestamp),
Token::Uint(self.new_protocol_version),
])
]);

Token::Tuple(tokens)
}

/// Decodes `ProposedUpgrade` from a RLP token.
/// Returns an error if token doesn't match the `schema()`.
pub fn decode(token: Token) -> anyhow::Result<Self> {
let tokens = token.into_tuple().context("not a tuple")?;
anyhow::ensure!(tokens.len() == 10);
let tokens_len = tokens.len();
anyhow::ensure!(tokens_len >= 9);
let mut t = tokens.into_iter();
let mut next = || t.next().unwrap();
Ok(Self {
l2_protocol_upgrade_tx: L2CanonicalTransaction::decode(next())
.context("l2_protocol_upgrade_tx")?
.into(),
factory_deps: next()
.into_array()
.context("factory_deps")?
.into_iter()
.enumerate()
.map(|(i, b)| b.into_bytes().context(i))
.collect::<Result<_, _>>()
.context("factory_deps")?,
bootloader_hash: next()
.into_fixed_bytes()
.and_then(|b| b.try_into().ok())
.context("bootloader_hash")?,

let l2_protocol_upgrade_tx = L2CanonicalTransaction::decode(next())
.context("l2_protocol_upgrade_tx")?
.into();
let next_token = next();
let (factory_deps, bootloader_hash) = match next_token {
Token::Array(tokens) => {
anyhow::ensure!(tokens_len == 10);
(
Some(
tokens
.into_iter()
.enumerate()
.map(|(i, b)| b.into_bytes().context(i))
.collect::<Result<_, _>>()
.context("factory_deps")?,
),
next().into_fixed_bytes(),
)
}
Token::FixedBytes(bytes) => {
anyhow::ensure!(tokens_len == 9);
(None, Some(bytes))
}
_ => anyhow::bail!("Unexpected type of the second token"),
};
let bootloader_hash = bootloader_hash
.and_then(|b| b.try_into().ok())
.context("bootloader_hash")?;
let upgrade = Self {
l2_protocol_upgrade_tx,
factory_deps,
bootloader_hash,
default_account_hash: next()
.into_fixed_bytes()
.and_then(|b| b.try_into().ok())
Expand All @@ -322,7 +368,16 @@ impl ProposedUpgrade {
post_upgrade_calldata: next().into_bytes().context("post_upgrade_calldata")?,
upgrade_timestamp: next().into_uint().context("upgrade_timestamp")?,
new_protocol_version: next().into_uint().context("new_protocol_version")?,
})
};

let protocol_version =
ProtocolSemanticVersion::try_from_packed(upgrade.new_protocol_version)
.map_err(|err| anyhow::anyhow!(err))
.context("Version is not supported")?
.minor;
anyhow::ensure!(protocol_version.is_pre_gateway() == upgrade.factory_deps.is_some());

Ok(upgrade)
}
}

Expand Down Expand Up @@ -365,3 +420,166 @@ impl Transaction {
})
}
}

pub struct ForceDeployment {
pub bytecode_hash: H256,
pub new_address: Address,
pub call_constructor: bool,
pub value: U256,
pub input: Vec<u8>,
}

impl ForceDeployment {
/// ABI schema of the `ForceDeployment`.
pub fn schema() -> ParamType {
ParamType::Tuple(vec![
ParamType::FixedBytes(32),
ParamType::Address,
ParamType::Bool,
ParamType::Uint(256),
ParamType::Bytes,
])
}

/// Encodes `ForceDeployment` to a RLP token.
pub fn encode(&self) -> Token {
Token::Tuple(vec![
Token::FixedBytes(self.bytecode_hash.0.to_vec()),
Token::Address(self.new_address),
Token::Bool(self.call_constructor),
Token::Uint(self.value),
Token::Bytes(self.input.clone()),
])
}

/// Decodes `ForceDeployment` from a RLP token.
/// Returns an error if token doesn't match the `schema()`.
pub fn decode(token: Token) -> anyhow::Result<Self> {
let tokens = token.into_tuple().context("not a tuple")?;
anyhow::ensure!(tokens.len() == 5);
let mut t = tokens.into_iter();
let mut next = || t.next().unwrap();
Ok(Self {
bytecode_hash: next()
.into_fixed_bytes()
.and_then(|b| Some(H256(b.try_into().ok()?)))
.context("bytecode_hash")?,
new_address: next().into_address().context("new_address")?,
call_constructor: next().into_bool().context("call_constructor")?,
value: next().into_uint().context("value")?,
input: next().into_bytes().context("input")?,
})
}
}

pub struct GatewayUpgradeEncodedInput {
pub force_deployments: Vec<ForceDeployment>,
pub l2_gateway_upgrade_position: usize,
pub fixed_force_deployments_data: Vec<u8>,
pub ctm_deployer: Address,
pub old_validator_timelock: Address,
pub new_validator_timelock: Address,
pub wrapped_base_token_store: Address,
}

impl GatewayUpgradeEncodedInput {
/// ABI schema of the `GatewayUpgradeEncodedInput`.
pub fn schema() -> ParamType {
ParamType::Tuple(vec![
ParamType::Array(Box::new(ForceDeployment::schema())),
ParamType::Uint(256),
ParamType::Bytes,
ParamType::Address,
ParamType::Address,
ParamType::Address,
ParamType::Address,
])
}

/// Decodes `GatewayUpgradeEncodedInput` from a RLP token.
/// Returns an error if token doesn't match the `schema()`.
pub fn decode(token: Token) -> anyhow::Result<Self> {
let tokens = token.into_tuple().context("not a tuple")?;
anyhow::ensure!(tokens.len() == 7);
let mut t = tokens.into_iter();
let mut next = || t.next().unwrap();

let force_deployments_array = next().into_array().context("force_deployments_array")?;
let mut force_deployments = vec![];
for token in force_deployments_array {
force_deployments.push(ForceDeployment::decode(token)?);
}

Ok(Self {
force_deployments,
l2_gateway_upgrade_position: next()
.into_uint()
.context("l2_gateway_upgrade_position")?
.as_usize(),
fixed_force_deployments_data: next()
.into_bytes()
.context("fixed_force_deployments_data")?,
ctm_deployer: next().into_address().context("ctm_deployer")?,
old_validator_timelock: next().into_address().context("old_validator_timelock")?,
new_validator_timelock: next().into_address().context("new_validator_timelock")?,
wrapped_base_token_store: next().into_address().context("wrapped_base_token_store")?,
})
}
}

#[derive(Debug, Clone)]
pub struct ZkChainSpecificUpgradeData {
pub base_token_asset_id: H256,
pub l2_legacy_shared_bridge: Address,
pub predeployed_l2_weth_address: Address,
pub base_token_l1_address: Address,
pub base_token_name: String,
pub base_token_symbol: String,
}

impl ZkChainSpecificUpgradeData {
pub fn from_partial_components(
base_token_asset_id: Option<H256>,
l2_legacy_shared_bridge: Option<Address>,
predeployed_l2_weth_address: Option<Address>,
base_token_l1_address: Option<Address>,
base_token_name: Option<String>,
base_token_symbol: Option<String>,
) -> Option<Self> {
Some(Self {
base_token_asset_id: base_token_asset_id?,
l2_legacy_shared_bridge: l2_legacy_shared_bridge?,
// Note, that some chains may not contain previous deployment of L2 wrapped base
// token. For those, zero address is used.
predeployed_l2_weth_address: predeployed_l2_weth_address.unwrap_or_default(),
base_token_l1_address: base_token_l1_address?,
base_token_name: base_token_name?,
base_token_symbol: base_token_symbol?,
})
}

/// ABI schema of the `ZkChainSpecificUpgradeData`.
pub fn schema() -> ParamType {
ParamType::Tuple(vec![
ParamType::FixedBytes(32),
ParamType::Address,
ParamType::Address,
])
}

/// Encodes `ZkChainSpecificUpgradeData` to a RLP token.
pub fn encode(&self) -> Token {
Token::Tuple(vec![
Token::FixedBytes(self.base_token_asset_id.0.to_vec()),
Token::Address(self.l2_legacy_shared_bridge),
Token::Address(self.predeployed_l2_weth_address),
Token::Address(self.base_token_l1_address),
Token::String(self.base_token_name.clone()),
Token::String(self.base_token_symbol.clone()),
])
}

pub fn encode_bytes(&self) -> Vec<u8> {
ethabi::encode(&[self.encode()])
}
}
Loading
Loading