reth_trie_common/
storage.rs

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