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