kona_protocol/batch/
core.rs

1//! Module containing the core [`Batch`] enum.
2
3use crate::{
4    BatchDecodingError, BatchEncodingError, BatchType, RawSpanBatch, SingleBatch, SpanBatch,
5};
6use alloy_primitives::bytes;
7use alloy_rlp::{Buf, Decodable, Encodable};
8use kona_genesis::RollupConfig;
9
10/// A Batch.
11#[derive(Debug, Clone, PartialEq, Eq)]
12#[allow(clippy::large_enum_variant)]
13pub enum Batch {
14    /// A single batch
15    Single(SingleBatch),
16    /// Span Batches
17    Span(SpanBatch),
18}
19
20impl core::fmt::Display for Batch {
21    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
22        match self {
23            Self::Single(_) => write!(f, "single"),
24            Self::Span(_) => write!(f, "span"),
25        }
26    }
27}
28
29impl Batch {
30    /// Returns the timestamp for the batch.
31    pub fn timestamp(&self) -> u64 {
32        match self {
33            Self::Single(sb) => sb.timestamp,
34            Self::Span(sb) => sb.starting_timestamp(),
35        }
36    }
37
38    /// Attempts to decode a batch from a reader.
39    pub fn decode(r: &mut &[u8], cfg: &RollupConfig) -> Result<Self, BatchDecodingError> {
40        if r.is_empty() {
41            return Err(BatchDecodingError::EmptyBuffer);
42        }
43
44        // Read the batch type
45        let batch_type = BatchType::from(r[0]);
46        r.advance(1);
47
48        match batch_type {
49            BatchType::Single => {
50                let single_batch =
51                    SingleBatch::decode(r).map_err(BatchDecodingError::AlloyRlpError)?;
52                Ok(Self::Single(single_batch))
53            }
54            BatchType::Span => {
55                let mut raw_span_batch = RawSpanBatch::decode(r)?;
56                let span_batch = raw_span_batch
57                    .derive(cfg.block_time, cfg.genesis.l2_time, cfg.l2_chain_id.id())
58                    .map_err(BatchDecodingError::SpanBatchError)?;
59                Ok(Self::Span(span_batch))
60            }
61        }
62    }
63
64    /// Attempts to encode the batch to a writer.
65    pub fn encode(&self, out: &mut dyn bytes::BufMut) -> Result<(), BatchEncodingError> {
66        match self {
67            Self::Single(sb) => {
68                out.put_u8(BatchType::Single as u8);
69                sb.encode(out);
70            }
71            Self::Span(sb) => {
72                out.put_u8(BatchType::Span as u8);
73                let raw_span_batch =
74                    sb.to_raw_span_batch().map_err(BatchEncodingError::SpanBatchError)?;
75                raw_span_batch.encode(out).map_err(BatchEncodingError::SpanBatchError)?;
76            }
77        }
78        Ok(())
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85    use crate::{SpanBatchElement, SpanBatchError, SpanBatchTransactions};
86    use alloc::{vec, vec::Vec};
87    use alloy_consensus::{Signed, TxEip2930, TxEnvelope};
88    use alloy_primitives::{Bytes, Signature, TxKind, address, hex};
89
90    #[test]
91    fn test_single_batch_encode_decode() {
92        let mut out = Vec::new();
93        let batch = Batch::Single(SingleBatch::default());
94        batch.encode(&mut out).unwrap();
95        let decoded = Batch::decode(&mut out.as_slice(), &RollupConfig::default()).unwrap();
96        assert_eq!(batch, decoded);
97    }
98
99    #[test]
100    fn test_span_batch_encode_decode() {
101        let sig = Signature::test_signature();
102        let to = address!("0123456789012345678901234567890123456789");
103        let tx = TxEnvelope::Eip2930(Signed::new_unchecked(
104            TxEip2930 { to: TxKind::Call(to), chain_id: 1, ..Default::default() },
105            sig,
106            Default::default(),
107        ));
108        let mut span_batch_txs = SpanBatchTransactions::default();
109        let mut buf = vec![];
110        tx.encode(&mut buf);
111        let txs = vec![Bytes::from(buf)];
112        let chain_id = 1;
113        span_batch_txs.add_txs(txs, chain_id).unwrap();
114
115        let mut out = Vec::new();
116        let batch = Batch::Span(SpanBatch {
117            block_tx_counts: vec![1],
118            batches: vec![SpanBatchElement::default()],
119            txs: span_batch_txs,
120            ..Default::default()
121        });
122        batch.encode(&mut out).unwrap();
123        let decoded = Batch::decode(&mut out.as_slice(), &RollupConfig::default()).unwrap();
124        assert_eq!(Batch::Span(SpanBatch {
125            batches: vec![SpanBatchElement {
126                transactions: vec![hex!("01f85f808080809401234567890123456789012345678901234567898080c080a0840cfc572845f5786e702984c2a582528cad4b49b2a10b9db1be7fca90058565a025e7109ceb98168d95b09b18bbf6b685130e0562f233877d492b94eee0c5b6d1").into()],
127                ..Default::default()
128            }],
129            txs: SpanBatchTransactions::default(),
130            ..Default::default()
131        }), decoded);
132    }
133
134    #[test]
135    fn test_empty_span_batch() {
136        let mut out = Vec::new();
137        let batch = Batch::Span(SpanBatch::default());
138        // Fails to even encode an empty span batch - decoding will do the same
139        let err = batch.encode(&mut out).unwrap_err();
140        assert_eq!(BatchEncodingError::SpanBatchError(SpanBatchError::EmptySpanBatch), err);
141    }
142}