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(feature = "arbitrary", derive(arbitrary::Arbitrary))]
11pub struct HashBuilderState {
12 pub key: Vec<u8>,
14 pub value: HashBuilderValue,
16 pub stack: Vec<RlpNode>,
18
19 pub groups: Vec<TrieMask>,
21 pub tree_masks: Vec<TrieMask>,
23 pub hash_masks: Vec<TrieMask>,
25
26 pub stored_in_database: bool,
28}
29
30impl From<HashBuilderState> for HashBuilder {
31 fn from(state: HashBuilderState) -> Self {
32 Self {
33 key: Nibbles::from_nibbles_unchecked(state.key),
34 stack: state.stack,
35 value: state.value,
36 state_masks: state.groups,
37 tree_masks: state.tree_masks,
38 hash_masks: state.hash_masks,
39 stored_in_database: state.stored_in_database,
40 updated_branch_nodes: None,
41 proof_retainer: None,
42 rlp_buf: Vec::with_capacity(32),
43 all_branch_nodes: false,
44 }
45 }
46}
47
48impl From<HashBuilder> for HashBuilderState {
49 fn from(state: HashBuilder) -> Self {
50 Self {
51 key: state.key.to_vec(),
52 stack: state.stack,
53 value: state.value,
54 groups: state.state_masks,
55 tree_masks: state.tree_masks,
56 hash_masks: state.hash_masks,
57 stored_in_database: state.stored_in_database,
58 }
59 }
60}
61
62#[cfg(any(test, feature = "reth-codec"))]
63impl reth_codecs::Compact for HashBuilderState {
64 fn to_compact<B>(&self, buf: &mut B) -> usize
65 where
66 B: bytes::BufMut + AsMut<[u8]>,
67 {
68 let mut len = 0;
69
70 let start = buf.remaining_mut();
72 self.key.to_compact(buf);
73 len += start - buf.remaining_mut();
74
75 buf.put_u16(self.stack.len() as u16);
76 len += 2;
77 for item in &self.stack {
78 buf.put_u16(item.len() as u16);
79 buf.put_slice(&item[..]);
80 len += 2 + item.len();
81 }
82
83 let start = buf.remaining_mut();
84 self.value.to_compact(buf);
85 len += start - buf.remaining_mut();
86
87 buf.put_u16(self.groups.len() as u16);
88 len += 2;
89 for item in &self.groups {
90 len += (*item).to_compact(buf);
91 }
92
93 buf.put_u16(self.tree_masks.len() as u16);
94 len += 2;
95 for item in &self.tree_masks {
96 len += (*item).to_compact(buf);
97 }
98
99 buf.put_u16(self.hash_masks.len() as u16);
100 len += 2;
101 for item in &self.hash_masks {
102 len += (*item).to_compact(buf);
103 }
104
105 buf.put_u8(self.stored_in_database as u8);
106 len += 1;
107 len
108 }
109
110 fn from_compact(buf: &[u8], _len: usize) -> (Self, &[u8]) {
111 use bytes::Buf;
112
113 let (key, mut buf) = Vec::from_compact(buf, 0);
114
115 let stack_len = buf.get_u16() as usize;
116 let mut stack = Vec::with_capacity(stack_len);
117 for _ in 0..stack_len {
118 let item_len = buf.get_u16() as usize;
119 stack.push(RlpNode::from_raw(&buf[..item_len]).unwrap());
120 buf.advance(item_len);
121 }
122
123 let (value, mut buf) = HashBuilderValue::from_compact(buf, 0);
124
125 let groups_len = buf.get_u16() as usize;
126 let mut groups = Vec::with_capacity(groups_len);
127 for _ in 0..groups_len {
128 let (item, rest) = TrieMask::from_compact(buf, 0);
129 groups.push(item);
130 buf = rest;
131 }
132
133 let tree_masks_len = buf.get_u16() as usize;
134 let mut tree_masks = Vec::with_capacity(tree_masks_len);
135 for _ in 0..tree_masks_len {
136 let (item, rest) = TrieMask::from_compact(buf, 0);
137 tree_masks.push(item);
138 buf = rest;
139 }
140
141 let hash_masks_len = buf.get_u16() as usize;
142 let mut hash_masks = Vec::with_capacity(hash_masks_len);
143 for _ in 0..hash_masks_len {
144 let (item, rest) = TrieMask::from_compact(buf, 0);
145 hash_masks.push(item);
146 buf = rest;
147 }
148
149 let stored_in_database = buf.get_u8() != 0;
150 (Self { key, stack, value, groups, tree_masks, hash_masks, stored_in_database }, buf)
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 #[cfg(feature = "arbitrary")]
158 use proptest::{collection::vec, prelude::*};
159 use reth_codecs::Compact;
160
161 #[test]
162 fn hash_builder_state_regression() {
163 let mut state = HashBuilderState::default();
164 state.stack.push(Default::default());
165 let mut buf = vec![];
166 let len = state.clone().to_compact(&mut buf);
167 let (decoded, _) = HashBuilderState::from_compact(&buf, len);
168 assert_eq!(state, decoded);
169 }
170
171 #[test]
172 fn hash_builder_state_fixed_slice() {
173 let mut state = HashBuilderState { key: vec![1, 2, 3], ..Default::default() };
174 state.value.set_bytes_owned(vec![42; 128]);
175 let mut expected = vec![];
176 assert_eq!(state.to_compact(&mut expected), expected.len());
177
178 let mut storage = vec![0; expected.len() + 1];
179 let mut remaining = storage.as_mut_slice();
180 let len = state.to_compact(&mut remaining);
181 assert_eq!(len, expected.len());
182 assert_eq!(remaining.len(), 1);
183 assert_eq!(&storage[..len], expected);
184 let (decoded, rest) = HashBuilderState::from_compact(&storage[..len], len);
185 assert_eq!(decoded, state);
186 assert!(rest.is_empty());
187 }
188
189 #[cfg(feature = "arbitrary")]
192 proptest::proptest! {
193 #[test]
194 fn hash_builder_state_roundtrip(
195 key in vec(0u8..16, 0..=64),
196 value in any::<HashBuilderValue>(),
197 stack in vec(any::<RlpNode>(), 0..=64),
198 groups in vec(any::<TrieMask>(), 0..=64),
199 tree_masks in vec(any::<TrieMask>(), 0..=64),
200 hash_masks in vec(any::<TrieMask>(), 0..=64),
201 stored_in_database in any::<bool>(),
202 ) {
203 let state = HashBuilderState {
204 key, value, stack, groups, tree_masks, hash_masks, stored_in_database,
205 };
206 let mut buf = vec![];
207 let len = state.to_compact(&mut buf);
208 prop_assert_eq!(len, buf.len());
209 let (decoded, rest) = HashBuilderState::from_compact(&buf, len);
210 prop_assert_eq!(state, decoded);
211 prop_assert!(rest.is_empty());
212 }
213 }
214}