zksync_node_sync/
client.rs1use std::fmt;
4
5use async_trait::async_trait;
6use zksync_config::GenesisConfig;
7use zksync_health_check::{CheckHealth, Health, HealthStatus};
8use zksync_system_constants::ACCOUNT_CODE_STORAGE_ADDRESS;
9use zksync_types::{
10 api::{self, en},
11 bytecode::BytecodeHash,
12 get_code_key, h256_to_u256, Address, L2BlockNumber, ProtocolVersionId, H256, U64,
13};
14use zksync_web3_decl::{
15 client::{DynClient, L2},
16 error::{ClientRpcContext, EnrichedClientError, EnrichedClientResult},
17 namespaces::{EnNamespaceClient, EthNamespaceClient, ZksNamespaceClient},
18};
19
20#[async_trait]
22pub trait MainNodeClient: 'static + Send + Sync + fmt::Debug {
23 async fn fetch_system_contract_by_hash(
24 &self,
25 hash: H256,
26 ) -> EnrichedClientResult<Option<Vec<u8>>>;
27
28 async fn fetch_genesis_contract_bytecode(
29 &self,
30 address: Address,
31 ) -> EnrichedClientResult<Option<Vec<u8>>>;
32
33 async fn fetch_protocol_version(
34 &self,
35 protocol_version: ProtocolVersionId,
36 ) -> EnrichedClientResult<Option<api::ProtocolVersion>>;
37
38 async fn fetch_l2_block_number(&self) -> EnrichedClientResult<L2BlockNumber>;
39
40 async fn fetch_l2_block(
41 &self,
42 number: L2BlockNumber,
43 with_transactions: bool,
44 ) -> EnrichedClientResult<Option<en::SyncBlock>>;
45
46 async fn fetch_genesis_config(&self) -> EnrichedClientResult<GenesisConfig>;
47}
48
49#[async_trait]
50impl MainNodeClient for Box<DynClient<L2>> {
51 async fn fetch_system_contract_by_hash(
52 &self,
53 hash: H256,
54 ) -> EnrichedClientResult<Option<Vec<u8>>> {
55 let bytecode = self
56 .get_bytecode_by_hash(hash)
57 .rpc_context("get_bytecode_by_hash")
58 .with_arg("hash", &hash)
59 .await?;
60 if let Some(bytecode) = &bytecode {
61 let actual_bytecode_hash = BytecodeHash::for_bytecode(bytecode).value();
62 if actual_bytecode_hash != hash {
63 return Err(EnrichedClientError::custom(
64 "Got invalid base system contract bytecode from main node",
65 "get_bytecode_by_hash",
66 )
67 .with_arg("hash", &hash)
68 .with_arg("actual_bytecode_hash", &actual_bytecode_hash));
69 }
70 }
71 Ok(bytecode)
72 }
73
74 async fn fetch_genesis_contract_bytecode(
75 &self,
76 address: Address,
77 ) -> EnrichedClientResult<Option<Vec<u8>>> {
78 const GENESIS_BLOCK: api::BlockIdVariant =
79 api::BlockIdVariant::BlockNumber(api::BlockNumber::Number(U64([0])));
80
81 let code_key = get_code_key(&address);
82 let code_hash = self
83 .get_storage_at(
84 ACCOUNT_CODE_STORAGE_ADDRESS,
85 h256_to_u256(*code_key.key()),
86 Some(GENESIS_BLOCK),
87 )
88 .rpc_context("get_storage_at")
89 .with_arg("address", &address)
90 .await?;
91 self.get_bytecode_by_hash(code_hash)
92 .rpc_context("get_bytecode_by_hash")
93 .with_arg("code_hash", &code_hash)
94 .await
95 }
96
97 async fn fetch_protocol_version(
98 &self,
99 protocol_version: ProtocolVersionId,
100 ) -> EnrichedClientResult<Option<api::ProtocolVersion>> {
101 self.get_protocol_version(Some(protocol_version as u16))
102 .rpc_context("fetch_protocol_version")
103 .with_arg("protocol_version", &protocol_version)
104 .await
105 }
106
107 async fn fetch_genesis_config(&self) -> EnrichedClientResult<GenesisConfig> {
108 let dto = self.genesis_config().rpc_context("genesis_config").await?;
109 Ok(GenesisConfig {
110 protocol_version: Some(dto.protocol_version),
111 genesis_root_hash: Some(dto.genesis_root_hash),
112 rollup_last_leaf_index: Some(dto.rollup_last_leaf_index),
113 genesis_commitment: Some(dto.genesis_commitment),
114 bootloader_hash: Some(dto.bootloader_hash),
115 default_aa_hash: Some(dto.default_aa_hash),
116 evm_emulator_hash: dto.evm_emulator_hash,
117 l1_chain_id: dto.l1_chain_id,
118 l2_chain_id: dto.l2_chain_id,
119 snark_wrapper_vk_hash: dto.snark_wrapper_vk_hash,
120 fflonk_snark_wrapper_vk_hash: dto.fflonk_snark_wrapper_vk_hash,
121 fee_account: dto.fee_account,
122 dummy_verifier: dto.dummy_verifier,
123 l1_batch_commit_data_generator_mode: dto.l1_batch_commit_data_generator_mode,
124 custom_genesis_state_path: None,
126 })
127 }
128
129 async fn fetch_l2_block_number(&self) -> EnrichedClientResult<L2BlockNumber> {
130 let number = self
131 .get_block_number()
132 .rpc_context("get_block_number")
133 .await?;
134 let number = u32::try_from(number)
135 .map_err(|err| EnrichedClientError::custom(err, "u32::try_from"))?;
136 Ok(L2BlockNumber(number))
137 }
138
139 async fn fetch_l2_block(
140 &self,
141 number: L2BlockNumber,
142 with_transactions: bool,
143 ) -> EnrichedClientResult<Option<en::SyncBlock>> {
144 self.sync_l2_block(number, with_transactions)
145 .rpc_context("fetch_l2_block")
146 .with_arg("number", &number)
147 .with_arg("with_transactions", &with_transactions)
148 .await
149 }
150}
151
152#[derive(Debug)]
154pub struct MainNodeHealthCheck(Box<DynClient<L2>>);
155
156impl From<Box<DynClient<L2>>> for MainNodeHealthCheck {
157 fn from(client: Box<DynClient<L2>>) -> Self {
158 Self(client.for_component("main_node_health_check"))
159 }
160}
161
162#[async_trait]
163impl CheckHealth for MainNodeHealthCheck {
164 fn name(&self) -> &'static str {
165 "main_node_http_rpc"
166 }
167
168 async fn check_health(&self) -> Health {
169 if let Err(err) = self.0.get_block_number().await {
170 tracing::warn!("Health-check call to main node HTTP RPC failed: {err}");
171 let details = serde_json::json!({
172 "error": err.to_string(),
173 });
174 return Health::from(HealthStatus::NotReady).with_details(details);
175 }
176 HealthStatus::Ready.into()
177 }
178}