bvs_library/
slashing.rs

1use cosmwasm_schema::cw_serde;
2use cosmwasm_std::{HexBinary, StdError, StdResult};
3use cw_storage_plus::{Key, KeyDeserialize, Prefixer, PrimaryKey};
4use std::fmt;
5use std::ops::{Deref, DerefMut};
6
7/// SlashingRequestId stores the id in hexbinary. It's a 32-byte hash of the slashing request
8#[cw_serde]
9pub struct SlashingRequestId(pub HexBinary);
10
11impl SlashingRequestId {
12    /// Returns the hex string representation of the slashing request id
13    pub fn to_hex(&self) -> String {
14        self.0.to_hex()
15    }
16
17    /// Create a SlashingRequestId from its hex string representation
18    pub fn from_hex(hex: &str) -> StdResult<Self> {
19        let bytes = HexBinary::from_hex(hex)?;
20        if bytes.len() != 32 {
21            return Err(StdError::generic_err("Invalid hex length"));
22        }
23        Ok(SlashingRequestId(bytes))
24    }
25}
26
27impl fmt::Display for SlashingRequestId {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        write!(f, "{}", self.0.to_hex())
30    }
31}
32
33impl Deref for SlashingRequestId {
34    type Target = HexBinary;
35
36    fn deref(&self) -> &Self::Target {
37        &self.0
38    }
39}
40
41impl DerefMut for SlashingRequestId {
42    fn deref_mut(&mut self) -> &mut Self::Target {
43        &mut self.0
44    }
45}
46
47impl From<HexBinary> for SlashingRequestId {
48    fn from(bytes: HexBinary) -> Self {
49        Self(bytes)
50    }
51}
52
53impl From<[u8; 32]> for SlashingRequestId {
54    fn from(bytes: [u8; 32]) -> Self {
55        Self(HexBinary::from(bytes))
56    }
57}
58
59impl PrimaryKey<'_> for SlashingRequestId {
60    type Prefix = ();
61    type SubPrefix = ();
62    type Suffix = Self;
63    type SuperSuffix = Self;
64
65    fn key(&self) -> Vec<Key> {
66        vec![Key::Ref(self.0.as_slice())]
67    }
68}
69
70impl Prefixer<'_> for SlashingRequestId {
71    fn prefix(&self) -> Vec<Key> {
72        vec![Key::Ref(self.0.as_slice())]
73    }
74}
75
76impl KeyDeserialize for SlashingRequestId {
77    type Output = Self;
78
79    const KEY_ELEMS: u16 = 1;
80
81    #[inline(always)]
82    fn from_vec(value: Vec<u8>) -> StdResult<Self::Output> {
83        Ok(SlashingRequestId(HexBinary::from(value)))
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90    use cosmwasm_std::testing::MockStorage;
91    use cosmwasm_std::Addr;
92    use cw_storage_plus::Map;
93
94    mod slashing_request_id {
95        use super::*;
96
97        #[test]
98        fn test_from_hex_valid() {
99            // Valid 32-byte hex string
100            let hex = "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20";
101            let result = SlashingRequestId::from_hex(hex);
102            assert!(result.is_ok());
103            let id = result.unwrap();
104            assert_eq!(id.to_hex(), hex);
105        }
106
107        #[test]
108        fn test_from_hex_invalid_format() {
109            // Invalid hex characters
110            let result = SlashingRequestId::from_hex("not a hex string");
111            assert!(result.is_err());
112
113            // Valid hex but wrong length (too short)
114            let result = SlashingRequestId::from_hex("0102030405");
115            assert!(result.is_err());
116
117            // Valid hex but wrong length (too long)
118            let result = SlashingRequestId::from_hex(
119                "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021",
120            );
121            assert!(result.is_err());
122
123            // Invalid hex with 0x prefix
124            let result = SlashingRequestId::from_hex("0x0102030405060708090a0b0c0d0e0f");
125            assert!(result.is_err());
126        }
127
128        #[test]
129        fn test_to_hex() {
130            let hexbinary = HexBinary::from_hex(
131                "0000000000000000000000000000000000000000000000000000000000000000",
132            )
133            .unwrap();
134            let id = SlashingRequestId::from(hexbinary.clone());
135            assert_eq!(id.to_hex(), hexbinary.to_hex());
136            assert_eq!(
137                id.to_hex(),
138                "0000000000000000000000000000000000000000000000000000000000000000"
139            );
140
141            let bytes = [
142                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
143                24, 25, 26, 27, 28, 29, 30, 31, 32,
144            ];
145            let hexbinary = HexBinary::from(bytes);
146            let id = SlashingRequestId::from(bytes);
147            assert_eq!(id.to_hex(), hexbinary.to_hex());
148            assert_eq!(
149                id.to_hex(),
150                "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"
151            );
152        }
153
154        #[test]
155        fn test_display_implementation() {
156            let hex_string = "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20";
157            let id = SlashingRequestId::from_hex(hex_string).unwrap();
158            assert_eq!(format!("{}", id), hex_string);
159            assert_eq!(
160                id.to_string(),
161                "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"
162            );
163        }
164
165        #[test]
166        fn test_from_implementations() {
167            // Test From<[u8; 32]>
168            let bytes = [
169                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
170                24, 25, 26, 27, 28, 29, 30, 31, 32,
171            ];
172            let id = SlashingRequestId::from(bytes);
173            assert_eq!(id.0.as_slice(), &bytes);
174
175            // Test From<HexBinary>
176            let hex_binary = HexBinary::from(bytes);
177            let id = SlashingRequestId::from(hex_binary.clone());
178            assert_eq!(id.0, hex_binary);
179        }
180
181        #[test]
182        fn test_storage_implementations() {
183            let hex_string1 = "5808db357efe7b2a8c61de8c772b7e09e676a0bdb880528acfdc259c8cd1840f";
184            let hex_string2 = "3dc9b61793974139e89c1fd40ba1c8da459a8fa387d8d6afb32e6386e6b42146";
185            let id1 = SlashingRequestId::from_hex(hex_string1).unwrap();
186            let id2 = SlashingRequestId::from_hex(hex_string2).unwrap();
187
188            // Test PrimaryKey implementation with borrowed keys
189            let map: Map<&SlashingRequestId, u64> = Map::new("test_map");
190            let mut storage = MockStorage::new();
191
192            // Store values
193            map.save(&mut storage, &id1, &42u64).unwrap();
194            map.save(&mut storage, &id2, &84u64).unwrap();
195
196            // Retrieve values
197            let value1 = map.load(&storage, &id1).unwrap();
198            let value2 = map.load(&storage, &id2).unwrap();
199
200            assert_eq!(value1, 42u64);
201            assert_eq!(value2, 84u64);
202
203            // Test PrimaryKey implementation
204            let map: Map<SlashingRequestId, u64> = Map::new("test_map");
205            let mut storage = MockStorage::new();
206
207            // Store values
208            map.save(&mut storage, id1.clone(), &42u64).unwrap();
209            map.save(&mut storage, id2.clone(), &84u64).unwrap();
210
211            // Retrieve values
212            let value1 = map.load(&storage, id1).unwrap();
213            let value2 = map.load(&storage, id2).unwrap();
214
215            assert_eq!(value1, 42u64);
216            assert_eq!(value2, 84u64);
217        }
218
219        #[test]
220        fn test_storage_implementations_with_prefix() {
221            // Create two SlashingRequestIds
222            let hex_string1 = "5808db357efe7b2a8c61de8c772b7e09e676a0bdb880528acfdc259c8cd1840f";
223            let hex_string2 = "3dc9b61793974139e89c1fd40ba1c8da459a8fa387d8d6afb32e6386e6b42146";
224            let id1 = SlashingRequestId::from_hex(hex_string1).unwrap();
225            let id2 = SlashingRequestId::from_hex(hex_string2).unwrap();
226
227            // Create two addresses
228            let addr1 = Addr::unchecked("addr1");
229            let addr2 = Addr::unchecked("addr2");
230
231            // Create a map with composite key (SlashingRequestId, &Addr)
232            let map: Map<(SlashingRequestId, &Addr), u64> = Map::new("composite_key_map");
233            let mut storage = MockStorage::new();
234
235            // Store values with different combinations of keys
236            map.save(&mut storage, (id1.clone(), &addr1), &100u64)
237                .unwrap();
238            map.save(&mut storage, (id1.clone(), &addr2), &200u64)
239                .unwrap();
240            map.save(&mut storage, (id2.clone(), &addr1), &300u64)
241                .unwrap();
242            map.save(&mut storage, (id2.clone(), &addr2), &400u64)
243                .unwrap();
244
245            // Retrieve values
246            let value1 = map.load(&storage, (id1.clone(), &addr1)).unwrap();
247            let value2 = map.load(&storage, (id1.clone(), &addr2)).unwrap();
248            let value3 = map.load(&storage, (id2.clone(), &addr1)).unwrap();
249            let value4 = map.load(&storage, (id2.clone(), &addr2)).unwrap();
250
251            // Verify values
252            assert_eq!(value1, 100u64);
253            assert_eq!(value2, 200u64);
254            assert_eq!(value3, 300u64);
255            assert_eq!(value4, 400u64);
256
257            // Test prefix queries - get all entries for id1
258            let id1_entries: Vec<_> = map
259                .prefix(id1)
260                .range(&storage, None, None, cosmwasm_std::Order::Ascending)
261                .collect::<StdResult<Vec<_>>>()
262                .unwrap();
263
264            // Should have 2 entries for id1
265            assert_eq!(id1_entries.len(), 2);
266            assert_eq!(id1_entries[0], (addr1.clone(), 100u64));
267            assert_eq!(id1_entries[1], (addr2.clone(), 200u64));
268
269            // Test prefix queries - get all entries for id2
270            let id2_entries: Vec<_> = map
271                .prefix(id2)
272                .range(&storage, None, None, cosmwasm_std::Order::Ascending)
273                .collect::<StdResult<Vec<_>>>()
274                .unwrap();
275
276            // Should have 2 entries for id2
277            assert_eq!(id2_entries.len(), 2);
278            assert_eq!(id2_entries[0], (addr1, 300u64));
279            assert_eq!(id2_entries[1], (addr2, 400u64));
280        }
281    }
282}