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