reth_trie/proof_v2/node.rs
1use crate::proof_v2::DeferredValueEncoder;
2use alloy_rlp::Encodable;
3use alloy_trie::nodes::ExtensionNodeRef;
4use reth_execution_errors::trie::StateProofError;
5use reth_trie_common::{
6 BranchNode, BranchNodeMasks, ExtensionNode, LeafNode, LeafNodeRef, Nibbles, ProofTrieNode,
7 RlpNode, TrieMask, TrieNode,
8};
9
10/// A trie node which is the child of a branch in the trie.
11#[derive(Debug)]
12pub(crate) enum ProofTrieBranchChild<RF> {
13 /// A leaf node whose value has yet to be calculated and encoded.
14 Leaf {
15 /// The short key of the leaf.
16 short_key: Nibbles,
17 /// The [`DeferredValueEncoder`] which will encode the leaf's value.
18 value: RF,
19 },
20 /// An extension node whose child branch has been converted to an [`RlpNode`]
21 Extension {
22 /// The short key of the leaf.
23 short_key: Nibbles,
24 /// The [`RlpNode`] of the child branch.
25 child: RlpNode,
26 },
27 /// A branch node whose children have already been flattened into [`RlpNode`]s.
28 Branch {
29 /// The node itself, for use during RLP encoding.
30 node: BranchNode,
31 /// Bitmasks carried over from cached `BranchNodeCompact` values, if any.
32 masks: Option<BranchNodeMasks>,
33 },
34 /// A node whose type is not known, as it has already been converted to an [`RlpNode`].
35 RlpNode(RlpNode),
36}
37
38impl<RF: DeferredValueEncoder> ProofTrieBranchChild<RF> {
39 /// Converts this child into its RLP node representation. This potentially also returns an
40 /// `RlpNode` buffer which can be re-used for other [`ProofTrieBranchChild`]s.
41 pub(crate) fn into_rlp(
42 self,
43 buf: &mut Vec<u8>,
44 ) -> Result<(RlpNode, Option<Vec<RlpNode>>), StateProofError> {
45 match self {
46 Self::Leaf { short_key, value } => {
47 // RLP encode the value itself
48 value.encode(buf)?;
49 let value_enc_len = buf.len();
50
51 // Determine the required buffer size for the encoded leaf
52 let leaf_enc_len = LeafNodeRef::new(&short_key, buf).length();
53
54 // We want to re-use buf for the encoding of the leaf node as well. To do this we
55 // will keep appending to it, leaving the already encoded value in-place. First we
56 // must ensure the buffer is big enough, then we'll split.
57 buf.resize(value_enc_len + leaf_enc_len, 0);
58
59 // SAFETY we have just resized the above to be greater than `value_enc_len`, so it
60 // must be in-bounds.
61 let (value_buf, mut leaf_buf) =
62 unsafe { buf.split_at_mut_unchecked(value_enc_len) };
63
64 // Encode the leaf into the right side of the split buffer, and return the RlpNode.
65 LeafNodeRef::new(&short_key, value_buf).encode(&mut leaf_buf);
66 Ok((RlpNode::from_rlp(&buf[value_enc_len..]), None))
67 }
68 Self::Extension { short_key, child } => {
69 ExtensionNodeRef::new(&short_key, child.as_slice()).encode(buf);
70 Ok((RlpNode::from_rlp(buf), None))
71 }
72 Self::Branch { node: branch_node, .. } => {
73 branch_node.encode(buf);
74 Ok((RlpNode::from_rlp(buf), Some(branch_node.stack)))
75 }
76 Self::RlpNode(rlp_node) => Ok((rlp_node, None)),
77 }
78 }
79
80 /// Converts this child into a [`ProofTrieNode`] having the given path.
81 ///
82 /// # Panics
83 ///
84 /// If called on a [`Self::RlpNode`].
85 pub(crate) fn into_proof_trie_node(
86 self,
87 path: Nibbles,
88 buf: &mut Vec<u8>,
89 ) -> Result<ProofTrieNode, StateProofError> {
90 let (node, masks) = match self {
91 Self::Leaf { short_key, value } => {
92 value.encode(buf)?;
93 // Counter-intuitively a clone is better here than a `core::mem::take`. If we take
94 // the buffer then future RLP-encodes will need to re-allocate a new one, and
95 // RLP-encodes after those may need a bigger buffer and therefore re-alloc again.
96 //
97 // By cloning here we do a single allocation of exactly the size we need to take
98 // this value, and the passed in buffer can remain with whatever large capacity it
99 // already has.
100 let rlp_val = buf.clone();
101 (TrieNode::Leaf(LeafNode::new(short_key, rlp_val)), None)
102 }
103 Self::Extension { short_key, child } => {
104 (TrieNode::Extension(ExtensionNode { key: short_key, child }), None)
105 }
106 Self::Branch { node, masks } => (TrieNode::Branch(node), masks),
107 Self::RlpNode(_) => panic!("Cannot call `into_proof_trie_node` on RlpNode"),
108 };
109
110 Ok(ProofTrieNode { node, path, masks })
111 }
112
113 /// Returns the short key of the child, if it is a leaf or extension, or empty if its a
114 /// [`Self::Branch`] or [`Self::RlpNode`].
115 pub(crate) fn short_key(&self) -> &Nibbles {
116 match self {
117 Self::Leaf { short_key, .. } | Self::Extension { short_key, .. } => short_key,
118 Self::Branch { .. } | Self::RlpNode(_) => {
119 static EMPTY_NIBBLES: Nibbles = Nibbles::new();
120 &EMPTY_NIBBLES
121 }
122 }
123 }
124
125 /// Trims the given number of nibbles off the head of the short key.
126 ///
127 /// If the node is an extension and the given length is the same as its short key length, then
128 /// the node is replaced with its child.
129 ///
130 /// # Panics
131 ///
132 /// - If the given len is longer than the short key
133 /// - If the given len is the same as the length of a leaf's short key
134 /// - If the node is a [`Self::Branch`] or [`Self::RlpNode`]
135 pub(crate) fn trim_short_key_prefix(&mut self, len: usize) {
136 match self {
137 Self::Extension { short_key, child } if short_key.len() == len => {
138 *self = Self::RlpNode(core::mem::take(child));
139 }
140 Self::Leaf { short_key, .. } | Self::Extension { short_key, .. } => {
141 *short_key = trim_nibbles_prefix(short_key, len);
142 }
143 Self::Branch { .. } | Self::RlpNode(_) => {
144 panic!("Cannot call `trim_short_key_prefix` on Branch or RlpNode")
145 }
146 }
147 }
148}
149
150/// A single branch in the trie which is under construction. The actual child nodes of the branch
151/// will be tracked as [`ProofTrieBranchChild`]s on a stack.
152#[derive(Debug)]
153pub(crate) struct ProofTrieBranch {
154 /// The length of the parent extension node's short key. If zero then the branch's parent is
155 /// not an extension but instead another branch.
156 pub(crate) ext_len: u8,
157 /// A mask tracking which child nibbles are set on the branch so far. There will be a single
158 /// child on the stack for each set bit.
159 pub(crate) state_mask: TrieMask,
160 /// Bitmasks which are subsets of `state_mask`.
161 pub(crate) masks: Option<BranchNodeMasks>,
162}
163
164/// Trims the first `len` nibbles from the head of the given `Nibbles`.
165///
166/// # Panics
167///
168/// Panics if the given `len` is greater than the length of the `Nibbles`.
169pub(crate) fn trim_nibbles_prefix(n: &Nibbles, len: usize) -> Nibbles {
170 debug_assert!(n.len() >= len);
171 n.slice_unchecked(len, n.len())
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177
178 #[test]
179 fn test_trim_nibbles_prefix_basic() {
180 // Create nibbles [1, 2, 3, 4, 5, 6]
181 let nibbles = Nibbles::from_nibbles([1, 2, 3, 4, 5, 6]);
182
183 // Trim first 2 nibbles
184 let trimmed = trim_nibbles_prefix(&nibbles, 2);
185 assert_eq!(trimmed.len(), 4);
186
187 // Verify the remaining nibbles are [3, 4, 5, 6]
188 assert_eq!(trimmed.get(0), Some(3));
189 assert_eq!(trimmed.get(1), Some(4));
190 assert_eq!(trimmed.get(2), Some(5));
191 assert_eq!(trimmed.get(3), Some(6));
192 }
193
194 #[test]
195 fn test_trim_nibbles_prefix_zero() {
196 // Create nibbles [10, 11, 12, 13]
197 let nibbles = Nibbles::from_nibbles([10, 11, 12, 13]);
198
199 // Trim zero nibbles - should return identical nibbles
200 let trimmed = trim_nibbles_prefix(&nibbles, 0);
201 assert_eq!(trimmed, nibbles);
202 }
203
204 #[test]
205 fn test_trim_nibbles_prefix_all() {
206 // Create nibbles [1, 2, 3, 4]
207 let nibbles = Nibbles::from_nibbles([1, 2, 3, 4]);
208
209 // Trim all nibbles - should return empty
210 let trimmed = trim_nibbles_prefix(&nibbles, 4);
211 assert!(trimmed.is_empty());
212 }
213
214 #[test]
215 fn test_trim_nibbles_prefix_empty() {
216 // Create empty nibbles
217 let nibbles = Nibbles::new();
218
219 // Trim zero from empty - should return empty
220 let trimmed = trim_nibbles_prefix(&nibbles, 0);
221 assert!(trimmed.is_empty());
222 }
223}