Skip to main content

reth_trie_common/
trie_node_v2.rs

1//! Version 2 types related to representing nodes in an MPT.
2
3use crate::BranchNodeMasks;
4use alloc::vec::Vec;
5use alloy_primitives::hex;
6use alloy_rlp::{bytes, Decodable, Encodable, EMPTY_STRING_CODE};
7use alloy_trie::{
8    nodes::{BranchNodeRef, ExtensionNode, ExtensionNodeRef, LeafNode, RlpNode, TrieNode},
9    Nibbles, TrieMask,
10};
11use core::fmt;
12
13/// Carries all information needed by a sparse trie to reveal a particular node.
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct ProofTrieNodeV2 {
16    /// Path of the node.
17    pub path: Nibbles,
18    /// The node itself.
19    pub node: TrieNodeV2,
20    /// Tree and hash masks for the node, if known.
21    /// Both masks are always set together (from database branch nodes).
22    pub masks: Option<BranchNodeMasks>,
23}
24
25impl ProofTrieNodeV2 {
26    /// Converts an iterator of `(path, TrieNode, masks)` tuples into `Vec<ProofTrieNodeV2>`,
27    /// merging extension nodes into their child branch nodes.
28    ///
29    /// The input **must** be sorted in depth-first order (children before parents) for extension
30    /// merging to work correctly.
31    pub fn from_sorted_trie_nodes(
32        iter: impl IntoIterator<Item = (Nibbles, TrieNode, Option<BranchNodeMasks>)>,
33    ) -> Vec<Self> {
34        let iter = iter.into_iter();
35        let mut result = Vec::with_capacity(iter.size_hint().0);
36
37        for (path, node, masks) in iter {
38            match node {
39                TrieNode::EmptyRoot => {
40                    result.push(Self { path, node: TrieNodeV2::EmptyRoot, masks });
41                }
42                TrieNode::Leaf(leaf) => {
43                    result.push(Self { path, node: TrieNodeV2::Leaf(leaf), masks });
44                }
45                TrieNode::Branch(branch) => {
46                    result.push(Self {
47                        path,
48                        node: TrieNodeV2::Branch(BranchNodeV2 {
49                            key: Nibbles::new(),
50                            branch_rlp_node: None,
51                            stack: branch.stack,
52                            state_mask: branch.state_mask,
53                        }),
54                        masks,
55                    });
56                }
57                TrieNode::Extension(ext) => {
58                    // In depth-first order, the child branch comes BEFORE the parent
59                    // extension. The child branch should be the last item we added to
60                    // result, at path extension.path + extension.key.
61                    let expected_branch_path = path.join(&ext.key);
62
63                    // Check if the last item in result is the child branch
64                    if let Some(last) = result.last_mut() &&
65                        last.path == expected_branch_path &&
66                        let TrieNodeV2::Branch(branch_v2) = &mut last.node
67                    {
68                        debug_assert!(
69                            branch_v2.key.is_empty(),
70                            "Branch at {:?} already has extension key {:?}",
71                            last.path,
72                            branch_v2.key
73                        );
74                        branch_v2.key = ext.key;
75                        branch_v2.branch_rlp_node = Some(ext.child);
76                        last.path = path;
77                    }
78
79                    // If we reach here, the extension's child is not a branch in the
80                    // result. This happens when the child branch is hashed (not revealed
81                    // in the proof). In V2 format, extension nodes are always combined
82                    // with their child branch, so we skip extension nodes whose child
83                    // isn't revealed.
84                }
85            }
86        }
87
88        result
89    }
90}
91
92/// Enum representing an MPT trie node.
93///
94/// This is a V2 representiation, differing from [`TrieNode`] in that branch and extension nodes are
95/// compressed into a single node.
96#[derive(PartialEq, Eq, Clone, Debug)]
97#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
98pub enum TrieNodeV2 {
99    /// Variant representing empty root node.
100    EmptyRoot,
101    /// Variant representing a [`BranchNodeV2`].
102    Branch(BranchNodeV2),
103    /// Variant representing a [`LeafNode`].
104    Leaf(LeafNode),
105    /// Variant representing an [`ExtensionNode`].
106    ///
107    /// This will only be used for extension nodes for which child is not inlined. This variant
108    /// will never be produced by proof workers that will always reveal a full path to a requested
109    /// leaf.
110    Extension(ExtensionNode),
111}
112
113impl Encodable for TrieNodeV2 {
114    fn encode(&self, out: &mut dyn bytes::BufMut) {
115        match self {
116            Self::EmptyRoot => {
117                out.put_u8(EMPTY_STRING_CODE);
118            }
119            Self::Leaf(leaf) => {
120                leaf.as_ref().encode(out);
121            }
122            Self::Branch(branch) => branch.encode(out),
123            Self::Extension(ext) => {
124                ext.encode(out);
125            }
126        }
127    }
128}
129
130impl Decodable for TrieNodeV2 {
131    fn decode(buf: &mut &[u8]) -> Result<Self, alloy_rlp::Error> {
132        match TrieNode::decode(buf)? {
133            TrieNode::EmptyRoot => Ok(Self::EmptyRoot),
134            TrieNode::Leaf(leaf) => Ok(Self::Leaf(leaf)),
135            TrieNode::Branch(branch) => Ok(Self::Branch(BranchNodeV2::new(
136                Default::default(),
137                branch.stack,
138                branch.state_mask,
139                None,
140            ))),
141            TrieNode::Extension(ext) => {
142                if ext.child.is_hash() {
143                    Ok(Self::Extension(ext))
144                } else {
145                    let Self::Branch(mut branch) = Self::decode(&mut ext.child.as_ref())? else {
146                        return Err(alloy_rlp::Error::Custom(
147                            "extension node child is not a branch",
148                        ));
149                    };
150
151                    branch.key = ext.key;
152
153                    Ok(Self::Branch(branch))
154                }
155            }
156        }
157    }
158}
159
160/// A branch node in an Ethereum Merkle Patricia Trie.
161///
162/// Branch node is a 17-element array consisting of 16 slots that correspond to each hexadecimal
163/// character and an additional slot for a value. We do exclude the node value since all paths have
164/// a fixed size.
165///
166/// This node also encompasses the possible parent extension node of a branch via the `key` field.
167#[derive(PartialEq, Eq, Clone, Default)]
168#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
169pub struct BranchNodeV2 {
170    /// The key for the branch's parent extension. if key is empty then the branch does not have a
171    /// parent extension.
172    pub key: Nibbles,
173    /// The collection of RLP encoded children.
174    pub stack: Vec<RlpNode>,
175    /// The bitmask indicating the presence of children at the respective nibble positions
176    pub state_mask: TrieMask,
177    /// [`RlpNode`] encoding of the branch node. Always provided when `key` is not empty (i.e this
178    /// is an extension node).
179    pub branch_rlp_node: Option<RlpNode>,
180}
181
182impl fmt::Debug for BranchNodeV2 {
183    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184        f.debug_struct("BranchNode")
185            .field("key", &self.key)
186            .field("stack", &self.stack.iter().map(hex::encode).collect::<Vec<_>>())
187            .field("state_mask", &self.state_mask)
188            .field("branch_rlp_node", &self.branch_rlp_node)
189            .finish()
190    }
191}
192
193impl BranchNodeV2 {
194    /// Creates a new branch node with the given short key, stack, and state mask.
195    pub const fn new(
196        key: Nibbles,
197        stack: Vec<RlpNode>,
198        state_mask: TrieMask,
199        branch_rlp_node: Option<RlpNode>,
200    ) -> Self {
201        Self { key, stack, state_mask, branch_rlp_node }
202    }
203}
204
205impl Encodable for BranchNodeV2 {
206    fn encode(&self, out: &mut dyn bytes::BufMut) {
207        if self.key.is_empty() {
208            BranchNodeRef::new(&self.stack, self.state_mask).encode(out);
209            return;
210        }
211
212        let branch_rlp_node = self
213            .branch_rlp_node
214            .as_ref()
215            .expect("branch_rlp_node must always be present for extension nodes");
216
217        ExtensionNodeRef::new(&self.key, branch_rlp_node.as_slice()).encode(out);
218    }
219}