1use crate::{GenericMemoryDB, NodeCodec, StorageProof};
24use codec::{Compact, Decode, Encode};
25use hash_db::Hasher;
26use memory_db::KeyFunction;
27use parking_lot::{Mutex, MutexGuard};
28use std::{
29 collections::{HashMap, HashSet},
30 marker::PhantomData,
31 mem,
32 ops::DerefMut,
33 sync::{
34 atomic::{AtomicUsize, Ordering},
35 Arc,
36 },
37};
38use trie_db::{RecordedForKey, TrieAccess};
39
40const LOG_TARGET: &str = "trie-recorder";
41
42#[derive(Clone, Debug)]
46pub struct IgnoredNodes<H> {
47 nodes: HashSet<H>,
48}
49
50impl<H: Encode> Encode for IgnoredNodes<H> {
51 fn encode(&self) -> Vec<u8> {
52 let mut encoded = Compact::<u32>(self.nodes.len() as _).encode();
53 self.nodes.iter().for_each(|n| n.encode_to(&mut encoded));
54 encoded
55 }
56}
57
58impl<H: Decode + std::hash::Hash + Eq> Decode for IgnoredNodes<H> {
59 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
60 let len = Compact::<u32>::decode(input)?;
61 let data = codec::decode_vec_with_len(input, len.0 as _)?;
62 Ok(Self { nodes: HashSet::from_iter(data.into_iter()) })
63 }
64}
65
66impl<H> Default for IgnoredNodes<H> {
67 fn default() -> Self {
68 Self { nodes: HashSet::default() }
69 }
70}
71
72impl<H: Eq + std::hash::Hash + Clone> IgnoredNodes<H> {
73 pub fn from_storage_proof<Hasher: trie_db::Hasher<Out = H>>(proof: &StorageProof) -> Self {
77 Self { nodes: proof.iter_nodes().map(|n| Hasher::hash(&n)).collect() }
78 }
79
80 pub fn from_memory_db<Hasher: trie_db::Hasher<Out = H>, KF: KeyFunction<Hasher>>(
84 mut memory_db: GenericMemoryDB<Hasher, KF>,
85 ) -> Self {
86 Self {
87 nodes: memory_db
88 .drain()
89 .into_iter()
90 .filter(|(_, (_, counter))| *counter > 0)
92 .map(|(_, (data, _))| Hasher::hash(&data))
93 .collect(),
94 }
95 }
96
97 pub fn extend(&mut self, other: Self) {
99 self.nodes.extend(other.nodes.into_iter());
100 }
101
102 pub fn is_ignored(&self, node: &H) -> bool {
104 self.nodes.contains(node)
105 }
106}
107
108#[derive(Default)]
110struct Transaction<H> {
111 recorded_keys: HashMap<H, HashMap<Arc<[u8]>, Option<RecordedForKey>>>,
116 accessed_nodes: HashSet<H>,
120}
121
122struct RecorderInner<H> {
124 recorded_keys: HashMap<H, HashMap<Arc<[u8]>, RecordedForKey>>,
128
129 transactions: Vec<Transaction<H>>,
131
132 accessed_nodes: HashMap<H, Vec<u8>>,
136
137 ignored_nodes: IgnoredNodes<H>,
139}
140
141impl<H> Default for RecorderInner<H> {
142 fn default() -> Self {
143 Self {
144 recorded_keys: Default::default(),
145 accessed_nodes: Default::default(),
146 transactions: Vec::new(),
147 ignored_nodes: Default::default(),
148 }
149 }
150}
151
152pub struct Recorder<H: Hasher> {
158 inner: Arc<Mutex<RecorderInner<H::Out>>>,
159 encoded_size_estimation: Arc<AtomicUsize>,
163}
164
165impl<H: Hasher> Default for Recorder<H> {
166 fn default() -> Self {
167 Self { inner: Default::default(), encoded_size_estimation: Arc::new(0.into()) }
168 }
169}
170
171impl<H: Hasher> Clone for Recorder<H> {
172 fn clone(&self) -> Self {
173 Self {
174 inner: self.inner.clone(),
175 encoded_size_estimation: self.encoded_size_estimation.clone(),
176 }
177 }
178}
179
180impl<H: Hasher> Recorder<H> {
181 pub fn with_ignored_nodes(ignored_nodes: IgnoredNodes<H::Out>) -> Self {
185 Self {
186 inner: Arc::new(Mutex::new(RecorderInner { ignored_nodes, ..Default::default() })),
187 ..Default::default()
188 }
189 }
190
191 pub fn recorded_keys(&self) -> HashMap<H::Out, HashMap<Arc<[u8]>, RecordedForKey>> {
195 self.inner.lock().recorded_keys.clone()
196 }
197
198 #[inline]
205 pub fn as_trie_recorder(&self, storage_root: H::Out) -> TrieRecorder<'_, H> {
206 TrieRecorder::<H> {
207 inner: self.inner.lock(),
208 storage_root,
209 encoded_size_estimation: self.encoded_size_estimation.clone(),
210 _phantom: PhantomData,
211 }
212 }
213
214 pub fn drain_storage_proof(self) -> StorageProof {
223 let mut recorder = mem::take(&mut *self.inner.lock());
224 StorageProof::new(recorder.accessed_nodes.drain().map(|(_, v)| v))
225 }
226
227 pub fn to_storage_proof(&self) -> StorageProof {
234 let recorder = self.inner.lock();
235 StorageProof::new(recorder.accessed_nodes.values().cloned())
236 }
237
238 pub fn estimate_encoded_size(&self) -> usize {
243 self.encoded_size_estimation.load(Ordering::Relaxed)
244 }
245
246 pub fn reset(&self) {
250 mem::take(&mut *self.inner.lock());
251 self.encoded_size_estimation.store(0, Ordering::Relaxed);
252 }
253
254 pub fn start_transaction(&self) {
256 let mut inner = self.inner.lock();
257 inner.transactions.push(Default::default());
258 }
259
260 pub fn rollback_transaction(&self) -> Result<(), ()> {
264 let mut inner = self.inner.lock();
265
266 let mut new_encoded_size_estimation = self.encoded_size_estimation.load(Ordering::Relaxed);
269 let transaction = inner.transactions.pop().ok_or(())?;
270
271 transaction.accessed_nodes.into_iter().for_each(|n| {
272 if let Some(old) = inner.accessed_nodes.remove(&n) {
273 new_encoded_size_estimation =
274 new_encoded_size_estimation.saturating_sub(old.encoded_size());
275 }
276 });
277
278 transaction.recorded_keys.into_iter().for_each(|(storage_root, keys)| {
279 keys.into_iter().for_each(|(k, old_state)| {
280 if let Some(state) = old_state {
281 inner.recorded_keys.entry(storage_root).or_default().insert(k, state);
282 } else {
283 inner.recorded_keys.entry(storage_root).or_default().remove(&k);
284 }
285 });
286 });
287
288 self.encoded_size_estimation
289 .store(new_encoded_size_estimation, Ordering::Relaxed);
290
291 Ok(())
292 }
293
294 pub fn commit_transaction(&self) -> Result<(), ()> {
298 let mut inner = self.inner.lock();
299
300 let transaction = inner.transactions.pop().ok_or(())?;
301
302 if let Some(parent_transaction) = inner.transactions.last_mut() {
303 parent_transaction.accessed_nodes.extend(transaction.accessed_nodes);
304
305 transaction.recorded_keys.into_iter().for_each(|(storage_root, keys)| {
306 keys.into_iter().for_each(|(k, old_state)| {
307 parent_transaction
308 .recorded_keys
309 .entry(storage_root)
310 .or_default()
311 .entry(k)
312 .or_insert(old_state);
313 })
314 });
315 }
316
317 Ok(())
318 }
319}
320
321impl<H: Hasher> crate::ProofSizeProvider for Recorder<H> {
322 fn estimate_encoded_size(&self) -> usize {
323 Recorder::estimate_encoded_size(self)
324 }
325}
326
327pub struct TrieRecorder<'a, H: Hasher> {
329 inner: MutexGuard<'a, RecorderInner<H::Out>>,
330 storage_root: H::Out,
331 encoded_size_estimation: Arc<AtomicUsize>,
332 _phantom: PhantomData<H>,
333}
334
335impl<H: Hasher> crate::TrieRecorderProvider<H> for Recorder<H> {
336 type Recorder<'a>
337 = TrieRecorder<'a, H>
338 where
339 H: 'a;
340
341 fn drain_storage_proof(self) -> Option<StorageProof> {
342 Some(Recorder::drain_storage_proof(self))
343 }
344
345 fn as_trie_recorder(&self, storage_root: H::Out) -> Self::Recorder<'_> {
346 Recorder::as_trie_recorder(&self, storage_root)
347 }
348}
349
350impl<'a, H: Hasher> TrieRecorder<'a, H> {
351 fn update_recorded_keys(&mut self, full_key: &[u8], access: RecordedForKey) {
353 let inner = self.inner.deref_mut();
354
355 let entry =
356 inner.recorded_keys.entry(self.storage_root).or_default().entry(full_key.into());
357
358 let key = entry.key().clone();
359
360 let entry = if matches!(access, RecordedForKey::Value) {
363 entry.and_modify(|e| {
364 if let Some(tx) = inner.transactions.last_mut() {
365 tx.recorded_keys
367 .entry(self.storage_root)
368 .or_default()
369 .entry(key.clone())
370 .or_insert(Some(*e));
371 }
372
373 *e = access;
374 })
375 } else {
376 entry
377 };
378
379 entry.or_insert_with(|| {
380 if let Some(tx) = inner.transactions.last_mut() {
381 tx.recorded_keys
383 .entry(self.storage_root)
384 .or_default()
385 .entry(key)
386 .or_insert(None);
387 }
388
389 access
390 });
391 }
392}
393
394impl<'a, H: Hasher> trie_db::TrieRecorder<H::Out> for TrieRecorder<'a, H> {
395 fn record(&mut self, access: TrieAccess<H::Out>) {
396 let mut encoded_size_update = 0;
397
398 match access {
399 TrieAccess::NodeOwned { hash, node_owned } => {
400 let inner = self.inner.deref_mut();
401
402 if inner.ignored_nodes.is_ignored(&hash) {
403 tracing::trace!(
404 target: LOG_TARGET,
405 ?hash,
406 "Ignoring node",
407 );
408 return;
409 }
410
411 tracing::trace!(
412 target: LOG_TARGET,
413 ?hash,
414 "Recording node",
415 );
416
417 inner.accessed_nodes.entry(hash).or_insert_with(|| {
418 let node = node_owned.to_encoded::<NodeCodec<H>>();
419
420 encoded_size_update += node.encoded_size();
421
422 if let Some(tx) = inner.transactions.last_mut() {
423 tx.accessed_nodes.insert(hash);
424 }
425
426 node
427 });
428 },
429 TrieAccess::EncodedNode { hash, encoded_node } => {
430 let inner = self.inner.deref_mut();
431
432 if inner.ignored_nodes.is_ignored(&hash) {
433 tracing::trace!(
434 target: LOG_TARGET,
435 ?hash,
436 "Ignoring node",
437 );
438 return;
439 }
440
441 tracing::trace!(
442 target: LOG_TARGET,
443 hash = ?hash,
444 "Recording node",
445 );
446
447 inner.accessed_nodes.entry(hash).or_insert_with(|| {
448 let node = encoded_node.into_owned();
449
450 encoded_size_update += node.encoded_size();
451
452 if let Some(tx) = inner.transactions.last_mut() {
453 tx.accessed_nodes.insert(hash);
454 }
455
456 node
457 });
458 },
459 TrieAccess::Value { hash, value, full_key } => {
460 let inner = self.inner.deref_mut();
461
462 if inner.ignored_nodes.is_ignored(&hash) {
464 tracing::trace!(
465 target: LOG_TARGET,
466 ?hash,
467 "Ignoring value",
468 );
469 return;
470 }
471
472 tracing::trace!(
473 target: LOG_TARGET,
474 hash = ?hash,
475 key = ?sp_core::hexdisplay::HexDisplay::from(&full_key),
476 "Recording value",
477 );
478
479 inner.accessed_nodes.entry(hash).or_insert_with(|| {
480 let value = value.into_owned();
481
482 encoded_size_update += value.encoded_size();
483
484 if let Some(tx) = inner.transactions.last_mut() {
485 tx.accessed_nodes.insert(hash);
486 }
487
488 value
489 });
490
491 self.update_recorded_keys(full_key, RecordedForKey::Value);
492 },
493 TrieAccess::Hash { full_key } => {
494 tracing::trace!(
495 target: LOG_TARGET,
496 key = ?sp_core::hexdisplay::HexDisplay::from(&full_key),
497 "Recorded hash access for key",
498 );
499
500 self.update_recorded_keys(full_key, RecordedForKey::Hash);
503 },
504 TrieAccess::NonExisting { full_key } => {
505 tracing::trace!(
506 target: LOG_TARGET,
507 key = ?sp_core::hexdisplay::HexDisplay::from(&full_key),
508 "Recorded non-existing value access for key",
509 );
510
511 self.update_recorded_keys(full_key, RecordedForKey::Value);
515 },
516 TrieAccess::InlineValue { full_key } => {
517 tracing::trace!(
518 target: LOG_TARGET,
519 key = ?sp_core::hexdisplay::HexDisplay::from(&full_key),
520 "Recorded inline value access for key",
521 );
522
523 self.update_recorded_keys(full_key, RecordedForKey::Value);
526 },
527 };
528
529 self.encoded_size_estimation.fetch_add(encoded_size_update, Ordering::Relaxed);
530 }
531
532 fn trie_nodes_recorded_for_key(&self, key: &[u8]) -> RecordedForKey {
533 self.inner
534 .recorded_keys
535 .get(&self.storage_root)
536 .and_then(|k| k.get(key).copied())
537 .unwrap_or(RecordedForKey::None)
538 }
539}
540
541#[cfg(test)]
542mod tests {
543 use super::*;
544 use crate::tests::create_trie;
545 use trie_db::{Trie, TrieDBBuilder, TrieRecorder};
546
547 type MemoryDB = crate::MemoryDB<sp_core::Blake2Hasher>;
548 type Layout = crate::LayoutV1<sp_core::Blake2Hasher>;
549 type Recorder = super::Recorder<sp_core::Blake2Hasher>;
550
551 const TEST_DATA: &[(&[u8], &[u8])] =
552 &[(b"key1", &[1; 64]), (b"key2", &[2; 64]), (b"key3", &[3; 64]), (b"key4", &[4; 64])];
553
554 #[test]
555 fn recorder_works() {
556 let (db, root) = create_trie::<Layout>(TEST_DATA);
557
558 let recorder = Recorder::default();
559
560 {
561 let mut trie_recorder = recorder.as_trie_recorder(root);
562 let trie = TrieDBBuilder::<Layout>::new(&db, &root)
563 .with_recorder(&mut trie_recorder)
564 .build();
565 assert_eq!(TEST_DATA[0].1.to_vec(), trie.get(TEST_DATA[0].0).unwrap().unwrap());
566 }
567
568 let storage_proof = recorder.drain_storage_proof();
569 let memory_db: MemoryDB = storage_proof.into_memory_db();
570
571 let trie = TrieDBBuilder::<Layout>::new(&memory_db, &root).build();
573 assert_eq!(TEST_DATA[0].1.to_vec(), trie.get(TEST_DATA[0].0).unwrap().unwrap());
574 }
575
576 #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
577 struct RecorderStats {
578 accessed_nodes: usize,
579 recorded_keys: usize,
580 estimated_size: usize,
581 }
582
583 impl RecorderStats {
584 fn extract(recorder: &Recorder) -> Self {
585 let inner = recorder.inner.lock();
586
587 let recorded_keys =
588 inner.recorded_keys.iter().flat_map(|(_, keys)| keys.keys()).count();
589
590 Self {
591 recorded_keys,
592 accessed_nodes: inner.accessed_nodes.len(),
593 estimated_size: recorder.estimate_encoded_size(),
594 }
595 }
596 }
597
598 #[test]
599 fn recorder_transactions_rollback_work() {
600 let (db, root) = create_trie::<Layout>(TEST_DATA);
601
602 let recorder = Recorder::default();
603 let mut stats = vec![RecorderStats::default()];
604
605 for i in 0..4 {
606 recorder.start_transaction();
607 {
608 let mut trie_recorder = recorder.as_trie_recorder(root);
609 let trie = TrieDBBuilder::<Layout>::new(&db, &root)
610 .with_recorder(&mut trie_recorder)
611 .build();
612
613 assert_eq!(TEST_DATA[i].1.to_vec(), trie.get(TEST_DATA[i].0).unwrap().unwrap());
614 }
615 stats.push(RecorderStats::extract(&recorder));
616 }
617
618 assert_eq!(4, recorder.inner.lock().transactions.len());
619
620 for i in 0..5 {
621 assert_eq!(stats[4 - i], RecorderStats::extract(&recorder));
622
623 let storage_proof = recorder.to_storage_proof();
624 let memory_db: MemoryDB = storage_proof.into_memory_db();
625
626 let trie = TrieDBBuilder::<Layout>::new(&memory_db, &root).build();
628
629 for a in 0..4 {
631 if a < 4 - i {
632 assert_eq!(TEST_DATA[a].1.to_vec(), trie.get(TEST_DATA[a].0).unwrap().unwrap());
633 } else {
634 assert!(trie.get(TEST_DATA[a].0).is_err());
636 }
637 }
638
639 if i < 4 {
640 recorder.rollback_transaction().unwrap();
641 }
642 }
643
644 assert_eq!(0, recorder.inner.lock().transactions.len());
645 }
646
647 #[test]
648 fn recorder_transactions_commit_work() {
649 let (db, root) = create_trie::<Layout>(TEST_DATA);
650
651 let recorder = Recorder::default();
652
653 for i in 0..4 {
654 recorder.start_transaction();
655 {
656 let mut trie_recorder = recorder.as_trie_recorder(root);
657 let trie = TrieDBBuilder::<Layout>::new(&db, &root)
658 .with_recorder(&mut trie_recorder)
659 .build();
660
661 assert_eq!(TEST_DATA[i].1.to_vec(), trie.get(TEST_DATA[i].0).unwrap().unwrap());
662 }
663 }
664
665 let stats = RecorderStats::extract(&recorder);
666 assert_eq!(4, recorder.inner.lock().transactions.len());
667
668 for _ in 0..4 {
669 recorder.commit_transaction().unwrap();
670 }
671 assert_eq!(0, recorder.inner.lock().transactions.len());
672 assert_eq!(stats, RecorderStats::extract(&recorder));
673
674 let storage_proof = recorder.to_storage_proof();
675 let memory_db: MemoryDB = storage_proof.into_memory_db();
676
677 let trie = TrieDBBuilder::<Layout>::new(&memory_db, &root).build();
679
680 for i in 0..4 {
682 assert_eq!(TEST_DATA[i].1.to_vec(), trie.get(TEST_DATA[i].0).unwrap().unwrap());
683 }
684 }
685
686 #[test]
687 fn recorder_transactions_commit_and_rollback_work() {
688 let (db, root) = create_trie::<Layout>(TEST_DATA);
689
690 let recorder = Recorder::default();
691
692 for i in 0..2 {
693 recorder.start_transaction();
694 {
695 let mut trie_recorder = recorder.as_trie_recorder(root);
696 let trie = TrieDBBuilder::<Layout>::new(&db, &root)
697 .with_recorder(&mut trie_recorder)
698 .build();
699
700 assert_eq!(TEST_DATA[i].1.to_vec(), trie.get(TEST_DATA[i].0).unwrap().unwrap());
701 }
702 }
703
704 recorder.rollback_transaction().unwrap();
705
706 for i in 2..4 {
707 recorder.start_transaction();
708 {
709 let mut trie_recorder = recorder.as_trie_recorder(root);
710 let trie = TrieDBBuilder::<Layout>::new(&db, &root)
711 .with_recorder(&mut trie_recorder)
712 .build();
713
714 assert_eq!(TEST_DATA[i].1.to_vec(), trie.get(TEST_DATA[i].0).unwrap().unwrap());
715 }
716 }
717
718 recorder.rollback_transaction().unwrap();
719
720 assert_eq!(2, recorder.inner.lock().transactions.len());
721
722 for _ in 0..2 {
723 recorder.commit_transaction().unwrap();
724 }
725
726 assert_eq!(0, recorder.inner.lock().transactions.len());
727
728 let storage_proof = recorder.to_storage_proof();
729 let memory_db: MemoryDB = storage_proof.into_memory_db();
730
731 let trie = TrieDBBuilder::<Layout>::new(&memory_db, &root).build();
733
734 for i in 0..4 {
736 if i % 2 == 0 {
737 assert_eq!(TEST_DATA[i].1.to_vec(), trie.get(TEST_DATA[i].0).unwrap().unwrap());
738 } else {
739 assert!(trie.get(TEST_DATA[i].0).is_err());
740 }
741 }
742 }
743
744 #[test]
745 fn recorder_transaction_accessed_keys_works() {
746 let key = TEST_DATA[0].0;
747 let (db, root) = create_trie::<Layout>(TEST_DATA);
748
749 let recorder = Recorder::default();
750
751 {
752 let trie_recorder = recorder.as_trie_recorder(root);
753 assert!(matches!(trie_recorder.trie_nodes_recorded_for_key(key), RecordedForKey::None));
754 }
755
756 recorder.start_transaction();
757 {
758 let mut trie_recorder = recorder.as_trie_recorder(root);
759 let trie = TrieDBBuilder::<Layout>::new(&db, &root)
760 .with_recorder(&mut trie_recorder)
761 .build();
762
763 assert_eq!(
764 sp_core::Blake2Hasher::hash(TEST_DATA[0].1),
765 trie.get_hash(TEST_DATA[0].0).unwrap().unwrap()
766 );
767 assert!(matches!(trie_recorder.trie_nodes_recorded_for_key(key), RecordedForKey::Hash));
768 }
769
770 recorder.start_transaction();
771 {
772 let mut trie_recorder = recorder.as_trie_recorder(root);
773 let trie = TrieDBBuilder::<Layout>::new(&db, &root)
774 .with_recorder(&mut trie_recorder)
775 .build();
776
777 assert_eq!(TEST_DATA[0].1.to_vec(), trie.get(TEST_DATA[0].0).unwrap().unwrap());
778 assert!(matches!(
779 trie_recorder.trie_nodes_recorded_for_key(key),
780 RecordedForKey::Value,
781 ));
782 }
783
784 recorder.rollback_transaction().unwrap();
785 {
786 let trie_recorder = recorder.as_trie_recorder(root);
787 assert!(matches!(trie_recorder.trie_nodes_recorded_for_key(key), RecordedForKey::Hash));
788 }
789
790 recorder.rollback_transaction().unwrap();
791 {
792 let trie_recorder = recorder.as_trie_recorder(root);
793 assert!(matches!(trie_recorder.trie_nodes_recorded_for_key(key), RecordedForKey::None));
794 }
795
796 recorder.start_transaction();
797 {
798 let mut trie_recorder = recorder.as_trie_recorder(root);
799 let trie = TrieDBBuilder::<Layout>::new(&db, &root)
800 .with_recorder(&mut trie_recorder)
801 .build();
802
803 assert_eq!(TEST_DATA[0].1.to_vec(), trie.get(TEST_DATA[0].0).unwrap().unwrap());
804 assert!(matches!(
805 trie_recorder.trie_nodes_recorded_for_key(key),
806 RecordedForKey::Value,
807 ));
808 }
809
810 recorder.start_transaction();
811 {
812 let mut trie_recorder = recorder.as_trie_recorder(root);
813 let trie = TrieDBBuilder::<Layout>::new(&db, &root)
814 .with_recorder(&mut trie_recorder)
815 .build();
816
817 assert_eq!(
818 sp_core::Blake2Hasher::hash(TEST_DATA[0].1),
819 trie.get_hash(TEST_DATA[0].0).unwrap().unwrap()
820 );
821 assert!(matches!(
822 trie_recorder.trie_nodes_recorded_for_key(key),
823 RecordedForKey::Value
824 ));
825 }
826
827 recorder.rollback_transaction().unwrap();
828 {
829 let trie_recorder = recorder.as_trie_recorder(root);
830 assert!(matches!(
831 trie_recorder.trie_nodes_recorded_for_key(key),
832 RecordedForKey::Value
833 ));
834 }
835
836 recorder.rollback_transaction().unwrap();
837 {
838 let trie_recorder = recorder.as_trie_recorder(root);
839 assert!(matches!(trie_recorder.trie_nodes_recorded_for_key(key), RecordedForKey::None));
840 }
841 }
842
843 #[test]
844 fn recorder_ignoring_nodes_works() {
845 let (db, root) = create_trie::<Layout>(TEST_DATA);
846
847 let recorder = Recorder::default();
848
849 {
850 let mut trie_recorder = recorder.as_trie_recorder(root);
851 let trie = TrieDBBuilder::<Layout>::new(&db, &root)
852 .with_recorder(&mut trie_recorder)
853 .build();
854
855 for (key, data) in TEST_DATA.iter().take(3) {
856 assert_eq!(data.to_vec(), trie.get(&key).unwrap().unwrap());
857 }
858 }
859
860 assert!(recorder.estimate_encoded_size() > 10);
861 let mut ignored_nodes = IgnoredNodes::from_storage_proof::<sp_core::Blake2Hasher>(
862 &recorder.drain_storage_proof(),
863 );
864
865 let recorder = Recorder::with_ignored_nodes(ignored_nodes.clone());
866
867 {
868 let mut trie_recorder = recorder.as_trie_recorder(root);
869 let trie = TrieDBBuilder::<Layout>::new(&db, &root)
870 .with_recorder(&mut trie_recorder)
871 .build();
872
873 for (key, data) in TEST_DATA {
874 assert_eq!(data.to_vec(), trie.get(&key).unwrap().unwrap());
875 }
876 }
877
878 assert!(recorder.estimate_encoded_size() > TEST_DATA[3].1.len());
879 let ignored_nodes2 = IgnoredNodes::from_storage_proof::<sp_core::Blake2Hasher>(
880 &recorder.drain_storage_proof(),
881 );
882
883 ignored_nodes.extend(ignored_nodes2);
884
885 let recorder = Recorder::with_ignored_nodes(ignored_nodes);
886
887 {
888 let mut trie_recorder = recorder.as_trie_recorder(root);
889 let trie = TrieDBBuilder::<Layout>::new(&db, &root)
890 .with_recorder(&mut trie_recorder)
891 .build();
892
893 for (key, data) in TEST_DATA {
894 assert_eq!(data.to_vec(), trie.get(&key).unwrap().unwrap());
895 }
896 }
897 assert_eq!(0, recorder.estimate_encoded_size());
898 }
899}