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