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
69/// The storage representation of block withdrawals.
70#[derive(Debug, Default, Eq, PartialEq, Clone)]
71#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
72#[cfg_attr(any(test, feature = "reth-codec"), derive(reth_codecs::Compact))]
73#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(compact))]
74#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
75pub struct StoredBlockWithdrawals {
76    /// The block withdrawals.
77    pub withdrawals: Withdrawals,
78}
79
80/// A storage representation of block withdrawals that is static file friendly. An inner `None`
81/// represents a pre-merge block.
82#[derive(Debug, Default, Eq, PartialEq, Clone)]
83#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
84#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(compact))]
85#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
86pub struct StaticFileBlockWithdrawals {
87    /// The block withdrawals. A `None` value represents a pre-merge block.
88    pub withdrawals: Option<Withdrawals>,
89}
90
91#[cfg(any(test, feature = "reth-codec"))]
92impl reth_codecs::Compact for StaticFileBlockWithdrawals {
93    fn to_compact<B>(&self, buf: &mut B) -> usize
94    where
95        B: bytes::BufMut + AsMut<[u8]>,
96    {
97        buf.put_u8(self.withdrawals.is_some() as u8);
98        if let Some(withdrawals) = &self.withdrawals {
99            return 1 + withdrawals.to_compact(buf);
100        }
101        1
102    }
103    fn from_compact(mut buf: &[u8], _: usize) -> (Self, &[u8]) {
104        use bytes::Buf;
105        if buf.get_u8() == 1 {
106            let (w, buf) = Withdrawals::from_compact(buf, buf.len());
107            (Self { withdrawals: Some(w) }, buf)
108        } else {
109            (Self { withdrawals: None }, buf)
110        }
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use crate::StoredBlockBodyIndices;
117
118    #[test]
119    fn block_indices() {
120        let first_tx_num = 10;
121        let tx_count = 6;
122        let block_indices = StoredBlockBodyIndices { first_tx_num, tx_count };
123
124        assert_eq!(block_indices.first_tx_num(), first_tx_num);
125        assert_eq!(block_indices.last_tx_num(), first_tx_num + tx_count - 1);
126        assert_eq!(block_indices.next_tx_num(), first_tx_num + tx_count);
127        assert_eq!(block_indices.tx_count(), tx_count);
128        assert_eq!(block_indices.tx_num_range(), first_tx_num..first_tx_num + tx_count);
129    }
130}