1use super::{TrieCursor, TrieCursorFactory, TrieStorageCursor};
2use alloy_primitives::{map::B256Map, B256};
3use reth_storage_errors::db::DatabaseError;
4use reth_trie_common::{
5 prefix_set::{PrefixSet, TriePrefixSets},
6 BranchNodeCompact, Nibbles,
7};
8use std::sync::Arc;
9
10#[derive(Debug, Clone)]
16pub struct MaskedTrieCursorFactory<CF> {
17 cursor_factory: CF,
19 prefix_sets: TriePrefixSets,
21}
22
23impl<CF> MaskedTrieCursorFactory<CF> {
24 pub const fn new(cursor_factory: CF, prefix_sets: TriePrefixSets) -> Self {
26 Self { cursor_factory, prefix_sets }
27 }
28}
29
30impl<CF: TrieCursorFactory> TrieCursorFactory for MaskedTrieCursorFactory<CF> {
31 type AccountTrieCursor<'a>
32 = MaskedTrieCursor<CF::AccountTrieCursor<'a>>
33 where
34 Self: 'a;
35
36 type StorageTrieCursor<'a>
37 = MaskedTrieCursor<CF::StorageTrieCursor<'a>>
38 where
39 Self: 'a;
40
41 fn account_trie_cursor(&self) -> Result<Self::AccountTrieCursor<'_>, DatabaseError> {
42 let cursor = self.cursor_factory.account_trie_cursor()?;
43 Ok(MaskedTrieCursor::new(cursor, self.prefix_sets.account_prefix_set.clone()))
44 }
45
46 fn storage_trie_cursor(
47 &self,
48 hashed_address: B256,
49 ) -> Result<Self::StorageTrieCursor<'_>, DatabaseError> {
50 let cursor = self.cursor_factory.storage_trie_cursor(hashed_address)?;
51 let prefix_set =
52 self.prefix_sets.storage_prefix_sets.get(&hashed_address).cloned().unwrap_or_default();
53 Ok(MaskedTrieCursor::new_storage(
54 cursor,
55 prefix_set,
56 self.prefix_sets.storage_prefix_sets.clone(),
57 ))
58 }
59}
60
61#[derive(Debug)]
68pub struct MaskedTrieCursor<C> {
69 cursor: C,
71 prefix_set: PrefixSet,
73 storage_prefix_sets: Option<B256Map<PrefixSet>>,
75}
76
77impl<C> MaskedTrieCursor<C> {
78 pub const fn new(cursor: C, prefix_set: PrefixSet) -> Self {
81 Self { cursor, prefix_set, storage_prefix_sets: None }
82 }
83
84 pub const fn new_storage(
86 cursor: C,
87 prefix_set: PrefixSet,
88 storage_prefix_sets: B256Map<PrefixSet>,
89 ) -> Self {
90 Self { cursor, prefix_set, storage_prefix_sets: Some(storage_prefix_sets) }
91 }
92}
93
94impl<C: TrieCursor> MaskedTrieCursor<C> {
95 fn mask_node(&mut self, key: &Nibbles, node: &mut BranchNodeCompact) -> bool {
100 if !self.prefix_set.contains(key) {
101 return true;
102 }
103
104 node.root_hash = None;
106
107 let original_hash_mask = node.hash_mask;
108 if original_hash_mask.is_empty() {
109 return true;
110 }
111
112 let mut new_hash_mask = original_hash_mask;
113 let mut child_path = *key;
114 let key_len = key.len();
115
116 for nibble in original_hash_mask.iter() {
117 child_path.truncate(key_len);
118 child_path.push(nibble);
119
120 if self.prefix_set.contains(&child_path) {
121 new_hash_mask.unset_bit(nibble);
122 }
123 }
124
125 if new_hash_mask != original_hash_mask {
126 let hashes = Arc::make_mut(&mut node.hashes);
128 let mut write = 0;
129 for (read, nibble) in original_hash_mask.iter().enumerate() {
130 if new_hash_mask.is_bit_set(nibble) {
131 hashes[write] = hashes[read];
132 write += 1;
133 }
134 }
135 hashes.truncate(write);
136
137 node.hash_mask = new_hash_mask;
138
139 if node.hash_mask.is_empty() && node.tree_mask.is_empty() {
140 return false;
141 }
142 }
143
144 true
145 }
146
147 fn mask_entries(
149 &mut self,
150 mut entry: Option<(Nibbles, BranchNodeCompact)>,
151 ) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
152 while let Some((key, mut node)) = entry {
153 if self.mask_node(&key, &mut node) {
154 return Ok(Some((key, node)));
155 }
156 entry = self.cursor.next()?;
157 }
158 Ok(None)
159 }
160}
161
162impl<C: TrieCursor> TrieCursor for MaskedTrieCursor<C> {
163 fn seek_exact(
164 &mut self,
165 key: Nibbles,
166 ) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
167 if let Some((key, mut node)) = self.cursor.seek_exact(key)? {
168 if self.mask_node(&key, &mut node) {
169 Ok(Some((key, node)))
170 } else {
171 Ok(None)
172 }
173 } else {
174 Ok(None)
175 }
176 }
177
178 fn seek(
179 &mut self,
180 key: Nibbles,
181 ) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
182 let entry = self.cursor.seek(key)?;
183 self.mask_entries(entry)
184 }
185
186 fn next(&mut self) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
187 let entry = self.cursor.next()?;
188 self.mask_entries(entry)
189 }
190
191 fn current(&mut self) -> Result<Option<Nibbles>, DatabaseError> {
192 self.cursor.current()
193 }
194
195 fn reset(&mut self) {
196 self.cursor.reset();
197 }
198}
199
200impl<C: TrieStorageCursor> TrieStorageCursor for MaskedTrieCursor<C> {
201 fn set_hashed_address(&mut self, hashed_address: B256) {
202 self.cursor.set_hashed_address(hashed_address);
203 if let Some(storage_prefix_sets) = &self.storage_prefix_sets {
204 self.prefix_set = storage_prefix_sets.get(&hashed_address).cloned().unwrap_or_default();
205 }
206 }
207}
208
209#[cfg(test)]
210mod tests {
211 use super::*;
212 use crate::trie_cursor::mock::MockTrieCursor;
213 use parking_lot::Mutex;
214 use reth_trie_common::prefix_set::PrefixSetMut;
215 use std::{collections::BTreeMap, sync::Arc};
216
217 fn make_cursor(nodes: Vec<(Nibbles, BranchNodeCompact)>) -> MockTrieCursor {
218 let map: BTreeMap<Nibbles, BranchNodeCompact> = nodes.into_iter().collect();
219 MockTrieCursor::new(Arc::new(map), Arc::new(Mutex::new(Vec::new())))
220 }
221
222 fn node(state_mask: u16) -> BranchNodeCompact {
223 BranchNodeCompact::new(state_mask, 0, 0, vec![], None)
224 }
225
226 fn node_with_hashes(state_mask: u16, hash_mask: u16, hashes: Vec<B256>) -> BranchNodeCompact {
227 BranchNodeCompact::new(state_mask, 0, hash_mask, hashes, None)
228 }
229
230 fn node_with_tree_mask(
231 state_mask: u16,
232 tree_mask: u16,
233 hash_mask: u16,
234 hashes: Vec<B256>,
235 ) -> BranchNodeCompact {
236 BranchNodeCompact::new(state_mask, tree_mask, hash_mask, hashes, None)
237 }
238
239 fn hash(byte: u8) -> B256 {
240 B256::repeat_byte(byte)
241 }
242
243 #[test]
244 fn test_seek_masks_matching_child_hashes() {
245 let nodes = vec![(
248 Nibbles::from_nibbles([0x1]),
249 node_with_hashes(0b0000_0000_0010_0100, 0b0000_0000_0010_0100, vec![hash(2), hash(5)]),
250 )];
251
252 let mut ps = PrefixSetMut::default();
253 ps.insert(Nibbles::from_nibbles([0x1, 0x2]));
254
255 let inner = make_cursor(nodes);
256 let mut cursor = MaskedTrieCursor::new(inner, ps.freeze());
257
258 let result = cursor.seek(Nibbles::default()).unwrap();
259 let (key, node) = result.unwrap();
260 assert_eq!(key, Nibbles::from_nibbles([0x1]));
261 assert!(!node.hash_mask.is_bit_set(2));
263 assert!(node.hash_mask.is_bit_set(5));
264 assert_eq!(&*node.hashes, &[hash(5)]);
265 }
266
267 #[test]
268 fn test_seek_skips_fully_masked_node() {
269 let nodes = vec![
273 (
274 Nibbles::from_nibbles([0x1]),
275 node_with_hashes(0b0000_0000_0000_1000, 0b0000_0000_0000_1000, vec![hash(3)]),
276 ),
277 (Nibbles::from_nibbles([0x2]), node(0b0000_0000_0000_0001)),
278 ];
279
280 let mut ps = PrefixSetMut::default();
281 ps.insert(Nibbles::from_nibbles([0x1, 0x3]));
282
283 let inner = make_cursor(nodes);
284 let mut cursor = MaskedTrieCursor::new(inner, ps.freeze());
285
286 let result = cursor.seek(Nibbles::default()).unwrap();
287 assert_eq!(result, Some((Nibbles::from_nibbles([0x2]), node(0b0000_0000_0000_0001))));
288 }
289
290 #[test]
291 fn test_node_with_tree_mask_not_skipped() {
292 let nodes = vec![(
295 Nibbles::from_nibbles([0x1]),
296 node_with_tree_mask(
297 0b0000_0000_0000_1000,
298 0b0000_0000_0000_1000,
299 0b0000_0000_0000_1000,
300 vec![hash(3)],
301 ),
302 )];
303
304 let mut ps = PrefixSetMut::default();
305 ps.insert(Nibbles::from_nibbles([0x1, 0x3]));
306
307 let inner = make_cursor(nodes);
308 let mut cursor = MaskedTrieCursor::new(inner, ps.freeze());
309
310 let result = cursor.seek(Nibbles::default()).unwrap();
311 let (key, node) = result.unwrap();
312 assert_eq!(key, Nibbles::from_nibbles([0x1]));
313 assert!(node.hash_mask.is_empty());
314 assert!(node.tree_mask.is_bit_set(3));
315 assert!(node.hashes.is_empty());
316 }
317
318 #[test]
319 fn test_seek_exact_masks_hash_bits() {
320 let nodes = vec![(
321 Nibbles::from_nibbles([0x1]),
322 node_with_tree_mask(
323 0b0000_0000_0010_0100,
324 0b0000_0000_0010_0100,
325 0b0000_0000_0010_0100,
326 vec![hash(2), hash(5)],
327 ),
328 )];
329
330 let mut ps = PrefixSetMut::default();
331 ps.insert(Nibbles::from_nibbles([0x1, 0x5]));
332
333 let inner = make_cursor(nodes);
334 let mut cursor = MaskedTrieCursor::new(inner, ps.freeze());
335
336 let result = cursor.seek_exact(Nibbles::from_nibbles([0x1])).unwrap();
337 let (_, node) = result.unwrap();
338 assert!(node.hash_mask.is_bit_set(2));
339 assert!(!node.hash_mask.is_bit_set(5));
340 assert_eq!(&*node.hashes, &[hash(2)]);
341 }
342
343 #[test]
344 fn test_seek_exact_returns_none_for_fully_masked() {
345 let nodes = vec![(
346 Nibbles::from_nibbles([0x1]),
347 node_with_hashes(0b0000_0000_0000_0100, 0b0000_0000_0000_0100, vec![hash(2)]),
348 )];
349
350 let mut ps = PrefixSetMut::default();
351 ps.insert(Nibbles::from_nibbles([0x1, 0x2]));
352
353 let inner = make_cursor(nodes);
354 let mut cursor = MaskedTrieCursor::new(inner, ps.freeze());
355
356 let result = cursor.seek_exact(Nibbles::from_nibbles([0x1])).unwrap();
357 assert_eq!(result, None);
358 }
359
360 #[test]
361 fn test_next_masks_and_skips() {
362 let nodes = vec![
364 (
365 Nibbles::from_nibbles([0x1]),
366 node_with_hashes(0b0000_0000_0000_0010, 0b0000_0000_0000_0010, vec![hash(1)]),
367 ),
368 (
369 Nibbles::from_nibbles([0x2]),
370 node_with_hashes(0b0000_0000_0001_0000, 0b0000_0000_0001_0000, vec![hash(4)]),
371 ),
372 (
373 Nibbles::from_nibbles([0x3]),
374 node_with_hashes(0b0000_0000_0100_0000, 0b0000_0000_0100_0000, vec![hash(6)]),
375 ),
376 ];
377
378 let mut ps = PrefixSetMut::default();
379 ps.insert(Nibbles::from_nibbles([0x2, 0x4]));
380
381 let inner = make_cursor(nodes);
382 let mut cursor = MaskedTrieCursor::new(inner, ps.freeze());
383
384 let result = cursor.seek(Nibbles::from_nibbles([0x1])).unwrap();
386 let (key, node) = result.unwrap();
387 assert_eq!(key, Nibbles::from_nibbles([0x1]));
388 assert_eq!(&*node.hashes, &[hash(1)]);
389
390 let result = cursor.next().unwrap();
392 let (key, node) = result.unwrap();
393 assert_eq!(key, Nibbles::from_nibbles([0x3]));
394 assert_eq!(&*node.hashes, &[hash(6)]);
395 }
396
397 #[test]
398 fn test_no_match_returns_unchanged() {
399 let nodes = vec![(
400 Nibbles::from_nibbles([0x2]),
401 node_with_hashes(0b0000_0000_0000_0010, 0b0000_0000_0000_0010, vec![hash(1)]),
402 )];
403
404 let mut ps = PrefixSetMut::default();
405 ps.insert(Nibbles::from_nibbles([0x1, 0x3]));
406
407 let inner = make_cursor(nodes);
408 let mut cursor = MaskedTrieCursor::new(inner, ps.freeze());
409
410 let result = cursor.seek(Nibbles::default()).unwrap();
411 let (key, node) = result.unwrap();
412 assert_eq!(key, Nibbles::from_nibbles([0x2]));
413 assert!(node.hash_mask.is_bit_set(1));
415 assert_eq!(&*node.hashes, &[hash(1)]);
416 }
417
418 #[test]
419 fn test_empty_prefix_set_returns_all_unchanged() {
420 let h1 = hash(1);
421 let h2 = hash(2);
422 let nodes = vec![
423 (
424 Nibbles::from_nibbles([0x1]),
425 node_with_hashes(0b0000_0000_0000_0010, 0b0000_0000_0000_0010, vec![h1]),
426 ),
427 (
428 Nibbles::from_nibbles([0x2]),
429 node_with_hashes(0b0000_0000_0000_0100, 0b0000_0000_0000_0100, vec![h2]),
430 ),
431 ];
432
433 let ps = PrefixSetMut::default();
434 let inner = make_cursor(nodes);
435 let mut cursor = MaskedTrieCursor::new(inner, ps.freeze());
436
437 let r1 = cursor.seek(Nibbles::default()).unwrap().unwrap();
438 assert_eq!(r1.0, Nibbles::from_nibbles([0x1]));
439 assert_eq!(&*r1.1.hashes, &[h1]);
440
441 let r2 = cursor.next().unwrap().unwrap();
442 assert_eq!(r2.0, Nibbles::from_nibbles([0x2]));
443 assert_eq!(&*r2.1.hashes, &[h2]);
444
445 assert_eq!(cursor.next().unwrap(), None);
446 }
447
448 #[test]
449 fn test_root_hash_cleared_on_mask() {
450 let mut n =
451 node_with_hashes(0b0000_0000_0010_0100, 0b0000_0000_0010_0100, vec![hash(2), hash(5)]);
452 n.root_hash = Some(hash(0xFF));
453
454 let nodes = vec![(Nibbles::from_nibbles([0x1]), n)];
455
456 let mut ps = PrefixSetMut::default();
457 ps.insert(Nibbles::from_nibbles([0x1, 0x2]));
458
459 let inner = make_cursor(nodes);
460 let mut cursor = MaskedTrieCursor::new(inner, ps.freeze());
461
462 let (_, node) = cursor.seek(Nibbles::default()).unwrap().unwrap();
463 assert_eq!(node.root_hash, None);
464 }
465
466 #[test]
467 fn test_node_without_hashes_returned_unchanged() {
468 let nodes = vec![(Nibbles::from_nibbles([0x1]), node(0b0000_0000_0000_0011))];
470
471 let mut ps = PrefixSetMut::default();
472 ps.insert(Nibbles::from_nibbles([0x1, 0x0]));
473
474 let inner = make_cursor(nodes);
475 let mut cursor = MaskedTrieCursor::new(inner, ps.freeze());
476
477 let result = cursor.seek(Nibbles::default()).unwrap();
478 assert_eq!(result, Some((Nibbles::from_nibbles([0x1]), node(0b0000_0000_0000_0011))));
479 }
480
481 #[test]
482 fn test_empty_cursor_returns_none() {
483 let nodes = vec![];
484 let ps = PrefixSetMut::default();
485 let inner = make_cursor(nodes);
486 let mut cursor = MaskedTrieCursor::new(inner, ps.freeze());
487
488 assert_eq!(cursor.seek(Nibbles::default()).unwrap(), None);
489 }
490
491 #[test]
492 fn test_reset_delegates() {
493 let nodes =
494 vec![(Nibbles::from_nibbles([0x1]), node(1)), (Nibbles::from_nibbles([0x2]), node(2))];
495
496 let ps = PrefixSetMut::default();
497 let inner = make_cursor(nodes);
498 let mut cursor = MaskedTrieCursor::new(inner, ps.freeze());
499
500 let _ = cursor.seek(Nibbles::from_nibbles([0x1])).unwrap();
501 assert_eq!(cursor.current().unwrap(), Some(Nibbles::from_nibbles([0x1])));
502
503 cursor.reset();
504 assert_eq!(cursor.current().unwrap(), None);
505 }
506
507 #[test]
508 fn test_partial_mask_preserves_remaining_hashes() {
509 let nodes = vec![(
513 Nibbles::from_nibbles([0x1]),
514 node_with_tree_mask(
515 0b0000_0000_1000_1001,
516 0b0000_0000_1000_1001,
517 0b0000_0000_1000_1001,
518 vec![hash(0), hash(3), hash(7)],
519 ),
520 )];
521
522 let mut ps = PrefixSetMut::default();
523 ps.insert(Nibbles::from_nibbles([0x1, 0x0]));
524 ps.insert(Nibbles::from_nibbles([0x1, 0x7]));
525
526 let inner = make_cursor(nodes);
527 let mut cursor = MaskedTrieCursor::new(inner, ps.freeze());
528
529 let (key, node) = cursor.seek(Nibbles::default()).unwrap().unwrap();
530 assert_eq!(key, Nibbles::from_nibbles([0x1]));
531 assert!(!node.hash_mask.is_bit_set(0));
532 assert!(node.hash_mask.is_bit_set(3));
533 assert!(!node.hash_mask.is_bit_set(7));
534 assert_eq!(&*node.hashes, &[hash(3)]);
535 assert_eq!(node.root_hash, None);
536 }
537
538 mod proptest_tests {
539 use crate::{
540 hashed_cursor::{mock::MockHashedCursorFactory, HashedPostStateCursorFactory},
541 proof::Proof,
542 trie_cursor::{
543 masked::MaskedTrieCursorFactory, mock::MockTrieCursorFactory,
544 noop::NoopTrieCursorFactory,
545 },
546 StateRoot,
547 };
548 use alloy_primitives::{map::B256Set, B256, U256};
549 use proptest::prelude::*;
550 use reth_primitives_traits::Account;
551 use reth_trie_common::{HashedPostState, HashedStorage, MultiProofTargets};
552
553 fn account_strategy() -> impl Strategy<Value = Account> {
554 (any::<u64>(), any::<u64>(), any::<[u8; 32]>()).prop_map(
555 |(nonce, balance, code_hash)| Account {
556 nonce,
557 balance: U256::from(balance),
558 bytecode_hash: Some(B256::from(code_hash)),
559 },
560 )
561 }
562
563 fn storage_value_strategy() -> impl Strategy<Value = U256> {
564 any::<u64>().prop_filter("non-zero", |v| *v != 0).prop_map(U256::from)
565 }
566
567 #[allow(clippy::type_complexity)]
571 fn test_input_strategy(
572 ) -> impl Strategy<Value = (Vec<(B256, U256)>, Account, Vec<(B256, Option<U256>)>, Vec<B256>)>
573 {
574 (
575 prop::collection::vec(
577 (any::<[u8; 32]>().prop_map(B256::from), storage_value_strategy()),
578 1000,
579 ),
580 account_strategy(),
581 prop::collection::vec(
583 (
584 any::<[u8; 32]>().prop_map(B256::from),
585 prop::option::of(storage_value_strategy()),
586 ),
587 200,
588 ),
589 prop::collection::vec(any::<[u8; 32]>().prop_map(B256::from), 50),
591 )
592 .prop_flat_map(
593 |(base_slots, account, changeset_raw, extra_targets)| {
594 let mut base_map = alloy_primitives::map::B256Map::default();
596 for (k, v) in &base_slots {
597 base_map.insert(*k, *v);
598 }
599 let base_deduped: Vec<(B256, U256)> =
600 base_map.iter().map(|(&k, &v)| (k, v)).collect();
601 let base_keys: Vec<B256> = base_deduped.iter().map(|(k, _)| *k).collect();
602
603 let changeset_len = changeset_raw.len();
605 let half = changeset_len / 2;
606 let base_keys_for_overlap = base_keys.clone();
607
608 let overlap_indices =
610 prop::collection::vec(0..base_keys_for_overlap.len().max(1), half);
611
612 overlap_indices.prop_map(move |indices| {
613 let mut changeset: Vec<(B256, Option<U256>)> = Vec::new();
614
615 for (i, (_, value)) in
617 indices.iter().zip(changeset_raw.iter()).take(half)
618 {
619 let key = if base_keys_for_overlap.is_empty() {
620 changeset_raw[*i].0
621 } else {
622 base_keys_for_overlap[*i % base_keys_for_overlap.len()]
623 };
624 changeset.push((key, *value));
625 }
626
627 for (key, value) in changeset_raw.iter().skip(half) {
629 changeset.push((*key, *value));
630 }
631
632 let changeset_keys: Vec<B256> =
634 changeset.iter().map(|(k, _)| *k).collect();
635 let mut proof_slot_targets: Vec<B256> = Vec::new();
636
637 for k in base_keys.iter().take(40) {
639 proof_slot_targets.push(*k);
640 }
641 for k in changeset_keys.iter().take(30) {
643 proof_slot_targets.push(*k);
644 }
645 for k in extra_targets.iter().take(30) {
647 proof_slot_targets.push(*k);
648 }
649
650 (base_deduped.clone(), account, changeset, proof_slot_targets)
651 })
652 },
653 )
654 }
655
656 proptest! {
657 #![proptest_config(ProptestConfig::with_cases(50))]
658 #[test]
659 fn proptest_masked_cursor_multiproof_equivalence(
660 (base_slots, account, changeset, proof_slot_targets) in test_input_strategy()
661 ) {
662 reth_tracing::init_test_tracing();
663
664 let hashed_address = B256::ZERO;
665
666 let base_state = HashedPostState {
669 accounts: std::iter::once((hashed_address, Some(account))).collect(),
670 storages: std::iter::once((
671 hashed_address,
672 HashedStorage::from_iter(false, base_slots),
673 ))
674 .collect(),
675 };
676
677 let base_hashed_cursor_factory =
679 MockHashedCursorFactory::from_hashed_post_state(base_state);
680 let (_, trie_updates) = StateRoot::new(
681 NoopTrieCursorFactory,
682 base_hashed_cursor_factory.clone(),
683 )
684 .root_with_updates()
685 .expect("state root computation should succeed");
686
687 let mock_trie_cursor_factory =
689 MockTrieCursorFactory::from_trie_updates(trie_updates);
690
691 let changeset_storage: Vec<(B256, U256)> = changeset
693 .iter()
694 .map(|(k, v)| (*k, v.unwrap_or(U256::ZERO)))
695 .collect();
696 let changeset_state = HashedPostState {
697 accounts: std::iter::once((hashed_address, Some(account))).collect(),
698 storages: std::iter::once((
699 hashed_address,
700 HashedStorage::from_iter(false, changeset_storage),
701 ))
702 .collect(),
703 };
704
705 let prefix_sets_mut = changeset_state.construct_prefix_sets();
707
708 let slot_targets: B256Set = proof_slot_targets.into_iter().collect();
710 let targets =
711 MultiProofTargets::from_iter([(hashed_address, slot_targets)]);
712
713 let changeset_sorted = changeset_state.into_sorted();
716 let overlay_cursor_factory = HashedPostStateCursorFactory::new(
717 base_hashed_cursor_factory,
718 &changeset_sorted,
719 );
720
721 let proof_a = Proof::new(
723 mock_trie_cursor_factory.clone(),
724 overlay_cursor_factory.clone(),
725 )
726 .with_prefix_sets_mut(prefix_sets_mut.clone());
727 let multiproof_a = proof_a
728 .multiproof(targets.clone())
729 .expect("multiproof A should succeed");
730
731 let masked_trie_cursor_factory = MaskedTrieCursorFactory::new(
733 mock_trie_cursor_factory,
734 prefix_sets_mut.freeze(),
735 );
736 let proof_b = Proof::new(
737 masked_trie_cursor_factory,
738 overlay_cursor_factory,
739 );
740 let multiproof_b = proof_b
741 .multiproof(targets)
742 .expect("multiproof B should succeed");
743
744 assert_eq!(
746 multiproof_a, multiproof_b,
747 "multiproof with prefix sets should equal multiproof with masked cursor"
748 );
749 }
750 }
751 }
752}