signet_test_utils/
chain.rs1use alloy::consensus::{BlockHeader, Header, ReceiptEnvelope};
2pub use signet_constants::test_utils::*;
3use signet_evm::ExecutionOutcome;
4use signet_extract::{BlockAndReceipts, Extractable};
5use signet_types::primitives::{RecoveredBlock, SealedBlock, SignetHeaderV1};
6
7#[derive(Clone, PartialEq, Eq)]
9pub struct Chain {
10 blocks: Vec<RecoveredBlock>,
12 execution_outcome: ExecutionOutcome,
13}
14
15impl core::fmt::Debug for Chain {
16 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
17 f.debug_struct("Chain").field("blocks", &self.blocks.len()).finish_non_exhaustive()
18 }
19}
20
21impl Chain {
22 pub fn from_block(block: RecoveredBlock, execution_outcome: ExecutionOutcome) -> Self {
24 Self { blocks: vec![block], execution_outcome }
25 }
26
27 pub fn append_block(&mut self, block: RecoveredBlock, outcome: ExecutionOutcome) {
29 self.blocks.push(block);
30 self.execution_outcome.append(outcome);
31 }
32
33 pub fn blocks(&self) -> &[RecoveredBlock] {
35 &self.blocks
36 }
37
38 pub fn execution_outcome(&self) -> &ExecutionOutcome {
40 &self.execution_outcome
41 }
42
43 pub fn into_parts(self) -> (Vec<RecoveredBlock>, ExecutionOutcome) {
45 (self.blocks, self.execution_outcome)
46 }
47}
48
49impl Extractable for Chain {
50 type Block = RecoveredBlock;
51 type Receipt = ReceiptEnvelope;
52
53 fn blocks_and_receipts(
54 &self,
55 ) -> impl Iterator<Item = BlockAndReceipts<'_, Self::Block, Self::Receipt>> {
56 self.blocks
57 .iter()
58 .zip(self.execution_outcome.receipts().iter())
59 .map(|(block, receipts)| BlockAndReceipts { block, receipts })
60 }
61
62 fn first_number(&self) -> u64 {
63 self.blocks.first().expect("Chain must be non-empty").number()
64 }
65
66 fn tip_number(&self) -> u64 {
67 self.blocks.last().expect("Chain must be non-empty").number()
68 }
69
70 fn len(&self) -> usize {
71 self.blocks.len()
72 }
73}
74
75pub fn fake_chain(count: u64) -> Chain {
81 assert!(count > 0, "fake_chain requires at least one block");
82 let blocks: Vec<_> = (0..count).map(fake_block).collect();
83 let receipts = vec![vec![]; count as usize];
84 let execution_outcome = ExecutionOutcome::new(Default::default(), receipts, 0);
85 Chain { blocks, execution_outcome }
86}
87
88pub fn fake_block(number: u64) -> RecoveredBlock {
90 let header = Header {
91 number,
92 timestamp: 1716555576, ..Default::default()
94 };
95 let v1 = SignetHeaderV1::try_from(header).expect("fake header is valid V1");
96 SealedBlock::new(v1, vec![]).recover_unchecked(vec![])
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 #[should_panic(expected = "fake_chain requires at least one block")]
105 fn fake_chain_rejects_zero() {
106 fake_chain(0);
107 }
108
109 #[test]
110 fn single_block_metadata() {
111 let chain = fake_chain(1);
112 assert_eq!(chain.len(), 1);
113 assert_eq!(chain.first_number(), 0);
114 assert_eq!(chain.tip_number(), 0);
115 }
116
117 #[test]
118 fn multi_block_metadata() {
119 let chain = fake_chain(5);
120 assert_eq!(chain.len(), 5);
121 assert_eq!(chain.first_number(), 0);
122 assert_eq!(chain.tip_number(), 4);
123 }
124}