1use alloc::vec::Vec;
9use alloy_eips::eip2718::Encodable2718;
10use alloy_primitives::FixedBytes;
11use kona_genesis::RollupConfig;
12use op_alloy_consensus::OpTxType;
13use tracing::{info, warn};
14
15use crate::{
16 BatchValidationProvider, BatchValidity, BlockInfo, L2BlockInfo, RawSpanBatch, SingleBatch,
17 SpanBatchBits, SpanBatchElement, SpanBatchError, SpanBatchPayload, SpanBatchPrefix,
18 SpanBatchTransactions,
19};
20
21#[derive(Debug, Default, Clone, PartialEq, Eq)]
67pub struct SpanBatch {
68 pub parent_check: FixedBytes<20>,
74 pub l1_origin_check: FixedBytes<20>,
80 pub genesis_timestamp: u64,
86 pub chain_id: u64,
92 pub batches: Vec<SpanBatchElement>,
98 pub origin_bits: SpanBatchBits,
104 pub block_tx_counts: Vec<u64>,
110 pub txs: SpanBatchTransactions,
116}
117
118impl SpanBatch {
119 pub fn starting_timestamp(&self) -> u64 {
133 self.batches[0].timestamp
134 }
135
136 pub fn final_timestamp(&self) -> u64 {
151 self.batches[self.batches.len() - 1].timestamp
152 }
153
154 pub fn starting_epoch_num(&self) -> u64 {
169 self.batches[0].epoch_num
170 }
171
172 pub fn check_origin_hash(&self, hash: FixedBytes<32>) -> bool {
193 self.l1_origin_check == hash[..20]
194 }
195
196 pub fn check_parent_hash(&self, hash: FixedBytes<32>) -> bool {
217 self.parent_check == hash[..20]
218 }
219
220 fn peek(&self, n: usize) -> &SpanBatchElement {
242 &self.batches[self.batches.len() - 1 - n]
243 }
244
245 pub fn to_raw_span_batch(&self) -> Result<RawSpanBatch, SpanBatchError> {
273 if self.batches.is_empty() {
274 return Err(SpanBatchError::EmptySpanBatch);
275 }
276
277 let span_start = self.batches.first().ok_or(SpanBatchError::EmptySpanBatch)?;
279 let span_end = self.batches.last().ok_or(SpanBatchError::EmptySpanBatch)?;
280
281 Ok(RawSpanBatch {
282 prefix: SpanBatchPrefix {
283 rel_timestamp: span_start.timestamp - self.genesis_timestamp,
284 l1_origin_num: span_end.epoch_num,
285 parent_check: self.parent_check,
286 l1_origin_check: self.l1_origin_check,
287 },
288 payload: SpanBatchPayload {
289 block_count: self.batches.len() as u64,
290 origin_bits: self.origin_bits.clone(),
291 block_tx_counts: self.block_tx_counts.clone(),
292 txs: self.txs.clone(),
293 },
294 })
295 }
296
297 pub fn get_singular_batches(
301 &self,
302 l1_origins: &[BlockInfo],
303 l2_safe_head: L2BlockInfo,
304 ) -> Result<Vec<SingleBatch>, SpanBatchError> {
305 let mut single_batches = Vec::new();
306 let mut origin_index = 0;
307 for batch in &self.batches {
308 if batch.timestamp <= l2_safe_head.block_info.timestamp {
309 continue;
310 }
311 let origin_epoch_hash = l1_origins[origin_index..l1_origins.len()]
312 .iter()
313 .enumerate()
314 .find(|(_, origin)| origin.number == batch.epoch_num)
315 .map(|(i, origin)| {
316 origin_index = i;
317 origin.hash
318 })
319 .ok_or(SpanBatchError::MissingL1Origin)?;
320 let single_batch = SingleBatch {
321 epoch_num: batch.epoch_num,
322 epoch_hash: origin_epoch_hash,
323 timestamp: batch.timestamp,
324 transactions: batch.transactions.clone(),
325 ..Default::default()
326 };
327 single_batches.push(single_batch);
328 }
329 Ok(single_batches)
330 }
331
332 pub fn append_singular_batch(
334 &mut self,
335 singular_batch: SingleBatch,
336 seq_num: u64,
337 ) -> Result<(), SpanBatchError> {
338 if !self.batches.is_empty() && self.peek(0).timestamp > singular_batch.timestamp {
340 panic!("Batch is not ordered");
341 }
342
343 let SingleBatch { epoch_hash, parent_hash, .. } = singular_batch;
344
345 self.batches.push(singular_batch.into());
347 self.l1_origin_check = epoch_hash[..20].try_into().expect("Sub-slice cannot fail");
349
350 let epoch_bit = if self.batches.len() == 1 {
351 self.parent_check = parent_hash[..20].try_into().expect("Sub-slice cannot fail");
354 seq_num == 0
355 } else {
356 self.peek(1).epoch_num < self.peek(0).epoch_num
358 };
359
360 self.origin_bits.set_bit(self.batches.len() - 1, epoch_bit);
362
363 let new_txs = self.peek(0).transactions.clone();
364
365 self.block_tx_counts.push(new_txs.len() as u64);
367
368 self.txs.add_txs(new_txs, self.chain_id)
370 }
371
372 pub async fn check_batch<BV: BatchValidationProvider>(
374 &self,
375 cfg: &RollupConfig,
376 l1_blocks: &[BlockInfo],
377 l2_safe_head: L2BlockInfo,
378 inclusion_block: &BlockInfo,
379 fetcher: &mut BV,
380 ) -> BatchValidity {
381 let (prefix_validity, parent_block) =
382 self.check_batch_prefix(cfg, l1_blocks, l2_safe_head, inclusion_block, fetcher).await;
383 if !matches!(prefix_validity, BatchValidity::Accept) {
384 return prefix_validity;
385 }
386
387 let starting_epoch_num = self.starting_epoch_num();
388 let parent_block = parent_block.expect("parent_block must be Some");
389
390 let mut origin_index = 0;
391 let mut origin_advanced = starting_epoch_num == parent_block.l1_origin.number + 1;
392 for (i, batch) in self.batches.iter().enumerate() {
393 if batch.timestamp <= l2_safe_head.block_info.timestamp {
394 continue;
395 }
396 for (j, j_block) in l1_blocks.iter().enumerate().skip(origin_index) {
398 if batch.epoch_num == j_block.number {
399 origin_index = j;
400 break;
401 }
402 }
403 let l1_origin = l1_blocks[origin_index];
404 if i > 0 {
405 origin_advanced = false;
406 if batch.epoch_num > self.batches[i - 1].epoch_num {
407 origin_advanced = true;
408 }
409 }
410 let block_timestamp = batch.timestamp;
411 if block_timestamp < l1_origin.timestamp {
412 warn!(
413 target: "batch_span",
414 "block timestamp is less than L1 origin timestamp, l2_timestamp: {}, l1_timestamp: {}, origin: {:?}",
415 block_timestamp,
416 l1_origin.timestamp,
417 l1_origin.id()
418 );
419 return BatchValidity::Drop;
420 }
421
422 let max_drift = cfg.max_sequencer_drift(l1_origin.timestamp);
424 if block_timestamp > l1_origin.timestamp + max_drift {
425 if batch.transactions.is_empty() {
426 if !origin_advanced {
432 if origin_index + 1 >= l1_blocks.len() {
433 info!(
434 target: "batch_span",
435 "without the next L1 origin we cannot determine yet if this empty batch that exceeds the time drift is still valid"
436 );
437 return BatchValidity::Undecided;
438 }
439 if block_timestamp >= l1_blocks[origin_index + 1].timestamp {
440 info!(
442 target: "batch_span",
443 "batch exceeded sequencer time drift without adopting next origin, and next L1 origin would have been valid"
444 );
445 return BatchValidity::Drop;
446 } else {
447 info!(
448 target: "batch_span",
449 "continuing with empty batch before late L1 block to preserve L2 time invariant"
450 );
451 }
452 }
453 } else {
454 warn!(
458 target: "batch_span",
459 "batch exceeded sequencer time drift, sequencer must adopt new L1 origin to include transactions again, max_time: {}",
460 l1_origin.timestamp + max_drift
461 );
462 return BatchValidity::Drop;
463 }
464 }
465
466 for (i, tx) in batch.transactions.iter().enumerate() {
468 if tx.is_empty() {
469 warn!(
470 target: "batch_span",
471 "transaction data must not be empty, but found empty tx, tx_index: {}",
472 i
473 );
474 return BatchValidity::Drop;
475 }
476 if tx.as_ref().first() == Some(&(OpTxType::Deposit as u8)) {
477 warn!(
478 target: "batch_span",
479 "sequencers may not embed any deposits into batch data, but found tx that has one, tx_index: {}",
480 i
481 );
482 return BatchValidity::Drop;
483 }
484
485 if !cfg.is_isthmus_active(batch.timestamp) &&
487 tx.as_ref().first() == Some(&(OpTxType::Eip7702 as u8))
488 {
489 warn!(target: "batch_span", "EIP-7702 transactions are not supported pre-isthmus. tx_index: {}", i);
490 return BatchValidity::Drop;
491 }
492 }
493 }
494
495 let parent_num = parent_block.block_info.number;
497 let next_timestamp = l2_safe_head.block_info.timestamp + cfg.block_time;
498 if self.starting_timestamp() < next_timestamp {
499 for i in 0..(l2_safe_head.block_info.number - parent_num) {
500 let safe_block_num = parent_num + i + 1;
501 let safe_block_payload = match fetcher.block_by_number(safe_block_num).await {
502 Ok(p) => p,
503 Err(e) => {
504 warn!(target: "batch_span", "failed to fetch block number {safe_block_num}: {e}");
505 return BatchValidity::Undecided;
506 }
507 };
508 let safe_block = &safe_block_payload.body;
509 let batch_txs = &self.batches[i as usize].transactions;
510 let deposit_count: usize = safe_block
512 .transactions
513 .iter()
514 .map(|tx| if tx.is_deposit() { 1 } else { 0 })
515 .sum();
516 if safe_block.transactions.len() - deposit_count != batch_txs.len() {
517 warn!(
518 target: "batch_span",
519 "overlapped block's tx count does not match, safe_block_txs: {}, batch_txs: {}",
520 safe_block.transactions.len(),
521 batch_txs.len()
522 );
523 return BatchValidity::Drop;
524 }
525 let batch_txs_len = batch_txs.len();
526 #[allow(clippy::needless_range_loop)]
527 for j in 0..batch_txs_len {
528 let mut buf = Vec::new();
529 safe_block.transactions[j + deposit_count].encode_2718(&mut buf);
530 if buf != batch_txs[j].0 {
531 warn!(target: "batch_span", "overlapped block's transaction does not match");
532 return BatchValidity::Drop;
533 }
534 }
535 let safe_block_ref = match L2BlockInfo::from_block_and_genesis(
536 &safe_block_payload,
537 &cfg.genesis,
538 ) {
539 Ok(r) => r,
540 Err(e) => {
541 warn!(
542 target: "batch_span",
543 "failed to extract L2BlockInfo from execution payload, hash: {}, err: {e}",
544 safe_block_payload.header.hash_slow()
545 );
546 return BatchValidity::Drop;
547 }
548 };
549 if safe_block_ref.l1_origin.number != self.batches[i as usize].epoch_num {
550 warn!(
551 "overlapped block's L1 origin number does not match {}, {}",
552 safe_block_ref.l1_origin.number, self.batches[i as usize].epoch_num
553 );
554 return BatchValidity::Drop;
555 }
556 }
557 }
558
559 BatchValidity::Accept
560 }
561
562 pub async fn check_batch_prefix<BF: BatchValidationProvider>(
567 &self,
568 cfg: &RollupConfig,
569 l1_origins: &[BlockInfo],
570 l2_safe_head: L2BlockInfo,
571 inclusion_block: &BlockInfo,
572 fetcher: &mut BF,
573 ) -> (BatchValidity, Option<L2BlockInfo>) {
574 if l1_origins.is_empty() {
575 warn!(target: "batch_span", "missing L1 block input, cannot proceed with batch checking");
576 return (BatchValidity::Undecided, None);
577 }
578 if self.batches.is_empty() {
579 warn!(target: "batch_span", "empty span batch, cannot proceed with batch checking");
580 return (BatchValidity::Undecided, None);
581 }
582
583 let epoch = l1_origins[0];
584 let next_timestamp = l2_safe_head.block_info.timestamp + cfg.block_time;
585
586 let starting_epoch_num = self.starting_epoch_num();
587 let mut batch_origin = epoch;
588 if starting_epoch_num == batch_origin.number + 1 {
589 if l1_origins.len() < 2 {
590 info!(
591 target: "batch_span",
592 "eager batch wants to advance current epoch {:?}, but could not without more L1 blocks",
593 epoch.id()
594 );
595 return (BatchValidity::Undecided, None);
596 }
597 batch_origin = l1_origins[1];
598 }
599 if !cfg.is_delta_active(batch_origin.timestamp) {
600 warn!(
601 target: "batch_span",
602 "received SpanBatch (id {:?}) with L1 origin (timestamp {}) before Delta hard fork",
603 batch_origin.id(),
604 batch_origin.timestamp
605 );
606 return (BatchValidity::Drop, None);
607 }
608
609 if self.starting_timestamp() > next_timestamp {
610 warn!(
611 target: "batch_span",
612 "received out-of-order batch for future processing after next batch ({} > {})",
613 self.starting_timestamp(),
614 next_timestamp
615 );
616
617 if cfg.is_holocene_active(inclusion_block.timestamp) {
619 return (BatchValidity::Drop, None);
620 }
621 return (BatchValidity::Future, None);
622 }
623
624 if self.final_timestamp() < next_timestamp {
626 warn!(target: "batch_span", "span batch has no new blocks after safe head");
627 return if cfg.is_holocene_active(inclusion_block.timestamp) {
628 (BatchValidity::Past, None)
629 } else {
630 (BatchValidity::Drop, None)
631 };
632 }
633
634 let mut parent_num = l2_safe_head.block_info.number;
638 let mut parent_block = l2_safe_head;
639 if self.starting_timestamp() < next_timestamp {
640 if self.starting_timestamp() > l2_safe_head.block_info.timestamp {
641 warn!(target: "batch_span", "batch has misaligned timestamp, block time is too short");
643 return (BatchValidity::Drop, None);
644 }
645 if (l2_safe_head.block_info.timestamp - self.starting_timestamp()) % cfg.block_time != 0
646 {
647 warn!(target: "batch_span", "batch has misaligned timestamp, not overlapped exactly");
648 return (BatchValidity::Drop, None);
649 }
650 parent_num = l2_safe_head.block_info.number -
651 (l2_safe_head.block_info.timestamp - self.starting_timestamp()) / cfg.block_time -
652 1;
653 parent_block = match fetcher.l2_block_info_by_number(parent_num).await {
654 Ok(block) => block,
655 Err(e) => {
656 warn!(target: "batch_span", "failed to fetch L2 block number {parent_num}: {e}");
657 return (BatchValidity::Undecided, None);
659 }
660 };
661 }
662 if !self.check_parent_hash(parent_block.block_info.hash) {
663 warn!(
664 target: "batch_span",
665 "parent block mismatch, expected: {parent_num}, received: {}. parent hash: {}, parent hash check: {}",
666 parent_block.block_info.number, parent_block.block_info.hash, self.parent_check,
667 );
668 return (BatchValidity::Drop, None);
669 }
670
671 if starting_epoch_num + cfg.seq_window_size < inclusion_block.number {
673 warn!(target: "batch_span", "batch was included too late, sequence window expired");
674 return (BatchValidity::Drop, None);
675 }
676
677 if starting_epoch_num > parent_block.l1_origin.number + 1 {
679 warn!(
680 target: "batch_span",
681 "batch is for future epoch too far ahead, while it has the next timestamp, so it must be invalid. starting epoch: {} | next epoch: {}",
682 starting_epoch_num,
683 parent_block.l1_origin.number + 1
684 );
685 return (BatchValidity::Drop, None);
686 }
687
688 let end_epoch_num = self.batches.last().unwrap().epoch_num;
691 let mut origin_checked = false;
692 for l1_block in l1_origins {
694 if l1_block.number == end_epoch_num {
695 if !self.check_origin_hash(l1_block.hash) {
696 warn!(
697 target: "batch_span",
698 "batch is for different L1 chain, epoch hash does not match, expected: {}",
699 l1_block.hash
700 );
701 return (BatchValidity::Drop, None);
702 }
703 origin_checked = true;
704 break;
705 }
706 }
707 if !origin_checked {
708 info!(target: "batch_span", "need more l1 blocks to check entire origins of span batch");
709 return (BatchValidity::Undecided, None);
710 }
711
712 if starting_epoch_num < parent_block.l1_origin.number {
713 warn!(target: "batch_span", "dropped batch, epoch is too old, minimum: {:?}", parent_block.block_info.id());
714 return (BatchValidity::Drop, None);
715 }
716
717 (BatchValidity::Accept, Some(parent_block))
718 }
719}
720
721#[cfg(test)]
722mod tests {
723 use super::*;
724 use crate::test_utils::{CollectingLayer, TestBatchValidator, TraceStorage};
725 use alloc::vec;
726 use alloy_consensus::{Header, constants::EIP1559_TX_TYPE_ID};
727 use alloy_eips::BlockNumHash;
728 use alloy_primitives::{Bytes, b256};
729 use kona_genesis::{ChainGenesis, HardForkConfig};
730 use op_alloy_consensus::OpBlock;
731 use tracing::Level;
732 use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
733
734 #[test]
735 fn test_timestamp() {
736 let timestamp = 10;
737 let first_element = SpanBatchElement { timestamp, ..Default::default() };
738 let batch =
739 SpanBatch { batches: vec![first_element, Default::default()], ..Default::default() };
740 assert_eq!(batch.starting_timestamp(), timestamp);
741 }
742
743 #[test]
744 fn test_starting_timestamp() {
745 let timestamp = 10;
746 let first_element = SpanBatchElement { timestamp, ..Default::default() };
747 let batch =
748 SpanBatch { batches: vec![first_element, Default::default()], ..Default::default() };
749 assert_eq!(batch.starting_timestamp(), timestamp);
750 }
751
752 #[test]
753 fn test_final_timestamp() {
754 let timestamp = 10;
755 let last_element = SpanBatchElement { timestamp, ..Default::default() };
756 let batch =
757 SpanBatch { batches: vec![Default::default(), last_element], ..Default::default() };
758 assert_eq!(batch.final_timestamp(), timestamp);
759 }
760
761 #[test]
762 fn test_starting_epoch_num() {
763 let epoch_num = 10;
764 let first_element = SpanBatchElement { epoch_num, ..Default::default() };
765 let batch =
766 SpanBatch { batches: vec![first_element, Default::default()], ..Default::default() };
767 assert_eq!(batch.starting_epoch_num(), epoch_num);
768 }
769
770 #[test]
771 fn test_peek() {
772 let first_element = SpanBatchElement { epoch_num: 10, ..Default::default() };
773 let second_element = SpanBatchElement { epoch_num: 11, ..Default::default() };
774 let batch =
775 SpanBatch { batches: vec![first_element, second_element], ..Default::default() };
776 assert_eq!(batch.peek(0).epoch_num, 11);
777 assert_eq!(batch.peek(1).epoch_num, 10);
778 }
779
780 #[test]
781 fn test_append_empty_singular_batch() {
782 let mut batch = SpanBatch::default();
783 let singular_batch = SingleBatch {
784 epoch_num: 10,
785 epoch_hash: FixedBytes::from([17u8; 32]),
786 parent_hash: FixedBytes::from([17u8; 32]),
787 timestamp: 10,
788 transactions: vec![],
789 };
790 assert!(batch.append_singular_batch(singular_batch, 0).is_ok());
791 assert_eq!(batch.batches.len(), 1);
792 assert_eq!(batch.origin_bits.get_bit(0), Some(1));
793 assert_eq!(batch.block_tx_counts, vec![0]);
794 assert_eq!(batch.txs.tx_datas.len(), 0);
795
796 let singular_batch = SingleBatch {
798 epoch_num: 11,
799 epoch_hash: FixedBytes::from([17u8; 32]),
800 parent_hash: FixedBytes::from([17u8; 32]),
801 timestamp: 20,
802 transactions: vec![],
803 };
804 assert!(batch.append_singular_batch(singular_batch, 1).is_ok());
805 }
806
807 #[test]
808 fn test_check_origin_hash() {
809 let l1_origin_check = FixedBytes::from([17u8; 20]);
810 let hash = b256!("1111111111111111111111111111111111111111000000000000000000000000");
811 let batch = SpanBatch { l1_origin_check, ..Default::default() };
812 assert!(batch.check_origin_hash(hash));
813 let invalid = b256!("1111111111111111111111111111111111111100000000000000000000000000");
815 assert!(!batch.check_origin_hash(invalid));
816 }
817
818 #[test]
819 fn test_check_parent_hash() {
820 let parent_check = FixedBytes::from([17u8; 20]);
821 let hash = b256!("1111111111111111111111111111111111111111000000000000000000000000");
822 let batch = SpanBatch { parent_check, ..Default::default() };
823 assert!(batch.check_parent_hash(hash));
824 let invalid = b256!("1111111111111111111111111111111111111100000000000000000000000000");
826 assert!(!batch.check_parent_hash(invalid));
827 }
828
829 #[tokio::test]
830 async fn test_check_batch_missing_l1_block_input() {
831 let trace_store: TraceStorage = Default::default();
832 let layer = CollectingLayer::new(trace_store.clone());
833 tracing_subscriber::Registry::default().with(layer).init();
834
835 let cfg = RollupConfig::default();
836 let l1_blocks = vec![];
837 let l2_safe_head = L2BlockInfo::default();
838 let inclusion_block = BlockInfo::default();
839 let mut fetcher: TestBatchValidator = TestBatchValidator::default();
840 let batch = SpanBatch::default();
841 assert_eq!(
842 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
843 BatchValidity::Undecided
844 );
845 let logs = trace_store.get_by_level(Level::WARN);
846 assert_eq!(logs.len(), 1);
847 assert!(logs[0].contains("missing L1 block input, cannot proceed with batch checking"));
848 }
849
850 #[tokio::test]
851 async fn test_check_batches_is_empty() {
852 let trace_store: TraceStorage = Default::default();
853 let layer = CollectingLayer::new(trace_store.clone());
854 tracing_subscriber::Registry::default().with(layer).init();
855
856 let cfg = RollupConfig::default();
857 let l1_blocks = vec![BlockInfo::default()];
858 let l2_safe_head = L2BlockInfo::default();
859 let inclusion_block = BlockInfo::default();
860 let mut fetcher: TestBatchValidator = TestBatchValidator::default();
861 let batch = SpanBatch::default();
862 assert_eq!(
863 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
864 BatchValidity::Undecided
865 );
866 let logs = trace_store.get_by_level(Level::WARN);
867 assert_eq!(logs.len(), 1);
868 assert!(logs[0].contains("empty span batch, cannot proceed with batch checking"));
869 }
870
871 #[tokio::test]
872 async fn test_singular_batches_missing_l1_origin() {
873 let l1_block = BlockInfo { number: 10, timestamp: 20, ..Default::default() };
874 let l1_blocks = vec![l1_block];
875 let l2_safe_head = L2BlockInfo {
876 block_info: BlockInfo { timestamp: 10, ..Default::default() },
877 l1_origin: BlockNumHash { number: 10, ..Default::default() },
878 ..Default::default()
879 };
880 let first = SpanBatchElement { epoch_num: 9, timestamp: 20, ..Default::default() };
881 let second = SpanBatchElement { epoch_num: 11, timestamp: 30, ..Default::default() };
882 let batch = SpanBatch { batches: vec![first, second], ..Default::default() };
883 assert_eq!(
884 batch.get_singular_batches(&l1_blocks, l2_safe_head),
885 Err(SpanBatchError::MissingL1Origin),
886 );
887 }
888
889 #[tokio::test]
890 async fn test_eager_block_missing_origins() {
891 let trace_store: TraceStorage = Default::default();
892 let layer = CollectingLayer::new(trace_store.clone());
893 tracing_subscriber::Registry::default().with(layer).init();
894
895 let cfg = RollupConfig::default();
896 let block = BlockInfo { number: 9, ..Default::default() };
897 let l1_blocks = vec![block];
898 let l2_safe_head = L2BlockInfo::default();
899 let inclusion_block = BlockInfo::default();
900 let mut fetcher: TestBatchValidator = TestBatchValidator::default();
901 let first = SpanBatchElement { epoch_num: 10, ..Default::default() };
902 let batch = SpanBatch { batches: vec![first], ..Default::default() };
903 assert_eq!(
904 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
905 BatchValidity::Undecided
906 );
907 let logs = trace_store.get_by_level(Level::INFO);
908 assert_eq!(logs.len(), 1);
909 let str = alloc::format!(
910 "eager batch wants to advance current epoch {:?}, but could not without more L1 blocks",
911 block.id()
912 );
913 assert!(logs[0].contains(&str));
914 }
915
916 #[tokio::test]
917 async fn test_check_batch_delta_inactive() {
918 let trace_store: TraceStorage = Default::default();
919 let layer = CollectingLayer::new(trace_store.clone());
920 tracing_subscriber::Registry::default().with(layer).init();
921
922 let cfg = RollupConfig {
923 hardforks: HardForkConfig { delta_time: Some(10), ..Default::default() },
924 ..Default::default()
925 };
926 let block = BlockInfo { number: 10, timestamp: 9, ..Default::default() };
927 let l1_blocks = vec![block];
928 let l2_safe_head = L2BlockInfo::default();
929 let inclusion_block = BlockInfo::default();
930 let mut fetcher: TestBatchValidator = TestBatchValidator::default();
931 let first = SpanBatchElement { epoch_num: 10, timestamp: 10, ..Default::default() };
932 let batch = SpanBatch { batches: vec![first], ..Default::default() };
933 assert_eq!(
934 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
935 BatchValidity::Drop
936 );
937 let logs = trace_store.get_by_level(Level::WARN);
938 assert_eq!(logs.len(), 1);
939 let str = alloc::format!(
940 "received SpanBatch (id {:?}) with L1 origin (timestamp {}) before Delta hard fork",
941 block.id(),
942 block.timestamp
943 );
944 assert!(logs[0].contains(&str));
945 }
946
947 #[tokio::test]
948 async fn test_check_batch_out_of_order() {
949 let trace_store: TraceStorage = Default::default();
950 let layer = CollectingLayer::new(trace_store.clone());
951 tracing_subscriber::Registry::default().with(layer).init();
952
953 let cfg = RollupConfig {
954 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
955 block_time: 10,
956 ..Default::default()
957 };
958 let block = BlockInfo { number: 10, timestamp: 10, ..Default::default() };
959 let l1_blocks = vec![block];
960 let l2_safe_head = L2BlockInfo {
961 block_info: BlockInfo { timestamp: 10, ..Default::default() },
962 ..Default::default()
963 };
964 let inclusion_block = BlockInfo::default();
965 let mut fetcher: TestBatchValidator = TestBatchValidator::default();
966 let first = SpanBatchElement { epoch_num: 10, timestamp: 21, ..Default::default() };
967 let batch = SpanBatch { batches: vec![first], ..Default::default() };
968 assert_eq!(
969 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
970 BatchValidity::Future
971 );
972 let logs = trace_store.get_by_level(Level::WARN);
973 assert_eq!(logs.len(), 1);
974 assert!(logs[0].contains(
975 "received out-of-order batch for future processing after next batch (21 > 20)"
976 ));
977 }
978
979 #[tokio::test]
980 async fn test_check_batch_no_new_blocks() {
981 let trace_store: TraceStorage = Default::default();
982 let layer = CollectingLayer::new(trace_store.clone());
983 tracing_subscriber::Registry::default().with(layer).init();
984
985 let cfg = RollupConfig {
986 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
987 block_time: 10,
988 ..Default::default()
989 };
990 let block = BlockInfo { number: 10, timestamp: 10, ..Default::default() };
991 let l1_blocks = vec![block];
992 let l2_safe_head = L2BlockInfo {
993 block_info: BlockInfo { timestamp: 10, ..Default::default() },
994 ..Default::default()
995 };
996 let inclusion_block = BlockInfo::default();
997 let mut fetcher: TestBatchValidator = TestBatchValidator::default();
998 let first = SpanBatchElement { epoch_num: 10, timestamp: 10, ..Default::default() };
999 let batch = SpanBatch { batches: vec![first], ..Default::default() };
1000 assert_eq!(
1001 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
1002 BatchValidity::Drop
1003 );
1004 let logs = trace_store.get_by_level(Level::WARN);
1005 assert_eq!(logs.len(), 1);
1006 assert!(logs[0].contains("span batch has no new blocks after safe head"));
1007 }
1008
1009 #[tokio::test]
1010 async fn test_check_batch_overlapping_blocks_tx_count_mismatch() {
1011 let trace_store: TraceStorage = Default::default();
1012 let layer = CollectingLayer::new(trace_store.clone());
1013 tracing_subscriber::Registry::default().with(layer).init();
1014
1015 let cfg = RollupConfig {
1016 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
1017 block_time: 10,
1018 max_sequencer_drift: 1000,
1019 ..Default::default()
1020 };
1021 let l1_blocks = vec![
1022 BlockInfo { number: 9, timestamp: 0, ..Default::default() },
1023 BlockInfo { number: 10, timestamp: 10, ..Default::default() },
1024 BlockInfo { number: 11, timestamp: 20, ..Default::default() },
1025 ];
1026 let l2_safe_head = L2BlockInfo {
1027 block_info: BlockInfo { number: 10, timestamp: 20, ..Default::default() },
1028 l1_origin: BlockNumHash { number: 11, ..Default::default() },
1029 ..Default::default()
1030 };
1031 let inclusion_block = BlockInfo::default();
1032 let mut fetcher: TestBatchValidator = TestBatchValidator {
1033 op_blocks: vec![OpBlock {
1034 header: Header { number: 9, ..Default::default() },
1035 body: alloy_consensus::BlockBody {
1036 transactions: Vec::new(),
1037 ommers: Vec::new(),
1038 withdrawals: None,
1039 },
1040 }],
1041 blocks: vec![
1042 L2BlockInfo {
1043 block_info: BlockInfo { number: 8, timestamp: 0, ..Default::default() },
1044 l1_origin: BlockNumHash { number: 9, ..Default::default() },
1045 ..Default::default()
1046 },
1047 L2BlockInfo {
1048 block_info: BlockInfo { number: 9, timestamp: 10, ..Default::default() },
1049 l1_origin: BlockNumHash { number: 10, ..Default::default() },
1050 ..Default::default()
1051 },
1052 L2BlockInfo {
1053 block_info: BlockInfo { number: 10, timestamp: 20, ..Default::default() },
1054 l1_origin: BlockNumHash { number: 11, ..Default::default() },
1055 ..Default::default()
1056 },
1057 ],
1058 ..Default::default()
1059 };
1060 let first = SpanBatchElement {
1061 epoch_num: 10,
1062 timestamp: 10,
1063 transactions: vec![Bytes(vec![EIP1559_TX_TYPE_ID].into())],
1064 };
1065 let second = SpanBatchElement { epoch_num: 10, timestamp: 60, ..Default::default() };
1066 let batch = SpanBatch { batches: vec![first, second], ..Default::default() };
1067 assert_eq!(
1068 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
1069 BatchValidity::Drop
1070 );
1071 let logs = trace_store.get_by_level(Level::WARN);
1072 assert_eq!(logs.len(), 1);
1073 assert!(logs[0].contains(
1074 "overlapped block's tx count does not match, safe_block_txs: 0, batch_txs: 1"
1075 ));
1076 }
1077
1078 #[tokio::test]
1079 async fn test_check_batch_overlapping_blocks_tx_mismatch() {
1080 let trace_store: TraceStorage = Default::default();
1081 let layer = CollectingLayer::new(trace_store.clone());
1082 tracing_subscriber::Registry::default().with(layer).init();
1083
1084 let cfg = RollupConfig {
1085 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
1086 block_time: 10,
1087 max_sequencer_drift: 1000,
1088 ..Default::default()
1089 };
1090 let l1_blocks = vec![
1091 BlockInfo { number: 9, timestamp: 0, ..Default::default() },
1092 BlockInfo { number: 10, timestamp: 10, ..Default::default() },
1093 BlockInfo { number: 11, timestamp: 20, ..Default::default() },
1094 ];
1095 let l2_safe_head = L2BlockInfo {
1096 block_info: BlockInfo { number: 10, timestamp: 20, ..Default::default() },
1097 l1_origin: BlockNumHash { number: 11, ..Default::default() },
1098 ..Default::default()
1099 };
1100 let inclusion_block = BlockInfo::default();
1101 let mut fetcher: TestBatchValidator = TestBatchValidator {
1102 op_blocks: vec![OpBlock {
1103 header: Header { number: 9, ..Default::default() },
1104 body: alloy_consensus::BlockBody {
1105 transactions: vec![op_alloy_consensus::OpTxEnvelope::Eip1559(
1106 alloy_consensus::Signed::new_unchecked(
1107 alloy_consensus::TxEip1559 {
1108 chain_id: 0,
1109 nonce: 0,
1110 gas_limit: 2,
1111 max_fee_per_gas: 1,
1112 max_priority_fee_per_gas: 1,
1113 to: alloy_primitives::TxKind::Create,
1114 value: alloy_primitives::U256::from(3),
1115 ..Default::default()
1116 },
1117 alloy_primitives::Signature::test_signature(),
1118 alloy_primitives::B256::ZERO,
1119 ),
1120 )],
1121 ommers: Vec::new(),
1122 withdrawals: None,
1123 },
1124 }],
1125 blocks: vec![
1126 L2BlockInfo {
1127 block_info: BlockInfo { number: 8, timestamp: 0, ..Default::default() },
1128 l1_origin: BlockNumHash { number: 9, ..Default::default() },
1129 ..Default::default()
1130 },
1131 L2BlockInfo {
1132 block_info: BlockInfo { number: 9, timestamp: 10, ..Default::default() },
1133 l1_origin: BlockNumHash { number: 10, ..Default::default() },
1134 ..Default::default()
1135 },
1136 L2BlockInfo {
1137 block_info: BlockInfo { number: 10, timestamp: 20, ..Default::default() },
1138 l1_origin: BlockNumHash { number: 11, ..Default::default() },
1139 ..Default::default()
1140 },
1141 ],
1142 ..Default::default()
1143 };
1144 let first = SpanBatchElement {
1145 epoch_num: 10,
1146 timestamp: 10,
1147 transactions: vec![Bytes(vec![EIP1559_TX_TYPE_ID].into())],
1148 };
1149 let second = SpanBatchElement { epoch_num: 10, timestamp: 60, ..Default::default() };
1150 let batch = SpanBatch { batches: vec![first, second], ..Default::default() };
1151 assert_eq!(
1152 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
1153 BatchValidity::Drop
1154 );
1155 let logs = trace_store.get_by_level(Level::WARN);
1156 assert_eq!(logs.len(), 1);
1157 assert!(logs[0].contains("overlapped block's transaction does not match"));
1158 }
1159
1160 #[tokio::test]
1161 async fn test_check_batch_block_timestamp_lt_l1_origin() {
1162 let trace_store: TraceStorage = Default::default();
1163 let layer = CollectingLayer::new(trace_store.clone());
1164 tracing_subscriber::Registry::default().with(layer).init();
1165
1166 let cfg = RollupConfig {
1167 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
1168 block_time: 10,
1169 ..Default::default()
1170 };
1171 let l1_block = BlockInfo { number: 10, timestamp: 20, ..Default::default() };
1172 let l1_blocks = vec![l1_block];
1173 let l2_safe_head = L2BlockInfo {
1174 block_info: BlockInfo { timestamp: 10, ..Default::default() },
1175 l1_origin: BlockNumHash { number: 10, ..Default::default() },
1176 ..Default::default()
1177 };
1178 let inclusion_block = BlockInfo::default();
1179 let mut fetcher: TestBatchValidator = TestBatchValidator::default();
1180 let first = SpanBatchElement { epoch_num: 10, timestamp: 20, ..Default::default() };
1181 let second = SpanBatchElement { epoch_num: 10, timestamp: 19, ..Default::default() };
1182 let third = SpanBatchElement { epoch_num: 10, timestamp: 30, ..Default::default() };
1183 let batch = SpanBatch { batches: vec![first, second, third], ..Default::default() };
1184 assert_eq!(
1185 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
1186 BatchValidity::Drop
1187 );
1188 let logs = trace_store.get_by_level(Level::WARN);
1189 assert_eq!(logs.len(), 1);
1190 let str = alloc::format!(
1191 "block timestamp is less than L1 origin timestamp, l2_timestamp: 19, l1_timestamp: 20, origin: {:?}",
1192 l1_block.id(),
1193 );
1194 assert!(logs[0].contains(&str));
1195 }
1196
1197 #[tokio::test]
1198 async fn test_check_batch_misaligned_timestamp() {
1199 let trace_store: TraceStorage = Default::default();
1200 let layer = CollectingLayer::new(trace_store.clone());
1201 tracing_subscriber::Registry::default().with(layer).init();
1202
1203 let cfg = RollupConfig {
1204 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
1205 block_time: 10,
1206 ..Default::default()
1207 };
1208 let block = BlockInfo { number: 10, timestamp: 10, ..Default::default() };
1209 let l1_blocks = vec![block];
1210 let l2_safe_head = L2BlockInfo {
1211 block_info: BlockInfo { timestamp: 10, ..Default::default() },
1212 ..Default::default()
1213 };
1214 let inclusion_block = BlockInfo::default();
1215 let mut fetcher: TestBatchValidator = TestBatchValidator::default();
1216 let first = SpanBatchElement { epoch_num: 10, timestamp: 11, ..Default::default() };
1217 let second = SpanBatchElement { epoch_num: 11, timestamp: 21, ..Default::default() };
1218 let batch = SpanBatch { batches: vec![first, second], ..Default::default() };
1219 assert_eq!(
1220 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
1221 BatchValidity::Drop
1222 );
1223 let logs = trace_store.get_by_level(Level::WARN);
1224 assert_eq!(logs.len(), 1);
1225 assert!(logs[0].contains("batch has misaligned timestamp, block time is too short"));
1226 }
1227
1228 #[tokio::test]
1229 async fn test_check_batch_misaligned_without_overlap() {
1230 let trace_store: TraceStorage = Default::default();
1231 let layer = CollectingLayer::new(trace_store.clone());
1232 tracing_subscriber::Registry::default().with(layer).init();
1233
1234 let cfg = RollupConfig {
1235 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
1236 block_time: 10,
1237 ..Default::default()
1238 };
1239 let block = BlockInfo { number: 10, timestamp: 10, ..Default::default() };
1240 let l1_blocks = vec![block];
1241 let l2_safe_head = L2BlockInfo {
1242 block_info: BlockInfo { timestamp: 10, ..Default::default() },
1243 ..Default::default()
1244 };
1245 let inclusion_block = BlockInfo::default();
1246 let mut fetcher: TestBatchValidator = TestBatchValidator::default();
1247 let first = SpanBatchElement { epoch_num: 10, timestamp: 8, ..Default::default() };
1248 let second = SpanBatchElement { epoch_num: 11, timestamp: 20, ..Default::default() };
1249 let batch = SpanBatch { batches: vec![first, second], ..Default::default() };
1250 assert_eq!(
1251 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
1252 BatchValidity::Drop
1253 );
1254 let logs = trace_store.get_by_level(Level::WARN);
1255 assert_eq!(logs.len(), 1);
1256 assert!(logs[0].contains("batch has misaligned timestamp, not overlapped exactly"));
1257 }
1258
1259 #[tokio::test]
1260 async fn test_check_batch_failed_to_fetch_l2_block() {
1261 let trace_store: TraceStorage = Default::default();
1262 let layer = CollectingLayer::new(trace_store.clone());
1263 tracing_subscriber::Registry::default().with(layer).init();
1264
1265 let cfg = RollupConfig {
1266 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
1267 block_time: 10,
1268 ..Default::default()
1269 };
1270 let block = BlockInfo { number: 10, timestamp: 10, ..Default::default() };
1271 let l1_blocks = vec![block];
1272 let l2_safe_head = L2BlockInfo {
1273 block_info: BlockInfo { number: 41, timestamp: 10, ..Default::default() },
1274 ..Default::default()
1275 };
1276 let inclusion_block = BlockInfo::default();
1277 let mut fetcher: TestBatchValidator = TestBatchValidator::default();
1278 let first = SpanBatchElement { epoch_num: 10, timestamp: 10, ..Default::default() };
1279 let second = SpanBatchElement { epoch_num: 11, timestamp: 20, ..Default::default() };
1280 let batch = SpanBatch { batches: vec![first, second], ..Default::default() };
1281 assert_eq!(
1283 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
1284 BatchValidity::Undecided
1285 );
1286 let logs = trace_store.get_by_level(Level::WARN);
1287 assert_eq!(logs.len(), 1);
1288 assert!(logs[0].contains("failed to fetch L2 block number 40: Block not found"));
1289 }
1290
1291 #[tokio::test]
1292 async fn test_check_batch_parent_hash_fail() {
1293 let trace_store: TraceStorage = Default::default();
1294 let layer = CollectingLayer::new(trace_store.clone());
1295 tracing_subscriber::Registry::default().with(layer).init();
1296
1297 let cfg = RollupConfig {
1298 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
1299 block_time: 10,
1300 ..Default::default()
1301 };
1302 let block = BlockInfo { number: 10, timestamp: 10, ..Default::default() };
1303 let l1_blocks = vec![block];
1304 let l2_safe_head = L2BlockInfo {
1305 block_info: BlockInfo { number: 41, timestamp: 10, ..Default::default() },
1306 ..Default::default()
1307 };
1308 let inclusion_block = BlockInfo::default();
1309 let l2_block = L2BlockInfo {
1310 block_info: BlockInfo { number: 41, timestamp: 10, ..Default::default() },
1311 l1_origin: BlockNumHash { number: 9, ..Default::default() },
1312 ..Default::default()
1313 };
1314 let mut fetcher: TestBatchValidator =
1315 TestBatchValidator { blocks: vec![l2_block], ..Default::default() };
1316 fetcher.short_circuit = true;
1317 let first = SpanBatchElement { epoch_num: 10, timestamp: 10, ..Default::default() };
1318 let second = SpanBatchElement { epoch_num: 11, timestamp: 20, ..Default::default() };
1319 let batch = SpanBatch {
1320 batches: vec![first, second],
1321 parent_check: FixedBytes::<20>::from_slice(
1322 &b256!("1111111111111111111111111111111111111111000000000000000000000000")[..20],
1323 ),
1324 ..Default::default()
1325 };
1326 assert_eq!(
1328 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
1329 BatchValidity::Drop
1330 );
1331 let logs = trace_store.get_by_level(Level::WARN);
1332 assert_eq!(logs.len(), 1);
1333 assert!(logs[0].contains("parent block mismatch, expected: 40, received: 41"));
1334 }
1335
1336 #[tokio::test]
1337 async fn test_check_sequence_window_expired() {
1338 let trace_store: TraceStorage = Default::default();
1339 let layer = CollectingLayer::new(trace_store.clone());
1340 tracing_subscriber::Registry::default().with(layer).init();
1341
1342 let cfg = RollupConfig {
1343 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
1344 block_time: 10,
1345 ..Default::default()
1346 };
1347 let block = BlockInfo { number: 10, timestamp: 10, ..Default::default() };
1348 let l1_blocks = vec![block];
1349 let parent_hash = b256!("1111111111111111111111111111111111111111000000000000000000000000");
1350 let l2_safe_head = L2BlockInfo {
1351 block_info: BlockInfo { number: 41, timestamp: 10, parent_hash, ..Default::default() },
1352 ..Default::default()
1353 };
1354 let inclusion_block = BlockInfo { number: 50, ..Default::default() };
1355 let l2_block = L2BlockInfo {
1356 block_info: BlockInfo {
1357 number: 40,
1358 hash: parent_hash,
1359 timestamp: 10,
1360 ..Default::default()
1361 },
1362 ..Default::default()
1363 };
1364 let mut fetcher: TestBatchValidator =
1365 TestBatchValidator { blocks: vec![l2_block], ..Default::default() };
1366 let first = SpanBatchElement { epoch_num: 10, timestamp: 10, ..Default::default() };
1367 let second = SpanBatchElement { epoch_num: 11, timestamp: 20, ..Default::default() };
1368 let batch = SpanBatch {
1369 batches: vec![first, second],
1370 parent_check: FixedBytes::<20>::from_slice(&parent_hash[..20]),
1371 ..Default::default()
1372 };
1373 assert_eq!(
1375 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
1376 BatchValidity::Drop
1377 );
1378 let logs = trace_store.get_by_level(Level::WARN);
1379 assert_eq!(logs.len(), 1);
1380 assert!(logs[0].contains("batch was included too late, sequence window expired"));
1381 }
1382
1383 #[tokio::test]
1384 async fn test_starting_epoch_too_far_ahead() {
1385 let trace_store: TraceStorage = Default::default();
1386 let layer = CollectingLayer::new(trace_store.clone());
1387 tracing_subscriber::Registry::default().with(layer).init();
1388
1389 let cfg = RollupConfig {
1390 seq_window_size: 100,
1391 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
1392 block_time: 10,
1393 ..Default::default()
1394 };
1395 let block = BlockInfo { number: 10, timestamp: 10, ..Default::default() };
1396 let l1_blocks = vec![block];
1397 let parent_hash = b256!("1111111111111111111111111111111111111111000000000000000000000000");
1398 let l2_safe_head = L2BlockInfo {
1399 block_info: BlockInfo { number: 41, timestamp: 10, parent_hash, ..Default::default() },
1400 l1_origin: BlockNumHash { number: 8, ..Default::default() },
1401 ..Default::default()
1402 };
1403 let inclusion_block = BlockInfo { number: 50, ..Default::default() };
1404 let l2_block = L2BlockInfo {
1405 block_info: BlockInfo {
1406 number: 40,
1407 hash: parent_hash,
1408 timestamp: 10,
1409 ..Default::default()
1410 },
1411 l1_origin: BlockNumHash { number: 8, ..Default::default() },
1412 ..Default::default()
1413 };
1414 let mut fetcher: TestBatchValidator =
1415 TestBatchValidator { blocks: vec![l2_block], ..Default::default() };
1416 let first = SpanBatchElement { epoch_num: 10, timestamp: 10, ..Default::default() };
1417 let second = SpanBatchElement { epoch_num: 11, timestamp: 20, ..Default::default() };
1418 let batch = SpanBatch {
1419 batches: vec![first, second],
1420 parent_check: FixedBytes::<20>::from_slice(&parent_hash[..20]),
1421 ..Default::default()
1422 };
1423 assert_eq!(
1425 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
1426 BatchValidity::Drop
1427 );
1428 let logs = trace_store.get_by_level(Level::WARN);
1429 assert_eq!(logs.len(), 1);
1430 let str = "batch is for future epoch too far ahead, while it has the next timestamp, so it must be invalid. starting epoch: 10 | next epoch: 9";
1431 assert!(logs[0].contains(str));
1432 }
1433
1434 #[tokio::test]
1435 async fn test_check_batch_epoch_hash_mismatch() {
1436 let trace_store: TraceStorage = Default::default();
1437 let layer = CollectingLayer::new(trace_store.clone());
1438 tracing_subscriber::Registry::default().with(layer).init();
1439
1440 let cfg = RollupConfig {
1441 seq_window_size: 100,
1442 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
1443 block_time: 10,
1444 ..Default::default()
1445 };
1446 let l1_block_hash =
1447 b256!("3333333333333333333333333333333333333333000000000000000000000000");
1448 let block =
1449 BlockInfo { number: 11, timestamp: 10, hash: l1_block_hash, ..Default::default() };
1450 let l1_blocks = vec![block];
1451 let parent_hash = b256!("1111111111111111111111111111111111111111000000000000000000000000");
1452 let l2_safe_head = L2BlockInfo {
1453 block_info: BlockInfo {
1454 number: 41,
1455 timestamp: 10,
1456 hash: parent_hash,
1457 ..Default::default()
1458 },
1459 l1_origin: BlockNumHash { number: 9, ..Default::default() },
1460 ..Default::default()
1461 };
1462 let inclusion_block = BlockInfo { number: 50, ..Default::default() };
1463 let l2_block = L2BlockInfo {
1464 block_info: BlockInfo {
1465 number: 40,
1466 timestamp: 10,
1467 hash: parent_hash,
1468 ..Default::default()
1469 },
1470 l1_origin: BlockNumHash { number: 9, ..Default::default() },
1471 ..Default::default()
1472 };
1473 let mut fetcher: TestBatchValidator =
1474 TestBatchValidator { blocks: vec![l2_block], ..Default::default() };
1475 let first = SpanBatchElement { epoch_num: 10, timestamp: 10, ..Default::default() };
1476 let second = SpanBatchElement { epoch_num: 11, timestamp: 20, ..Default::default() };
1477 let batch = SpanBatch {
1478 batches: vec![first, second],
1479 parent_check: FixedBytes::<20>::from_slice(&parent_hash[..20]),
1480 ..Default::default()
1481 };
1482 assert_eq!(
1483 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
1484 BatchValidity::Drop
1485 );
1486 let logs = trace_store.get_by_level(Level::WARN);
1487 assert_eq!(logs.len(), 1);
1488 let str = alloc::format!(
1489 "batch is for different L1 chain, epoch hash does not match, expected: {}",
1490 l1_block_hash,
1491 );
1492 assert!(logs[0].contains(&str));
1493 }
1494
1495 #[tokio::test]
1496 async fn test_need_more_l1_blocks() {
1497 let trace_store: TraceStorage = Default::default();
1498 let layer = CollectingLayer::new(trace_store.clone());
1499 tracing_subscriber::Registry::default().with(layer).init();
1500
1501 let cfg = RollupConfig {
1502 seq_window_size: 100,
1503 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
1504 block_time: 10,
1505 ..Default::default()
1506 };
1507 let l1_block_hash =
1508 b256!("3333333333333333333333333333333333333333000000000000000000000000");
1509 let block =
1510 BlockInfo { number: 10, timestamp: 10, hash: l1_block_hash, ..Default::default() };
1511 let l1_blocks = vec![block];
1512 let parent_hash = b256!("1111111111111111111111111111111111111111000000000000000000000000");
1513 let l2_safe_head = L2BlockInfo {
1514 block_info: BlockInfo { number: 41, timestamp: 10, parent_hash, ..Default::default() },
1515 l1_origin: BlockNumHash { number: 9, ..Default::default() },
1516 ..Default::default()
1517 };
1518 let inclusion_block = BlockInfo { number: 50, ..Default::default() };
1519 let l2_block = L2BlockInfo {
1520 block_info: BlockInfo {
1521 number: 40,
1522 timestamp: 10,
1523 hash: parent_hash,
1524 ..Default::default()
1525 },
1526 l1_origin: BlockNumHash { number: 9, ..Default::default() },
1527 ..Default::default()
1528 };
1529 let mut fetcher: TestBatchValidator =
1530 TestBatchValidator { blocks: vec![l2_block], ..Default::default() };
1531 let first = SpanBatchElement { epoch_num: 10, timestamp: 10, ..Default::default() };
1532 let second = SpanBatchElement { epoch_num: 11, timestamp: 20, ..Default::default() };
1533 let batch = SpanBatch {
1534 batches: vec![first, second],
1535 parent_check: FixedBytes::<20>::from_slice(&parent_hash[..20]),
1536 l1_origin_check: FixedBytes::<20>::from_slice(&l1_block_hash[..20]),
1537 ..Default::default()
1538 };
1539 assert_eq!(
1540 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
1541 BatchValidity::Undecided
1542 );
1543 let logs = trace_store.get_by_level(Level::INFO);
1544 assert_eq!(logs.len(), 1);
1545 assert!(logs[0].contains("need more l1 blocks to check entire origins of span batch"));
1546 }
1547
1548 #[tokio::test]
1549 async fn test_drop_batch_epoch_too_old() {
1550 let trace_store: TraceStorage = Default::default();
1551 let layer = CollectingLayer::new(trace_store.clone());
1552 tracing_subscriber::Registry::default().with(layer).init();
1553
1554 let cfg = RollupConfig {
1555 seq_window_size: 100,
1556 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
1557 block_time: 10,
1558 ..Default::default()
1559 };
1560 let l1_block_hash =
1561 b256!("3333333333333333333333333333333333333333000000000000000000000000");
1562 let block =
1563 BlockInfo { number: 11, timestamp: 10, hash: l1_block_hash, ..Default::default() };
1564 let l1_blocks = vec![block];
1565 let parent_hash = b256!("1111111111111111111111111111111111111111000000000000000000000000");
1566 let l2_safe_head = L2BlockInfo {
1567 block_info: BlockInfo { number: 41, timestamp: 10, parent_hash, ..Default::default() },
1568 l1_origin: BlockNumHash { number: 13, ..Default::default() },
1569 ..Default::default()
1570 };
1571 let inclusion_block = BlockInfo { number: 50, ..Default::default() };
1572 let l2_block = L2BlockInfo {
1573 block_info: BlockInfo {
1574 number: 40,
1575 timestamp: 10,
1576 hash: parent_hash,
1577 ..Default::default()
1578 },
1579 l1_origin: BlockNumHash { number: 14, ..Default::default() },
1580 ..Default::default()
1581 };
1582 let mut fetcher: TestBatchValidator =
1583 TestBatchValidator { blocks: vec![l2_block], ..Default::default() };
1584 let first = SpanBatchElement { epoch_num: 10, timestamp: 10, ..Default::default() };
1585 let second = SpanBatchElement { epoch_num: 11, timestamp: 20, ..Default::default() };
1586 let batch = SpanBatch {
1587 batches: vec![first, second],
1588 parent_check: FixedBytes::<20>::from_slice(&parent_hash[..20]),
1589 l1_origin_check: FixedBytes::<20>::from_slice(&l1_block_hash[..20]),
1590 ..Default::default()
1591 };
1592 assert_eq!(
1593 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
1594 BatchValidity::Drop
1595 );
1596 let logs = trace_store.get_by_level(Level::WARN);
1597 assert_eq!(logs.len(), 1);
1598 let str = alloc::format!(
1599 "dropped batch, epoch is too old, minimum: {:?}",
1600 l2_block.block_info.id(),
1601 );
1602 assert!(logs[0].contains(&str));
1603 }
1604
1605 #[tokio::test]
1606 async fn test_check_batch_exceeds_max_seq_drif() {
1607 let trace_store: TraceStorage = Default::default();
1608 let layer = CollectingLayer::new(trace_store.clone());
1609 tracing_subscriber::Registry::default().with(layer).init();
1610
1611 let cfg = RollupConfig {
1612 seq_window_size: 100,
1613 max_sequencer_drift: 0,
1614 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
1615 block_time: 10,
1616 ..Default::default()
1617 };
1618 let l1_block_hash =
1619 b256!("3333333333333333333333333333333333333333000000000000000000000000");
1620 let block =
1621 BlockInfo { number: 11, timestamp: 10, hash: l1_block_hash, ..Default::default() };
1622 let second_block =
1623 BlockInfo { number: 12, timestamp: 10, hash: l1_block_hash, ..Default::default() };
1624 let l1_blocks = vec![block, second_block];
1625 let parent_hash = b256!("1111111111111111111111111111111111111111000000000000000000000000");
1626 let l2_safe_head = L2BlockInfo {
1627 block_info: BlockInfo {
1628 number: 41,
1629 timestamp: 10,
1630 hash: parent_hash,
1631 ..Default::default()
1632 },
1633 l1_origin: BlockNumHash { number: 9, ..Default::default() },
1634 ..Default::default()
1635 };
1636 let inclusion_block = BlockInfo { number: 50, ..Default::default() };
1637 let l2_block = L2BlockInfo {
1638 block_info: BlockInfo { number: 40, ..Default::default() },
1639 ..Default::default()
1640 };
1641 let mut fetcher: TestBatchValidator =
1642 TestBatchValidator { blocks: vec![l2_block], ..Default::default() };
1643 let first = SpanBatchElement { epoch_num: 10, timestamp: 20, ..Default::default() };
1644 let second = SpanBatchElement { epoch_num: 10, timestamp: 20, ..Default::default() };
1645 let third = SpanBatchElement { epoch_num: 11, timestamp: 20, ..Default::default() };
1646 let batch = SpanBatch {
1647 batches: vec![first, second, third],
1648 parent_check: FixedBytes::<20>::from_slice(&parent_hash[..20]),
1649 l1_origin_check: FixedBytes::<20>::from_slice(&l1_block_hash[..20]),
1650 ..Default::default()
1651 };
1652 assert_eq!(
1653 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
1654 BatchValidity::Drop
1655 );
1656 let logs = trace_store.get_by_level(Level::INFO);
1657 assert_eq!(logs.len(), 1);
1658 assert!(logs[0].contains("batch exceeded sequencer time drift without adopting next origin, and next L1 origin would have been valid"));
1659 }
1660
1661 #[tokio::test]
1662 async fn test_continuing_with_empty_batch() {
1663 let trace_store: TraceStorage = Default::default();
1664 let layer = CollectingLayer::new(trace_store.clone());
1665 tracing_subscriber::Registry::default().with(layer).init();
1666
1667 let cfg = RollupConfig {
1668 seq_window_size: 100,
1669 max_sequencer_drift: 0,
1670 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
1671 block_time: 10,
1672 ..Default::default()
1673 };
1674 let l1_block_hash =
1675 b256!("3333333333333333333333333333333333333333000000000000000000000000");
1676 let block =
1677 BlockInfo { number: 11, timestamp: 10, hash: l1_block_hash, ..Default::default() };
1678 let second_block =
1679 BlockInfo { number: 12, timestamp: 21, hash: l1_block_hash, ..Default::default() };
1680 let l1_blocks = vec![block, second_block];
1681 let parent_hash = b256!("1111111111111111111111111111111111111111000000000000000000000000");
1682 let l2_safe_head = L2BlockInfo {
1683 block_info: BlockInfo {
1684 number: 41,
1685 timestamp: 10,
1686 hash: parent_hash,
1687 ..Default::default()
1688 },
1689 l1_origin: BlockNumHash { number: 9, ..Default::default() },
1690 ..Default::default()
1691 };
1692 let inclusion_block = BlockInfo { number: 50, ..Default::default() };
1693 let l2_block = L2BlockInfo {
1694 block_info: BlockInfo { number: 40, ..Default::default() },
1695 ..Default::default()
1696 };
1697 let mut fetcher: TestBatchValidator =
1698 TestBatchValidator { blocks: vec![l2_block], ..Default::default() };
1699 let first = SpanBatchElement { epoch_num: 10, timestamp: 20, transactions: vec![] };
1700 let second = SpanBatchElement { epoch_num: 10, timestamp: 20, transactions: vec![] };
1701 let third = SpanBatchElement { epoch_num: 11, timestamp: 20, transactions: vec![] };
1702 let batch = SpanBatch {
1703 batches: vec![first, second, third],
1704 parent_check: FixedBytes::<20>::from_slice(&parent_hash[..20]),
1705 l1_origin_check: FixedBytes::<20>::from_slice(&l1_block_hash[..20]),
1706 txs: SpanBatchTransactions::default(),
1707 ..Default::default()
1708 };
1709 assert_eq!(
1710 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
1711 BatchValidity::Accept
1712 );
1713 let infos = trace_store.get_by_level(Level::INFO);
1714 assert_eq!(infos.len(), 1);
1715 assert!(infos[0].contains(
1716 "continuing with empty batch before late L1 block to preserve L2 time invariant"
1717 ));
1718 }
1719
1720 #[tokio::test]
1721 async fn test_check_batch_exceeds_sequencer_time_drift() {
1722 let trace_store: TraceStorage = Default::default();
1723 let layer = CollectingLayer::new(trace_store.clone());
1724 tracing_subscriber::Registry::default().with(layer).init();
1725
1726 let cfg = RollupConfig {
1727 seq_window_size: 100,
1728 max_sequencer_drift: 0,
1729 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
1730 block_time: 10,
1731 ..Default::default()
1732 };
1733 let l1_block_hash =
1734 b256!("3333333333333333333333333333333333333333000000000000000000000000");
1735 let block =
1736 BlockInfo { number: 11, timestamp: 10, hash: l1_block_hash, ..Default::default() };
1737 let second_block =
1738 BlockInfo { number: 12, timestamp: 10, hash: l1_block_hash, ..Default::default() };
1739 let l1_blocks = vec![block, second_block];
1740 let parent_hash = b256!("1111111111111111111111111111111111111111000000000000000000000000");
1741 let l2_safe_head = L2BlockInfo {
1742 block_info: BlockInfo {
1743 number: 41,
1744 timestamp: 10,
1745 hash: parent_hash,
1746 ..Default::default()
1747 },
1748 l1_origin: BlockNumHash { number: 9, ..Default::default() },
1749 ..Default::default()
1750 };
1751 let inclusion_block = BlockInfo { number: 50, ..Default::default() };
1752 let l2_block = L2BlockInfo {
1753 block_info: BlockInfo { number: 40, ..Default::default() },
1754 ..Default::default()
1755 };
1756 let mut fetcher: TestBatchValidator =
1757 TestBatchValidator { blocks: vec![l2_block], ..Default::default() };
1758 let first = SpanBatchElement {
1759 epoch_num: 10,
1760 timestamp: 20,
1761 transactions: vec![Default::default()],
1762 };
1763 let second = SpanBatchElement {
1764 epoch_num: 10,
1765 timestamp: 20,
1766 transactions: vec![Default::default()],
1767 };
1768 let third = SpanBatchElement {
1769 epoch_num: 11,
1770 timestamp: 20,
1771 transactions: vec![Default::default()],
1772 };
1773 let batch = SpanBatch {
1774 batches: vec![first, second, third],
1775 parent_check: FixedBytes::<20>::from_slice(&parent_hash[..20]),
1776 l1_origin_check: FixedBytes::<20>::from_slice(&l1_block_hash[..20]),
1777 txs: SpanBatchTransactions::default(),
1778 ..Default::default()
1779 };
1780 assert_eq!(
1781 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
1782 BatchValidity::Drop
1783 );
1784 let logs = trace_store.get_by_level(Level::WARN);
1785 assert_eq!(logs.len(), 1);
1786 assert!(logs[0].contains("batch exceeded sequencer time drift, sequencer must adopt new L1 origin to include transactions again, max_time: 10"));
1787 }
1788
1789 #[tokio::test]
1790 async fn test_check_batch_empty_txs() {
1791 let trace_store: TraceStorage = Default::default();
1792 let layer = CollectingLayer::new(trace_store.clone());
1793 tracing_subscriber::Registry::default().with(layer).init();
1794
1795 let cfg = RollupConfig {
1796 seq_window_size: 100,
1797 max_sequencer_drift: 100,
1798 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
1799 block_time: 10,
1800 ..Default::default()
1801 };
1802 let l1_block_hash =
1803 b256!("3333333333333333333333333333333333333333000000000000000000000000");
1804 let block =
1805 BlockInfo { number: 11, timestamp: 10, hash: l1_block_hash, ..Default::default() };
1806 let second_block =
1807 BlockInfo { number: 12, timestamp: 21, hash: l1_block_hash, ..Default::default() };
1808 let l1_blocks = vec![block, second_block];
1809 let parent_hash = b256!("1111111111111111111111111111111111111111000000000000000000000000");
1810 let l2_safe_head = L2BlockInfo {
1811 block_info: BlockInfo {
1812 number: 41,
1813 timestamp: 10,
1814 hash: parent_hash,
1815 ..Default::default()
1816 },
1817 l1_origin: BlockNumHash { number: 9, ..Default::default() },
1818 ..Default::default()
1819 };
1820 let inclusion_block = BlockInfo { number: 50, ..Default::default() };
1821 let l2_block = L2BlockInfo {
1822 block_info: BlockInfo { number: 40, ..Default::default() },
1823 ..Default::default()
1824 };
1825 let mut fetcher: TestBatchValidator =
1826 TestBatchValidator { blocks: vec![l2_block], ..Default::default() };
1827 let first = SpanBatchElement {
1828 epoch_num: 10,
1829 timestamp: 20,
1830 transactions: vec![Default::default()],
1831 };
1832 let second = SpanBatchElement {
1833 epoch_num: 10,
1834 timestamp: 20,
1835 transactions: vec![Default::default()],
1836 };
1837 let third = SpanBatchElement { epoch_num: 11, timestamp: 20, transactions: vec![] };
1838 let batch = SpanBatch {
1839 batches: vec![first, second, third],
1840 parent_check: FixedBytes::<20>::from_slice(&parent_hash[..20]),
1841 l1_origin_check: FixedBytes::<20>::from_slice(&l1_block_hash[..20]),
1842 txs: SpanBatchTransactions::default(),
1843 ..Default::default()
1844 };
1845 assert_eq!(
1846 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
1847 BatchValidity::Drop
1848 );
1849 let logs = trace_store.get_by_level(Level::WARN);
1850 assert_eq!(logs.len(), 1);
1851 assert!(logs[0].contains("transaction data must not be empty, but found empty tx"));
1852 }
1853
1854 #[tokio::test]
1855 async fn test_check_batch_with_deposit_tx() {
1856 let trace_store: TraceStorage = Default::default();
1857 let layer = CollectingLayer::new(trace_store.clone());
1858 tracing_subscriber::Registry::default().with(layer).init();
1859
1860 let cfg = RollupConfig {
1861 seq_window_size: 100,
1862 max_sequencer_drift: 100,
1863 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
1864 block_time: 10,
1865 ..Default::default()
1866 };
1867 let l1_block_hash =
1868 b256!("3333333333333333333333333333333333333333000000000000000000000000");
1869 let block =
1870 BlockInfo { number: 11, timestamp: 10, hash: l1_block_hash, ..Default::default() };
1871 let second_block =
1872 BlockInfo { number: 12, timestamp: 21, hash: l1_block_hash, ..Default::default() };
1873 let l1_blocks = vec![block, second_block];
1874 let parent_hash = b256!("1111111111111111111111111111111111111111000000000000000000000000");
1875 let l2_safe_head = L2BlockInfo {
1876 block_info: BlockInfo {
1877 number: 41,
1878 timestamp: 10,
1879 hash: parent_hash,
1880 ..Default::default()
1881 },
1882 l1_origin: BlockNumHash { number: 9, ..Default::default() },
1883 ..Default::default()
1884 };
1885 let inclusion_block = BlockInfo { number: 50, ..Default::default() };
1886 let l2_block = L2BlockInfo {
1887 block_info: BlockInfo { number: 40, ..Default::default() },
1888 ..Default::default()
1889 };
1890 let mut fetcher: TestBatchValidator =
1891 TestBatchValidator { blocks: vec![l2_block], ..Default::default() };
1892 let filler_bytes = Bytes::copy_from_slice(&[EIP1559_TX_TYPE_ID]);
1893 let first = SpanBatchElement {
1894 epoch_num: 10,
1895 timestamp: 20,
1896 transactions: vec![filler_bytes.clone()],
1897 };
1898 let second = SpanBatchElement {
1899 epoch_num: 10,
1900 timestamp: 20,
1901 transactions: vec![Bytes::copy_from_slice(&[OpTxType::Deposit as u8])],
1902 };
1903 let third =
1904 SpanBatchElement { epoch_num: 11, timestamp: 20, transactions: vec![filler_bytes] };
1905 let batch = SpanBatch {
1906 batches: vec![first, second, third],
1907 parent_check: FixedBytes::<20>::from_slice(&parent_hash[..20]),
1908 l1_origin_check: FixedBytes::<20>::from_slice(&l1_block_hash[..20]),
1909 txs: SpanBatchTransactions::default(),
1910 ..Default::default()
1911 };
1912 assert_eq!(
1913 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
1914 BatchValidity::Drop
1915 );
1916 let logs = trace_store.get_by_level(Level::WARN);
1917 assert_eq!(logs.len(), 1);
1918 assert!(logs[0].contains("sequencers may not embed any deposits into batch data, but found tx that has one, tx_index: 0"));
1919 }
1920
1921 #[tokio::test]
1922 async fn test_check_batch_with_eip7702_tx() {
1923 let trace_store: TraceStorage = Default::default();
1924 let layer = CollectingLayer::new(trace_store.clone());
1925 tracing_subscriber::Registry::default().with(layer).init();
1926
1927 let cfg = RollupConfig {
1928 seq_window_size: 100,
1929 max_sequencer_drift: 100,
1930 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
1931 block_time: 10,
1932 ..Default::default()
1933 };
1934 let l1_block_hash =
1935 b256!("3333333333333333333333333333333333333333000000000000000000000000");
1936 let block =
1937 BlockInfo { number: 11, timestamp: 10, hash: l1_block_hash, ..Default::default() };
1938 let second_block =
1939 BlockInfo { number: 12, timestamp: 21, hash: l1_block_hash, ..Default::default() };
1940 let l1_blocks = vec![block, second_block];
1941 let parent_hash = b256!("1111111111111111111111111111111111111111000000000000000000000000");
1942 let l2_safe_head = L2BlockInfo {
1943 block_info: BlockInfo {
1944 number: 41,
1945 timestamp: 10,
1946 hash: parent_hash,
1947 ..Default::default()
1948 },
1949 l1_origin: BlockNumHash { number: 9, ..Default::default() },
1950 ..Default::default()
1951 };
1952 let inclusion_block = BlockInfo { number: 50, ..Default::default() };
1953 let l2_block = L2BlockInfo {
1954 block_info: BlockInfo { number: 40, ..Default::default() },
1955 ..Default::default()
1956 };
1957 let mut fetcher: TestBatchValidator =
1958 TestBatchValidator { blocks: vec![l2_block], ..Default::default() };
1959 let filler_bytes = Bytes::copy_from_slice(&[EIP1559_TX_TYPE_ID]);
1960 let first = SpanBatchElement {
1961 epoch_num: 10,
1962 timestamp: 20,
1963 transactions: vec![filler_bytes.clone()],
1964 };
1965 let second = SpanBatchElement {
1966 epoch_num: 10,
1967 timestamp: 20,
1968 transactions: vec![Bytes::copy_from_slice(&[alloy_consensus::TxType::Eip7702 as u8])],
1969 };
1970 let third =
1971 SpanBatchElement { epoch_num: 11, timestamp: 20, transactions: vec![filler_bytes] };
1972 let batch = SpanBatch {
1973 batches: vec![first, second, third],
1974 parent_check: FixedBytes::<20>::from_slice(&parent_hash[..20]),
1975 l1_origin_check: FixedBytes::<20>::from_slice(&l1_block_hash[..20]),
1976 txs: SpanBatchTransactions::default(),
1977 ..Default::default()
1978 };
1979 assert_eq!(
1980 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
1981 BatchValidity::Drop
1982 );
1983 let logs = trace_store.get_by_level(Level::WARN);
1984 assert_eq!(logs.len(), 1);
1985 assert!(
1986 logs[0].contains("EIP-7702 transactions are not supported pre-isthmus. tx_index: 0")
1987 );
1988 }
1989
1990 #[tokio::test]
1991 async fn test_check_batch_failed_to_fetch_payload() {
1992 let trace_store: TraceStorage = Default::default();
1993 let layer = CollectingLayer::new(trace_store.clone());
1994 tracing_subscriber::Registry::default().with(layer).init();
1995
1996 let cfg = RollupConfig {
1997 seq_window_size: 100,
1998 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
1999 block_time: 10,
2000 ..Default::default()
2001 };
2002 let l1_block_hash =
2003 b256!("3333333333333333333333333333333333333333000000000000000000000000");
2004 let block =
2005 BlockInfo { number: 11, timestamp: 10, hash: l1_block_hash, ..Default::default() };
2006 let l1_blocks = vec![block];
2007 let parent_hash = b256!("1111111111111111111111111111111111111111000000000000000000000000");
2008 let l2_safe_head = L2BlockInfo {
2009 block_info: BlockInfo { number: 41, timestamp: 10, parent_hash, ..Default::default() },
2010 l1_origin: BlockNumHash { number: 9, ..Default::default() },
2011 ..Default::default()
2012 };
2013 let inclusion_block = BlockInfo { number: 50, ..Default::default() };
2014 let l2_block = L2BlockInfo {
2015 block_info: BlockInfo {
2016 number: 40,
2017 timestamp: 10,
2018 hash: parent_hash,
2019 ..Default::default()
2020 },
2021 l1_origin: BlockNumHash { number: 9, ..Default::default() },
2022 ..Default::default()
2023 };
2024 let mut fetcher: TestBatchValidator =
2025 TestBatchValidator { blocks: vec![l2_block], ..Default::default() };
2026 let first = SpanBatchElement { epoch_num: 10, timestamp: 10, ..Default::default() };
2027 let second = SpanBatchElement { epoch_num: 11, timestamp: 20, ..Default::default() };
2028 let batch = SpanBatch {
2029 batches: vec![first, second],
2030 parent_check: FixedBytes::<20>::from_slice(&parent_hash[..20]),
2031 l1_origin_check: FixedBytes::<20>::from_slice(&l1_block_hash[..20]),
2032 ..Default::default()
2033 };
2034 assert_eq!(
2035 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
2036 BatchValidity::Undecided
2037 );
2038 let logs = trace_store.get_by_level(Level::WARN);
2039 assert_eq!(logs.len(), 1);
2040 assert!(logs[0].contains("failed to fetch block number 41: L2 Block not found"));
2041 }
2042
2043 #[tokio::test]
2044 async fn test_check_batch_failed_to_extract_l2_block_info() {
2045 let trace_store: TraceStorage = Default::default();
2046 let layer = CollectingLayer::new(trace_store.clone());
2047 tracing_subscriber::Registry::default().with(layer).init();
2048
2049 let cfg = RollupConfig {
2050 seq_window_size: 100,
2051 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
2052 block_time: 10,
2053 ..Default::default()
2054 };
2055 let l1_block_hash =
2056 b256!("3333333333333333333333333333333333333333000000000000000000000000");
2057 let block =
2058 BlockInfo { number: 11, timestamp: 10, hash: l1_block_hash, ..Default::default() };
2059 let l1_blocks = vec![block];
2060 let parent_hash = b256!("1111111111111111111111111111111111111111000000000000000000000000");
2061 let l2_safe_head = L2BlockInfo {
2062 block_info: BlockInfo { number: 41, timestamp: 10, parent_hash, ..Default::default() },
2063 l1_origin: BlockNumHash { number: 9, ..Default::default() },
2064 ..Default::default()
2065 };
2066 let inclusion_block = BlockInfo { number: 50, ..Default::default() };
2067 let l2_block = L2BlockInfo {
2068 block_info: BlockInfo {
2069 number: 40,
2070 timestamp: 10,
2071 hash: parent_hash,
2072 ..Default::default()
2073 },
2074 l1_origin: BlockNumHash { number: 9, ..Default::default() },
2075 ..Default::default()
2076 };
2077 let block = OpBlock {
2078 header: Header { number: 41, ..Default::default() },
2079 body: alloy_consensus::BlockBody {
2080 transactions: Vec::new(),
2081 ommers: Vec::new(),
2082 withdrawals: None,
2083 },
2084 };
2085 let mut fetcher: TestBatchValidator = TestBatchValidator {
2086 blocks: vec![l2_block],
2087 op_blocks: vec![block],
2088 ..Default::default()
2089 };
2090 let first = SpanBatchElement { epoch_num: 10, timestamp: 10, ..Default::default() };
2091 let second = SpanBatchElement { epoch_num: 11, timestamp: 20, ..Default::default() };
2092 let batch = SpanBatch {
2093 batches: vec![first, second],
2094 parent_check: FixedBytes::<20>::from_slice(&parent_hash[..20]),
2095 l1_origin_check: FixedBytes::<20>::from_slice(&l1_block_hash[..20]),
2096 ..Default::default()
2097 };
2098 assert_eq!(
2099 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
2100 BatchValidity::Drop
2101 );
2102 let logs = trace_store.get_by_level(Level::WARN);
2103 assert_eq!(logs.len(), 1);
2104 let str = alloc::format!(
2105 "failed to extract L2BlockInfo from execution payload, hash: {:?}",
2106 b256!("0e2ee9abe94ee4514b170d7039d8151a7469d434a8575dbab5bd4187a27732dd"),
2107 );
2108 assert!(logs[0].contains(&str));
2109 }
2110
2111 #[tokio::test]
2112 async fn test_overlapped_blocks_origin_mismatch() {
2113 let trace_store: TraceStorage = Default::default();
2114 let layer = CollectingLayer::new(trace_store.clone());
2115 tracing_subscriber::Registry::default().with(layer).init();
2116
2117 let payload_block_hash =
2118 b256!("0e2ee9abe94ee4514b170d7039d8151a7469d434a8575dbab5bd4187a27732dd");
2119 let cfg = RollupConfig {
2120 seq_window_size: 100,
2121 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
2122 block_time: 10,
2123 genesis: ChainGenesis {
2124 l2: BlockNumHash { number: 41, hash: payload_block_hash },
2125 ..Default::default()
2126 },
2127 ..Default::default()
2128 };
2129 let l1_block_hash =
2130 b256!("3333333333333333333333333333333333333333000000000000000000000000");
2131 let block =
2132 BlockInfo { number: 11, timestamp: 10, hash: l1_block_hash, ..Default::default() };
2133 let l1_blocks = vec![block];
2134 let parent_hash = b256!("1111111111111111111111111111111111111111000000000000000000000000");
2135 let l2_safe_head = L2BlockInfo {
2136 block_info: BlockInfo { number: 41, timestamp: 10, parent_hash, ..Default::default() },
2137 l1_origin: BlockNumHash { number: 9, ..Default::default() },
2138 ..Default::default()
2139 };
2140 let inclusion_block = BlockInfo { number: 50, ..Default::default() };
2141 let l2_block = L2BlockInfo {
2142 block_info: BlockInfo {
2143 number: 40,
2144 hash: parent_hash,
2145 timestamp: 10,
2146 ..Default::default()
2147 },
2148 l1_origin: BlockNumHash { number: 9, ..Default::default() },
2149 ..Default::default()
2150 };
2151 let block = OpBlock {
2152 header: Header { number: 41, ..Default::default() },
2153 body: alloy_consensus::BlockBody {
2154 transactions: Vec::new(),
2155 ommers: Vec::new(),
2156 withdrawals: None,
2157 },
2158 };
2159 let mut fetcher: TestBatchValidator = TestBatchValidator {
2160 blocks: vec![l2_block],
2161 op_blocks: vec![block],
2162 ..Default::default()
2163 };
2164 let first = SpanBatchElement { epoch_num: 10, timestamp: 10, ..Default::default() };
2165 let second = SpanBatchElement { epoch_num: 11, timestamp: 20, ..Default::default() };
2166 let batch = SpanBatch {
2167 batches: vec![first, second],
2168 parent_check: FixedBytes::<20>::from_slice(&parent_hash[..20]),
2169 l1_origin_check: FixedBytes::<20>::from_slice(&l1_block_hash[..20]),
2170 ..Default::default()
2171 };
2172 assert_eq!(
2173 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
2174 BatchValidity::Drop
2175 );
2176 let logs = trace_store.get_by_level(Level::WARN);
2177 assert_eq!(logs.len(), 1);
2178 assert!(logs[0].contains("overlapped block's L1 origin number does not match"));
2179 }
2180
2181 #[tokio::test]
2182 async fn test_check_batch_valid_with_genesis_epoch() {
2183 let trace_store: TraceStorage = Default::default();
2184 let layer = CollectingLayer::new(trace_store.clone());
2185 tracing_subscriber::Registry::default().with(layer).init();
2186
2187 let payload_block_hash =
2188 b256!("0e2ee9abe94ee4514b170d7039d8151a7469d434a8575dbab5bd4187a27732dd");
2189 let cfg = RollupConfig {
2190 seq_window_size: 100,
2191 hardforks: HardForkConfig { delta_time: Some(0), ..Default::default() },
2192 block_time: 10,
2193 genesis: ChainGenesis {
2194 l2: BlockNumHash { number: 41, hash: payload_block_hash },
2195 l1: BlockNumHash { number: 10, ..Default::default() },
2196 ..Default::default()
2197 },
2198 ..Default::default()
2199 };
2200 let l1_block_hash =
2201 b256!("3333333333333333333333333333333333333333000000000000000000000000");
2202 let block =
2203 BlockInfo { number: 11, timestamp: 10, hash: l1_block_hash, ..Default::default() };
2204 let l1_blocks = vec![block];
2205 let parent_hash = b256!("1111111111111111111111111111111111111111000000000000000000000000");
2206 let l2_safe_head = L2BlockInfo {
2207 block_info: BlockInfo {
2208 number: 41,
2209 timestamp: 10,
2210 hash: parent_hash,
2211 ..Default::default()
2212 },
2213 l1_origin: BlockNumHash { number: 9, ..Default::default() },
2214 ..Default::default()
2215 };
2216 let inclusion_block = BlockInfo { number: 50, ..Default::default() };
2217 let l2_block = L2BlockInfo {
2218 block_info: BlockInfo {
2219 number: 40,
2220 hash: parent_hash,
2221 timestamp: 10,
2222 ..Default::default()
2223 },
2224 l1_origin: BlockNumHash { number: 9, ..Default::default() },
2225 ..Default::default()
2226 };
2227 let block = OpBlock {
2228 header: Header { number: 41, ..Default::default() },
2229 body: alloy_consensus::BlockBody {
2230 transactions: Vec::new(),
2231 ommers: Vec::new(),
2232 withdrawals: None,
2233 },
2234 };
2235 let mut fetcher: TestBatchValidator = TestBatchValidator {
2236 blocks: vec![l2_block],
2237 op_blocks: vec![block],
2238 ..Default::default()
2239 };
2240 let first = SpanBatchElement { epoch_num: 10, timestamp: 10, ..Default::default() };
2241 let second = SpanBatchElement { epoch_num: 11, timestamp: 20, ..Default::default() };
2242 let batch = SpanBatch {
2243 batches: vec![first, second],
2244 parent_check: FixedBytes::<20>::from_slice(&parent_hash[..20]),
2245 l1_origin_check: FixedBytes::<20>::from_slice(&l1_block_hash[..20]),
2246 ..Default::default()
2247 };
2248 assert_eq!(
2249 batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block, &mut fetcher).await,
2250 BatchValidity::Accept
2251 );
2252 assert!(trace_store.is_empty());
2253 }
2254}