iota_types/
global_state_hash.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5/// A type to represent the root hash of all live objects in the network.
6pub type GlobalStateHash = fastcrypto::hash::EllipticCurveMultisetHash;
7
8#[cfg(test)]
9mod tests {
10    use fastcrypto::hash::MultisetHash;
11    use rand::seq::SliceRandom;
12
13    use crate::{base_types::ObjectDigest, global_state_hash::GlobalStateHash};
14
15    #[test]
16    fn test_global_state_hash() {
17        let ref1 = ObjectDigest::random();
18        let ref2 = ObjectDigest::random();
19        let ref3 = ObjectDigest::random();
20        let ref4 = ObjectDigest::random();
21
22        let mut a1 = GlobalStateHash::default();
23        a1.insert(ref1);
24        a1.insert(ref2);
25        a1.insert(ref3);
26
27        // Insertion out of order should arrive at the same result.
28        let mut a2 = GlobalStateHash::default();
29        a2.insert(ref3);
30        assert_ne!(a1, a2);
31        a2.insert(ref2);
32        assert_ne!(a1, a2);
33        a2.insert(ref1);
34        assert_eq!(a1, a2);
35
36        // GlobalStateHash is not a set, and inserting the same element twice will
37        // change the result.
38        a2.insert(ref3);
39        assert_ne!(a1, a2);
40        a2.remove(ref3);
41
42        a2.insert(ref4);
43        assert_ne!(a1, a2);
44
45        // Supports removal.
46        a2.remove(ref4);
47        assert_eq!(a1, a2);
48
49        // Removing elements out of order should arrive at the same result.
50        a2.remove(ref3);
51        a2.remove(ref1);
52
53        a1.remove(ref1);
54        a1.remove(ref3);
55
56        assert_eq!(a1, a2);
57
58        // After removing all elements, it should be the same as an empty one.
59        a1.remove(ref2);
60        assert_eq!(a1, GlobalStateHash::default());
61    }
62
63    #[test]
64    fn test_global_state_hash_commutativity() {
65        let ref1 = ObjectDigest::random();
66        let ref2 = ObjectDigest::random();
67        let ref3 = ObjectDigest::random();
68
69        let mut a1 = GlobalStateHash::default();
70        a1.remove(ref1);
71        a1.remove(ref2);
72        a1.insert(ref1);
73        a1.insert(ref2);
74
75        // Removal before insertion should yield the same result
76        assert_eq!(a1, GlobalStateHash::default());
77
78        a1.insert(ref1);
79        a1.insert(ref2);
80
81        // Insertion out of order should arrive at the same result.
82        let mut a2 = GlobalStateHash::default();
83        a2.remove(ref1);
84        a2.remove(ref2);
85
86        // Unioning where all objects from a are removed in b should
87        // result in empty global_state_hash
88        a1.union(&a2);
89        assert_eq!(a1, GlobalStateHash::default());
90
91        a1.insert(ref1);
92        a1.insert(ref2);
93        a1.insert(ref3);
94
95        let mut a3 = GlobalStateHash::default();
96        a3.insert(ref3);
97
98        // a1: (+ref1, +ref2, +ref3)
99        // a2: (-ref1, -ref2)
100        // a3: (+ref3)
101        // a1 + a2 = a3
102
103        a1.union(&a2);
104        assert_eq!(a1, a3);
105    }
106
107    #[test]
108    fn test_global_state_hash_insert_stress() {
109        let mut refs: Vec<_> = (0..100).map(|_| ObjectDigest::random()).collect();
110        let mut global_state_hash = GlobalStateHash::default();
111        global_state_hash.insert_all(&refs);
112        let mut rng = rand::thread_rng();
113        (0..10).for_each(|_| {
114            refs.shuffle(&mut rng);
115            let mut a = GlobalStateHash::default();
116            a.insert_all(&refs);
117            assert_eq!(global_state_hash, a);
118        })
119    }
120
121    #[test]
122    fn test_global_state_hash_remove_stress() {
123        let mut refs1: Vec<_> = (0..100).map(|_| ObjectDigest::random()).collect();
124        let mut refs2: Vec<_> = (0..100).map(|_| ObjectDigest::random()).collect();
125        let mut global_state_hash = GlobalStateHash::default();
126        global_state_hash.insert_all(&refs1);
127
128        let mut rng = rand::thread_rng();
129        (0..10).for_each(|_| {
130            refs1.shuffle(&mut rng);
131            let mut a = GlobalStateHash::default();
132            a.insert_all(&refs1);
133            a.insert_all(&refs2);
134            refs2.shuffle(&mut rng);
135            a.remove_all(&refs2);
136            assert_eq!(global_state_hash, a);
137        })
138    }
139}