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): Integrate decentralized upgrades #2401

Merged
merged 7 commits into from
Jul 11, 2024
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 core/lib/config/src/configs/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub struct ContractsConfig {
pub ecosystem_contracts: Option<EcosystemContracts>,
// Used by the RPC API and by the node builder in wiring the BaseTokenRatioProvider layer.
pub base_token_addr: Option<Address>,
pub chain_admin_addr: Option<Address>,
}

impl ContractsConfig {
Expand All @@ -59,6 +60,7 @@ impl ContractsConfig {
governance_addr: Address::repeat_byte(0x13),
base_token_addr: Some(Address::repeat_byte(0x14)),
ecosystem_contracts: Some(EcosystemContracts::for_tests()),
chain_admin_addr: Some(Address::repeat_byte(0x18)),
}
}
}
1 change: 1 addition & 0 deletions core/lib/config/src/testonly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ impl Distribution<configs::ContractsConfig> for EncodeDist {
l2_testnet_paymaster_addr: g.gen(),
l1_multicall3_addr: g.gen(),
base_token_addr: g.gen(),
chain_admin_addr: g.gen(),
ecosystem_contracts: self.sample(g),
}
}
Expand Down
62 changes: 62 additions & 0 deletions core/lib/contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const DIAMOND_INIT_CONTRACT_FILE: (&str, &str) = (
"chain-interfaces/IDiamondInit.sol/IDiamondInit.json",
);
const GOVERNANCE_CONTRACT_FILE: (&str, &str) = ("governance", "IGovernance.sol/IGovernance.json");
const CHAIN_ADMIN_CONTRACT_FILE: (&str, &str) = ("governance", "IChainAdmin.sol/IChainAdmin.json");
const MULTICALL3_CONTRACT_FILE: (&str, &str) = ("dev-contracts", "Multicall3.sol/Multicall3.json");
const VERIFIER_CONTRACT_FILE: (&str, &str) = ("state-transition", "Verifier.sol/Verifier.json");
const _IERC20_CONTRACT_FILE: &str =
Expand Down Expand Up @@ -128,6 +129,10 @@ pub fn governance_contract() -> Contract {
load_contract_for_both_compilers(GOVERNANCE_CONTRACT_FILE)
}

pub fn chain_admin_contract() -> Contract {
load_contract_for_both_compilers(CHAIN_ADMIN_CONTRACT_FILE)
}

pub fn state_transition_manager_contract() -> Contract {
load_contract_for_both_compilers(STATE_TRANSITION_CONTRACT_FILE)
}
Expand Down Expand Up @@ -804,3 +809,60 @@ pub static ADMIN_UPGRADE_CHAIN_FROM_VERSION_FUNCTION: Lazy<Function> = Lazy::new
}"#;
serde_json::from_str(abi).unwrap()
});

pub static DIAMOND_CUT: Lazy<Function> = Lazy::new(|| {
let abi = r#"
{
"inputs": [
{
"components": [
{
"components": [
{
"internalType": "address",
"name": "facet",
"type": "address"
},
{
"internalType": "enum Diamond.Action",
"name": "action",
"type": "uint8"
},
{
"internalType": "bool",
"name": "isFreezable",
"type": "bool"
},
{
"internalType": "bytes4[]",
"name": "selectors",
"type": "bytes4[]"
}
],
"internalType": "struct Diamond.FacetCut[]",
"name": "facetCuts",
"type": "tuple[]"
},
{
"internalType": "address",
"name": "initAddress",
"type": "address"
},
{
"internalType": "bytes",
"name": "initCalldata",
"type": "bytes"
}
],
"internalType": "struct Diamond.DiamondCutData",
"name": "_diamondCut",
"type": "tuple"
}
],
"name": "diamondCut",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}"#;
serde_json::from_str(abi).unwrap()
});
2 changes: 2 additions & 0 deletions core/lib/env_config/src/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ mod tests {
transparent_proxy_admin_addr: addr("0xdd6fa5c14e7550b4caf2aa2818d24c69cbc347e5"),
}),
base_token_addr: Some(SHARED_BRIDGE_ETHER_TOKEN_ADDRESS),
chain_admin_addr: Some(addr("0xdd6fa5c14e7550b4caf2aa2818d24c69cbc347ff")),
}
}

Expand All @@ -95,6 +96,7 @@ CONTRACTS_BRIDGEHUB_PROXY_ADDR="0x35ea7f92f4c5f433efe15284e99c040110cf6297"
CONTRACTS_STATE_TRANSITION_PROXY_ADDR="0xd90f1c081c6117241624e97cb6147257c3cb2097"
CONTRACTS_TRANSPARENT_PROXY_ADMIN_ADDR="0xdd6fa5c14e7550b4caf2aa2818d24c69cbc347e5"
CONTRACTS_BASE_TOKEN_ADDR="0x0000000000000000000000000000000000000001"
CONTRACTS_CHAIN_ADMIN_ADDR="0xdd6fa5c14e7550b4caf2aa2818d24c69cbc347ff"
"#;
lock.set_env(config);

