1#![cfg_attr(not(feature = "std"), no_std)]
2#![cfg_attr(not(feature = "std"), allow(clippy::arc_with_non_send_sync))]
11
12#[macro_use]
13extern crate alloc;
14
15pub mod db;
16pub mod error;
17#[cfg(feature = "std")]
19pub mod logger;
20mod nibbles;
21pub mod node;
22mod node_hash;
23pub mod rkyv_utils;
24mod rlp;
25#[cfg(test)]
26mod test_utils;
27#[cfg(feature = "std")]
29pub mod threadpool;
30mod trie_iter;
31#[cfg(feature = "std")]
32pub mod trie_sorted;
33mod verify_range;
34
35use alloc::collections::BTreeMap;
36use alloc::sync::Arc;
37#[cfg(not(feature = "std"))]
38use alloc::{boxed::Box, vec::Vec};
39use ethereum_types::H256;
40use ethrex_crypto::{Crypto, NativeCrypto};
41use ethrex_rlp::constants::RLP_NULL;
42use ethrex_rlp::encode::RLPEncode;
43
44#[cfg(feature = "std")]
45use rustc_hash::FxHashSet;
46#[cfg(feature = "std")]
50pub use rustc_hash::FxHashMap;
51#[cfg(not(feature = "std"))]
53pub type FxHashMap<K, V> = hashbrown::HashMap<K, V, rustc_hash::FxBuildHasher>;
54#[cfg(not(feature = "std"))]
55type FxHashSet<K> = hashbrown::HashSet<K, rustc_hash::FxBuildHasher>;
56
57pub use self::db::{InMemoryTrieDB, TrieDB};
58#[cfg(feature = "std")]
59pub use self::logger::{TrieLogger, TrieWitness};
60pub use self::nibbles::Nibbles;
61#[cfg(feature = "std")]
62pub use self::threadpool::ThreadPool;
63pub use self::verify_range::verify_range;
64pub use self::{
65 node::{Node, NodeRef, OnceLock},
66 node_hash::NodeHash,
67};
68
69pub use self::error::{ExtensionNodeErrorData, InconsistentTreeError, TrieError};
70use self::{node::LeafNode, trie_iter::TrieIterator};
71
72use ethrex_rlp::decode::RLPDecode;
73
74pub const EMPTY_TRIE_HASH: H256 = H256([
79 0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e,
80 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0, 0x01, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21,
81]);
82
83pub type PathRLP = Vec<u8>;
85pub type ValueRLP = Vec<u8>;
87pub type NodeRLP = Vec<u8>;
89pub type TrieNode = (Nibbles, NodeRLP);
91
92pub struct Trie {
94 db: Box<dyn TrieDB>,
95 pub root: NodeRef,
96 pending_removal: FxHashSet<Nibbles>,
97 dirty: FxHashSet<Nibbles>,
98}
99
100impl Default for Trie {
102 fn default() -> Self {
103 Self::new_temp()
104 }
105}
106
107impl Trie {
108 pub fn new(db: Box<dyn TrieDB>) -> Self {
110 Self {
111 db,
112 root: NodeRef::default(),
113 pending_removal: Default::default(),
114 dirty: Default::default(),
115 }
116 }
117
118 pub fn open(db: Box<dyn TrieDB>, root: H256) -> Self {
120 Self {
121 db,
122 root: if root != EMPTY_TRIE_HASH {
123 NodeHash::from(root).into()
124 } else {
125 Default::default()
126 },
127 pending_removal: Default::default(),
128 dirty: Default::default(),
129 }
130 }
131
132 pub fn db(&self) -> &dyn TrieDB {
137 self.db.as_ref()
138 }
139
140 pub fn get(&self, pathrlp: &[u8]) -> Result<Option<ValueRLP>, TrieError> {
142 let path = Nibbles::from_bytes(pathrlp);
143
144 if !self.dirty.contains(&path) && self.db().flatkeyvalue_computed(path.clone()) {
145 let Some(value_rlp) = self.db.get(path)? else {
146 return Ok(None);
147 };
148 if value_rlp.is_empty() {
149 return Ok(None);
150 }
151 return Ok(Some(value_rlp));
152 }
153
154 Ok(match self.root {
155 NodeRef::Node(ref node, _) => node.get(self.db.as_ref(), path)?,
156 NodeRef::Hash(hash) if hash.is_valid() => {
157 Node::decode(&self.db.get(Nibbles::default())?.ok_or_else(|| {
158 TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFound(
159 hash.finalize(&NativeCrypto),
160 )))
161 })?)
162 .map_err(TrieError::RLPDecode)?
163 .get(self.db.as_ref(), path)?
164 }
165 _ => None,
166 })
167 }
168
169 pub fn insert(&mut self, path: PathRLP, value: ValueRLP) -> Result<(), TrieError> {
171 let path = Nibbles::from_bytes(&path);
172 self.pending_removal.remove(&path);
173 self.dirty.insert(path.clone());
174
175 if self.root.is_valid() {
176 self.root
178 .get_node_mut(self.db.as_ref(), Nibbles::default())?
179 .ok_or_else(|| {
180 TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFoundNoHash))
181 })?
182 .insert(self.db.as_ref(), path, value)?
183 } else {
184 self.root = Node::from(LeafNode::new(path, value)).into()
186 };
187 self.root.clear_hash();
188
189 Ok(())
190 }
191
192 pub fn remove(&mut self, path: &[u8]) -> Result<Option<ValueRLP>, TrieError> {
195 self.dirty.insert(Nibbles::from_bytes(path));
196 if !self.root.is_valid() {
197 return Ok(None);
198 }
199 self.pending_removal.insert(Nibbles::from_bytes(path));
200
201 let (is_trie_empty, value) = self
203 .root
204 .get_node_mut(self.db.as_ref(), Nibbles::default())?
205 .ok_or_else(|| {
206 TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFoundNoHash))
207 })?
208 .remove(self.db.as_ref(), Nibbles::from_bytes(path))?;
209 if is_trie_empty {
210 self.root = NodeRef::default();
211 } else {
212 self.root.clear_hash();
213 }
214
215 Ok(value)
216 }
217
218 pub fn hash(&mut self, crypto: &dyn Crypto) -> Result<H256, TrieError> {
222 self.commit(crypto)?;
223 Ok(self.hash_no_commit(crypto))
224 }
225
226 pub fn hash_no_commit(&self, crypto: &dyn Crypto) -> H256 {
229 if self.root.is_valid() {
230 let mut buf = Vec::with_capacity(512);
232 self.root
233 .compute_hash_no_alloc(&mut buf, crypto)
234 .finalize(crypto)
235 } else {
236 EMPTY_TRIE_HASH
237 }
238 }
239
240 pub fn get_root_node(&self, path: Nibbles) -> Result<Arc<Node>, TrieError> {
241 self.root
242 .get_node_checked(self.db.as_ref(), path)?
243 .ok_or_else(|| {
244 TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFound(
245 self.root
246 .compute_hash(&NativeCrypto)
247 .finalize(&NativeCrypto),
248 )))
249 })
250 }
251
252 pub fn collect_changes_since_last_hash(
258 &mut self,
259 crypto: &dyn Crypto,
260 ) -> (H256, Vec<TrieNode>) {
261 let updates = self.commit_without_storing(crypto);
262 let ret_hash = self.hash_no_commit(crypto);
263 (ret_hash, updates)
264 }
265
266 pub fn commit(&mut self, crypto: &dyn Crypto) -> Result<(), TrieError> {
271 let acc = self.commit_without_storing(crypto);
272 self.db.put_batch(acc)?;
273
274 self.db.commit()?;
276
277 Ok(())
278 }
279
280 pub fn commit_without_storing(&mut self, crypto: &dyn Crypto) -> Vec<TrieNode> {
283 let mut acc = Vec::new();
284 if self.root.is_valid() {
285 self.root.commit(Nibbles::default(), &mut acc, crypto);
286 }
287 if self.root.compute_hash(crypto) == NodeHash::Hashed(EMPTY_TRIE_HASH) {
288 acc.push((Nibbles::default(), vec![RLP_NULL]))
289 }
290 acc.extend(self.pending_removal.drain().map(|nib| (nib, vec![])));
291
292 acc
293 }
294
295 pub fn get_proof(&self, path: &[u8]) -> Result<Vec<NodeRLP>, TrieError> {
303 if self.root.is_valid() {
304 let hash = self.root.compute_hash(&NativeCrypto);
305
306 let mut node_path = Vec::new();
307 if let NodeHash::Inline((data, len)) = hash {
308 node_path.push(data[..len as usize].to_vec());
309 }
310
311 let root = match self
312 .root
313 .get_node_checked(self.db.as_ref(), Nibbles::default())?
314 {
315 Some(x) => x,
316 None => return Ok(Vec::new()),
317 };
318 root.get_path(self.db.as_ref(), Nibbles::from_bytes(path), &mut node_path)?;
319
320 Ok(node_path)
321 } else {
322 Ok(Vec::new())
323 }
324 }
325
326 pub fn get_proofs(
330 &self,
331 paths: &[PathRLP],
332 ) -> Result<(Option<NodeRLP>, Vec<NodeRLP>), TrieError> {
333 if self.root.is_valid() {
334 let encoded_root = self.get_root_node(Nibbles::default())?.encode_to_vec();
335
336 let mut node_path: FxHashSet<_> = Default::default();
337 for path in paths {
338 let mut nodes = self.get_proof(path)?;
339 nodes.swap_remove(0);
340 node_path.extend(nodes);
341 }
342
343 Ok((Some(encoded_root), node_path.into_iter().collect()))
344 } else {
345 Ok((None, Vec::new()))
346 }
347 }
348
349 #[cfg(feature = "std")]
350 pub fn empty_in_memory() -> Self {
351 Self::new(Box::new(InMemoryTrieDB::new(Arc::new(
352 std::sync::Mutex::new(BTreeMap::new()),
353 ))))
354 }
355
356 pub fn get_embedded_root(
358 all_nodes: &BTreeMap<H256, Node>,
359 root_hash: H256,
360 ) -> Result<NodeRef, TrieError> {
361 if root_hash == EMPTY_TRIE_HASH {
363 return Ok(NodeRef::default());
364 }
365
366 let root_rlp = all_nodes.get(&root_hash).ok_or_else(|| {
367 TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFound(root_hash)))
368 })?;
369
370 fn get_embedded_node(
371 all_nodes: &BTreeMap<H256, Node>,
372 cur_node: &Node,
373 ) -> Result<Node, TrieError> {
374 Ok(match cur_node.clone() {
375 Node::Branch(mut node) => {
376 for choice in &mut node.choices {
377 let NodeRef::Hash(hash) = *choice else {
378 continue;
379 };
380
381 if hash.is_valid() {
382 *choice = match all_nodes.get(&hash.finalize(&NativeCrypto)) {
383 Some(node) => get_embedded_node(all_nodes, node)?.into(),
384 None => hash.into(),
385 };
386 }
387 }
388
389 (*node).into()
390 }
391 Node::Extension(mut node) => {
392 let NodeRef::Hash(hash) = node.child else {
393 return Ok(node.into());
394 };
395
396 node.child = match all_nodes.get(&hash.finalize(&NativeCrypto)) {
397 Some(node) => get_embedded_node(all_nodes, node)?.into(),
398 None => hash.into(),
399 };
400
401 node.into()
402 }
403 Node::Leaf(node) => node.into(),
404 })
405 }
406
407 let root = get_embedded_node(all_nodes, root_rlp)?;
408 Ok(root.into())
409 }
410
411 pub fn get_embedded_root_committed(
415 all_nodes: &FxHashMap<H256, Node>,
416 root_hash: H256,
417 crypto: &dyn Crypto,
418 ) -> Result<NodeRef, TrieError> {
419 if root_hash == EMPTY_TRIE_HASH {
421 return Ok(NodeRef::default());
422 }
423
424 let root_rlp = all_nodes.get(&root_hash).ok_or_else(|| {
425 TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFound(root_hash)))
426 })?;
427
428 fn node_with_hash(node: Node, hash: NodeHash) -> NodeRef {
435 NodeRef::Node(Arc::new(node), OnceLock::from(hash))
436 }
437
438 fn get_embedded_node_committed(
439 all_nodes: &FxHashMap<H256, Node>,
440 cur_node: &Node,
441 crypto: &dyn Crypto,
442 ) -> Result<Node, TrieError> {
443 Ok(match cur_node.clone() {
444 Node::Branch(mut node) => {
445 for choice in &mut node.choices {
446 let NodeRef::Hash(hash) = *choice else {
447 continue;
448 };
449
450 if hash.is_valid() {
451 *choice = match all_nodes.get(&hash.finalize(crypto)) {
452 Some(node) => node_with_hash(
453 get_embedded_node_committed(all_nodes, node, crypto)?,
454 hash,
455 ),
456 None => hash.into(),
457 };
458 }
459 }
460
461 (*node).into()
462 }
463 Node::Extension(mut node) => {
464 let NodeRef::Hash(hash) = node.child else {
465 return Ok(node.into());
466 };
467
468 node.child = match all_nodes.get(&hash.finalize(crypto)) {
469 Some(node) => node_with_hash(
470 get_embedded_node_committed(all_nodes, node, crypto)?,
471 hash,
472 ),
473 None => hash.into(),
474 };
475
476 node.into()
477 }
478 Node::Leaf(node) => node.into(),
479 })
480 }
481
482 let root = get_embedded_node_committed(all_nodes, root_rlp, crypto)?;
483 Ok(node_with_hash(root, NodeHash::Hashed(root_hash)))
484 }
485
486 #[cfg(feature = "std")]
494 pub fn from_nodes(
495 root_hash: H256,
496 state_nodes: &BTreeMap<H256, Node>,
497 ) -> Result<Self, TrieError> {
498 let mut trie = Trie::new(Box::new(InMemoryTrieDB::default()));
499 let root = Self::get_embedded_root(state_nodes, root_hash)?;
500 trie.root = root;
501
502 Ok(trie)
503 }
504
505 pub fn compute_hash_from_unsorted_iter(
507 iter: impl Iterator<Item = (PathRLP, ValueRLP)>,
508 crypto: &dyn Crypto,
509 ) -> H256 {
510 let mut trie = Trie::stateless();
511 for (path, value) in iter {
512 trie.insert(path, value).unwrap();
514 }
515
516 trie.hash_no_commit(crypto)
517 }
518
519 pub(crate) fn stateless() -> Trie {
522 struct NullTrieDB;
524
525 impl TrieDB for NullTrieDB {
526 fn get(&self, _key: Nibbles) -> Result<Option<Vec<u8>>, TrieError> {
527 Ok(None)
528 }
529
530 fn put_batch(&self, _key_values: Vec<TrieNode>) -> Result<(), TrieError> {
531 Ok(())
532 }
533 }
534
535 Trie::new(Box::new(NullTrieDB))
536 }
537
538 pub fn get_node(&self, partial_path: &PathRLP) -> Result<Vec<u8>, TrieError> {
541 let partial_path = match partial_path.len() {
543 n if n < 32 => Nibbles::decode_compact(partial_path),
545 32 => Nibbles::from_bytes(partial_path),
547 _ => return Ok(vec![]),
549 };
550
551 fn get_node_inner(
552 db: &dyn TrieDB,
553 current_path: Nibbles,
554 node: &Node,
555 mut partial_path: Nibbles,
556 ) -> Result<Vec<u8>, TrieError> {
557 if partial_path.is_empty() {
559 return Ok(node.encode_to_vec());
560 }
561 match node {
562 Node::Branch(branch_node) => match partial_path.next_choice() {
563 Some(idx) => {
564 let child_ref = &branch_node.choices[idx];
565 if child_ref.is_valid() {
566 let child_path = current_path.append_new(idx as u8);
567 let child_node = child_ref
568 .get_node_checked(db, child_path.clone())?
569 .ok_or_else(|| {
570 TrieError::InconsistentTree(Box::new(
571 InconsistentTreeError::NodeNotFoundOnBranchNode(
572 child_ref
573 .compute_hash(&NativeCrypto)
574 .finalize(&NativeCrypto),
575 branch_node
576 .compute_hash(&NativeCrypto)
577 .finalize(&NativeCrypto),
578 child_path.clone(),
579 ),
580 ))
581 })?;
582 get_node_inner(db, child_path, &child_node, partial_path)
583 } else {
584 Ok(vec![])
585 }
586 }
587 _ => Ok(vec![]),
588 },
589 Node::Extension(extension_node) => {
590 if partial_path.skip_prefix(&extension_node.prefix)
591 && extension_node.child.is_valid()
592 {
593 let child_path = partial_path.concat(&extension_node.prefix);
594 let child_node = extension_node
595 .child
596 .get_node_checked(db, child_path.clone())?
597 .ok_or_else(|| {
598 TrieError::InconsistentTree(Box::new(
599 InconsistentTreeError::ExtensionNodeChildNotFound(
600 ExtensionNodeErrorData {
601 node_hash: extension_node
602 .child
603 .compute_hash(&NativeCrypto)
604 .finalize(&NativeCrypto),
605 extension_node_hash: extension_node
606 .compute_hash(&NativeCrypto)
607 .finalize(&NativeCrypto),
608 extension_node_prefix: extension_node.prefix.clone(),
609 node_path: child_path.clone(),
610 },
611 ),
612 ))
613 })?;
614 get_node_inner(db, child_path, &child_node, partial_path)
615 } else {
616 Ok(vec![])
617 }
618 }
619 Node::Leaf(_) => Ok(vec![]),
620 }
621 }
622
623 if self.root.is_valid() {
625 let root_node = self.get_root_node(Default::default())?;
626 get_node_inner(
627 self.db.as_ref(),
628 Default::default(),
629 &root_node,
630 partial_path,
631 )
632 } else {
633 Ok(Vec::new())
634 }
635 }
636
637 pub fn root_node(&self) -> Result<Option<Arc<Node>>, TrieError> {
638 if self.root.is_valid() {
639 self.root.get_node(self.db.as_ref(), Nibbles::default())
640 } else {
641 Ok(None)
642 }
643 }
644
645 pub fn new_temp() -> Self {
647 let db = InMemoryTrieDB::new(Default::default());
648 Trie::new(Box::new(db))
649 }
650
651 pub fn new_temp_with_root(root: NodeRef) -> Self {
655 let db = InMemoryTrieDB::new(Default::default());
656 let mut trie = Trie::new(Box::new(db));
657 trie.root = root;
658 trie
659 }
660
661 pub fn validate(self) -> Result<(), TrieError> {
668 let mut expected_count = if self.root.is_valid() { 1 } else { 0 };
669 for (_, node) in self.into_iter() {
670 expected_count -= 1;
671 match node {
672 Node::Branch(branch_node) => {
673 expected_count += branch_node
674 .choices
675 .iter()
676 .filter(|child| child.is_valid())
677 .count();
678 }
679 Node::Extension(_) => {
680 expected_count += 1;
681 }
682 Node::Leaf(_) => {}
683 }
684 }
685 if expected_count != 0 {
686 return Err(TrieError::Verify(format!(
687 "Node count mismatch, expected {expected_count} more"
688 )));
689 }
690 Ok(())
691 }
692
693 #[cfg(feature = "std")]
696 pub fn validate_parallel(self) -> Result<(), TrieError> {
697 use rayon::prelude::*;
698
699 if !self.root.is_valid() {
700 return Ok(());
701 }
702
703 let db = &*self.db;
704 let root_node = self
705 .root
706 .get_node_checked(db, Nibbles::default())?
707 .ok_or_else(|| TrieError::Verify("Root node not found".to_string()))?;
708
709 match &*root_node {
710 Node::Branch(branch_node) => {
711 let children: Vec<(Nibbles, NodeRef)> = branch_node
712 .choices
713 .iter()
714 .enumerate()
715 .filter(|(_, child)| child.is_valid())
716 .map(|(i, child)| {
717 let path = Nibbles::default().append_new(i as u8);
718 (path, child.clone())
719 })
720 .collect();
721
722 children.par_iter().try_for_each(|(start_path, start_ref)| {
723 validate_subtree(db, start_path.clone(), start_ref.clone())
724 })
725 }
726 _ => {
727 validate_subtree(db, Nibbles::default(), self.root.clone())
729 }
730 }
731 }
732}
733
734#[cfg(feature = "std")]
737fn validate_subtree(
738 db: &dyn TrieDB,
739 start_path: Nibbles,
740 start_ref: NodeRef,
741) -> Result<(), TrieError> {
742 let mut expected_count: isize = 1;
743 let mut stack = vec![(start_path, start_ref)];
744
745 while let Some((path, node_ref)) = stack.pop() {
746 let node = node_ref
747 .get_node_checked(db, path.clone())?
748 .ok_or_else(|| TrieError::Verify(format!("Missing node at path {path:?}")))?;
749
750 expected_count -= 1;
751 match &*node {
752 Node::Branch(branch) => {
753 for (choice, child) in branch.choices.iter().enumerate().rev() {
754 if child.is_valid() {
755 expected_count += 1;
756 stack.push((path.append_new(choice as u8), child.clone()));
757 }
758 }
759 }
760 Node::Extension(ext) => {
761 expected_count += 1;
762 stack.push((path.concat(&ext.prefix), ext.child.clone()));
763 }
764 Node::Leaf(_) => {}
765 }
766 }
767
768 if expected_count != 0 {
769 return Err(TrieError::Verify(format!(
770 "Node count mismatch in subtree, expected {expected_count} more"
771 )));
772 }
773 Ok(())
774}
775
776impl IntoIterator for Trie {
777 type Item = (Nibbles, Node);
778
779 type IntoIter = TrieIterator;
780
781 fn into_iter(self) -> Self::IntoIter {
782 TrieIterator::new(self)
783 }
784}
785
786pub struct ProofTrie(Trie);
787
788impl ProofTrie {
789 pub fn insert(
790 &mut self,
791 partial_path: Nibbles,
792 external_ref: NodeHash,
793 ) -> Result<(), TrieError> {
794 if self.0.root.is_valid() {
795 self.0
797 .root
798 .get_node_mut(self.0.db.as_ref(), Nibbles::default())?
799 .ok_or_else(|| {
800 TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFoundNoHash))
801 })?
802 .insert(self.0.db.as_ref(), partial_path, external_ref)?;
803 self.0.root.clear_hash();
804 } else {
805 self.0.root = external_ref.into();
806 };
807
808 Ok(())
809 }
810
811 pub fn hash(&self, crypto: &dyn Crypto) -> H256 {
812 self.0.hash_no_commit(crypto)
813 }
814}
815
816impl From<Trie> for ProofTrie {
817 fn from(value: Trie) -> Self {
818 Self(value)
819 }
820}
821
822#[cfg(test)]
823mod tests {
824 use super::*;
825 use ethrex_crypto::keccak::keccak_hash;
826
827 #[test]
830 fn empty_trie_hash_matches_keccak() {
831 assert_eq!(EMPTY_TRIE_HASH, H256(keccak_hash([RLP_NULL])));
832 }
833}