Skip to content

Commit 1de5f63

Browse files
authored
fix(upgrades): Read all skipped events (#4504)
## What ❔ Read upgrades even if we skipped some of them. ## Why ❔ If we do an upgrade, we have to listen for all events since the latest upgrade and process them. E.g. we made an upgrade from v27 to v28.1. And we have to not only check the upgrade for v28.1 itself, but include v28.0 as well,. ## Is this a breaking change? - [ ] Yes - [ ] No ## Operational changes <!-- Any config changes? Any new flags? Any changes to any scripts? --> <!-- Please add anything that non-Matter Labs entities running their own ZK Chain may need to know --> ## 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 `zkstack dev fmt` and `zkstack dev lint`. --------- Signed-off-by: Danil <[email protected]>
1 parent 1a6ad60 commit 1de5f63

File tree

5 files changed

+177
-80
lines changed

5 files changed

+177
-80
lines changed

core/node/eth_watch/src/client.rs

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ use zksync_types::{
1515
abi::ZkChainSpecificUpgradeData,
1616
api::{ChainAggProof, Log},
1717
ethabi::{decode, Contract, ParamType},
18+
protocol_version::ProtocolSemanticVersion,
19+
u256_to_h256,
1820
utils::encode_ntv_asset_id,
1921
web3::{BlockId, BlockNumber, Filter, FilterBuilder},
2022
Address, L1BatchNumber, L2BlockNumber, L2ChainId, SLChainId, H256,
@@ -54,11 +56,13 @@ pub trait EthClient: 'static + fmt::Debug + Send + Sync {
5456
&self,
5557
verifier_address: Address,
5658
) -> Result<Option<H256>, ContractCallError>;
57-
/// Returns upgrade diamond cut by packed protocol version.
58-
async fn diamond_cut_by_version(
59+
60+
/// Returns upgrade diamond cuts since the protocol version.
61+
/// It will include all potentially skipped versions
62+
async fn diamond_cuts_since_version(
5963
&self,
60-
packed_version: H256,
61-
) -> EnrichedClientResult<Option<Vec<u8>>>;
64+
version: ProtocolSemanticVersion,
65+
) -> EnrichedClientResult<Vec<Vec<u8>>>;
6266

6367
async fn get_published_preimages(
6468
&self,
@@ -288,6 +292,31 @@ where
288292

289293
result
290294
}
295+
296+
async fn block_for_diamond_cut_for_version(
297+
&self,
298+
packed_version: H256,
299+
) -> EnrichedClientResult<Option<U64>> {
300+
let Some(state_transition_manager_address) = self.state_transition_manager_address else {
301+
return Ok(None);
302+
};
303+
304+
let to_block = self.client.block_number().await?;
305+
let from_block = to_block.saturating_sub((LOOK_BACK_BLOCK_RANGE - 1).into());
306+
307+
let logs = self
308+
.get_events_inner(
309+
from_block.into(),
310+
to_block.into(),
311+
Some(vec![self.new_upgrade_cut_data_signature]),
312+
Some(vec![packed_version]),
313+
Some(vec![state_transition_manager_address]),
314+
RETRY_LIMIT,
315+
)
316+
.await?;
317+
318+
Ok(logs.into_iter().next().and_then(|log| log.block_number))
319+
}
291320
}
292321

293322
#[async_trait::async_trait]
@@ -421,31 +450,43 @@ where
421450
}
422451
}
423452

