kona_protocol/batch/
raw.rs

1//! Module containing the [RawSpanBatch] struct.
2
3use alloc::{vec, vec::Vec};
4use alloy_primitives::bytes;
5
6use crate::{
7    BatchType, SpanBatch, SpanBatchElement, SpanBatchError, SpanBatchPayload, SpanBatchPrefix,
8    SpanDecodingError,
9};
10
11/// Raw Span Batch
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct RawSpanBatch {
14    /// The span batch prefix
15    pub prefix: SpanBatchPrefix,
16    /// The span batch payload
17    pub payload: SpanBatchPayload,
18}
19
20impl RawSpanBatch {
21    /// Returns the batch type
22    pub const fn get_batch_type(&self) -> BatchType {
23        BatchType::Span
24    }
25
26    /// Encodes the [RawSpanBatch] into a writer.
27    pub fn encode(&self, w: &mut dyn bytes::BufMut) -> Result<(), SpanBatchError> {
28        self.prefix.encode_prefix(w);
29        self.payload.encode_payload(w)
30    }
31
32    /// Decodes the [RawSpanBatch] from a reader.]
33    pub fn decode(r: &mut &[u8]) -> Result<Self, SpanBatchError> {
34        let prefix = SpanBatchPrefix::decode_prefix(r)?;
35        let payload = SpanBatchPayload::decode_payload(r)?;
36        Ok(Self { prefix, payload })
37    }
38
39    /// Converts a [RawSpanBatch] into a [SpanBatch], which has a list of [SpanBatchElement]s. Thos
40    /// function does not populate the [SpanBatch] with chain configuration data, which is
41    /// required for making payload attributes.
42    pub fn derive(
43        &mut self,
44        block_time: u64,
45        genesis_time: u64,
46        chain_id: u64,
47    ) -> Result<SpanBatch, SpanBatchError> {
48        if self.payload.block_count == 0 {
49            return Err(SpanBatchError::EmptySpanBatch);
50        }
51
52        let mut block_origin_nums = vec![0u64; self.payload.block_count as usize];
53        let mut l1_origin_number = self.prefix.l1_origin_num;
54        for i in (0..self.payload.block_count).rev() {
55            block_origin_nums[i as usize] = l1_origin_number;
56            if self
57                .payload
58                .origin_bits
59                .get_bit(i as usize)
60                .ok_or(SpanBatchError::Decoding(SpanDecodingError::L1OriginCheck))? ==
61                1 &&
62                i > 0
63            {
64                l1_origin_number -= 1;
65            }
66        }
67
68        // Get all transactions in the batch.
69        let enveloped_txs = self.payload.txs.full_txs(chain_id)?;
70
71        let mut tx_idx = 0;
72        let batches = (0..self.payload.block_count).fold(Vec::new(), |mut acc, i| {
73            let transactions =
74                (0..self.payload.block_tx_counts[i as usize]).fold(Vec::new(), |mut acc, _| {
75                    acc.push(enveloped_txs[tx_idx].clone());
76                    tx_idx += 1;
77                    acc
78                });
79            acc.push(SpanBatchElement {
80                epoch_num: block_origin_nums[i as usize],
81                timestamp: genesis_time + self.prefix.rel_timestamp + block_time * i,
82                transactions: transactions.into_iter().map(|v| v.into()).collect(),
83            });
84            acc
85        });
86
87        Ok(SpanBatch {
88            parent_check: self.prefix.parent_check,
89            l1_origin_check: self.prefix.l1_origin_check,
90            batches,
91            ..Default::default()
92        })
93    }
94}
95
96#[cfg(test)]
97mod test {
98    use super::*;
99    use alloy_primitives::FixedBytes;
100
101    #[test]
102    fn test_try_from_span_batch_empty_batches_errors() {
103        let span_batch = SpanBatch::default();
104        let raw_span_batch = span_batch.to_raw_span_batch().unwrap_err();
105        assert_eq!(raw_span_batch, SpanBatchError::EmptySpanBatch);
106    }
107
108    #[test]
109    fn test_try_from_span_batch_succeeds() {
110        let parent_check = FixedBytes::from([2u8; 20]);
111        let l1_origin_check = FixedBytes::from([3u8; 20]);
112        let first = SpanBatchElement { epoch_num: 100, timestamp: 400, transactions: Vec::new() };
113        let last = SpanBatchElement { epoch_num: 200, timestamp: 500, transactions: Vec::new() };
114        let span_batch = SpanBatch {
115            batches: vec![first, last],
116            genesis_timestamp: 300,
117            parent_check,
118            l1_origin_check,
119            ..Default::default()
120        };
121        let expected_prefix = SpanBatchPrefix {
122            rel_timestamp: 100,
123            l1_origin_num: 200,
124            parent_check,
125            l1_origin_check,
126        };
127        let expected_payload = SpanBatchPayload { block_count: 2, ..Default::default() };
128        let raw_span_batch = span_batch.to_raw_span_batch().unwrap();
129        assert_eq!(raw_span_batch.prefix, expected_prefix);
130        assert_eq!(raw_span_batch.payload, expected_payload);
131    }
132
133    #[test]
134    fn test_decode_encode_raw_span_batch() {
135        // Load in the raw span batch from the `op-node` derivation pipeline implementation.
136        let raw_span_batch_hex = include_bytes!("./testdata/raw_batch.hex");
137        let raw_span_batch = RawSpanBatch::decode(&mut raw_span_batch_hex.as_slice()).unwrap();
138
139        let mut encoding_buf = Vec::new();
140        raw_span_batch.encode(&mut encoding_buf).unwrap();
141        assert_eq!(encoding_buf, raw_span_batch_hex);
142    }
143}