fuel_merkle/sparse/
proof.rs

1use crate::{
2    common::{
3        path::{
4            Path,
5            Side,
6        },
7        sum,
8        Bytes32,
9        ProofSet,
10    },
11    sparse::{
12        hash::{
13            calculate_leaf_hash,
14            calculate_node_hash,
15        },
16        zero_sum,
17        MerkleTreeKey,
18    },
19};
20
21use alloc::vec::Vec;
22use core::{
23    fmt,
24    fmt::Debug,
25};
26
27#[derive(Debug, Clone, Eq, PartialEq)]
28pub enum Proof {
29    Inclusion(InclusionProof),
30    Exclusion(ExclusionProof),
31}
32
33impl Proof {
34    pub fn proof_set(&self) -> &ProofSet {
35        match self {
36            Proof::Inclusion(proof) => &proof.proof_set,
37            Proof::Exclusion(proof) => &proof.proof_set,
38        }
39    }
40
41    pub fn is_inclusion(&self) -> bool {
42        match self {
43            Proof::Inclusion(_) => true,
44            Proof::Exclusion(_) => false,
45        }
46    }
47
48    pub fn is_exclusion(&self) -> bool {
49        !self.is_inclusion()
50    }
51}
52
53#[derive(Clone, Eq, PartialEq)]
54pub struct InclusionProof {
55    pub proof_set: ProofSet,
56}
57
58impl InclusionProof {
59    pub fn verify(&self, root: &Bytes32, key: &MerkleTreeKey, value: &[u8]) -> bool {
60        let Self { proof_set } = self;
61
62        if proof_set.len() > 256usize {
63            return false;
64        }
65
66        let mut current = calculate_leaf_hash(key, &sum(value));
67        for (i, side_hash) in proof_set.iter().enumerate() {
68            #[allow(clippy::arithmetic_side_effects)] // Cannot underflow
69            let index =
70                u32::try_from(proof_set.len() - 1 - i).expect("We've checked it above");
71            current = match key.get_instruction(index).expect("Infallible") {
72                Side::Left => calculate_node_hash(&current, side_hash),
73                Side::Right => calculate_node_hash(side_hash, &current),
74            };
75        }
76        current == *root
77    }
78}
79
80impl Debug for InclusionProof {
81    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82        let proof_set = self.proof_set.iter().map(hex::encode).collect::<Vec<_>>();
83        f.debug_struct("InclusionProof")
84            .field("Proof set", &proof_set)
85            .finish()
86    }
87}
88
89#[derive(Debug, Clone, Eq, PartialEq)]
90pub enum ExclusionLeaf {
91    Leaf(ExclusionLeafData),
92    Placeholder,
93}
94
95#[derive(Clone, Eq, PartialEq)]
96pub struct ExclusionLeafData {
97    /// The leaf key.
98    pub leaf_key: Bytes32,
99    /// Hash of the value of the leaf.
100    pub leaf_value: Bytes32,
101}
102
103impl Debug for ExclusionLeafData {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        f.debug_struct("ExclusionLeafData")
106            .field("Leaf key", &hex::encode(self.leaf_key))
107            .field("Leaf value", &hex::encode(self.leaf_value))
108            .finish()
109    }
110}
111
112impl ExclusionLeaf {
113    fn hash(&self) -> Bytes32 {
114        match self {
115            ExclusionLeaf::Leaf(data) => {
116                calculate_leaf_hash(&data.leaf_key, &data.leaf_value)
117            }
118            ExclusionLeaf::Placeholder => *zero_sum(),
119        }
120    }
121}
122
123#[derive(Clone, Eq, PartialEq)]
124pub struct ExclusionProof {
125    pub proof_set: ProofSet,
126    pub leaf: ExclusionLeaf,
127}
128
129impl ExclusionProof {
130    pub fn verify(&self, root: &Bytes32, key: &MerkleTreeKey) -> bool {
131        let Self { proof_set, leaf } = self;
132
133        if let ExclusionLeaf::Leaf(data) = leaf {
134            if data.leaf_key == key.as_ref() {
135                return false;
136            }
137        }
138
139        if proof_set.len() > 256usize {
140            return false;
141        }
142
143        let mut current = leaf.hash();
144        for (i, side_hash) in proof_set.iter().enumerate() {
145            #[allow(clippy::arithmetic_side_effects)] // Cannot underflow
146            let index =
147                u32::try_from(proof_set.len() - 1 - i).expect("We've checked it above");
148            current = match key.get_instruction(index).expect("Infallible") {
149                Side::Left => calculate_node_hash(&current, side_hash),
150                Side::Right => calculate_node_hash(side_hash, &current),
151            };
152        }
153        current == *root
154    }
155}
156
157impl Debug for ExclusionProof {
158    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159        let proof_set = self.proof_set.iter().map(hex::encode).collect::<Vec<_>>();
160        f.debug_struct("ExclusionProof")
161            .field("Proof set", &proof_set)
162            .field("Leaf", &self.leaf)
163            .finish()
164    }
165}
166
167#[cfg(test)]
168#[allow(non_snake_case)]
169mod test {
170    use crate::{
171        common::{
172            Bytes32,
173            StorageMap,
174        },
175        sparse::{
176            proof::Proof,
177            MerkleTree,
178            Primitive,
179        },
180    };
181    use fuel_storage::Mappable;
182
183    #[derive(Debug)]
184    struct TestTable;
185
186    impl Mappable for TestTable {
187        type Key = Self::OwnedKey;
188        type OwnedKey = Bytes32;
189        type OwnedValue = Primitive;
190        type Value = Self::OwnedValue;
191    }
192
193    #[test]
194    fn inclusion_proof__verify__returns_true_for_correct_key_and_correct_value() {
195        let mut storage = StorageMap::<TestTable>::new();
196        let mut tree = MerkleTree::new(&mut storage);
197
198        // 256:           N4
199        //               /  \
200        // 255:         N3   \
201        //             /  \   \
202        // 254:       /   N2   \
203        //           /   /  \   \
204        // 253:     /   N1   \   \
205        //         /   /  \   \   \
206        // 252:   /   N0   \   \   \
207        // ...   /   /  \   \   \   \
208        //   0: L0  L1  L3  P1  L2  P0
209        //      K0  K1  K3      K2
210
211        let k0 = [0u8; 32].into();
212        let v0 = b"DATA_0";
213        tree.update(k0, v0).expect("Expected successful update");
214
215        let mut k1 = [0u8; 32];
216        k1[0] = 0b01000000;
217        let k1 = k1.into();
218        let v1 = b"DATA_1";
219        tree.update(k1, v1).expect("Expected successful update");
220
221        let mut k2 = [0u8; 32];
222        k2[0] = 0b01100000;
223        let k2 = k2.into();
224        let v2 = b"DATA_2";
225        tree.update(k2, v2).expect("Expected successful update");
226
227        let mut k3 = [0u8; 32];
228        k3[0] = 0b01001000;
229        let k3 = k3.into();
230        let v3 = b"DATA_3";
231        tree.update(k3, v3).expect("Expected successful update");
232
233        let root = tree.root();
234
235        {
236            // Given
237            let proof = tree.generate_proof(&k0).unwrap();
238
239            // When
240            let inclusion = match proof {
241                Proof::Inclusion(proof) => proof.verify(&root, &k0, b"DATA_0"),
242                Proof::Exclusion(_) => panic!("Expected InclusionProof"),
243            };
244
245            // Then
246            assert!(inclusion);
247        }
248
249        {
250            // Given
251            let proof = tree.generate_proof(&k1).unwrap();
252
253            // When
254            let inclusion = match proof {
255                Proof::Inclusion(proof) => proof.verify(&root, &k1, b"DATA_1"),
256                Proof::Exclusion(_) => panic!("Expected InclusionProof"),
257            };
258
259            // Then
260            assert!(inclusion);
261        }
262
263        {
264            // Given
265            let proof = tree.generate_proof(&k2).unwrap();
266
267            // When
268            let inclusion = match proof {
269                Proof::Inclusion(proof) => proof.verify(&root, &k2, b"DATA_2"),
270                Proof::Exclusion(_) => panic!("Expected InclusionProof"),
271            };
272
273            // Then
274            assert!(inclusion);
275        }
276
277        {
278            // Given
279            let proof = tree.generate_proof(&k3).unwrap();
280
281            // When
282            let inclusion = match proof {
283                Proof::Inclusion(proof) => proof.verify(&root, &k3, b"DATA_3"),
284                Proof::Exclusion(_) => panic!("Expected InclusionProof"),
285            };
286
287            // Then
288            assert!(inclusion);
289        }
290    }
291
292    #[test]
293    fn inclusion_proof__verify__returns_false_for_correct_key_and_incorrect_value() {
294        let mut storage = StorageMap::<TestTable>::new();
295        let mut tree = MerkleTree::new(&mut storage);
296
297        // 256:           N4
298        //               /  \
299        // 255:         N3   \
300        //             /  \   \
301        // 254:       /   N2   \
302        //           /   /  \   \
303        // 253:     /   N1   \   \
304        //         /   /  \   \   \
305        // 252:   /   N0   \   \   \
306        // ...   /   /  \   \   \   \
307        //   0: L0  L1  L3  P1  L2  P0
308        //      K0  K1  K3      K2
309
310        let k0 = [0u8; 32].into();
311        let v0 = b"DATA_0";
312        tree.update(k0, v0).expect("Expected successful update");
313
314        let mut k1 = [0u8; 32];
315        k1[0] = 0b01000000;
316        let k1 = k1.into();
317        let v1 = b"DATA_1";
318        tree.update(k1, v1).expect("Expected successful update");
319
320        let mut k2 = [0u8; 32];
321        k2[0] = 0b01100000;
322        let k2 = k2.into();
323        let v2 = b"DATA_2";
324        tree.update(k2, v2).expect("Expected successful update");
325
326        let mut k3 = [0u8; 32];
327        k3[0] = 0b01001000;
328        let k3 = k3.into();
329        let v3 = b"DATA_3";
330        tree.update(k3, v3).expect("Expected successful update");
331
332        let root = tree.root();
333
334        {
335            // Given
336            let proof = tree.generate_proof(&k0).unwrap();
337
338            // When
339            let inclusion = match proof {
340                Proof::Inclusion(proof) => proof.verify(&root, &k0, b"DATA_100"),
341                Proof::Exclusion(_) => panic!("Expected InclusionProof"),
342            };
343
344            // Then
345            assert!(!inclusion);
346        }
347
348        {
349            // Given
350            let proof = tree.generate_proof(&k1).unwrap();
351
352            // When
353            let inclusion = match proof {
354                Proof::Inclusion(proof) => proof.verify(&root, &k1, b"DATA_100"),
355                Proof::Exclusion(_) => panic!("Expected InclusionProof"),
356            };
357
358            // Then
359            assert!(!inclusion);
360        }
361
362        {
363            // Given
364            let proof = tree.generate_proof(&k2).unwrap();
365
366            // When
367            let inclusion = match proof {
368                Proof::Inclusion(proof) => proof.verify(&root, &k2, b"DATA_100"),
369                Proof::Exclusion(_) => panic!("Expected InclusionProof"),
370            };
371
372            // Then
373            assert!(!inclusion);
374        }
375
376        {
377            // Given
378            let proof = tree.generate_proof(&k3).unwrap();
379
380            // When
381            let inclusion = match proof {
382                Proof::Inclusion(proof) => proof.verify(&root, &k3, b"DATA_100"),
383                Proof::Exclusion(_) => panic!("Expected InclusionProof"),
384            };
385            // Then
386            assert!(!inclusion);
387        }
388    }
389
390    #[test]
391    fn inclusion_proof__verify__returns_false_for_incorrect_key() {
392        let mut storage = StorageMap::<TestTable>::new();
393        let mut tree = MerkleTree::new(&mut storage);
394
395        // 256:           N4
396        //               /  \
397        // 255:         N3   \
398        //             /  \   \
399        // 254:       /   N2   \
400        //           /   /  \   \
401        // 253:     /   N1   \   \
402        //         /   /  \   \   \
403        // 252:   /   N0   \   \   \
404        // ...   /   /  \   \   \   \
405        //   0: L0  L1  L3  P1  L2  P0
406        //      K0  K1  K3      K2
407
408        let k0 = [0u8; 32].into();
409        let v0 = b"DATA_0";
410        tree.update(k0, v0).expect("Expected successful update");
411
412        let mut k1 = [0u8; 32];
413        k1[0] = 0b01000000;
414        let k1 = k1.into();
415        let v1 = b"DATA_1";
416        tree.update(k1, v1).expect("Expected successful update");
417
418        let mut k2 = [0u8; 32];
419        k2[0] = 0b01100000;
420        let k2 = k2.into();
421        let v2 = b"DATA_2";
422        tree.update(k2, v2).expect("Expected successful update");
423
424        let mut k3 = [0u8; 32];
425        k3[0] = 0b01001000;
426        let k3 = k3.into();
427        let v3 = b"DATA_3";
428        tree.update(k3, v3).expect("Expected successful update");
429
430        let root = tree.root();
431
432        // Given
433        let proof = tree.generate_proof(&k3).unwrap();
434
435        // When
436        let key = [1u8; 32].into();
437        let inclusion = match proof {
438            Proof::Inclusion(proof) => proof.verify(&root, &key, b"DATA_3"),
439            Proof::Exclusion(_) => panic!("Expected InclusionProof"),
440        };
441
442        // Then
443        assert!(!inclusion);
444    }
445
446    #[test]
447    fn exclusion_proof__verify__returns_true_for_correct_key() {
448        let mut storage = StorageMap::<TestTable>::new();
449        let mut tree = MerkleTree::new(&mut storage);
450
451        // 256:           N4
452        //               /  \
453        // 255:         N3   \
454        //             /  \   \
455        // 254:       /   N2   \
456        //           /   /  \   \
457        // 253:     /   N1   \   \
458        //         /   /  \   \   \
459        // 252:   /   N0   \   \   \
460        // ...   /   /  \   \   \   \
461        //   0: L0  L1  L3  P1  L2  P0
462        //      K0  K1  K3      K2
463
464        let k0 = [0u8; 32];
465        let v0 = b"DATA_0";
466        tree.update(k0.into(), v0)
467            .expect("Expected successful update");
468
469        let mut k1 = [0u8; 32];
470        k1[0] = 0b01000000;
471        let v1 = b"DATA_1";
472        tree.update(k1.into(), v1)
473            .expect("Expected successful update");
474
475        let mut k2 = [0u8; 32];
476        k2[0] = 0b01100000;
477        let v2 = b"DATA_2";
478        tree.update(k2.into(), v2)
479            .expect("Expected successful update");
480
481        let mut k3 = [0u8; 32];
482        k3[0] = 0b01001000;
483        let v3 = b"DATA_3";
484        tree.update(k3.into(), v3)
485            .expect("Expected successful update");
486
487        let root = tree.root();
488
489        // Given
490        let key = [0xffu8; 32].into();
491        let proof = tree.generate_proof(&key).unwrap();
492
493        // When
494        let exclusion = match proof {
495            Proof::Inclusion(_) => panic!("Expected ExclusionProof"),
496            Proof::Exclusion(proof) => proof.verify(&root, &key),
497        };
498
499        // Then
500        assert!(exclusion);
501    }
502
503    #[test]
504    fn exclusion_proof__verify__returns_false_for_incorrect_key() {
505        let mut storage = StorageMap::<TestTable>::new();
506        let mut tree = MerkleTree::new(&mut storage);
507
508        // 256:           N4
509        //               /  \
510        // 255:         N3   \
511        //             /  \   \
512        // 254:       /   N2   \
513        //           /   /  \   \
514        // 253:     /   N1   \   \
515        //         /   /  \   \   \
516        // 252:   /   N0   \   \   \
517        // ...   /   /  \   \   \   \
518        //   0: L0  L1  L3  P1  L2  P0
519        //      K0  K1  K3      K2
520
521        let k0 = [0u8; 32].into();
522        let v0 = b"DATA_0";
523        tree.update(k0, v0).expect("Expected successful update");
524
525        let mut k1 = [0u8; 32];
526        k1[0] = 0b01000000;
527        let k1 = k1.into();
528        let v1 = b"DATA_1";
529        tree.update(k1, v1).expect("Expected successful update");
530
531        let mut k2 = [0u8; 32];
532        k2[0] = 0b01100000;
533        let k2 = k2.into();
534        let v2 = b"DATA_2";
535        tree.update(k2, v2).expect("Expected successful update");
536
537        let mut k3 = [0u8; 32];
538        k3[0] = 0b01001000;
539        let k3 = k3.into();
540        let v3 = b"DATA_3";
541        tree.update(k3, v3).expect("Expected successful update");
542
543        let root = tree.root();
544
545        // Given
546        let key = [0xffu8; 32].into();
547        let proof = tree.generate_proof(&key).unwrap();
548
549        // When
550        let exclusion = match proof {
551            Proof::Inclusion(_) => panic!("Expected ExclusionProof"),
552            Proof::Exclusion(proof) => proof.verify(&root, &k1),
553        };
554
555        // Then
556        assert!(!exclusion);
557    }
558
559    #[test]
560    fn exclusion_proof__verify__returns_true_for_placeholder() {
561        let mut storage = StorageMap::<TestTable>::new();
562        let mut tree = MerkleTree::new(&mut storage);
563
564        // 256:           N4
565        //               /  \
566        // 255:         N3   \
567        //             /  \   \
568        // 254:       /   N2   \
569        //           /   /  \   \
570        // 253:     /   N1   \   \
571        //         /   /  \   \   \
572        // 252:   /   N0   \   \   \
573        // ...   /   /  \   \   \   \
574        //   0: P1  L0  L2  P0  L1  P2
575        //          K0  K2      K1
576
577        let mut k0 = [0u8; 32];
578        k0[0] = 0b01000000;
579        let k0 = k0.into();
580        let v0 = b"DATA_0";
581        tree.update(k0, v0).expect("Expected successful update");
582
583        let mut k1 = [0u8; 32];
584        k1[0] = 0b01100000;
585        let k1 = k1.into();
586        let v1 = b"DATA_1";
587        tree.update(k1, v1).expect("Expected successful update");
588
589        let mut k2 = [0u8; 32];
590        k2[0] = 0b01001000;
591        let k2 = k2.into();
592        let v2 = b"DATA_2";
593        tree.update(k2, v2).expect("Expected successful update");
594
595        let root = tree.root();
596
597        // Given
598        let key = [0b00000000; 32].into();
599        let proof = tree.generate_proof(&key).unwrap();
600
601        // When
602        let exclusion = match proof {
603            Proof::Inclusion(_) => panic!("Expected ExclusionProof"),
604            Proof::Exclusion(proof) => proof.verify(&root, &key),
605        };
606
607        // Then
608        assert!(exclusion);
609    }
610}
611
612#[cfg(test)]
613#[allow(non_snake_case)]
614mod test_random {
615    use crate::{
616        common::{
617            Bytes32,
618            StorageMap,
619        },
620        sparse::{
621            proof::Proof,
622            MerkleTree,
623            MerkleTreeKey,
624            Primitive,
625        },
626    };
627    use fuel_storage::Mappable;
628
629    use rand::{
630        prelude::StdRng,
631        SeedableRng,
632    };
633
634    #[derive(Debug)]
635    struct TestTable;
636
637    impl Mappable for TestTable {
638        type Key = Self::OwnedKey;
639        type OwnedKey = Bytes32;
640        type OwnedValue = Primitive;
641        type Value = Self::OwnedValue;
642    }
643
644    fn random_bytes32<R>(rng: &mut R) -> Bytes32
645    where
646        R: rand::Rng + ?Sized,
647    {
648        let mut bytes = [0u8; 32];
649        rng.fill(bytes.as_mut());
650        bytes
651    }
652
653    #[test]
654    fn inclusion_proof__verify__returns_true_for_correct_key_and_correct_value() {
655        let mut rng = StdRng::seed_from_u64(0xDEADBEEF);
656        let mut storage = StorageMap::<TestTable>::new();
657        let mut tree = MerkleTree::new(&mut storage);
658
659        let key = random_bytes32(&mut rng).into();
660        let value = random_bytes32(&mut rng);
661        tree.update(key, &value).unwrap();
662
663        for _ in 0..1_000 {
664            let key = random_bytes32(&mut rng).into();
665            let value = random_bytes32(&mut rng);
666            tree.update(key, &value).unwrap();
667        }
668
669        let root = tree.root();
670
671        // Given
672        let proof = tree.generate_proof(&key).unwrap();
673
674        // When
675        let inclusion = match proof {
676            Proof::Inclusion(proof) => proof.verify(&root, &key, &value),
677            Proof::Exclusion(_) => panic!("Expected InclusionProof"),
678        };
679
680        // Then
681        assert!(inclusion);
682    }
683
684    #[test]
685    fn inclusion_proof__verify__returns_false_for_correct_key_and_incorrect_value() {
686        let mut rng = StdRng::seed_from_u64(0xDEADBEEF);
687        let mut storage = StorageMap::<TestTable>::new();
688        let mut tree = MerkleTree::new(&mut storage);
689
690        let key = random_bytes32(&mut rng).into();
691        let value = random_bytes32(&mut rng);
692        tree.update(key, &value).unwrap();
693
694        for _ in 0..1_000 {
695            let key = random_bytes32(&mut rng).into();
696            let value = random_bytes32(&mut rng);
697            tree.update(key, &value).unwrap();
698        }
699
700        let root = tree.root();
701
702        // Given
703        let proof = tree.generate_proof(&key).unwrap();
704
705        // When
706        let inclusion = match proof {
707            Proof::Inclusion(proof) => proof.verify(&root, &key, b"DATA"),
708            Proof::Exclusion(_) => panic!("Expected InclusionProof"),
709        };
710
711        // Then
712        assert!(!inclusion);
713    }
714
715    #[test]
716    fn inclusion_proof__verify__returns_false_for_incorrect_key() {
717        let mut rng = StdRng::seed_from_u64(0xDEADBEEF);
718        let mut storage = StorageMap::<TestTable>::new();
719        let mut tree = MerkleTree::new(&mut storage);
720
721        let key_1 = random_bytes32(&mut rng).into();
722        let value_1 = random_bytes32(&mut rng);
723        tree.update(key_1, &value_1).unwrap();
724
725        let key_2 = random_bytes32(&mut rng).into();
726        let value_2 = random_bytes32(&mut rng);
727        tree.update(key_2, &value_2).unwrap();
728
729        for _ in 0..1_000 {
730            let key = random_bytes32(&mut rng).into();
731            let value = random_bytes32(&mut rng);
732            tree.update(key, &value).unwrap();
733        }
734
735        let root = tree.root();
736
737        // Given
738        // - Generate a proof with key_1
739        let proof = tree.generate_proof(&key_1).unwrap();
740
741        // When
742        // - Attempt to verify the proof with key_2
743        let inclusion = match proof {
744            Proof::Inclusion(proof) => proof.verify(&root, &key_2, &value_2),
745            Proof::Exclusion(_) => panic!("Expected InclusionProof"),
746        };
747
748        // Then
749        assert!(!inclusion);
750    }
751
752    #[test]
753    fn exclusion_proof__verify__returns_true_for_correct_key() {
754        let mut rng = StdRng::seed_from_u64(0xDEADBEEF);
755        let mut storage = StorageMap::<TestTable>::new();
756        let mut tree = MerkleTree::new(&mut storage);
757
758        for _ in 0..1_000 {
759            let key = random_bytes32(&mut rng);
760            let value = random_bytes32(&mut rng);
761            tree.update(key.into(), &value).unwrap();
762        }
763
764        let root = tree.root();
765
766        // Given
767        let key: MerkleTreeKey = random_bytes32(&mut rng).into();
768        let proof = tree.generate_proof(&key).unwrap();
769
770        // When
771        let exclusion = match proof {
772            Proof::Inclusion(_) => panic!("Expected ExclusionProof"),
773            Proof::Exclusion(proof) => proof.verify(&root, &key),
774        };
775
776        // Then
777        assert!(exclusion);
778    }
779
780    #[test]
781    fn exclusion_proof__verify__returns_true_for_any_key_in_empty_tree() {
782        let mut rng = StdRng::seed_from_u64(0xDEADBEEF);
783        let mut storage = StorageMap::<TestTable>::new();
784        let tree = MerkleTree::new(&mut storage);
785        let root = tree.root();
786
787        // Given
788        let key = random_bytes32(&mut rng).into();
789        let proof = tree.generate_proof(&key).unwrap();
790
791        // When
792        let exclusion = match proof {
793            Proof::Inclusion(_) => panic!("Expected ExclusionProof"),
794            Proof::Exclusion(proof) => proof.verify(&root, &key),
795        };
796
797        // Then
798        assert!(exclusion);
799    }
800}