reth_trie_common/hash_builder/
state.rs

1use crate::TrieMask;
2use alloc::vec::Vec;
3use alloy_trie::{hash_builder::HashBuilderValue, nodes::RlpNode, HashBuilder};
4use nybbles::Nibbles;
5
6/// The hash builder state for storing in the database.
7/// Check the `reth-trie` crate for more info on hash builder.
8#[derive(Debug, Clone, PartialEq, Eq, Default)]
9#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
10#[cfg_attr(
11    feature = "arbitrary",
12    derive(arbitrary::Arbitrary),
13    reth_codecs::add_arbitrary_tests(compact)
14)]
15pub struct HashBuilderState {
16    /// The current key.
17    pub key: Vec<u8>,
18    /// The current node value.
19    pub value: HashBuilderValue,
20    /// Whether the state value is private.
21    pub is_private: Option<bool>,
22    /// The builder stack.
23    pub stack: Vec<RlpNode>,
24
25    /// Group masks.
26    pub groups: Vec<TrieMask>,
27    /// Tree masks.
28    pub tree_masks: Vec<TrieMask>,
29    /// Hash masks.
30    pub hash_masks: Vec<TrieMask>,
31
32    /// Flag indicating if the current node is stored in the database.
33    pub stored_in_database: bool,
34}
35
36impl From<HashBuilderState> for HashBuilder {
37    fn from(state: HashBuilderState) -> Self {
38        Self {
39            key: Nibbles::from_nibbles_unchecked(state.key),
40            stack: state.stack,
41            value: state.value,
42            is_private: state.is_private,
43            state_masks: state.groups,
44            tree_masks: state.tree_masks,
45            hash_masks: state.hash_masks,
46            stored_in_database: state.stored_in_database,
47            updated_branch_nodes: None,
48            proof_retainer: None,
49            rlp_buf: Vec::with_capacity(32),
50        }
51    }
52}
53
54impl From<HashBuilder> for HashBuilderState {
55    fn from(state: HashBuilder) -> Self {
56        Self {
57            key: state.key.into(),
58            stack: state.stack,
59            value: state.value,
60            is_private: state.is_private,
61            groups: state.state_masks,
62            tree_masks: state.tree_masks,
63            hash_masks: state.hash_masks,
64            stored_in_database: state.stored_in_database,
65        }
66    }
67}
68
69#[cfg(any(test, feature = "reth-codec"))]
70impl reth_codecs::Compact for HashBuilderState {
71    fn to_compact<B>(&self, buf: &mut B) -> usize
72    where
73        B: bytes::BufMut + AsMut<[u8]>,
74    {
75        let mut len = 0;
76
77        len += self.key.to_compact(buf);
78
79        buf.put_u16(self.stack.len() as u16);
80        len += 2;
81        for item in &self.stack {
82            buf.put_u16(item.len() as u16);
83            buf.put_slice(&item[..]);
84            len += 2 + item.len();
85        }
86
87        len += self.value.to_compact(buf);
88
89        buf.put_u16(self.groups.len() as u16);
90        len += 2;
91        for item in &self.groups {
92            len += (*item).to_compact(buf);
93        }
94
95        buf.put_u16(self.tree_masks.len() as u16);
96        len += 2;
97        for item in &self.tree_masks {
98            len += (*item).to_compact(buf);
99        }
100
101        buf.put_u16(self.hash_masks.len() as u16);
102        len += 2;
103        for item in &self.hash_masks {
104            len += (*item).to_compact(buf);
105        }
106
107        // Serialize Seismic-specific `is_private: Option<bool>`
108        let private_byte = match self.is_private {
109            None => 0u8,
110            Some(false) => 1u8,
111            Some(true) => 2u8,
112        };
113        buf.put_u8(private_byte);
114        len += 1;
115
116        buf.put_u8(self.stored_in_database as u8);
117        len += 1;
118
119        len
120    }
121
122    fn from_compact(buf: &[u8], _len: usize) -> (Self, &[u8]) {
123        use bytes::Buf;
124
125        let (key, mut buf) = Vec::from_compact(buf, 0);
126
127        let stack_len = buf.get_u16() as usize;
128        let mut stack = Vec::with_capacity(stack_len);
129        for _ in 0..stack_len {
130            let item_len = buf.get_u16() as usize;
131            stack.push(RlpNode::from_raw(&buf[..item_len]).unwrap());
132            buf.advance(item_len);
133        }
134
135        let (value, mut buf) = HashBuilderValue::from_compact(buf, 0);
136
137        let groups_len = buf.get_u16() as usize;
138        let mut groups = Vec::with_capacity(groups_len);
139        for _ in 0..groups_len {
140            let (item, rest) = TrieMask::from_compact(buf, 0);
141            groups.push(item);
142            buf = rest;
143        }
144
145        let tree_masks_len = buf.get_u16() as usize;
146        let mut tree_masks = Vec::with_capacity(tree_masks_len);
147        for _ in 0..tree_masks_len {
148            let (item, rest) = TrieMask::from_compact(buf, 0);
149            tree_masks.push(item);
150            buf = rest;
151        }
152
153        let hash_masks_len = buf.get_u16() as usize;
154        let mut hash_masks = Vec::with_capacity(hash_masks_len);
155        for _ in 0..hash_masks_len {
156            let (item, rest) = TrieMask::from_compact(buf, 0);
157            hash_masks.push(item);
158            buf = rest;
159        }
160
161        // Deserialize Seismic-specific `is_private`
162        let private_byte = buf.get_u8();
163        let is_private = match private_byte {
164            0 => None,
165            1 => Some(false),
166            2 => Some(true),
167            _ => panic!("Invalid byte for Option<bool>: {}", private_byte),
168        };
169
170        let stored_in_database = buf.get_u8() != 0;
171
172        (
173            Self {
174                key,
175                stack,
176                value,
177                is_private,
178                groups,
179                tree_masks,
180                hash_masks,
181                stored_in_database,
182            },
183            buf,
184        )
185    }
186}
187
188#[cfg(test)]
189mod tests {
190    use super::*;
191    use reth_codecs::Compact;
192
193    #[test]
194    fn hash_builder_state_regression() {
195        let mut state = HashBuilderState::default();
196        state.stack.push(Default::default());
197        let mut buf = vec![];
198        let len = state.clone().to_compact(&mut buf);
199        let (decoded, _) = HashBuilderState::from_compact(&buf, len);
200        assert_eq!(state, decoded);
201    }
202
203    #[test]
204    fn hash_builder_state_regression_with_private() {
205        let mut state = HashBuilderState::default();
206        state.is_private = Some(true);
207        state.stack.push(Default::default());
208        let mut buf = vec![];
209        let len = state.clone().to_compact(&mut buf);
210        let (decoded, _) = HashBuilderState::from_compact(&buf, len);
211        assert_eq!(state, decoded);
212    }
213
214    #[cfg(feature = "arbitrary")]
215    proptest::proptest! {
216        #[test]
217        fn hash_builder_state_roundtrip(state in proptest_arbitrary_interop::arb::<HashBuilderState>()) {
218            let mut buf = vec![];
219            let len = state.to_compact(&mut buf);
220            let (decoded, _) = HashBuilderState::from_compact(&buf, len);
221            assert_eq!(state, decoded);
222        }
223    }
224}