Skip to content

Commit

Permalink
feat(eth-watch): Integrate decentralized upgrades (#2401)
Browse files Browse the repository at this point in the history
## What ❔

Integrates new type of upgrade proposals into eth_watch

## Why ❔

Support new type of upgrade proposals

## Checklist

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

- [ ] PR title corresponds to the body of PR (we generate changelog
entries from PRs).
- [ ] Tests for the changes have been added / updated.
- [ ] Documentation comments have been added / updated.
- [ ] Code has been formatted via `zk fmt` and `zk lint`.
  • Loading branch information
perekopskiy authored Jul 11, 2024
1 parent ce43c42 commit 5a48e10
Show file tree
Hide file tree
Showing 23 changed files with 331 additions and 44 deletions.
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

0 comments on commit 5a48e10

Please sign in to comment.