reth_db_models/
blocks.rs

1use alloy_eips::eip4895::Withdrawals;
2use alloy_primitives::TxNumber;
3use core::ops::Range;
4
5/// Total number of transactions.
6pub type NumTransactions = u64;
7
8/// The storage of the block body indices.
9///
10/// It has the pointer to the transaction Number of the first
11/// transaction in the block and the total number of transactions.
12#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
13#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
14#[cfg_attr(any(test, feature = "reth-codec"), derive(reth_codecs::Compact))]
15#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(compact))]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17pub struct StoredBlockBodyIndices {
18    /// The number of the first transaction in this block
19    ///
20    /// Note: If the block is empty, this is the number of the first transaction
21    /// in the next non-empty block.
22    pub first_tx_num: TxNumber,
23    /// The total number of transactions in the block
24    ///
25    /// NOTE: Number of transitions is equal to number of transactions with
26    /// additional transition for block change if block has block reward or withdrawal.
27    pub tx_count: NumTransactions,
28}
29
30impl StoredBlockBodyIndices {
31    /// Return the range of transaction ids for this block.
32    pub const fn tx_num_range(&self) -> Range<TxNumber> {
33        self.first_tx_num..self.first_tx_num + self.tx_count
34    }
35
36    /// Return the index of last transaction in this block unless the block
37    /// is empty in which case it refers to the last transaction in a previous
38    /// non-empty block
39    pub const fn last_tx_num(&self) -> TxNumber {
40        self.first_tx_num.saturating_add(self.tx_count).saturating_sub(1)
41    }
42
43    /// First transaction index.
44    ///
45    /// Caution: If the block is empty, this is the number of the first transaction
46    /// in the next non-empty block.
47    pub const fn first_tx_num(&self) -> TxNumber {
48        self.first_tx_num
49    }
50
51    /// Return the index of the next transaction after this block.
52    pub const fn next_tx_num(&self) -> TxNumber {
53        self.first_tx_num + self.tx_count
54    }
55
56    /// Return a flag whether the block is empty
57    pub const fn is_empty(&self) -> bool {
58        self.tx_count == 0
59    }
60
61    /// Return number of transaction inside block
62    ///
63    /// NOTE: This is not the same as the number of transitions.
64    pub const fn tx_count(&self) -> NumTransactions {
65        self.tx_count
66    }
67
68    /// Returns true if the block contains a transaction with the given number.
69    pub const fn contains_tx(&self, tx_num: TxNumber) -> bool {
70        tx_num >= self.first_tx_num && tx_num < self.next_tx_num()
71    }
72}
73
74/// The storage representation of block withdrawals.
75#[derive(Debug, Default, Eq, PartialEq, Clone)]
76#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
77#[cfg_attr(any(test, feature = "reth-codec"), derive(reth_codecs::Compact))]
78#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(compact))]
79#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
80pub struct StoredBlockWithdrawals {
81    /// The block withdrawals.
82    pub withdrawals: Withdrawals,
83}
84
85/// A storage representation of block withdrawals that is static file friendly. An inner `None`
86/// represents a pre-merge block.
87#[derive(Debug, Default, Eq, PartialEq, Clone)]
88#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
89#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(compact))]
90#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
91pub struct StaticFileBlockWithdrawals {
92    /// The block withdrawals. A `None` value represents a pre-merge block.
93    pub withdrawals: Option<Withdrawals>,
94}
95
96#[cfg(any(test, feature = "reth-codec"))]
97impl reth_codecs::Compact for StaticFileBlockWithdrawals {
98    fn to_compact<B>(&self, buf: &mut B) -> usize
99    where
100        B: bytes::BufMut + AsMut<[u8]>,
101    {
102        buf.put_u8(self.withdrawals.is_some() as u8);
103        if let Some(withdrawals) = &self.withdrawals {
104            return 1 + withdrawals.to_compact(buf);
105        }
106        1
107    }
108    fn from_compact(mut buf: &[u8], _: usize) -> (Self, &[u8]) {
109        use bytes::Buf;
110        if buf.get_u8() == 1 {
111            let (w, buf) = Withdrawals::from_compact(buf, buf.len());
112            (Self { withdrawals: Some(w) }, buf)
113        } else {
114            (Self { withdrawals: None }, buf)
115        }
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use crate::StoredBlockBodyIndices;
122
123    #[test]
124    fn block_indices() {
125        let first_tx_num = 10;
126        let tx_count = 6;
127        let block_indices = StoredBlockBodyIndices { first_tx_num, tx_count };
128
129        assert_eq!(block_indices.first_tx_num(), first_tx_num);
130        assert_eq!(block_indices.last_tx_num(), first_tx_num + tx_count - 1);
131        assert_eq!(block_indices.next_tx_num(), first_tx_num + tx_count);
132        assert_eq!(block_indices.tx_count(), tx_count);
133        assert_eq!(block_indices.tx_num_range(), first_tx_num..first_tx_num + tx_count);
134    }
135}