reth_trie_common/hash_builder/
state.rs1use crate::TrieMask;
2use alloc::vec::Vec;
3use alloy_trie::{hash_builder::HashBuilderValue, nodes::RlpNode, HashBuilder};
4use nybbles::Nibbles;
5
6#[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 pub key: Vec<u8>,
18 pub value: HashBuilderValue,
20 pub is_private: Option<bool>,
22 pub stack: Vec<RlpNode>,
24
25 pub groups: Vec<TrieMask>,
27 pub tree_masks: Vec<TrieMask>,
29 pub hash_masks: Vec<TrieMask>,
31
32 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 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 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}