424-
async fn diamond_cut_by_version(
453+
async fn diamond_cuts_since_version(
425454
&self,
426-
packed_version: H256,
427-
) -> EnrichedClientResult<Option<Vec<u8>>> {
455+
from_version: ProtocolSemanticVersion,
456+
) -> EnrichedClientResult<Vec<Vec<u8>>> {
428457
let Some(state_transition_manager_address) = self.state_transition_manager_address else {
429-
return Ok(None);
458+
return Ok(vec![]);
430459
};
431460

432461
let to_block = self.client.block_number().await?;
433-
let from_block = to_block.saturating_sub((LOOK_BACK_BLOCK_RANGE - 1).into());
462+
463+
let from_block = self
464+
.block_for_diamond_cut_for_version(u256_to_h256(from_version.pack()))
465+
.await?
466+
.ok_or(EnrichedClientError::custom(
467+
format!("No diamond cut found for version {from_version}"),
468+
"diamond_cuts_since_version",
469+
))
470+
.map_err(|e| {
471+
tracing::error!("{e}");
472+
e
473+
})
474+
.unwrap_or(to_block.saturating_sub((LOOK_BACK_BLOCK_RANGE - 1).into()))
475+
+ 1;
434476

435477
let logs = self
436478
.get_events_inner(
437479
from_block.into(),
438480
to_block.into(),
439481
Some(vec![self.new_upgrade_cut_data_signature]),
440-
Some(vec![packed_version]),
482+
None,
441483
Some(vec![state_transition_manager_address]),
442484
RETRY_LIMIT,
443485
)
444486
.await?;
445487

446-
Ok(logs.into_iter().next().map(|log| log.data.0))
488+
Ok(logs.into_iter().map(|log| log.data.0).collect())
447489
}
448-
449490
async fn chain_id(&self) -> EnrichedClientResult<SLChainId> {
450491
self.client.fetch_chain_id().await
451492
}

core/node/eth_watch/src/event_processors/decentralized_upgrades.rs

Lines changed: 61 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
use std::sync::Arc;
1+
use std::{collections::HashMap, sync::Arc};
22

33
use anyhow::Context as _;
4+
use itertools::Itertools;
45
use zksync_contracts::chain_admin_contract;
56
use zksync_dal::{eth_watcher_dal::EventType, Connection, Core, CoreDal, DalError};
67
use zksync_types::{
7-
api::Log, protocol_upgrade::ProtocolUpgradePreimageOracle,
8+
api::Log, h256_to_u256, protocol_upgrade::ProtocolUpgradePreimageOracle,
89
protocol_version::ProtocolSemanticVersion, ProtocolUpgrade, H256, U256,
910
};
1011

@@ -73,7 +74,7 @@ impl EventProcessor for DecentralizedUpgradesEventProcessor {
7374
storage: &mut Connection<'_, Core>,
7475
events: Vec<Log>,
7576
) -> Result<usize, EventProcessorError> {
76-
let mut upgrades = Vec::new();
77+
let mut upgrades = HashMap::new();
7778
for event in &events {
7879
let version = event
7980
.topics
@@ -87,56 +88,73 @@ impl EventProcessor for DecentralizedUpgradesEventProcessor {
8788
.context("upgrade timestamp is too big")
8889
.map_err(EventProcessorError::internal)?;
8990

90-
let diamond_cut = self
91+
let diamond_cuts = self
9192
.sl_client
92-
.diamond_cut_by_version(version)
93+
.diamond_cuts_since_version(self.last_seen_protocol_version)
9394
.await
94-
.map_err(EventProcessorError::client)?
95-
.context("missing upgrade data on STM")
96-
.map_err(EventProcessorError::internal)?;
95+
.map_err(EventProcessorError::client)?;
96+
97+
let latest_protocol_version =
98+
ProtocolSemanticVersion::try_from_packed(h256_to_u256(version))
99+
.map_err(|err| EventProcessorError::internal(anyhow::anyhow!(err)))?;
100+
if diamond_cuts.is_empty() {
101+
return Err(EventProcessorError::internal(anyhow::anyhow!(
102+
"No diamond cuts found for protocol version {latest_protocol_version}"
103+
)));
104+
}
97105

98-
let upgrade = ProtocolUpgrade {
99-
timestamp,
100-
..ProtocolUpgrade::try_from_diamond_cut(
101-
&diamond_cut,
102-
self.l1_client.as_ref(),
103-
self.l1_client
104-
.get_chain_gateway_upgrade_info()
105-
.await
106-
.map_err(EventProcessorError::contract_call)?,
107-
)
108-
.await
109-
.map_err(EventProcessorError::internal)?
110-
};
106+
for diamond_cut in diamond_cuts {
107+
let upgrade = ProtocolUpgrade {
108+
timestamp,
109+
..ProtocolUpgrade::try_from_diamond_cut(
110+
&diamond_cut,
111+
self.l1_client.as_ref(),
112+
self.l1_client
113+
.get_chain_gateway_upgrade_info()
114+
.await
115+
.map_err(EventProcessorError::contract_call)?,
116+
)
117+
.await
118+
.map_err(EventProcessorError::internal)?
119+
};
111120

112-
// Scheduler VK is not present in proposal event. It is hard coded in verifier contract.
113-
let scheduler_vk_hash = if let Some(address) = upgrade.verifier_address {
114-
Some(
121+
if upgrade.version > latest_protocol_version {
122+
continue;
123+
}
124+
125+
// Scheduler VK is not present in proposal event. It is hard coded in verifier contract.
126+
let scheduler_vk_hash = if let Some(address) = upgrade.verifier_address {
127+
Some(
128+
self.sl_client
129+
.scheduler_vk_hash(address)
130+
.await
131+
.map_err(EventProcessorError::contract_call)?,
132+
)
133+
} else {
134+
None
135+
};
136+
137+
// Scheduler VK is not present in proposal event. It is hard coded in verifier contract.
138+
let fflonk_scheduler_vk_hash = if let Some(address) = upgrade.verifier_address {
115139
self.sl_client
116-
.scheduler_vk_hash(address)
140+
.fflonk_scheduler_vk_hash(address)
117141
.await
118-
.map_err(EventProcessorError::contract_call)?,
119-
)
120-
} else {
121-
None
122-
};
123-
124-
// Scheduler VK is not present in proposal event. It is hard coded in verifier contract.
125-
let fflonk_scheduler_vk_hash = if let Some(address) = upgrade.verifier_address {
126-
self.sl_client
127-
.fflonk_scheduler_vk_hash(address)
128-
.await
129-
.map_err(EventProcessorError::contract_call)?
130-
} else {
131-
None
132-
};
133-
134-
upgrades.push((upgrade, scheduler_vk_hash, fflonk_scheduler_vk_hash));
142+
.map_err(EventProcessorError::contract_call)?
143+
} else {
144+
None
145+
};
146+
upgrades.insert(
147+
upgrade.version,
148+
(upgrade, scheduler_vk_hash, fflonk_scheduler_vk_hash),
149+
);
150+
}
135151
}
136152

137153
let new_upgrades: Vec<_> = upgrades
138-
.into_iter()
154+
.values()
155+
.cloned()
139156
.skip_while(|(v, _, _)| v.version <= self.last_seen_protocol_version)
157+
.sorted_by(|(a, _, _), (b, _, _)| a.version.cmp(&b.version))
140158
.collect();
141159

142160
let Some((last_upgrade, _, _)) = new_upgrades.last() else {

core/node/eth_watch/src/tests/client.rs

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ use zksync_types::{
1010
api::{ChainAggProof, Log},
1111
bytecode::BytecodeHash,
1212
ethabi::{self, Token},
13+
h256_to_u256,
1314
l1::L1Tx,
1415
protocol_upgrade::ProtocolUpgradeTx,
16+
protocol_version::ProtocolSemanticVersion,
1517
u256_to_h256,
1618
utils::encode_ntv_asset_id,
1719
web3::{contract::Tokenizable, BlockNumber},
@@ -69,7 +71,10 @@ impl FakeEthClientData {
6971
self.upgrade_timestamp
7072
.entry(*eth_block)
7173
.or_default()
72-
.push(upgrade_timestamp_log(*eth_block));
74+
.push(upgrade_timestamp_log(
75+
u256_to_h256(upgrade.version.pack()),
76+
*eth_block,
77+
));
7378
self.diamond_upgrades
7479
.entry(*eth_block)
7580
.or_default()
@@ -258,18 +263,27 @@ impl EthClient for MockEthClient {
258263
Ok(self.inner.read().await.last_finalized_block_number)
259264
}
260265

261-
async fn diamond_cut_by_version(
266+
async fn diamond_cuts_since_version(
262267
&self,
263-
packed_version: H256,
264-
) -> EnrichedClientResult<Option<Vec<u8>>> {
265-
let from_block = *self
268+
since_version: ProtocolSemanticVersion,
269+
) -> EnrichedClientResult<Vec<Vec<u8>>> {
270+
let from_block = self
266271
.inner
267272
.read()
268273
.await
269274
.diamond_upgrades
270-
.keys()
275+
.values()
276+
.map(|logs| {
277+
logs.iter().find_map(|log| {
278+
let version = log.topics.get(1)?;
279+
let version =
280+
ProtocolSemanticVersion::try_from_packed(h256_to_u256(*version)).ok()?;
281+
(version > since_version).then_some(log.block_number?.as_u64())
282+
})
283+
})
271284
.min()
272-
.unwrap_or(&0);
285+
.flatten()
286+
.unwrap_or(0);
273287
let to_block = *self
274288
.inner
275289
.read()
@@ -289,12 +303,12 @@ impl EthClient for MockEthClient {
289303
.unwrap()
290304
.signature(),
291305
),
292-
Some(packed_version),
306+
None,
293307
RETRY_LIMIT,
294308
)
295309
.await?;
296310

297-
Ok(logs.into_iter().next().map(|log| log.data.0))
311+
Ok(logs.into_iter().map(|log| log.data.0).collect())
298312
}
299313

300314
async fn get_total_priority_txs(&self) -> Result<u64, ContractCallError> {
@@ -470,12 +484,12 @@ fn diamond_upgrade_log(upgrade: ProtocolUpgrade, eth_block: u64) -> Log {
470484
// address initAddress;
471485
// bytes initCalldata;
472486
// }
487+
let version = u256_to_h256(upgrade.version.pack());
473488
let final_data = ethabi::encode(&[Token::Tuple(vec![
474489
Token::Array(vec![]),
475490
Token::Address(Address::zero()),
476491
Token::Bytes(init_calldata(upgrade.clone())),
477492
])]);
478-
tracing::info!("{:?}", Token::Bytes(init_calldata(upgrade)));
479493

480494
Log {
481495
address: Address::repeat_byte(0x1),
@@ -484,7 +498,7 @@ fn diamond_upgrade_log(upgrade: ProtocolUpgrade, eth_block: u64) -> Log {
484498
.event("NewUpgradeCutData")
485499
.unwrap()
486500
.signature(),
487-
H256::from_low_u64_be(eth_block),
501+
version,
488502
],
489503
data: final_data.into(),
490504
block_hash: Some(H256::repeat_byte(0x11)),
@@ -499,7 +513,7 @@ fn diamond_upgrade_log(upgrade: ProtocolUpgrade, eth_block: u64) -> Log {
499513
block_timestamp: None,
500514
}
501515
}
502-
fn upgrade_timestamp_log(eth_block: u64) -> Log {
516+
fn upgrade_timestamp_log(packed_version: H256, eth_block: u64) -> Log {
503517
let final_data = ethabi::encode(&[U256::from(12345).into_token()]);
504518

505519
Log {
@@ -509,7 +523,7 @@ fn upgrade_timestamp_log(eth_block: u64) -> Log {
509523
.event("UpdateUpgradeTimestamp")
510524
.expect("UpdateUpgradeTimestamp event is missing in ABI")
511525
.signature(),
512-
H256::from_low_u64_be(eth_block),
526+
packed_version,
513527
],
514528
data: final_data.into(),
515529
block_hash: Some(H256::repeat_byte(0x11)),

0 commit comments

Comments
 (0)