fuel_types/
array_types.rs

1// serde-big-array doesn't allow documentation of its generated structure
2#![allow(missing_docs)]
3use core::{
4    array::TryFromSliceError,
5    borrow::{
6        Borrow,
7        BorrowMut,
8    },
9    convert::TryFrom,
10    fmt,
11    ops::{
12        Deref,
13        DerefMut,
14    },
15    str,
16};
17
18#[cfg(feature = "random")]
19use rand::{
20    Rng,
21    distributions::{
22        Distribution,
23        Standard,
24    },
25};
26
27#[cfg(all(feature = "alloc", feature = "typescript"))]
28use alloc::format;
29
30macro_rules! key {
31    ($i:ident, $s:expr) => {
32        /// FuelVM atomic array type.
33        #[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
34        #[repr(transparent)]
35        #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
36        #[derive(
37            fuel_types::canonical::Serialize, fuel_types::canonical::Deserialize,
38        )]
39        pub struct $i([u8; $s]);
40
41        key_methods!($i, $s);
42
43        #[cfg(feature = "random")]
44        impl Distribution<$i> for Standard {
45            fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $i {
46                $i(rng.r#gen())
47            }
48        }
49    };
50}
51
52macro_rules! key_with_big_array {
53    ($i:ident, $s:expr) => {
54        /// FuelVM atomic type.
55        #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
56        #[repr(transparent)]
57        #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
58        #[derive(
59            fuel_types::canonical::Serialize, fuel_types::canonical::Deserialize,
60        )]
61        pub struct $i([u8; $s]);
62
63        key_methods!($i, $s);
64
65        impl Default for $i {
66            fn default() -> $i {
67                $i([0u8; $s])
68            }
69        }
70
71        #[cfg(feature = "random")]
72        impl Distribution<$i> for Standard {
73            fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $i {
74                let mut bytes = $i::default();
75
76                rng.fill_bytes(bytes.as_mut());
77
78                bytes
79            }
80        }
81    };
82}
83
84macro_rules! key_methods {
85    ($i:ident, $s:expr) => {
86        impl $i {
87            /// Memory length of the type
88            pub const LEN: usize = $s;
89
90            /// Bytes constructor.
91            pub const fn new(bytes: [u8; $s]) -> Self {
92                Self(bytes)
93            }
94
95            /// Zeroes bytes constructor.
96            pub const fn zeroed() -> $i {
97                $i([0; $s])
98            }
99
100            #[cfg(feature = "unsafe")]
101            #[allow(unsafe_code)]
102            /// Add a conversion from arbitrary slices into owned
103            ///
104            /// # Safety
105            ///
106            /// This function will not panic if the length of the slice is smaller than
107            /// `Self::LEN`. Instead, it will cause undefined behavior and read random
108            /// disowned bytes
109            pub unsafe fn from_slice_unchecked(bytes: &[u8]) -> Self {
110                unsafe { $i($crate::bytes::from_slice_unchecked(bytes)) }
111            }
112
113            /// Copy-free reference cast
114            /// # Safety
115            /// Assumes the type is `repr[transparent]`.
116            pub fn from_bytes_ref_checked(bytes: &[u8]) -> Option<&Self> {
117                let bytes: &[u8; $s] = bytes.get(..$s)?.try_into().ok()?;
118                Some(Self::from_bytes_ref(bytes))
119            }
120
121            /// Copy-free reference cast
122            /// # Safety
123            /// Assumes the type is `repr[transparent]`.
124            pub fn from_bytes_ref(bytes: &[u8; $s]) -> &Self {
125                // The interpreter will frequently make references to keys and values
126                // using logically checked slices.
127                //
128                // This function will save unnecessary copy to owned slices for the
129                // interpreter access
130                #[allow(unsafe_code)]
131                unsafe {
132                    &*(bytes.as_ptr() as *const Self)
133                }
134            }
135
136            /// The memory size of the type by the method.
137            pub const fn size(&self) -> usize {
138                Self::LEN
139            }
140        }
141
142        #[cfg(feature = "typescript")]
143        #[wasm_bindgen::prelude::wasm_bindgen]
144        impl $i {
145            /// Bytes constructor.
146            ///
147            /// # Panics
148            ///
149            /// This function will panic if the length of `buf` is smaller than
150            /// `Self::LEN`.
151            #[wasm_bindgen(js_name = from_bytes)]
152            pub fn from_bytes_typescript(bytes: &[u8]) -> Self {
153                Self(bytes.try_into().expect(
154                    format!("The size of the arrays it not {} size", $s).as_str(),
155                ))
156            }
157
158            /// Zeroes bytes constructor.
159            #[wasm_bindgen(js_name = zeroed)]
160            pub fn zeroed_typescript() -> $i {
161                Self::zeroed()
162            }
163
164            /// The memory size of the type by the method.
165            #[wasm_bindgen(js_name = size)]
166            pub fn size_typescript(&self) -> usize {
167                self.size()
168            }
169        }
170
171        #[cfg(feature = "random")]
172        impl rand::Fill for $i {
173            fn try_fill<R: rand::Rng + ?Sized>(
174                &mut self,
175                rng: &mut R,
176            ) -> Result<(), rand::Error> {
177                rng.fill_bytes(self.as_mut());
178
179                Ok(())
180            }
181        }
182
183        impl Deref for $i {
184            type Target = [u8; $s];
185
186            fn deref(&self) -> &[u8; $s] {
187                &self.0
188            }
189        }
190
191        impl Borrow<[u8; $s]> for $i {
192            fn borrow(&self) -> &[u8; $s] {
193                &self.0
194            }
195        }
196
197        impl BorrowMut<[u8; $s]> for $i {
198            fn borrow_mut(&mut self) -> &mut [u8; $s] {
199                &mut self.0
200            }
201        }
202
203        impl DerefMut for $i {
204            fn deref_mut(&mut self) -> &mut [u8; $s] {
205                &mut self.0
206            }
207        }
208
209        impl AsRef<[u8]> for $i {
210            fn as_ref(&self) -> &[u8] {
211                &self.0
212            }
213        }
214
215        impl AsMut<[u8]> for $i {
216            fn as_mut(&mut self) -> &mut [u8] {
217                &mut self.0
218            }
219        }
220
221        impl From<[u8; $s]> for $i {
222            fn from(bytes: [u8; $s]) -> Self {
223                Self(bytes)
224            }
225        }
226
227        impl From<$i> for [u8; $s] {
228            fn from(salt: $i) -> [u8; $s] {
229                salt.0
230            }
231        }
232
233        impl TryFrom<&[u8]> for $i {
234            type Error = TryFromSliceError;
235
236            fn try_from(bytes: &[u8]) -> Result<$i, TryFromSliceError> {
237                <[u8; $s]>::try_from(bytes).map(|b| b.into())
238            }
239        }
240
241        impl fmt::LowerHex for $i {
242            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243                if f.alternate() {
244                    write!(f, "0x")?
245                }
246
247                if let Some(w) = f
248                    .width()
249                    .and_then(|w| Self::LEN.saturating_mul(2).checked_div(w))
250                {
251                    self.0.chunks(w).try_for_each(|c| {
252                        write!(f, "{:02x}", c.iter().fold(0u8, |acc, x| acc ^ x))
253                    })
254                } else {
255                    self.0.iter().try_for_each(|b| write!(f, "{:02x}", &b))
256                }
257            }
258        }
259
260        impl fmt::UpperHex for $i {
261            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262                if f.alternate() {
263                    write!(f, "0x")?
264                }
265
266                if let Some(w) = f
267                    .width()
268                    .and_then(|w| Self::LEN.saturating_mul(2).checked_div(w))
269                {
270                    self.0.chunks(w).try_for_each(|c| {
271                        write!(f, "{:02X}", c.iter().fold(0u8, |acc, x| acc ^ x))
272                    })
273                } else {
274                    self.0.iter().try_for_each(|b| write!(f, "{:02X}", &b))
275                }
276            }
277        }
278
279        impl fmt::Debug for $i {
280            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
281                <Self as fmt::LowerHex>::fmt(&self, f)
282            }
283        }
284
285        impl fmt::Display for $i {
286            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
287                <Self as fmt::LowerHex>::fmt(&self, f)
288            }
289        }
290
291        impl str::FromStr for $i {
292            type Err = &'static str;
293
294            fn from_str(s: &str) -> Result<Self, Self::Err> {
295                const ERR: &str = concat!("Invalid encoded byte in ", stringify!($i));
296                let mut ret = $i::zeroed();
297                let s = s.strip_prefix("0x").unwrap_or(s);
298                hex::decode_to_slice(&s, &mut ret.0).map_err(|_| ERR)?;
299                Ok(ret)
300            }
301        }
302
303        #[cfg(feature = "serde")]
304        impl serde::Serialize for $i {
305            #[inline(always)]
306            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
307            where
308                S: serde::Serializer,
309            {
310                use alloc::format;
311                use serde::ser::SerializeTuple;
312                if serializer.is_human_readable() {
313                    serializer.serialize_str(&format!("{:x}", &self))
314                } else {
315                    // Fixed-size arrays are tuples in serde data model
316                    let mut arr = serializer.serialize_tuple($s)?;
317                    for elem in &self.0 {
318                        arr.serialize_element(elem)?;
319                    }
320                    arr.end()
321                }
322            }
323        }
324
325        #[cfg(feature = "serde")]
326        impl<'de> serde::Deserialize<'de> for $i {
327            #[inline(always)]
328            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
329            where
330                D: serde::Deserializer<'de>,
331            {
332                use serde::de::Error;
333                if deserializer.is_human_readable() {
334                    let s: alloc::string::String =
335                        serde::Deserialize::deserialize(deserializer)?;
336                    s.parse().map_err(D::Error::custom)
337                } else {
338                    deserializer.deserialize_tuple($s, ArrayVisitor).map(Self)
339                }
340            }
341        }
342    };
343}
344
345key!(Address, 32);
346key!(AssetId, 32);
347key!(SubAssetId, 32);
348key!(BlobId, 32);
349key!(ContractId, 32);
350key!(TxId, 32);
351key!(Bytes4, 4);
352key!(Bytes8, 8);
353key!(Bytes20, 20);
354key!(Bytes32, 32);
355key!(Nonce, 32);
356key!(MessageId, 32);
357key!(Salt, 32);
358
359key_with_big_array!(Bytes64, 64);
360
361impl ContractId {
362    /// Seed for the calculation of the contract id from its code.
363    ///
364    /// <https://github.com/FuelLabs/fuel-specs/blob/master/src/identifiers/contract-id.md>
365    pub const SEED: [u8; 4] = 0x4655454C_u32.to_be_bytes();
366}
367
368impl AssetId {
369    pub const BASE: AssetId = AssetId::zeroed();
370}
371
372impl From<u64> for Nonce {
373    fn from(value: u64) -> Self {
374        let mut default = [0u8; 32];
375        default[..8].copy_from_slice(&value.to_be_bytes());
376        default.into()
377    }
378}
379
380/// A visitor for deserializing a fixed-size byte array.
381#[cfg(feature = "serde")]
382struct ArrayVisitor<const S: usize>;
383
384#[cfg(feature = "serde")]
385impl<'de, const S: usize> serde::de::Visitor<'de> for ArrayVisitor<S> {
386    type Value = [u8; S];
387
388    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
389        write!(formatter, "an array of {S} bytes")
390    }
391
392    #[inline(always)]
393    fn visit_borrowed_bytes<E>(self, items: &'de [u8]) -> Result<Self::Value, E> {
394        let mut result = [0u8; S];
395        result.copy_from_slice(items);
396        Ok(result)
397    }
398
399    #[cfg(feature = "alloc")]
400    #[inline(always)]
401    fn visit_byte_buf<E>(self, v: alloc::vec::Vec<u8>) -> Result<Self::Value, E>
402    where
403        E: serde::de::Error,
404    {
405        self.visit_borrowed_bytes(v.as_slice())
406    }
407
408    #[inline(always)]
409    fn visit_seq<A>(self, mut value: A) -> Result<Self::Value, A::Error>
410    where
411        A: serde::de::SeqAccess<'de>,
412    {
413        let mut arr = [0u8; S];
414        for (i, elem) in arr.iter_mut().enumerate() {
415            *elem = value
416                .next_element()?
417                .ok_or_else(|| serde::de::Error::invalid_length(i, &self))?;
418        }
419        Ok(arr)
420    }
421}
422
423/// Roundtrip serde encode/decode tests
424#[cfg(all(test, feature = "serde"))]
425mod tests_serde {
426    use rand::{
427        SeedableRng,
428        rngs::StdRng,
429    };
430
431    use super::*;
432
433    /// serde_json uses human-readable serialization by default
434    #[test]
435    fn test_human_readable() {
436        let rng = &mut StdRng::seed_from_u64(8586);
437        let original: Address = rng.r#gen();
438        let serialized = serde_json::to_string(&original).expect("Serialization failed");
439        assert_eq!(
440            serialized,
441            "\"7bbd8a4ea06e94461b959ab18d35802bbac3cf47e2bf29195f7db2ce41630cd7\""
442        );
443        let recreated: Address =
444            serde_json::from_str(&serialized).expect("Deserialization failed");
445        assert_eq!(original, recreated);
446    }
447
448    /// postcard uses non-human-readable serialization
449    #[test]
450    fn test_not_human_readable() {
451        let rng = &mut StdRng::seed_from_u64(8586);
452        let original: Address = rng.r#gen();
453        let serialized = postcard::to_stdvec(&original).expect("Serialization failed");
454        let expected_vec = original.0.to_vec();
455        assert_eq!(&serialized, &expected_vec);
456        let recreated: Address =
457            postcard::from_bytes(&serialized).expect("Deserialization failed");
458        assert_eq!(original, recreated);
459    }
460
461    /// postcard uses non-human-readable serialization
462    #[test]
463    fn test_not_human_readable_incorrect_deser() {
464        let rng = &mut StdRng::seed_from_u64(8586);
465        let original: Address = rng.r#gen();
466        let mut serialized =
467            postcard::to_stdvec(&original).expect("Serialization failed");
468        serialized.pop();
469        let res: Result<Address, _> = postcard::from_bytes(&serialized);
470        res.expect_err("Deserialization should have failed");
471    }
472
473    /// bincode uses non-human-readable serialization
474    #[test]
475    fn test_bincode() {
476        let rng = &mut StdRng::seed_from_u64(8586);
477        let original: Address = rng.r#gen();
478        let serialized = bincode::serialize(&original).expect("Serialization failed");
479        let recreated: Address =
480            bincode::deserialize(&serialized).expect("Deserialization failed");
481        assert_eq!(original, recreated);
482    }
483}