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}