1use std::collections::HashMap;
2
3use indexmap::IndexMap;
4use starknet_api::block::{BlockHeader, BlockHeaderWithoutHash, BlockNumber, BlockTimestamp};
5use starknet_api::block_hash::block_hash_calculator::BlockHeaderCommitments;
6use starknet_api::data_availability::L1DataAvailabilityMode;
7use starknet_api::felt;
8use starknet_rs_core::types::Felt;
9use starknet_types::contract_address::ContractAddress;
10use starknet_types::felt::{BlockHash, TransactionHash};
11use starknet_types::rpc::block::{BlockId, BlockStatus, BlockTag, ResourcePrice};
12use starknet_types::traits::HashProducer;
13use starknet_types_core::hash::{Poseidon, StarkHash};
14
15use crate::constants::{DEVNET_DEFAULT_STARTING_BLOCK_NUMBER, STARKNET_VERSION};
16use crate::error::{DevnetResult, Error};
17use crate::state::StarknetState;
18use crate::state::state_diff::StateDiff;
19use crate::traits::HashIdentified;
20
21pub(crate) struct StarknetBlocks {
22 pub(crate) num_to_hash: IndexMap<BlockNumber, BlockHash>,
23 pub(crate) hash_to_block: HashMap<BlockHash, StarknetBlock>,
24 pub(crate) pre_confirmed_block: StarknetBlock,
25 pub(crate) last_block_hash: Option<BlockHash>,
26 pub(crate) hash_to_state_diff: HashMap<BlockHash, StateDiff>,
27 pub(crate) hash_to_state: HashMap<BlockHash, StarknetState>,
28 pub(crate) aborted_blocks: Vec<Felt>,
29 pub(crate) starting_block_number: u64,
30 pub(crate) last_accepted_on_l1: Option<BlockHash>,
31}
32
33impl HashIdentified for StarknetBlocks {
34 type Element = StarknetBlock;
35 type Hash = BlockHash;
36
37 fn get_by_hash(&self, hash: Self::Hash) -> Option<&Self::Element> {
38 let block = self.hash_to_block.get(&hash)?;
39
40 Some(block)
41 }
42}
43
44impl Default for StarknetBlocks {
45 fn default() -> Self {
46 Self {
47 num_to_hash: IndexMap::new(),
48 hash_to_block: HashMap::new(),
49 pre_confirmed_block: StarknetBlock::create_pre_confirmed_block(),
50 last_block_hash: None,
51 hash_to_state_diff: HashMap::new(),
52 hash_to_state: HashMap::new(),
53 aborted_blocks: Vec::new(),
54 starting_block_number: DEVNET_DEFAULT_STARTING_BLOCK_NUMBER,
55 last_accepted_on_l1: None,
56 }
57 }
58}
59
60impl StarknetBlocks {
61 pub fn new(starting_block_number: u64, last_block_hash: Option<Felt>) -> Self {
62 let mut blocks = Self { starting_block_number, ..Default::default() };
63 blocks.pre_confirmed_block.set_block_number(starting_block_number);
64 blocks.last_block_hash = last_block_hash;
65 blocks
66 }
67
68 pub fn insert(&mut self, mut block: StarknetBlock, state_diff: StateDiff) {
71 if let Some(last_block_hash) = self.last_block_hash {
72 block.header.block_header_without_hash.parent_hash =
73 starknet_api::block::BlockHash(last_block_hash);
74 }
75
76 let hash = block.block_hash();
77 let block_number = block.block_number();
78
79 self.num_to_hash.insert(block_number, hash);
80 self.hash_to_block.insert(hash, block);
81 self.hash_to_state_diff.insert(hash, state_diff);
82 self.last_block_hash = Some(hash);
83 }
84
85 fn get_by_num(&self, num: &BlockNumber) -> Option<&StarknetBlock> {
86 let block_hash = self.num_to_hash.get(num)?;
87 let block = self.hash_to_block.get(block_hash)?;
88
89 Some(block)
90 }
91
92 pub fn save_state_at(&mut self, block_hash: Felt, state: StarknetState) {
93 self.hash_to_state.insert(block_hash, state);
94 }
95
96 fn get_by_latest_hash(&self) -> Option<&StarknetBlock> {
97 if let Some(hash) = self.last_block_hash { self.get_by_hash(hash) } else { None }
98 }
99
100 pub fn get_by_block_id(&self, block_id: &BlockId) -> Option<&StarknetBlock> {
101 match block_id {
102 BlockId::Hash(hash) => self.get_by_hash(*hash),
103 BlockId::Number(block_number) => self.get_by_num(&BlockNumber(*block_number)),
104 BlockId::Tag(BlockTag::PreConfirmed) => Some(&self.pre_confirmed_block),
105 BlockId::Tag(BlockTag::Latest) => self.get_by_latest_hash(),
106 BlockId::Tag(BlockTag::L1Accepted) => {
107 self.last_accepted_on_l1.and_then(|h| self.get_by_hash(h))
108 }
109 }
110 }
111
112 fn block_number_from_block_id(&self, block_id: &BlockId) -> Option<BlockNumber> {
114 self.get_by_block_id(block_id).map(|block| block.block_number())
115 }
116
117 pub fn get_blocks(
124 &self,
125 from: Option<BlockId>,
126 to: Option<BlockId>,
127 ) -> DevnetResult<Vec<&StarknetBlock>> {
128 let mut filtered_blocks: IndexMap<Felt, &StarknetBlock> = IndexMap::new();
130
131 let pre_confirmed_block_number = self.pre_confirmed_block.block_number();
132
133 let starting_block = if let Some(block_id) = from {
134 let block_number = self.block_number_from_block_id(&block_id).ok_or(Error::NoBlock)?;
137 Some(block_number)
138 } else {
139 None
140 };
141
142 let ending_block = if let Some(block_id) = to {
143 let block_number = self.block_number_from_block_id(&block_id).ok_or(Error::NoBlock)?;
146 Some(block_number)
147 } else {
148 None
149 };
150
151 fn is_block_number_in_range(
152 current_block_number: BlockNumber,
153 starting_block: Option<BlockNumber>,
154 ending_block: Option<BlockNumber>,
155 ) -> bool {
156 match (starting_block, ending_block) {
157 (None, None) => true,
158 (Some(start), None) => current_block_number >= start,
159 (None, Some(end)) => current_block_number <= end,
160 (Some(start), Some(end)) => {
161 current_block_number >= start && current_block_number <= end
162 }
163 }
164 }
165
166 let mut insert_pre_confirmed_block_in_final_result = true;
167 self.num_to_hash
170 .iter()
171 .filter(|(current_block_number, _)| {
172 is_block_number_in_range(**current_block_number, starting_block, ending_block)
173 })
174 .for_each(|(block_number, block_hash)| {
175 if *block_number == pre_confirmed_block_number {
176 insert_pre_confirmed_block_in_final_result = false;
177 }
178 filtered_blocks.insert(*block_hash, &self.hash_to_block[block_hash]);
179 });
180
181 let mut result: Vec<&StarknetBlock> = filtered_blocks.into_values().collect();
182
183 if is_block_number_in_range(pre_confirmed_block_number, starting_block, ending_block)
184 && insert_pre_confirmed_block_in_final_result
185 {
186 result.push(&self.pre_confirmed_block);
187 }
188
189 Ok(result)
190 }
191
192 pub(crate) fn remove(&mut self, block_hash: &BlockHash) -> Option<StarknetBlock> {
193 self.hash_to_state.remove(block_hash)?;
195 self.hash_to_state_diff.remove(block_hash)?;
196 let block = self.hash_to_block.remove(block_hash)?;
197 self.num_to_hash.shift_remove(&block.block_number())?;
198 Some(block)
199 }
200
201 pub fn next_latest_block_number(&self) -> BlockNumber {
202 BlockNumber(self.pre_confirmed_block.block_number().0)
203 }
204}
205
206#[derive(Clone)]
207#[cfg_attr(test, derive(PartialEq, Eq))]
208pub struct StarknetBlock {
209 pub(crate) header: BlockHeader,
210 transaction_hashes: Vec<TransactionHash>,
211 pub(crate) status: BlockStatus,
212}
213
214impl From<&StarknetBlock> for starknet_types::rpc::block::PreConfirmedBlockHeader {
215 fn from(value: &StarknetBlock) -> Self {
216 Self {
217 block_number: value.block_number(),
218 sequencer_address: value.sequencer_address(),
219 timestamp: value.timestamp(),
220 starknet_version: STARKNET_VERSION.to_string(),
221 l1_gas_price: ResourcePrice {
222 price_in_fri: value
223 .header
224 .block_header_without_hash
225 .l1_gas_price
226 .price_in_fri
227 .0
228 .into(),
229 price_in_wei: value
230 .header
231 .block_header_without_hash
232 .l1_gas_price
233 .price_in_wei
234 .0
235 .into(),
236 },
237 l1_data_gas_price: ResourcePrice {
238 price_in_fri: value
239 .header
240 .block_header_without_hash
241 .l1_data_gas_price
242 .price_in_fri
243 .0
244 .into(),
245 price_in_wei: value
246 .header
247 .block_header_without_hash
248 .l1_data_gas_price
249 .price_in_wei
250 .0
251 .into(),
252 },
253 l1_da_mode: value.header.block_header_without_hash.l1_da_mode,
254 l2_gas_price: ResourcePrice {
255 price_in_fri: value
256 .header
257 .block_header_without_hash
258 .l2_gas_price
259 .price_in_fri
260 .0
261 .into(),
262 price_in_wei: value
263 .header
264 .block_header_without_hash
265 .l2_gas_price
266 .price_in_wei
267 .0
268 .into(),
269 },
270 }
271 }
272}
273
274impl From<&StarknetBlock> for starknet_types::rpc::block::BlockHeader {
275 fn from(value: &StarknetBlock) -> Self {
276 Self {
277 block_hash: value.block_hash(),
278 parent_hash: value.parent_hash(),
279 block_number: value.block_number(),
280 sequencer_address: value.sequencer_address(),
281 new_root: value.new_root(),
282 timestamp: value.timestamp(),
283 starknet_version: STARKNET_VERSION.to_string(),
284 l1_gas_price: ResourcePrice {
285 price_in_fri: value
286 .header
287 .block_header_without_hash
288 .l1_gas_price
289 .price_in_fri
290 .0
291 .into(),
292 price_in_wei: value
293 .header
294 .block_header_without_hash
295 .l1_gas_price
296 .price_in_wei
297 .0
298 .into(),
299 },
300 l1_data_gas_price: ResourcePrice {
301 price_in_fri: value
302 .header
303 .block_header_without_hash
304 .l1_data_gas_price
305 .price_in_fri
306 .0
307 .into(),
308 price_in_wei: value
309 .header
310 .block_header_without_hash
311 .l1_data_gas_price
312 .price_in_wei
313 .0
314 .into(),
315 },
316 l1_da_mode: value.header.block_header_without_hash.l1_da_mode,
317 l2_gas_price: ResourcePrice {
318 price_in_fri: value
319 .header
320 .block_header_without_hash
321 .l2_gas_price
322 .price_in_fri
323 .0
324 .into(),
325 price_in_wei: value
326 .header
327 .block_header_without_hash
328 .l2_gas_price
329 .price_in_wei
330 .0
331 .into(),
332 },
333 n_transactions: value.header.n_transactions as u64,
334 n_events: value.header.n_events as u64,
335 state_diff_length: value.header.state_diff_length.unwrap_or(0) as u64,
336 state_diff_commitment: value.header.state_diff_commitment.unwrap_or_default(),
337 transaction_commitment: value.header.transaction_commitment.unwrap_or_default(),
338 event_commitment: value.header.event_commitment.unwrap_or_default(),
339 receipt_commitment: value.header.receipt_commitment.unwrap_or_default(),
340 }
341 }
342}
343
344impl StarknetBlock {
345 pub(crate) fn add_transaction(&mut self, transaction_hash: TransactionHash) {
346 self.transaction_hashes.push(transaction_hash);
347 }
348
349 pub fn get_transactions(&self) -> &Vec<TransactionHash> {
350 &self.transaction_hashes
351 }
352
353 pub fn status(&self) -> &BlockStatus {
354 &self.status
355 }
356
357 pub fn block_hash(&self) -> BlockHash {
358 self.header.block_hash.0
359 }
360
361 pub fn parent_hash(&self) -> BlockHash {
362 self.header.block_header_without_hash.parent_hash.0
363 }
364
365 pub fn sequencer_address(&self) -> ContractAddress {
366 self.header.block_header_without_hash.sequencer.0.into()
367 }
368
369 pub fn timestamp(&self) -> BlockTimestamp {
370 self.header.block_header_without_hash.timestamp
371 }
372
373 pub fn new_root(&self) -> Felt {
374 self.header.block_header_without_hash.state_root.0
375 }
376
377 pub(crate) fn set_block_hash(&mut self, block_hash: BlockHash) {
378 self.header.block_hash = starknet_api::block::BlockHash(block_hash);
379 }
380
381 pub fn block_number(&self) -> BlockNumber {
382 self.header.block_header_without_hash.block_number
383 }
384
385 pub(crate) fn create_pre_confirmed_block() -> Self {
386 Self {
387 header: BlockHeader {
388 block_header_without_hash: BlockHeaderWithoutHash {
389 l1_da_mode: L1DataAvailabilityMode::Blob,
390 ..Default::default()
391 },
392 ..BlockHeader::default()
393 },
394 status: BlockStatus::PreConfirmed,
395 transaction_hashes: Vec::new(),
396 }
397 }
398
399 pub fn create_empty_accepted() -> Self {
400 Self {
401 header: BlockHeader::default(),
402 transaction_hashes: vec![],
403 status: BlockStatus::AcceptedOnL2,
404 }
405 }
406
407 pub(crate) fn set_block_number(&mut self, block_number: u64) {
408 self.header.block_header_without_hash.block_number = BlockNumber(block_number)
409 }
410
411 pub(crate) fn set_timestamp(&mut self, timestamp: BlockTimestamp) {
412 self.header.block_header_without_hash.timestamp = timestamp;
413 }
414
415 pub(crate) fn set_counts(
416 &mut self,
417 n_transactions: usize,
418 n_events: usize,
419 state_diff_length: usize,
420 ) {
421 self.header.n_transactions = n_transactions;
422 self.header.n_events = n_events;
423 self.header.state_diff_length = Some(state_diff_length);
424 }
425
426 pub(crate) fn set_commitments(&mut self, commitments: BlockHeaderCommitments) {
427 self.header.transaction_commitment = Some(commitments.transaction_commitment);
428 self.header.event_commitment = Some(commitments.event_commitment);
429 self.header.receipt_commitment = Some(commitments.receipt_commitment);
430 self.header.state_diff_commitment = Some(commitments.state_diff_commitment);
431 }
432}
433
434impl HashProducer for StarknetBlock {
435 type Error = Error;
436 fn generate_hash(&self) -> DevnetResult<BlockHash> {
437 let hash = Poseidon::hash_array(&[
438 felt!(self.header.block_header_without_hash.block_number.0), self.header.block_header_without_hash.state_root.0, *self.header.block_header_without_hash.sequencer.0.key(), Felt::ZERO, felt!(self.transaction_hashes.len() as u64), Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::ZERO, self.header.block_header_without_hash.parent_hash.0, ]);
456
457 Ok(hash)
458 }
459}
460
461#[cfg(test)]
462mod tests {
463 use starknet_api::block::{BlockHash, BlockHeader, BlockHeaderWithoutHash, BlockNumber};
464 use starknet_api::data_availability::L1DataAvailabilityMode;
465 use starknet_rs_core::types::Felt;
466 use starknet_types::rpc::block::{BlockId, BlockStatus, BlockTag};
467 use starknet_types::traits::HashProducer;
468
469 use super::{StarknetBlock, StarknetBlocks};
470 use crate::state::state_diff::StateDiff;
471 use crate::traits::HashIdentified;
472
473 #[test]
474 fn get_blocks_return_in_correct_order() {
475 let mut blocks = StarknetBlocks::default();
476 for block_number in 1..=10 {
477 let mut block_to_insert = StarknetBlock::create_pre_confirmed_block();
478 block_to_insert.header.block_header_without_hash.block_number =
479 BlockNumber(block_number);
480 block_to_insert.header.block_hash =
481 starknet_api::block::BlockHash(Felt::from(block_number as u128));
482 blocks.insert(block_to_insert, StateDiff::default());
483 blocks.pre_confirmed_block.header.block_header_without_hash.block_number =
484 BlockNumber(block_number).unchecked_next();
485 }
486
487 let block_numbers: Vec<u64> = blocks
488 .get_blocks(None, None)
489 .unwrap()
490 .iter()
491 .map(|block| block.block_number().0)
492 .collect();
493 assert_eq!(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], block_numbers);
494
495 let block_numbers: Vec<u64> = blocks
496 .get_blocks(Some(BlockId::Number(7)), None)
497 .unwrap()
498 .iter()
499 .map(|block| block.block_number().0)
500 .collect();
501 assert_eq!(vec![7, 8, 9, 10, 11], block_numbers);
502
503 let block_numbers: Vec<u64> = blocks
504 .get_blocks(Some(BlockId::Number(7)), Some(BlockId::Tag(BlockTag::Latest)))
505 .unwrap()
506 .iter()
507 .map(|block| block.block_number().0)
508 .collect();
509 assert_eq!(vec![7, 8, 9, 10], block_numbers);
510 }
511
512 #[test]
513 fn block_number_from_block_id_should_return_correct_result() {
514 let mut blocks = StarknetBlocks::new(0, None);
515 let mut block_to_insert = StarknetBlock::create_pre_confirmed_block();
516 blocks.pre_confirmed_block = block_to_insert.clone();
517
518 assert!(blocks.block_number_from_block_id(&BlockId::Tag(BlockTag::Latest)).is_none());
520 assert!(blocks.block_number_from_block_id(&BlockId::Tag(BlockTag::PreConfirmed)).is_some());
522
523 let block_hash = block_to_insert.generate_hash().unwrap();
524 block_to_insert.header.block_header_without_hash.block_number = BlockNumber(10);
525 block_to_insert.header.block_hash = starknet_api::block::BlockHash(block_hash);
526
527 blocks.insert(block_to_insert, StateDiff::default());
528
529 assert!(blocks.block_number_from_block_id(&BlockId::Number(11)).is_none());
531 assert!(blocks.block_number_from_block_id(&BlockId::Number(10)).is_some());
532 assert!(blocks.block_number_from_block_id(&BlockId::Hash(Felt::ONE)).is_none());
534 assert!(blocks.block_number_from_block_id(&BlockId::Tag(BlockTag::Latest)).is_some());
535 assert!(blocks.block_number_from_block_id(&BlockId::Tag(BlockTag::PreConfirmed)).is_some());
536 assert!(blocks.block_number_from_block_id(&BlockId::Hash(block_hash)).is_some());
537 }
538
539 #[test]
540 fn get_blocks_with_filter() {
541 let mut blocks = StarknetBlocks::default();
542
543 let last_block_number = 11;
544 for block_number in 2..=last_block_number {
545 let mut block_to_insert = StarknetBlock::create_pre_confirmed_block();
546 block_to_insert.header.block_header_without_hash.block_number =
547 BlockNumber(block_number);
548 block_to_insert.header.block_hash =
549 starknet_api::block::BlockHash(Felt::from(block_number as u128));
550 blocks.insert(block_to_insert.clone(), StateDiff::default());
551
552 if block_number == last_block_number {
554 blocks.pre_confirmed_block = block_to_insert;
555 }
556 }
557
558 assert!(blocks.hash_to_block.len() == 10);
560
561 assert_eq!(blocks.get_blocks(None, None).unwrap().len(), 10);
564
565 assert_eq!(blocks.get_blocks(Some(BlockId::Number(9)), None).unwrap().len(), 3);
567 assert!(blocks.get_blocks(Some(BlockId::Number(12)), None).is_err());
569 assert_eq!(blocks.get_blocks(Some(BlockId::Number(11)), None).unwrap().len(), 1);
571 assert_eq!(blocks.get_blocks(Some(BlockId::Hash(Felt::from(9))), None).unwrap().len(), 3);
573 assert_eq!(blocks.get_blocks(Some(BlockId::Tag(BlockTag::Latest)), None).unwrap().len(), 1);
575 assert_eq!(
576 blocks.get_blocks(Some(BlockId::Tag(BlockTag::PreConfirmed)), None).unwrap().len(),
577 1
578 );
579
580 assert_eq!(blocks.get_blocks(None, Some(BlockId::Number(9))).unwrap().len(), 8);
583 assert!(blocks.get_blocks(None, Some(BlockId::Number(0))).is_err());
585 assert_eq!(blocks.get_blocks(None, Some(BlockId::Hash(Felt::from(9)))).unwrap().len(), 8);
587 assert!(blocks.get_blocks(None, Some(BlockId::Hash(Felt::ZERO))).is_err());
589 assert_eq!(
591 blocks.get_blocks(None, Some(BlockId::Tag(BlockTag::Latest))).unwrap().len(),
592 10
593 );
594 assert_eq!(
595 blocks.get_blocks(None, Some(BlockId::Tag(BlockTag::PreConfirmed))).unwrap().len(),
596 10
597 );
598 assert_eq!(blocks.get_blocks(None, Some(BlockId::Number(2))).unwrap().len(), 1);
600 assert!(blocks.get_blocks(None, Some(BlockId::Number(1))).is_err());
602
603 assert_eq!(
606 blocks.get_blocks(Some(BlockId::Number(2)), Some(BlockId::Number(9))).unwrap().len(),
607 8
608 );
609 assert_eq!(
611 blocks
612 .get_blocks(Some(BlockId::Number(2)), Some(BlockId::Hash(Felt::from(9))))
613 .unwrap()
614 .len(),
615 8
616 );
617 assert_eq!(
619 blocks
620 .get_blocks(Some(BlockId::Number(2)), Some(BlockId::Tag(BlockTag::Latest)))
621 .unwrap()
622 .len(),
623 10
624 );
625 assert_eq!(
626 blocks
627 .get_blocks(Some(BlockId::Number(2)), Some(BlockId::Tag(BlockTag::PreConfirmed)))
628 .unwrap()
629 .len(),
630 10
631 );
632
633 assert!(
635 blocks
636 .get_blocks(Some(BlockId::Number(10)), Some(BlockId::Number(2)))
637 .unwrap()
638 .is_empty()
639 );
640 assert_eq!(
642 blocks
643 .get_blocks(Some(BlockId::Number(11)), Some(BlockId::Tag(BlockTag::Latest)))
644 .unwrap()
645 .len(),
646 1
647 );
648 assert_eq!(
649 blocks
650 .get_blocks(Some(BlockId::Number(11)), Some(BlockId::Tag(BlockTag::PreConfirmed)))
651 .unwrap()
652 .len(),
653 1
654 );
655
656 assert!(blocks.get_blocks(Some(BlockId::Number(0)), Some(BlockId::Number(1000))).is_err());
658
659 assert_eq!(
661 blocks
662 .get_blocks(Some(BlockId::Hash(Felt::TWO)), Some(BlockId::Hash(Felt::from(9))))
663 .unwrap()
664 .len(),
665 8
666 );
667 assert!(
668 blocks
669 .get_blocks(Some(BlockId::Hash(Felt::TWO)), Some(BlockId::Hash(Felt::ZERO)))
670 .is_err()
671 );
672 assert!(
673 blocks
674 .get_blocks(Some(BlockId::Hash(Felt::from(10))), Some(BlockId::Hash(Felt::from(5))))
675 .unwrap()
676 .is_empty()
677 );
678 assert_eq!(
680 blocks
681 .get_blocks(Some(BlockId::Hash(Felt::TWO)), Some(BlockId::Number(9)))
682 .unwrap()
683 .len(),
684 8
685 );
686 assert_eq!(
688 blocks
689 .get_blocks(
690 Some(BlockId::Hash(Felt::from(11))),
691 Some(BlockId::Tag(BlockTag::Latest))
692 )
693 .unwrap()
694 .len(),
695 1
696 );
697 assert_eq!(
698 blocks
699 .get_blocks(
700 Some(BlockId::Hash(Felt::from(11))),
701 Some(BlockId::Tag(BlockTag::PreConfirmed))
702 )
703 .unwrap()
704 .len(),
705 1
706 );
707
708 assert_eq!(
710 blocks
711 .get_blocks(
712 Some(BlockId::Tag(BlockTag::Latest)),
713 Some(BlockId::Tag(BlockTag::Latest))
714 )
715 .unwrap()
716 .len(),
717 1
718 );
719 assert_eq!(
720 blocks
721 .get_blocks(
722 Some(BlockId::Tag(BlockTag::Latest)),
723 Some(BlockId::Tag(BlockTag::PreConfirmed))
724 )
725 .unwrap()
726 .len(),
727 1
728 );
729 assert_eq!(
730 blocks
731 .get_blocks(
732 Some(BlockId::Tag(BlockTag::PreConfirmed)),
733 Some(BlockId::Tag(BlockTag::Latest))
734 )
735 .unwrap()
736 .len(),
737 1
738 );
739 assert_eq!(
740 blocks
741 .get_blocks(
742 Some(BlockId::Tag(BlockTag::PreConfirmed)),
743 Some(BlockId::Tag(BlockTag::PreConfirmed))
744 )
745 .unwrap()
746 .len(),
747 1
748 );
749
750 assert_eq!(
752 blocks
753 .get_blocks(Some(BlockId::Tag(BlockTag::Latest)), Some(BlockId::Number(11)))
754 .unwrap()
755 .len(),
756 1
757 );
758 assert_eq!(
759 blocks
760 .get_blocks(
761 Some(BlockId::Tag(BlockTag::Latest)),
762 Some(BlockId::Hash(Felt::from(11)))
763 )
764 .unwrap()
765 .len(),
766 1
767 );
768 assert_eq!(
769 blocks
770 .get_blocks(Some(BlockId::Tag(BlockTag::PreConfirmed)), Some(BlockId::Number(11)))
771 .unwrap()
772 .len(),
773 1
774 );
775 assert_eq!(
776 blocks
777 .get_blocks(
778 Some(BlockId::Tag(BlockTag::PreConfirmed)),
779 Some(BlockId::Hash(Felt::from(11)))
780 )
781 .unwrap()
782 .len(),
783 1
784 );
785 assert!(
786 blocks
787 .get_blocks(Some(BlockId::Tag(BlockTag::Latest)), Some(BlockId::Number(2)))
788 .unwrap()
789 .is_empty()
790 );
791 assert!(
792 blocks
793 .get_blocks(Some(BlockId::Tag(BlockTag::Latest)), Some(BlockId::Hash(Felt::TWO)))
794 .unwrap()
795 .is_empty()
796 );
797 }
798
799 #[test]
800 fn get_by_block_id_is_correct() {
801 let mut blocks = StarknetBlocks::default();
802 let mut block_to_insert = StarknetBlock::create_pre_confirmed_block();
803 block_to_insert.header.block_hash =
804 starknet_api::block::BlockHash(block_to_insert.generate_hash().unwrap());
805 block_to_insert.header.block_header_without_hash.block_number = BlockNumber(10);
806 blocks.pre_confirmed_block = block_to_insert.clone();
807
808 blocks.insert(block_to_insert.clone(), StateDiff::default());
809
810 let extracted_block = blocks.get_by_block_id(&BlockId::Number(10)).unwrap();
811 assert!(block_to_insert == extracted_block.clone());
812
813 let extracted_block =
814 blocks.get_by_block_id(&BlockId::Hash(block_to_insert.block_hash())).unwrap();
815 assert!(block_to_insert == extracted_block.clone());
816
817 let extracted_block = blocks.get_by_block_id(&BlockId::Tag(BlockTag::Latest)).unwrap();
818 assert!(block_to_insert == extracted_block.clone());
819
820 let extracted_block =
821 blocks.get_by_block_id(&BlockId::Tag(BlockTag::PreConfirmed)).unwrap();
822 assert!(block_to_insert == extracted_block.clone());
823
824 match blocks.get_by_block_id(&BlockId::Number(11)) {
825 None => (),
826 _ => panic!("Expected none"),
827 }
828 }
829
830 #[test]
831 fn correct_block_linking_via_parent_hash() {
832 let mut blocks = StarknetBlocks::default();
833
834 for block_number in 0..3 {
835 let mut block = StarknetBlock::create_pre_confirmed_block();
836
837 block.status = BlockStatus::AcceptedOnL2;
838 block.header.block_header_without_hash.block_number = BlockNumber(block_number);
839 block.set_block_hash(block.generate_hash().unwrap());
840
841 blocks.insert(block, StateDiff::default());
842 }
843
844 assert!(
845 blocks
846 .get_by_num(&BlockNumber(0))
847 .unwrap()
848 .header
849 .block_header_without_hash
850 .parent_hash
851 == BlockHash::default()
852 );
853 assert!(
854 blocks.get_by_num(&BlockNumber(0)).unwrap().header.block_hash
855 == blocks
856 .get_by_num(&BlockNumber(1))
857 .unwrap()
858 .header
859 .block_header_without_hash
860 .parent_hash
861 );
862 assert!(
863 blocks.get_by_num(&BlockNumber(1)).unwrap().header.block_hash
864 == blocks
865 .get_by_num(&BlockNumber(2))
866 .unwrap()
867 .header
868 .block_header_without_hash
869 .parent_hash
870 );
871 assert!(
872 blocks
873 .get_by_num(&BlockNumber(1))
874 .unwrap()
875 .header
876 .block_header_without_hash
877 .parent_hash
878 != blocks
879 .get_by_num(&BlockNumber(2))
880 .unwrap()
881 .header
882 .block_header_without_hash
883 .parent_hash
884 )
885 }
886
887 #[test]
888 fn get_by_hash_is_correct() {
889 let mut blocks = StarknetBlocks::default();
890 let mut block_to_insert = StarknetBlock::create_pre_confirmed_block();
891 block_to_insert.header.block_hash =
892 starknet_api::block::BlockHash(block_to_insert.generate_hash().unwrap());
893 block_to_insert.header.block_header_without_hash.block_number = BlockNumber(1);
894
895 blocks.insert(block_to_insert.clone(), StateDiff::default());
896
897 let extracted_block = blocks.get_by_hash(block_to_insert.block_hash()).unwrap();
898 assert!(block_to_insert == extracted_block.clone());
899 }
900
901 #[test]
902 fn check_pre_confirmed_block() {
903 let block = StarknetBlock::create_pre_confirmed_block();
904 assert!(block.status == BlockStatus::PreConfirmed);
905 assert!(block.transaction_hashes.is_empty());
906 assert_eq!(
907 block.header,
908 BlockHeader {
909 block_header_without_hash: BlockHeaderWithoutHash {
910 l1_da_mode: L1DataAvailabilityMode::Blob,
911 ..Default::default()
912 },
913 ..Default::default()
914 }
915 );
916 }
917}