reth_trie_common/
storage.rs

1use super::{BranchNodeCompact, StoredNibblesSubKey};
2use reth_primitives_traits::ValueWithSubKey;
3
4/// Account storage trie node.
5///
6/// `nibbles` is the subkey when used as a value in the `StorageTrie` table.
7#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
8#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
9pub struct StorageTrieEntry {
10    /// The nibbles of the intermediate node
11    pub nibbles: StoredNibblesSubKey,
12    /// Encoded node.
13    pub node: BranchNodeCompact,
14}
15
16impl ValueWithSubKey for StorageTrieEntry {
17    type SubKey = StoredNibblesSubKey;
18
19    fn get_subkey(&self) -> Self::SubKey {
20        self.nibbles.clone()
21    }
22}
23
24// NOTE: Removing reth_codec and manually encode subkey
25// and compress second part of the value. If we have compression
26// over whole value (Even SubKey) that would mess up fetching of values with seek_by_key_subkey
27#[cfg(any(test, feature = "reth-codec"))]
28impl reth_codecs::Compact for StorageTrieEntry {
29    fn to_compact<B>(&self, buf: &mut B) -> usize
30    where
31        B: bytes::BufMut + AsMut<[u8]>,
32    {
33        let nibbles_len = self.nibbles.to_compact(buf);
34        let node_len = self.node.to_compact(buf);
35        nibbles_len + node_len
36    }
37
38    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
39        let (nibbles, buf) = StoredNibblesSubKey::from_compact(buf, 65);
40        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 65);
41        let this = Self { nibbles, node };
42        (this, buf)
43    }
44}
45
46/// Trie changeset entry representing the state of a trie node before a block.
47///
48/// `nibbles` is the subkey when used as a value in the changeset tables.
49#[derive(Debug, Clone, PartialEq, Eq)]
50#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
51pub struct TrieChangeSetsEntry {
52    /// The nibbles of the intermediate node
53    pub nibbles: StoredNibblesSubKey,
54    /// Node value prior to the block being processed, None indicating it didn't exist.
55    pub node: Option<BranchNodeCompact>,
56}
57
58impl ValueWithSubKey for TrieChangeSetsEntry {
59    type SubKey = StoredNibblesSubKey;
60
61    fn get_subkey(&self) -> Self::SubKey {
62        self.nibbles.clone()
63    }
64}
65
66#[cfg(any(test, feature = "reth-codec"))]
67impl reth_codecs::Compact for TrieChangeSetsEntry {
68    fn to_compact<B>(&self, buf: &mut B) -> usize
69    where
70        B: bytes::BufMut + AsMut<[u8]>,
71    {
72        let nibbles_len = self.nibbles.to_compact(buf);
73        let node_len = self.node.as_ref().map(|node| node.to_compact(buf)).unwrap_or(0);
74        nibbles_len + node_len
75    }
76
77    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
78        if len == 0 {
79            // Return an empty entry without trying to parse anything
80            return (
81                Self { nibbles: StoredNibblesSubKey::from(super::Nibbles::default()), node: None },
82                buf,
83            )
84        }
85
86        let (nibbles, buf) = StoredNibblesSubKey::from_compact(buf, 65);
87
88        if len <= 65 {
89            return (Self { nibbles, node: None }, buf)
90        }
91
92        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 65);
93        (Self { nibbles, node: Some(node) }, buf)
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100    use bytes::BytesMut;
101    use reth_codecs::Compact;
102
103    #[test]
104    fn test_trie_changesets_entry_full_empty() {
105        // Test a fully empty entry (empty nibbles, None node)
106        let entry = TrieChangeSetsEntry { nibbles: StoredNibblesSubKey::from(vec![]), node: None };
107
108        let mut buf = BytesMut::new();
109        let len = entry.to_compact(&mut buf);
110
111        // Empty nibbles takes 65 bytes (64 for padding + 1 for length)
112        // None node adds 0 bytes
113        assert_eq!(len, 65);
114        assert_eq!(buf.len(), 65);
115
116        // Deserialize and verify
117        let (decoded, remaining) = TrieChangeSetsEntry::from_compact(&buf, len);
118        assert_eq!(decoded.nibbles.0.to_vec(), Vec::<u8>::new());
119        assert_eq!(decoded.node, None);
120        assert_eq!(remaining.len(), 0);
121    }
122
123    #[test]
124    fn test_trie_changesets_entry_none_node() {
125        // Test non-empty nibbles with None node
126        let nibbles_data = vec![0x01, 0x02, 0x03, 0x04];
127        let entry = TrieChangeSetsEntry {
128            nibbles: StoredNibblesSubKey::from(nibbles_data.clone()),
129            node: None,
130        };
131
132        let mut buf = BytesMut::new();
133        let len = entry.to_compact(&mut buf);
134
135        // Nibbles takes 65 bytes regardless of content
136        assert_eq!(len, 65);
137
138        // Deserialize and verify
139        let (decoded, remaining) = TrieChangeSetsEntry::from_compact(&buf, len);
140        assert_eq!(decoded.nibbles.0.to_vec(), nibbles_data);
141        assert_eq!(decoded.node, None);
142        assert_eq!(remaining.len(), 0);
143    }
144
145    #[test]
146    fn test_trie_changesets_entry_empty_path_with_node() {
147        // Test empty path with Some node
148        // Using the same signature as in the codebase: (state_mask, hash_mask, tree_mask, hashes,
149        // value)
150        let test_node = BranchNodeCompact::new(
151            0b1111_1111_1111_1111, // state_mask: all children present
152            0b1111_1111_1111_1111, // hash_mask: all have hashes
153            0b0000_0000_0000_0000, // tree_mask: no embedded trees
154            vec![],                // hashes
155            None,                  // value
156        );
157
158        let entry = TrieChangeSetsEntry {
159            nibbles: StoredNibblesSubKey::from(vec![]),
160            node: Some(test_node.clone()),
161        };
162
163        let mut buf = BytesMut::new();
164        let len = entry.to_compact(&mut buf);
165
166        // Calculate expected length
167        let mut temp_buf = BytesMut::new();
168        let node_len = test_node.to_compact(&mut temp_buf);
169        assert_eq!(len, 65 + node_len);
170
171        // Deserialize and verify
172        let (decoded, remaining) = TrieChangeSetsEntry::from_compact(&buf, len);
173        assert_eq!(decoded.nibbles.0.to_vec(), Vec::<u8>::new());
174        assert_eq!(decoded.node, Some(test_node));
175        assert_eq!(remaining.len(), 0);
176    }
177
178    #[test]
179    fn test_trie_changesets_entry_normal() {
180        // Test normal case: non-empty path with Some node
181        let nibbles_data = vec![0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f];
182        // Using the same signature as in the codebase
183        let test_node = BranchNodeCompact::new(
184            0b0000_0000_1111_0000, // state_mask: some children present
185            0b0000_0000_0011_0000, // hash_mask: some have hashes
186            0b0000_0000_0000_0000, // tree_mask: no embedded trees
187            vec![],                // hashes (empty for this test)
188            None,                  // value
189        );
190
191        let entry = TrieChangeSetsEntry {
192            nibbles: StoredNibblesSubKey::from(nibbles_data.clone()),
193            node: Some(test_node.clone()),
194        };
195
196        let mut buf = BytesMut::new();
197        let len = entry.to_compact(&mut buf);
198
199        // Verify serialization length
200        let mut temp_buf = BytesMut::new();
201        let node_len = test_node.to_compact(&mut temp_buf);
202        assert_eq!(len, 65 + node_len);
203
204        // Deserialize and verify
205        let (decoded, remaining) = TrieChangeSetsEntry::from_compact(&buf, len);
206        assert_eq!(decoded.nibbles.0.to_vec(), nibbles_data);
207        assert_eq!(decoded.node, Some(test_node));
208        assert_eq!(remaining.len(), 0);
209    }
210
211    #[test]
212    fn test_trie_changesets_entry_from_compact_zero_len() {
213        // Test from_compact with zero length
214        let buf = vec![0x01, 0x02, 0x03];
215        let (decoded, remaining) = TrieChangeSetsEntry::from_compact(&buf, 0);
216
217        // Should return empty nibbles and None node
218        assert_eq!(decoded.nibbles.0.to_vec(), Vec::<u8>::new());
219        assert_eq!(decoded.node, None);
220        assert_eq!(remaining, &buf[..]); // Buffer should be unchanged
221    }
222}