1use crate::filters::LogFilter;
2use crate::node::inner::fork::ForkDetails;
3use crate::node::time::{ReadTime, Time};
4use crate::node::{create_genesis, create_genesis_from_json, TransactionResult};
5use crate::utils::utc_datetime_from_epoch_ms;
6use anvil_zksync_config::types::Genesis;
7use anvil_zksync_types::api::DetailedTransaction;
8use anyhow::Context;
9use async_trait::async_trait;
10use itertools::Itertools;
11use std::collections::HashMap;
12use std::fmt::Debug;
13use std::sync::Arc;
14use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
15use zksync_contracts::BaseSystemContractsHashes;
16use zksync_multivm::interface::storage::{ReadStorage, StoragePtr};
17use zksync_multivm::interface::{FinishedL1Batch, L2Block, VmEvent};
18use zksync_multivm::vm_latest::utils::l2_blocks::load_last_l2_block;
19use zksync_types::block::{unpack_block_info, L1BatchHeader, L2BlockHasher};
20use zksync_types::l2::L2Tx;
21use zksync_types::writes::StateDiffRecord;
22use zksync_types::{
23 api, api::BlockId, h256_to_u256, web3::Bytes, AccountTreeId, Address, ExecuteTransactionCommon,
24 L1BatchNumber, L2BlockNumber, ProtocolVersionId, StorageKey, H256, SYSTEM_CONTEXT_ADDRESS,
25 SYSTEM_CONTEXT_BLOCK_INFO_POSITION, U256, U64,
26};
27
28#[async_trait]
30pub trait ReadBlockchain: Send + Sync + Debug {
31 fn dyn_cloned(&self) -> Box<dyn ReadBlockchain>;
33
34 fn protocol_version(&self) -> ProtocolVersionId;
36
37 async fn current_batch(&self) -> L1BatchNumber;
40
41 async fn current_block_number(&self) -> L2BlockNumber;
44
45 async fn current_block_hash(&self) -> H256;
48
49 async fn get_block_by_hash(&self, hash: &H256) -> Option<api::Block<api::TransactionVariant>>;
52
53 async fn get_block_by_number(
56 &self,
57 number: L2BlockNumber,
58 ) -> Option<api::Block<api::TransactionVariant>>;
59
60 async fn get_block_by_id(
63 &self,
64 block_id: api::BlockId,
65 ) -> Option<api::Block<api::TransactionVariant>>;
66
67 async fn get_block_hash_by_number(&self, number: L2BlockNumber) -> Option<H256>;
70
71 async fn get_block_hash_by_id(&self, block_id: api::BlockId) -> Option<H256>;
74
75 async fn get_block_number_by_hash(&self, hash: &H256) -> Option<L2BlockNumber>;
78
79 async fn get_block_number_by_id(&self, block_id: api::BlockId) -> Option<L2BlockNumber>;
82
83 async fn get_block_tx_hashes_by_number(&self, number: L2BlockNumber) -> Option<Vec<H256>>;
87
88 async fn get_block_tx_hashes_by_id(&self, block_id: api::BlockId) -> Option<Vec<H256>>;
92
93 async fn get_block_tx_by_id(
98 &self,
99 block_id: api::BlockId,
100 index: usize,
101 ) -> Option<api::Transaction>;
102
103 async fn get_block_tx_count_by_id(&self, block_id: api::BlockId) -> Option<usize>;
107
108 async fn get_block_details_by_number(
112 &self,
113 number: L2BlockNumber,
114 l2_fair_gas_price: u64,
116 fair_pubdata_price: Option<u64>,
117 base_system_contracts_hashes: BaseSystemContractsHashes,
118 ) -> Option<api::BlockDetails>;
119
120 async fn get_tx_receipt(&self, tx_hash: &H256) -> Option<api::TransactionReceipt>;
124
125 async fn get_tx_debug_info(&self, tx_hash: &H256, only_top: bool) -> Option<api::DebugCall>;
129
130 async fn get_tx_api(&self, tx_hash: &H256) -> anyhow::Result<Option<api::Transaction>>;
134
135 async fn get_detailed_tx(&self, tx: api::Transaction) -> Option<DetailedTransaction>;
139
140 async fn get_tx_details(&self, tx_hash: &H256) -> Option<api::TransactionDetails>;
144
145 async fn get_zksync_tx(&self, tx_hash: &H256) -> Option<zksync_types::Transaction>;
149
150 async fn get_filter_logs(&self, log_filter: &LogFilter) -> Vec<api::Log>;
152
153 async fn get_batch_header(&self, batch_number: L1BatchNumber) -> Option<L1BatchHeader>;
155
156 async fn get_batch_state_diffs(
158 &self,
159 batch_number: L1BatchNumber,
160 ) -> Option<Vec<StateDiffRecord>>;
161
162 async fn get_batch_aggregation_root(&self, batch_number: L1BatchNumber) -> Option<H256>;
164
165 async fn get_raw_transaction(&self, tx_hash: H256) -> Option<Bytes>;
167
168 async fn get_raw_transactions(&self, block_number: BlockId) -> Vec<Bytes>;
170}
171
172impl Clone for Box<dyn ReadBlockchain> {
173 fn clone(&self) -> Self {
174 self.dyn_cloned()
175 }
176}
177
178#[derive(Debug, Clone)]
179pub(super) struct Blockchain {
180 inner: Arc<RwLock<BlockchainState>>,
181 pub(super) protocol_version: ProtocolVersionId,
182}
183
184impl Blockchain {
185 async fn inspect_block_by_hash<T>(
186 &self,
187 hash: &H256,
188 f: impl FnOnce(&api::Block<api::TransactionVariant>) -> T,
189 ) -> Option<T> {
190 Some(f(self.inner.read().await.blocks.get(hash)?))
191 }
192
193 async fn inspect_block_by_number<T>(
194 &self,
195 number: L2BlockNumber,
196 f: impl FnOnce(&api::Block<api::TransactionVariant>) -> T,
197 ) -> Option<T> {
198 let storage = self.inner.read().await;
199 let hash = storage.get_block_hash_by_number(number)?;
200 Some(f(storage.blocks.get(&hash)?))
201 }
202
203 async fn inspect_block_by_id<T>(
204 &self,
205 block_id: api::BlockId,
206 f: impl FnOnce(&api::Block<api::TransactionVariant>) -> T,
207 ) -> Option<T> {
208 let storage = self.inner.read().await;
209 let hash = storage.get_block_hash_by_id(block_id)?;
210 Some(f(storage.blocks.get(&hash)?))
211 }
212
213 async fn inspect_tx<T>(
214 &self,
215 tx_hash: &H256,
216 f: impl FnOnce(&TransactionResult) -> T,
217 ) -> Option<T> {
218 Some(f(self.inner.read().await.tx_results.get(tx_hash)?))
219 }
220
221 async fn inspect_batch<T>(
222 &self,
223 batch_number: &L1BatchNumber,
224 f: impl FnOnce(&StoredL1BatchInfo) -> T,
225 ) -> Option<T> {
226 Some(f(self.inner.read().await.batches.get(batch_number)?))
227 }
228
229 async fn inspect_all_txs<T>(
231 &self,
232 f: impl FnOnce(&HashMap<H256, TransactionResult>) -> T,
233 ) -> T {
234 f(&self.inner.read().await.tx_results)
235 }
236}
237
238#[async_trait]
239impl ReadBlockchain for Blockchain {
240 fn dyn_cloned(&self) -> Box<dyn ReadBlockchain> {
241 Box::new(self.clone())
242 }
243
244 fn protocol_version(&self) -> ProtocolVersionId {
245 self.protocol_version
246 }
247
248 async fn current_batch(&self) -> L1BatchNumber {
249 self.inner.read().await.current_batch
250 }
251
252 async fn current_block_number(&self) -> L2BlockNumber {
253 self.inner.read().await.current_block
254 }
255
256 async fn current_block_hash(&self) -> H256 {
257 self.inner.read().await.current_block_hash
258 }
259
260 async fn get_block_by_hash(&self, hash: &H256) -> Option<api::Block<api::TransactionVariant>> {
261 self.inspect_block_by_hash(hash, |block| block.clone())
262 .await
263 }
264
265 async fn get_block_by_number(
266 &self,
267 number: L2BlockNumber,
268 ) -> Option<api::Block<api::TransactionVariant>> {
269 self.inspect_block_by_number(number, |block| block.clone())
270 .await
271 }
272
273 async fn get_block_by_id(
274 &self,
275 block_id: api::BlockId,
276 ) -> Option<api::Block<api::TransactionVariant>> {
277 self.inspect_block_by_id(block_id, |block| block.clone())
278 .await
279 }
280
281 async fn get_block_hash_by_number(&self, number: L2BlockNumber) -> Option<H256> {
282 self.inspect_block_by_number(number, |block| block.hash)
283 .await
284 }
285
286 async fn get_block_hash_by_id(&self, block_id: api::BlockId) -> Option<H256> {
287 self.inspect_block_by_id(block_id, |block| block.hash).await
288 }
289
290 async fn get_block_number_by_hash(&self, hash: &H256) -> Option<L2BlockNumber> {
291 self.inspect_block_by_hash(hash, |block| L2BlockNumber(block.number.as_u32()))
292 .await
293 }
294
295 async fn get_block_number_by_id(&self, block_id: api::BlockId) -> Option<L2BlockNumber> {
296 self.inspect_block_by_id(block_id, |block| L2BlockNumber(block.number.as_u32()))
297 .await
298 }
299
300 async fn get_block_tx_hashes_by_number(&self, number: L2BlockNumber) -> Option<Vec<H256>> {
301 self.get_block_tx_hashes_by_id(api::BlockId::Number(api::BlockNumber::Number(
302 number.0.into(),
303 )))
304 .await
305 }
306
307 async fn get_block_tx_hashes_by_id(&self, block_id: api::BlockId) -> Option<Vec<H256>> {
308 self.inspect_block_by_id(block_id, |block| {
309 block
310 .transactions
311 .iter()
312 .map(|tx| match tx {
313 api::TransactionVariant::Full(tx) => tx.hash,
314 api::TransactionVariant::Hash(hash) => *hash,
315 })
316 .collect_vec()
317 })
318 .await
319 }
320
321 async fn get_block_tx_by_id(
322 &self,
323 block_id: api::BlockId,
324 index: usize,
325 ) -> Option<api::Transaction> {
326 self.inspect_block_by_id(block_id, |block| {
327 block.transactions.get(index).map(|tv| match tv {
328 api::TransactionVariant::Full(tx) => tx.clone(),
329 api::TransactionVariant::Hash(_) => {
330 unreachable!("we only store full txs in blocks")
331 }
332 })
333 })
334 .await
335 .flatten()
336 }
337
338 async fn get_block_tx_count_by_id(&self, block_id: api::BlockId) -> Option<usize> {
339 self.inspect_block_by_id(block_id, |block| block.transactions.len())
340 .await
341 }
342
343 async fn get_block_details_by_number(
344 &self,
345 number: L2BlockNumber,
346 l2_fair_gas_price: u64,
347 fair_pubdata_price: Option<u64>,
348 base_system_contracts_hashes: BaseSystemContractsHashes,
349 ) -> Option<api::BlockDetails> {
350 self.inspect_block_by_number(number, |block| api::BlockDetails {
351 number: L2BlockNumber(block.number.as_u32()),
352 l1_batch_number: L1BatchNumber(block.l1_batch_number.unwrap_or_default().as_u32()),
353 base: api::BlockDetailsBase {
354 timestamp: block.timestamp.as_u64(),
355 l1_tx_count: 1,
356 l2_tx_count: block.transactions.len(),
357 root_hash: Some(block.hash),
358 status: api::BlockStatus::Verified,
359 commit_tx_hash: None,
360 commit_chain_id: None,
361 committed_at: None,
362 prove_tx_hash: None,
363 prove_chain_id: None,
364 proven_at: None,
365 execute_tx_hash: None,
366 execute_chain_id: None,
367 executed_at: None,
368 l1_gas_price: 0,
369 l2_fair_gas_price,
370 fair_pubdata_price,
371 base_system_contracts_hashes,
372 commit_tx_finality: None,
373 prove_tx_finality: None,
374 execute_tx_finality: None,
375 },
376 operator_address: Address::zero(),
377 protocol_version: Some(self.protocol_version),
378 })
379 .await
380 }
381
382 async fn get_tx_receipt(&self, tx_hash: &H256) -> Option<api::TransactionReceipt> {
383 self.inspect_tx(tx_hash, |tx| tx.receipt.clone()).await
384 }
385
386 async fn get_tx_debug_info(&self, tx_hash: &H256, only_top: bool) -> Option<api::DebugCall> {
387 self.inspect_tx(tx_hash, |tx| tx.debug_info(only_top)).await
388 }
389
390 async fn get_tx_api(&self, tx_hash: &H256) -> anyhow::Result<Option<api::Transaction>> {
391 self.inspect_tx(tx_hash, |TransactionResult { info, receipt, .. }| {
392 let l2_tx: L2Tx =
393 info.tx.clone().try_into().map_err(|_| {
394 anyhow::anyhow!("inspection of non-L2 transactions is unsupported")
395 })?;
396 let chain_id = l2_tx
397 .common_data
398 .extract_chain_id()
399 .context("tx has malformed chain id")?;
400 let input_data = l2_tx
401 .common_data
402 .input
403 .context("tx is missing input data")?;
404 anyhow::Ok(api::Transaction {
405 hash: *tx_hash,
406 nonce: U256::from(l2_tx.common_data.nonce.0),
407 block_hash: Some(*tx_hash),
409 block_number: Some(U64::from(info.miniblock_number)),
410 transaction_index: Some(receipt.transaction_index),
411 from: Some(info.tx.initiator_account()),
412 to: info.tx.recipient_account(),
413 value: info.tx.execute.value,
414 gas_price: Some(U256::from(0)),
415 gas: Default::default(),
416 input: input_data.data.into(),
417 v: Some(chain_id.into()),
418 r: Some(U256::zero()), s: Some(U256::zero()), y_parity: Some(U64::zero()), raw: None,
422 transaction_type: {
423 let tx_type = match l2_tx.common_data.transaction_type {
424 zksync_types::l2::TransactionType::LegacyTransaction => 0,
425 zksync_types::l2::TransactionType::EIP2930Transaction => 1,
426 zksync_types::l2::TransactionType::EIP1559Transaction => 2,
427 zksync_types::l2::TransactionType::EIP712Transaction => 113,
428 zksync_types::l2::TransactionType::PriorityOpTransaction => 255,
429 zksync_types::l2::TransactionType::ProtocolUpgradeTransaction => 254,
430 };
431 Some(tx_type.into())
432 },
433 access_list: None,
434 max_fee_per_gas: Some(l2_tx.common_data.fee.max_fee_per_gas),
435 max_priority_fee_per_gas: Some(l2_tx.common_data.fee.max_priority_fee_per_gas),
436 chain_id: U256::from(chain_id),
437 l1_batch_number: Some(U64::from(info.batch_number as u64)),
438 l1_batch_tx_index: None,
439 })
440 })
441 .await
442 .transpose()
443 }
444
445 async fn get_detailed_tx(&self, tx: api::Transaction) -> Option<DetailedTransaction> {
446 self.inspect_tx(
447 &tx.hash.clone(),
448 |TransactionResult { ref debug, .. }| {
449 let output = Some(debug.output.clone());
450 let revert_reason = debug.revert_reason.clone();
451 DetailedTransaction {
452 inner: tx,
453 output,
454 revert_reason,
455 }
456 },
457 )
458 .await
459 }
460
461 async fn get_tx_details(&self, tx_hash: &H256) -> Option<api::TransactionDetails> {
462 self.inspect_tx(tx_hash, |TransactionResult { info, receipt, .. }| {
463 api::TransactionDetails {
464 is_l1_originated: false,
465 status: api::TransactionStatus::Included,
466 fee: receipt.effective_gas_price.unwrap_or_default()
468 * receipt.gas_used.unwrap_or_default(),
469 gas_per_pubdata: info.tx.gas_per_pubdata_byte_limit(),
470 initiator_address: info.tx.initiator_account(),
471 received_at: utc_datetime_from_epoch_ms(info.tx.received_timestamp_ms),
472 eth_commit_tx_hash: None,
473 eth_prove_tx_hash: None,
474 eth_execute_tx_hash: None,
475 }
476 })
477 .await
478 }
479
480 async fn get_zksync_tx(&self, tx_hash: &H256) -> Option<zksync_types::Transaction> {
481 self.inspect_tx(tx_hash, |TransactionResult { info, .. }| info.tx.clone())
482 .await
483 }
484
485 async fn get_filter_logs(&self, log_filter: &LogFilter) -> Vec<api::Log> {
486 let latest_block_number = self.current_block_number().await;
487 self.inspect_all_txs(|tx_results| {
491 tx_results
492 .values()
493 .flat_map(|tx_result| {
494 tx_result
495 .receipt
496 .logs
497 .iter()
498 .filter(|log| log_filter.matches(log, U64::from(latest_block_number.0)))
499 .cloned()
500 })
501 .collect_vec()
502 })
503 .await
504 }
505
506 async fn get_batch_header(&self, batch_number: L1BatchNumber) -> Option<L1BatchHeader> {
507 self.inspect_batch(&batch_number, |StoredL1BatchInfo { header, .. }| {
508 header.clone()
509 })
510 .await
511 }
512
513 async fn get_batch_state_diffs(
514 &self,
515 batch_number: L1BatchNumber,
516 ) -> Option<Vec<StateDiffRecord>> {
517 self.inspect_batch(
518 &batch_number,
519 |StoredL1BatchInfo { state_diffs, .. }| state_diffs.clone(),
520 )
521 .await
522 }
523
524 async fn get_batch_aggregation_root(&self, batch_number: L1BatchNumber) -> Option<H256> {
525 self.inspect_batch(
526 &batch_number,
527 |StoredL1BatchInfo {
528 aggregation_root, ..
529 }| *aggregation_root,
530 )
531 .await
532 }
533
534 async fn get_raw_transaction(&self, tx_hash: H256) -> Option<Bytes> {
535 self.inspect_tx(&tx_hash, |TransactionResult { info, .. }| {
536 info.tx.raw_bytes.clone()
537 })
538 .await
539 .flatten()
540 }
541
542 async fn get_raw_transactions(&self, block_id: BlockId) -> Vec<Bytes> {
543 self.inspect_block_by_id(block_id, |block| {
544 block
545 .transactions
546 .iter()
547 .filter_map(|tv| match tv {
548 api::TransactionVariant::Full(tx) => tx.raw.clone(),
549 api::TransactionVariant::Hash(_) => None,
550 })
551 .collect::<Vec<Bytes>>()
552 })
553 .await
554 .unwrap_or_default()
555 }
556}
557
558impl Blockchain {
559 pub(super) fn new(
560 protocol_version: ProtocolVersionId,
561 fork_details: Option<&ForkDetails>,
562 genesis: Option<&Genesis>,
563 genesis_timestamp: Option<u64>,
564 ) -> Blockchain {
565 let state = if let Some(fork_details) = fork_details {
566 BlockchainState {
567 protocol_version: fork_details.protocol_version,
568 current_batch: fork_details.batch_number,
569 current_block: fork_details.block_number,
570 current_block_hash: fork_details.block_hash,
571 tx_results: Default::default(),
572 blocks: HashMap::from_iter([(
573 fork_details.block_hash,
574 fork_details.api_block.clone(),
575 )]),
576 hashes: HashMap::from_iter([(fork_details.block_number, fork_details.block_hash)]),
577 batches: HashMap::from_iter([]),
580 }
581 } else {
582 let (genesis_block, genesis_batch_header) = if let Some(genesis) = genesis {
583 create_genesis_from_json(protocol_version, genesis, genesis_timestamp)
584 } else {
585 create_genesis(protocol_version, genesis_timestamp)
586 };
587 let block_hash = genesis_block.hash;
588 let genesis_batch_info = StoredL1BatchInfo {
589 header: genesis_batch_header,
590 state_diffs: Vec::new(),
591 aggregation_root: H256::zero(),
592 };
593
594 BlockchainState {
595 protocol_version,
596 current_batch: L1BatchNumber(0),
597 current_block: L2BlockNumber(0),
598 current_block_hash: block_hash,
599 tx_results: Default::default(),
600 blocks: HashMap::from_iter([(block_hash, genesis_block)]),
601 hashes: HashMap::from_iter([(L2BlockNumber(0), block_hash)]),
602 batches: HashMap::from_iter([(L1BatchNumber(0), genesis_batch_info)]),
603 }
604 };
605 let protocol_version = state.protocol_version;
606 let inner = Arc::new(RwLock::new(state));
607 Self {
608 inner,
609 protocol_version,
610 }
611 }
612}
613
614impl Blockchain {
615 pub(super) async fn read(&self) -> RwLockReadGuard<BlockchainState> {
616 self.inner.read().await
617 }
618
619 pub(super) async fn write(&self) -> RwLockWriteGuard<BlockchainState> {
620 self.inner.write().await
621 }
622}
623
624#[derive(Debug, Clone)]
626pub(super) struct BlockchainState {
627 pub(super) protocol_version: ProtocolVersionId,
629 pub(super) current_batch: L1BatchNumber,
632 pub(super) current_block: L2BlockNumber,
635 pub(super) current_block_hash: H256,
637 pub(super) tx_results: HashMap<H256, TransactionResult>,
639 pub(super) blocks: HashMap<H256, api::Block<api::TransactionVariant>>,
641 pub(super) hashes: HashMap<L2BlockNumber, H256>,
643 batches: HashMap<L1BatchNumber, StoredL1BatchInfo>,
647}
648
649#[derive(Debug, Clone)]
651struct StoredL1BatchInfo {
652 header: L1BatchHeader,
653 state_diffs: Vec<StateDiffRecord>,
654 aggregation_root: H256,
655}
656
657impl BlockchainState {
658 pub(super) fn get_block_hash_by_number(&self, number: L2BlockNumber) -> Option<H256> {
659 self.hashes.get(&number).copied()
660 }
661
662 pub(super) fn get_block_hash_by_id(&self, block_id: api::BlockId) -> Option<H256> {
663 match block_id {
664 api::BlockId::Number(number) => {
665 let number = match number {
666 api::BlockNumber::FastFinalized
667 | api::BlockNumber::Finalized
668 | api::BlockNumber::Pending
669 | api::BlockNumber::Committed
670 | api::BlockNumber::L1Committed
671 | api::BlockNumber::Latest => self.current_block,
672 api::BlockNumber::Earliest => L2BlockNumber(0),
673 api::BlockNumber::Number(n) => L2BlockNumber(n.as_u32()),
674 };
675 self.hashes.get(&number).copied()
676 }
677 api::BlockId::Hash(hash) => Some(hash),
678 }
679 }
680
681 pub(super) fn last_env<S: ReadStorage>(
682 &self,
683 storage: &StoragePtr<S>,
684 time_writer: &Time,
685 ) -> (L1BatchNumber, L2Block) {
686 let last_l1_batch_number = load_last_l1_batch(storage)
690 .map(|(num, _)| L1BatchNumber(num as u32))
691 .unwrap_or(self.current_batch);
692 let last_l2_block = load_last_l2_block(storage).unwrap_or_else(|| L2Block {
693 number: self.current_block.0,
694 hash: L2BlockHasher::legacy_hash(self.current_block),
695 timestamp: time_writer.current_timestamp(),
696 });
697 (last_l1_batch_number, last_l2_block)
698 }
699
700 pub(super) fn apply_block(&mut self, block: api::Block<api::TransactionVariant>, index: u32) {
701 let latest_block = self.blocks.get(&self.current_block_hash).unwrap();
702 self.current_block += 1;
703
704 let actual_l1_batch_number = block
705 .l1_batch_number
706 .expect("block must have a l1_batch_number");
707 if L1BatchNumber(actual_l1_batch_number.as_u32()) != self.current_batch {
708 panic!(
709 "expected next block to have batch_number {}, got {}",
710 self.current_batch,
711 actual_l1_batch_number.as_u32()
712 );
713 }
714
715 if L2BlockNumber(block.number.as_u32()) != self.current_block {
716 panic!(
717 "expected next block to have miniblock {}, got {} | {index}",
718 self.current_block,
719 block.number.as_u64()
720 );
721 }
722
723 if block.timestamp.as_u64() <= latest_block.timestamp.as_u64() {
724 panic!(
725 "expected next block to have timestamp bigger than {}, got {} | {index}",
726 latest_block.timestamp.as_u64(),
727 block.timestamp.as_u64()
728 );
729 }
730
731 let block_hash = block.hash;
732 self.current_block_hash = block_hash;
733 self.hashes
734 .insert(L2BlockNumber(block.number.as_u32()), block.hash);
735 self.blocks.insert(block.hash, block);
736 }
737
738 pub(super) fn apply_batch(
739 &mut self,
740 batch_timestamp: u64,
741 base_system_contracts_hashes: BaseSystemContractsHashes,
742 tx_results: Vec<TransactionResult>,
743 finished_l1_batch: FinishedL1Batch,
744 aggregation_root: H256,
745 ) {
746 self.current_batch += 1;
747
748 let l2_to_l1_messages = VmEvent::extract_long_l2_to_l1_messages(
749 &finished_l1_batch.final_execution_state.events,
750 );
751 let l1_tx_count = tx_results
752 .iter()
753 .filter(|tx| matches!(tx.info.tx.common_data, ExecuteTransactionCommon::L1(_)))
754 .count() as u16;
755 let priority_ops_onchain_data = tx_results
756 .iter()
757 .filter_map(|tx| match &tx.info.tx.common_data {
758 ExecuteTransactionCommon::L1(l1_tx) => {
759 Some(l1_tx.onchain_metadata().onchain_data.clone())
760 }
761 ExecuteTransactionCommon::L2(_) => None,
762 ExecuteTransactionCommon::ProtocolUpgrade(_) => None,
763 })
764 .collect();
765 let header = L1BatchHeader {
766 number: self.current_batch,
767 timestamp: batch_timestamp,
768 l1_tx_count,
769 l2_tx_count: tx_results.len() as u16 - l1_tx_count,
770 priority_ops_onchain_data,
771 l2_to_l1_logs: finished_l1_batch.final_execution_state.user_l2_to_l1_logs,
772 l2_to_l1_messages,
773 bloom: Default::default(), used_contract_hashes: finished_l1_batch.final_execution_state.used_contract_hashes,
775 base_system_contracts_hashes,
776 system_logs: finished_l1_batch.final_execution_state.system_logs,
777 protocol_version: Some(self.protocol_version),
778 pubdata_input: finished_l1_batch.pubdata_input,
779 fee_address: Default::default(), batch_fee_input: Default::default(), };
782 let batch_info = StoredL1BatchInfo {
783 header,
784 state_diffs: finished_l1_batch.state_diffs.unwrap_or_default(),
785 aggregation_root,
786 };
787 self.batches.insert(self.current_batch, batch_info);
788 self.tx_results.extend(
789 tx_results
790 .into_iter()
791 .map(|r| (r.receipt.transaction_hash, r)),
792 );
793 }
794
795 pub(super) fn load_blocks(
796 &mut self,
797 time: &mut Time,
798 blocks: Vec<api::Block<api::TransactionVariant>>,
799 ) {
800 tracing::trace!(
801 blocks = blocks.len(),
802 "loading new blocks from supplied state"
803 );
804 for block in blocks {
805 let number = block.number.as_u64();
806 tracing::trace!(
807 number,
808 hash = %block.hash,
809 "loading new block from supplied state"
810 );
811
812 self.hashes.insert(L2BlockNumber(number as u32), block.hash);
813 self.blocks.insert(block.hash, block);
814 }
815
816 let latest_block = self.blocks.values().max_by_key(|b| b.number).unwrap();
818 let latest_number = latest_block.number.as_u64();
819 let latest_hash = latest_block.hash;
820 let Some(latest_batch_number) = latest_block.l1_batch_number.map(|n| n.as_u32()) else {
821 panic!("encountered a block with no batch; this is not supposed to happen")
822 };
823 let latest_timestamp = latest_block.timestamp.as_u64();
824 tracing::info!(
825 number = latest_number,
826 hash = %latest_hash,
827 batch_number = latest_batch_number,
828 timestamp = latest_timestamp,
829 "latest block after loading state"
830 );
831 self.current_block = L2BlockNumber(latest_number as u32);
832 self.current_block_hash = latest_hash;
833 self.current_batch = L1BatchNumber(latest_batch_number);
834 time.reset_to(latest_timestamp);
835 }
836
837 pub(super) fn load_transactions(&mut self, transactions: Vec<TransactionResult>) {
838 tracing::trace!(
839 transactions = transactions.len(),
840 "loading new transactions from supplied state"
841 );
842 for transaction in transactions {
843 tracing::trace!(
844 hash = %transaction.receipt.transaction_hash,
845 "loading new transaction from supplied state"
846 );
847 self.tx_results
848 .insert(transaction.receipt.transaction_hash, transaction);
849 }
850 }
851}
852
853fn load_last_l1_batch<S: ReadStorage>(storage: &StoragePtr<S>) -> Option<(u64, u64)> {
854 let current_l1_batch_info_key = StorageKey::new(
856 AccountTreeId::new(SYSTEM_CONTEXT_ADDRESS),
857 SYSTEM_CONTEXT_BLOCK_INFO_POSITION,
858 );
859 let mut storage_ptr = storage.borrow_mut();
860 let current_l1_batch_info = storage_ptr.read_value(¤t_l1_batch_info_key);
861 let (batch_number, batch_timestamp) = unpack_block_info(h256_to_u256(current_l1_batch_info));
862 let block_number = batch_number as u32;
863 if block_number == 0 {
864 return None;
866 }
867 Some((batch_number, batch_timestamp))
868}