Skip to main content

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