Expand Down
7 changes: 7 additions & 0 deletions core/lib/protobuf_config/src/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ impl ProtoRepr for proto::Contracts {
.map(|x| parse_h160(x))
.transpose()
.context("base_token_addr")?,
chain_admin_addr: l1
.chain_admin_addr
.as_ref()
.map(|x| parse_h160(x))
.transpose()
.context("chain_admin_addr")?,
})
}

Expand Down Expand Up @@ -132,6 +138,7 @@ impl ProtoRepr for proto::Contracts {
default_upgrade_addr: Some(format!("{:?}", this.default_upgrade_addr)),
multicall3_addr: Some(format!("{:?}", this.l1_multicall3_addr)),
base_token_addr: this.base_token_addr.map(|a| format!("{:?}", a)),
chain_admin_addr: this.chain_admin_addr.map(|a| format!("{:?}", a)),
}),
l2: Some(proto::L2 {
testnet_paymaster_addr: this.l2_testnet_paymaster_addr.map(|a| format!("{:?}", a)),
Expand Down
1 change: 1 addition & 0 deletions core/lib/protobuf_config/src/proto/config/contracts.proto
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ message L1 {
optional string default_upgrade_addr = 5; // required; H160
optional string multicall3_addr = 6; // required; H160
optional string base_token_addr = 7; // required; H160
optional string chain_admin_addr = 8; // required; H160
}

message L2 {
Expand Down
37 changes: 13 additions & 24 deletions core/lib/types/src/protocol_upgrade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use zksync_basic_types::{
};
use zksync_contracts::{
BaseSystemContractsHashes, ADMIN_EXECUTE_UPGRADE_FUNCTION,
ADMIN_UPGRADE_CHAIN_FROM_VERSION_FUNCTION,
ADMIN_UPGRADE_CHAIN_FROM_VERSION_FUNCTION, DIAMOND_CUT,
};
use zksync_utils::h256_to_u256;

Expand All @@ -28,10 +28,6 @@ pub struct Call {
pub value: U256,
/// The calldata to be executed on the `target` address.
pub data: Vec<u8>,
/// Hash of the corresponding Ethereum transaction. Size should be 32 bytes.
pub eth_hash: H256,
/// Block in which Ethereum transaction was included.
pub eth_block: u64,
}

impl std::fmt::Debug for Call {
Expand All @@ -40,8 +36,6 @@ impl std::fmt::Debug for Call {
.field("target", &self.target)
.field("value", &self.value)
.field("data", &hex::encode(&self.data))
.field("eth_hash", &self.eth_hash)
.field("eth_block", &self.eth_block)
.finish()
}
}
Expand Down Expand Up @@ -99,8 +93,17 @@ impl From<abi::VerifierParams> for VerifierParams {
}

impl ProtocolUpgrade {
pub fn try_from_diamond_cut(diamond_cut_data: &[u8]) -> anyhow::Result<Self> {
// Unwraps are safe because we have validated the input against the function signature.
let diamond_cut_tokens = DIAMOND_CUT.decode_input(diamond_cut_data)?[0]
.clone()
.into_tuple()
.unwrap();
Self::try_from_init_calldata(&diamond_cut_tokens[2].clone().into_bytes().unwrap())
}

/// `l1-contracts/contracts/state-transition/libraries/diamond.sol:DiamondCutData.initCalldata`
fn try_from_init_calldata(init_calldata: &[u8], eth_block: u64) -> anyhow::Result<Self> {
fn try_from_init_calldata(init_calldata: &[u8]) -> anyhow::Result<Self> {
let upgrade = ethabi::decode(
&[abi::ProposedUpgrade::schema()],
init_calldata.get(4..).context("need >= 4 bytes")?,
Expand All @@ -124,7 +127,7 @@ impl ProtocolUpgrade {
Transaction::try_from(abi::Transaction::L1 {
tx: upgrade.l2_protocol_upgrade_tx,
factory_deps: upgrade.factory_deps,
eth_block,
eth_block: 0,
})
.context("Transaction::try_from()")?
.try_into()
Expand All @@ -148,10 +151,7 @@ pub fn decode_set_chain_id_event(
protocol_version,
Transaction::try_from(abi::Transaction::L1 {
tx: tx.into(),
eth_block: event
.block_number
.expect("Event block number is missing")
.as_u64(),
eth_block: 0,
factory_deps: vec![],
})
.unwrap()
Expand Down Expand Up @@ -199,7 +199,6 @@ impl TryFrom<Call> for ProtocolUpgrade {
ProtocolUpgrade::try_from_init_calldata(
// Unwrap is safe because we have validated the input against the function signature.
&diamond_cut_tokens[2].clone().into_bytes().unwrap(),
call.eth_block,
)
.context("ProtocolUpgrade::try_from_init_calldata()")
}
Expand All @@ -226,14 +225,6 @@ impl TryFrom<Log> for GovernanceOperation {
// Extract `GovernanceOperation` data.
let mut decoded_governance_operation = decoded.remove(1).into_tuple().unwrap();

let eth_hash = event
.transaction_hash
.expect("Event transaction hash is missing");
let eth_block = event
.block_number
.expect("Event block number is missing")
.as_u64();

let calls = decoded_governance_operation.remove(0).into_array().unwrap();
let predecessor = H256::from_slice(
&decoded_governance_operation
Expand All @@ -260,8 +251,6 @@ impl TryFrom<Log> for GovernanceOperation {
.unwrap(),
value: decoded_governance_operation.remove(0).into_uint().unwrap(),
data: decoded_governance_operation.remove(0).into_bytes().unwrap(),
eth_hash,
eth_block,
}
})
.collect();
Expand Down
41 changes: 40 additions & 1 deletion core/node/eth_watch/src/client.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::fmt;

use zksync_contracts::verifier_contract;
use anyhow::Context;
use zksync_contracts::{state_transition_manager_contract, verifier_contract};
use zksync_eth_client::{
clients::{DynClient, L1},
CallFunctionArgs, ClientError, ContractCallError, EnrichedClientError, EnrichedClientResult,
Expand All @@ -27,6 +28,11 @@ pub trait EthClient: 'static + fmt::Debug + Send + Sync {
/// Returns scheduler verification key hash by verifier address.
async fn scheduler_vk_hash(&self, verifier_address: Address)
-> Result<H256, ContractCallError>;
/// Returns upgrade diamond cut by packed protocol version.
async fn diamond_cut_by_version(
&self,
packed_version: H256,
) -> EnrichedClientResult<Option<Vec<u8>>>;
/// Sets list of topics to return events for.
fn set_topics(&mut self, topics: Vec<H256>);
}
Expand All @@ -42,8 +48,10 @@ pub struct EthHttpQueryClient {
topics: Vec<H256>,
diamond_proxy_addr: Address,
governance_address: Address,
new_upgrade_cut_data_signature: H256,
// Only present for post-shared bridge chains.
state_transition_manager_address: Option<Address>,
chain_admin_address: Option<Address>,
verifier_contract_abi: Contract,
confirmations_for_eth_event: Option<u64>,
}
Expand All @@ -53,6 +61,7 @@ impl EthHttpQueryClient {
client: Box<DynClient<L1>>,
diamond_proxy_addr: Address,
state_transition_manager_address: Option<Address>,
chain_admin_address: Option<Address>,
governance_address: Address,
confirmations_for_eth_event: Option<u64>,
) -> Self {
Expand All @@ -66,7 +75,13 @@ impl EthHttpQueryClient {
topics: Vec::new(),
diamond_proxy_addr,
state_transition_manager_address,
chain_admin_address,
governance_address,
new_upgrade_cut_data_signature: state_transition_manager_contract()
.event("NewUpgradeCutData")
.context("NewUpgradeCutData event is missing in ABI")
.unwrap()
.signature(),
verifier_contract_abi: verifier_contract(),
confirmations_for_eth_event,
}
Expand All @@ -84,6 +99,7 @@ impl EthHttpQueryClient {
Some(self.diamond_proxy_addr),
Some(self.governance_address),
self.state_transition_manager_address,
self.chain_admin_address,
]
.into_iter()
.flatten()
Expand All @@ -110,6 +126,29 @@ impl EthClient for EthHttpQueryClient {
.await
}

async fn diamond_cut_by_version(
&self,
packed_version: H256,
) -> EnrichedClientResult<Option<Vec<u8>>> {
let Some(state_transition_manager_address) = self.state_transition_manager_address else {
return Ok(None);
};

let filter = FilterBuilder::default()
.address(vec![state_transition_manager_address])
.from_block(BlockNumber::Earliest)
.to_block(BlockNumber::Latest)
.topics(
Some(vec![self.new_upgrade_cut_data_signature]),
Some(vec![packed_version]),
None,
None,
)
.build();
let logs = self.client.logs(&filter).await?;
Ok(logs.into_iter().next().map(|log| log.data.0))
}

async fn get_events(
&self,
from: BlockNumber,
Expand Down
Loading
Loading