kona_protocol/batch/
transactions.rs

1//! This module contains the [SpanBatchTransactions] type and logic for encoding and decoding
2//! transactions in a span batch.
3
4use crate::{
5    MAX_SPAN_BATCH_ELEMENTS, SpanBatchBits, SpanBatchError, SpanBatchTransactionData,
6    SpanDecodingError, read_tx_data,
7};
8use alloc::vec::Vec;
9use alloy_consensus::{Transaction, TxEnvelope, TxType};
10use alloy_eips::eip2718::Encodable2718;
11use alloy_primitives::{Address, Bytes, PrimitiveSignature as Signature, U256, bytes};
12use alloy_rlp::{Buf, Decodable, Encodable};
13
14/// This struct contains the decoded information for transactions in a span batch.
15#[derive(Debug, Default, Clone, PartialEq, Eq)]
16pub struct SpanBatchTransactions {
17    /// The total number of transactions in a span batch. Must be manually set.
18    pub total_block_tx_count: u64,
19    /// The contract creation bits, standard span-batch bitlist.
20    pub contract_creation_bits: SpanBatchBits,
21    /// The transaction signatures.
22    pub tx_sigs: Vec<Signature>,
23    /// The transaction nonces
24    pub tx_nonces: Vec<u64>,
25    /// The transaction gas limits.
26    pub tx_gases: Vec<u64>,
27    /// The `to` addresses of the transactions.
28    pub tx_tos: Vec<Address>,
29    /// The transaction data.
30    pub tx_datas: Vec<Vec<u8>>,
31    /// The protected bits, standard span-batch bitlist.
32    pub protected_bits: SpanBatchBits,
33    /// The types of the transactions.
34    pub tx_types: Vec<TxType>,
35    /// Total legacy transaction count in the span batch.
36    pub legacy_tx_count: u64,
37}
38
39impl SpanBatchTransactions {
40    /// Encodes the [SpanBatchTransactions] into a writer.
41    pub fn encode(&self, w: &mut dyn bytes::BufMut) -> Result<(), SpanBatchError> {
42        self.encode_contract_creation_bits(w)?;
43        self.encode_tx_sigs(w)?;
44        self.encode_tx_tos(w)?;
45        self.encode_tx_datas(w)?;
46        self.encode_tx_nonces(w)?;
47        self.encode_tx_gases(w)?;
48        self.encode_protected_bits(w)?;
49        Ok(())
50    }
51
52    /// Decodes the [SpanBatchTransactions] from a reader.
53    pub fn decode(&mut self, r: &mut &[u8]) -> Result<(), SpanBatchError> {
54        self.decode_contract_creation_bits(r)?;
55        self.decode_tx_sigs(r)?;
56        self.decode_tx_tos(r)?;
57        self.decode_tx_datas(r)?;
58        self.decode_tx_nonces(r)?;
59        self.decode_tx_gases(r)?;
60        self.decode_protected_bits(r)?;
61        Ok(())
62    }
63
64    /// Encode the contract creation bits into a writer.
65    pub fn encode_contract_creation_bits(
66        &self,
67        w: &mut dyn bytes::BufMut,
68    ) -> Result<(), SpanBatchError> {
69        SpanBatchBits::encode(w, self.total_block_tx_count as usize, &self.contract_creation_bits)?;
70        Ok(())
71    }
72
73    /// Encode the protected bits into a writer.
74    pub fn encode_protected_bits(&self, w: &mut dyn bytes::BufMut) -> Result<(), SpanBatchError> {
75        SpanBatchBits::encode(w, self.legacy_tx_count as usize, &self.protected_bits)?;
76        Ok(())
77    }
78
79    /// Encode the transaction signatures into a writer (excluding `v` field).
80    pub fn encode_tx_sigs(&self, w: &mut dyn bytes::BufMut) -> Result<(), SpanBatchError> {
81        let mut y_parity_bits = SpanBatchBits::default();
82        for (i, sig) in self.tx_sigs.iter().enumerate() {
83            y_parity_bits.set_bit(i, sig.v());
84        }
85
86        SpanBatchBits::encode(w, self.total_block_tx_count as usize, &y_parity_bits)?;
87        for sig in &self.tx_sigs {
88            w.put_slice(&sig.r().to_be_bytes::<32>());
89            w.put_slice(&sig.s().to_be_bytes::<32>());
90        }
91        Ok(())
92    }
93
94    /// Encode the transaction nonces into a writer.
95    pub fn encode_tx_nonces(&self, w: &mut dyn bytes::BufMut) -> Result<(), SpanBatchError> {
96        let mut buf = [0u8; 10];
97        for nonce in &self.tx_nonces {
98            let slice = unsigned_varint::encode::u64(*nonce, &mut buf);
99            w.put_slice(slice);
100        }
101        Ok(())
102    }
103
104    /// Encode the transaction gas limits into a writer.
105    pub fn encode_tx_gases(&self, w: &mut dyn bytes::BufMut) -> Result<(), SpanBatchError> {
106        let mut buf = [0u8; 10];
107        for gas in &self.tx_gases {
108            let slice = unsigned_varint::encode::u64(*gas, &mut buf);
109            w.put_slice(slice);
110        }
111        Ok(())
112    }
113
114    /// Encode the `to` addresses of the transactions into a writer.
115    pub fn encode_tx_tos(&self, w: &mut dyn bytes::BufMut) -> Result<(), SpanBatchError> {
116        for to in &self.tx_tos {
117            w.put_slice(to.as_ref());
118        }
119        Ok(())
120    }
121
122    /// Encode the transaction data into a writer.
123    pub fn encode_tx_datas(&self, w: &mut dyn bytes::BufMut) -> Result<(), SpanBatchError> {
124        for data in &self.tx_datas {
125            w.put_slice(data);
126        }
127        Ok(())
128    }
129
130    /// Decode the contract creation bits from a reader.
131    pub fn decode_contract_creation_bits(&mut self, r: &mut &[u8]) -> Result<(), SpanBatchError> {
132        if self.total_block_tx_count > MAX_SPAN_BATCH_ELEMENTS {
133            return Err(SpanBatchError::TooBigSpanBatchSize);
134        }
135
136        self.contract_creation_bits = SpanBatchBits::decode(r, self.total_block_tx_count as usize)?;
137        Ok(())
138    }
139
140    /// Decode the protected bits from a reader.
141    pub fn decode_protected_bits(&mut self, r: &mut &[u8]) -> Result<(), SpanBatchError> {
142        if self.legacy_tx_count > MAX_SPAN_BATCH_ELEMENTS {
143            return Err(SpanBatchError::TooBigSpanBatchSize);
144        }
145
146        self.protected_bits = SpanBatchBits::decode(r, self.legacy_tx_count as usize)?;
147        Ok(())
148    }
149
150    /// Decode the transaction signatures from a reader (excluding `v` field).
151    pub fn decode_tx_sigs(&mut self, r: &mut &[u8]) -> Result<(), SpanBatchError> {
152        let y_parity_bits = SpanBatchBits::decode(r, self.total_block_tx_count as usize)?;
153        let mut sigs = Vec::with_capacity(self.total_block_tx_count as usize);
154        for i in 0..self.total_block_tx_count {
155            let y_parity = y_parity_bits.get_bit(i as usize).expect("same length");
156            let r_val = U256::from_be_slice(&r[..32]);
157            let s_val = U256::from_be_slice(&r[32..64]);
158            sigs.push(Signature::new(r_val, s_val, y_parity == 1));
159            r.advance(64);
160        }
161        self.tx_sigs = sigs;
162        Ok(())
163    }
164
165    /// Decode the transaction nonces from a reader.
166    pub fn decode_tx_nonces(&mut self, r: &mut &[u8]) -> Result<(), SpanBatchError> {
167        let mut nonces = Vec::with_capacity(self.total_block_tx_count as usize);
168        for _ in 0..self.total_block_tx_count {
169            let (nonce, remaining) = unsigned_varint::decode::u64(r)
170                .map_err(|_| SpanBatchError::Decoding(SpanDecodingError::TxNonces))?;
171            nonces.push(nonce);
172            *r = remaining;
173        }
174        self.tx_nonces = nonces;
175        Ok(())
176    }
177
178    /// Decode the transaction gas limits from a reader.
179    pub fn decode_tx_gases(&mut self, r: &mut &[u8]) -> Result<(), SpanBatchError> {
180        let mut gases = Vec::with_capacity(self.total_block_tx_count as usize);
181        for _ in 0..self.total_block_tx_count {
182            let (gas, remaining) = unsigned_varint::decode::u64(r)
183                .map_err(|_| SpanBatchError::Decoding(SpanDecodingError::TxNonces))?;
184            gases.push(gas);
185            *r = remaining;
186        }
187        self.tx_gases = gases;
188        Ok(())
189    }
190
191    /// Decode the `to` addresses of the transactions from a reader.
192    pub fn decode_tx_tos(&mut self, r: &mut &[u8]) -> Result<(), SpanBatchError> {
193        let mut tos = Vec::with_capacity(self.total_block_tx_count as usize);
194        let contract_creation_count = self.contract_creation_count();
195        for _ in 0..(self.total_block_tx_count - contract_creation_count) {
196            let to = Address::from_slice(&r[..20]);
197            tos.push(to);
198            r.advance(20);
199        }
200        self.tx_tos = tos;
201        Ok(())
202    }
203
204    /// Decode the transaction data from a reader.
205    pub fn decode_tx_datas(&mut self, r: &mut &[u8]) -> Result<(), SpanBatchError> {
206        let mut tx_datas = Vec::new();
207        let mut tx_types = Vec::new();
208
209        // Do not need the transaction data header because the RLP stream already includes the
210        // length information.
211        for _ in 0..self.total_block_tx_count {
212            let (tx_data, tx_type) = read_tx_data(r)?;
213            tx_datas.push(tx_data);
214            tx_types.push(tx_type);
215            if matches!(tx_type, TxType::Legacy) {
216                self.legacy_tx_count += 1;
217            }
218        }
219
220        self.tx_datas = tx_datas;
221        self.tx_types = tx_types;
222
223        Ok(())
224    }
225
226    /// Returns the number of contract creation transactions in the span batch.
227    pub fn contract_creation_count(&self) -> u64 {
228        self.contract_creation_bits.as_ref().iter().map(|b| b.count_ones() as u64).sum()
229    }
230
231    /// Retrieve all of the raw transactions from the [SpanBatchTransactions].
232    pub fn full_txs(&self, chain_id: u64) -> Result<Vec<Vec<u8>>, SpanBatchError> {
233        let mut txs = Vec::new();
234        let mut to_idx = 0;
235        let mut protected_bit_idx = 0;
236        for idx in 0..self.total_block_tx_count {
237            let mut datas = self.tx_datas[idx as usize].as_slice();
238            let tx = SpanBatchTransactionData::decode(&mut datas)
239                .map_err(|_| SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionData))?;
240            let nonce = self
241                .tx_nonces
242                .get(idx as usize)
243                .ok_or(SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionData))?;
244            let gas = self
245                .tx_gases
246                .get(idx as usize)
247                .ok_or(SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionData))?;
248            let bit = self
249                .contract_creation_bits
250                .get_bit(idx as usize)
251                .ok_or(SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionData))?;
252            let to = if bit == 0 {
253                if self.tx_tos.len() <= to_idx {
254                    return Err(SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionData));
255                }
256                to_idx += 1;
257                Some(self.tx_tos[to_idx - 1])
258            } else {
259                None
260            };
261            let sig = *self
262                .tx_sigs
263                .get(idx as usize)
264                .ok_or(SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionData))?;
265            let is_protected = if tx.tx_type() == TxType::Legacy {
266                protected_bit_idx += 1;
267                self.protected_bits.get_bit(protected_bit_idx - 1).unwrap_or_default() == 1
268            } else {
269                true
270            };
271            let tx_envelope = tx.to_signed_tx(*nonce, *gas, to, chain_id, sig, is_protected)?;
272            let mut buf = Vec::new();
273            tx_envelope.encode_2718(&mut buf);
274            txs.push(buf);
275        }
276        Ok(txs)
277    }
278
279    /// Add raw transactions into the [SpanBatchTransactions].
280    pub fn add_txs(&mut self, txs: Vec<Bytes>, chain_id: u64) -> Result<(), SpanBatchError> {
281        let total_block_tx_count = txs.len() as u64;
282        let offset = self.total_block_tx_count;
283
284        for i in 0..total_block_tx_count {
285            let tx_enveloped = TxEnvelope::decode(&mut txs[i as usize].as_ref())
286                .map_err(|_| SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionData))?;
287            let span_batch_tx = SpanBatchTransactionData::try_from(&tx_enveloped)?;
288
289            let tx_type = tx_enveloped.tx_type();
290            if matches!(tx_type, TxType::Legacy) {
291                let protected_bit = tx_enveloped.is_replay_protected();
292                self.protected_bits.set_bit(self.legacy_tx_count as usize, protected_bit);
293                self.legacy_tx_count += 1;
294            }
295
296            let (signature, to, nonce, gas, tx_chain_id) = match &tx_enveloped {
297                TxEnvelope::Legacy(tx) => {
298                    let (tx, sig) = (tx.tx(), tx.signature());
299                    (sig, tx.to(), tx.nonce(), tx.gas_limit(), tx.chain_id())
300                }
301                TxEnvelope::Eip2930(tx) => {
302                    let (tx, sig) = (tx.tx(), tx.signature());
303                    (sig, tx.to(), tx.nonce(), tx.gas_limit(), tx.chain_id())
304                }
305                TxEnvelope::Eip1559(tx) => {
306                    let (tx, sig) = (tx.tx(), tx.signature());
307                    (sig, tx.to(), tx.nonce(), tx.gas_limit(), tx.chain_id())
308                }
309                TxEnvelope::Eip7702(tx) => {
310                    let (tx, sig) = (tx.tx(), tx.signature());
311                    (sig, tx.to(), tx.nonce(), tx.gas_limit(), tx.chain_id())
312                }
313                _ => {
314                    return Err(SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionData))
315                }
316            };
317
318            if tx_enveloped.is_replay_protected() &&
319                tx_chain_id
320                    .ok_or(SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionData))? !=
321                    chain_id
322            {
323                return Err(SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionData));
324            }
325
326            let contract_creation_bit = match to {
327                Some(address) => {
328                    self.tx_tos.push(address);
329                    0
330                }
331                None => 1,
332            };
333            let mut tx_data_buf = Vec::new();
334            span_batch_tx.encode(&mut tx_data_buf);
335
336            self.tx_sigs.push(*signature);
337            self.contract_creation_bits.set_bit((i + offset) as usize, contract_creation_bit == 1);
338            self.tx_nonces.push(nonce);
339            self.tx_datas.push(tx_data_buf);
340            self.tx_gases.push(gas);
341            self.tx_types.push(tx_type);
342        }
343        self.total_block_tx_count += total_block_tx_count;
344        Ok(())
345    }
346}
347
348#[cfg(test)]
349mod tests {
350    use super::*;
351    use alloc::vec;
352    use alloy_consensus::{Signed, TxEip1559, TxEip2930, TxEip7702};
353    use alloy_primitives::{PrimitiveSignature as Signature, TxKind, address};
354
355    #[test]
356    fn test_span_batch_transactions_add_empty_txs() {
357        let mut span_batch_txs = SpanBatchTransactions::default();
358        let txs = vec![];
359        let chain_id = 1;
360        let result = span_batch_txs.add_txs(txs, chain_id);
361        assert!(result.is_ok());
362        assert_eq!(span_batch_txs.total_block_tx_count, 0);
363    }
364
365    #[test]
366    fn test_span_batch_transactions_add_eip2930_tx_wrong_chain_id() {
367        let sig = Signature::test_signature();
368        let to = address!("0123456789012345678901234567890123456789");
369        let tx = TxEnvelope::Eip2930(Signed::new_unchecked(
370            TxEip2930 { to: TxKind::Call(to), ..Default::default() },
371            sig,
372            Default::default(),
373        ));
374        let mut span_batch_txs = SpanBatchTransactions::default();
375        let mut buf = vec![];
376        tx.encode(&mut buf);
377        let txs = vec![Bytes::from(buf)];
378        let chain_id = 1;
379        let err = span_batch_txs.add_txs(txs, chain_id).unwrap_err();
380        assert_eq!(err, SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionData));
381    }
382
383    #[test]
384    fn test_span_batch_transactions_add_eip2930_tx() {
385        let sig = Signature::test_signature();
386        let to = address!("0123456789012345678901234567890123456789");
387        let tx = TxEnvelope::Eip2930(Signed::new_unchecked(
388            TxEip2930 { to: TxKind::Call(to), chain_id: 1, ..Default::default() },
389            sig,
390            Default::default(),
391        ));
392        let mut span_batch_txs = SpanBatchTransactions::default();
393        let mut buf = vec![];
394        tx.encode(&mut buf);
395        let txs = vec![Bytes::from(buf)];
396        let chain_id = 1;
397        let result = span_batch_txs.add_txs(txs, chain_id);
398        assert_eq!(result, Ok(()));
399        assert_eq!(span_batch_txs.total_block_tx_count, 1);
400    }
401
402    #[test]
403    fn test_span_batch_transactions_add_eip1559_tx() {
404        let sig = Signature::test_signature();
405        let to = address!("0123456789012345678901234567890123456789");
406        let tx = TxEnvelope::Eip1559(Signed::new_unchecked(
407            TxEip1559 { to: TxKind::Call(to), chain_id: 1, ..Default::default() },
408            sig,
409            Default::default(),
410        ));
411        let mut span_batch_txs = SpanBatchTransactions::default();
412        let mut buf = vec![];
413        tx.encode(&mut buf);
414        let txs = vec![Bytes::from(buf)];
415        let chain_id = 1;
416        let result = span_batch_txs.add_txs(txs, chain_id);
417        assert_eq!(result, Ok(()));
418        assert_eq!(span_batch_txs.total_block_tx_count, 1);
419    }
420
421    #[test]
422    fn test_span_batch_transactions_add_eip7702_tx() {
423        let sig = Signature::test_signature();
424        let to = address!("0123456789012345678901234567890123456789");
425        let tx = TxEnvelope::Eip7702(Signed::new_unchecked(
426            TxEip7702 { to, chain_id: 1, ..Default::default() },
427            sig,
428            Default::default(),
429        ));
430        let mut span_batch_txs = SpanBatchTransactions::default();
431        let mut buf = vec![];
432        tx.encode(&mut buf);
433        let txs = vec![Bytes::from(buf)];
434        let chain_id = 1;
435        let result = span_batch_txs.add_txs(txs, chain_id);
436        assert_eq!(result, Ok(()));
437        assert_eq!(span_batch_txs.total_block_tx_count, 1);
438    }
439}