fuels_core/
codec.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
mod abi_decoder;
mod abi_encoder;
mod function_selector;
mod logs;
mod utils;

pub use abi_decoder::*;
pub use abi_encoder::*;
pub use function_selector::*;
pub use logs::*;

use crate::{
    traits::{Parameterize, Tokenizable},
    types::errors::Result,
};

/// Decodes `bytes` into type `T` following the schema defined by T's `Parameterize` impl
pub fn try_from_bytes<T>(bytes: &[u8], decoder_config: DecoderConfig) -> Result<T>
where
    T: Parameterize + Tokenizable,
{
    let token = ABIDecoder::new(decoder_config).decode(&T::param_type(), bytes)?;

    T::from_token(token)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        constants::WORD_SIZE,
        types::{Address, AsciiString, AssetId, ContractId},
    };

    #[test]
    fn convert_all_from_bool_to_u64() -> Result<()> {
        let bytes = [255; WORD_SIZE];

        macro_rules! test_decode {
            ($($for_type: ident),*) => {
                $(assert_eq!(
                        try_from_bytes::<$for_type>(&bytes, DecoderConfig::default())?,
                        $for_type::MAX
                );)*
            };
        }

        assert!(try_from_bytes::<bool>(&bytes, DecoderConfig::default())?);

        test_decode!(u8, u16, u32, u64);

        Ok(())
    }

    #[test]
    fn convert_bytes_into_tuple() -> Result<()> {
        let tuple_in_bytes = [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2];

        let the_tuple: (u64, u32) = try_from_bytes(&tuple_in_bytes, DecoderConfig::default())?;

        assert_eq!(the_tuple, (1, 2));

        Ok(())
    }

    #[test]
    fn convert_native_types() -> Result<()> {
        let bytes = [255; 32];

        macro_rules! test_decode {
            ($($for_type: ident),*) => {
                $(assert_eq!(
                        try_from_bytes::<$for_type>(&bytes, DecoderConfig::default())?,
                        $for_type::new(bytes.as_slice().try_into()?)
                );)*
            };
        }

        test_decode!(Address, ContractId, AssetId);

        Ok(())
    }

    #[test]
    fn string_slice_is_read_in_total() {
        // This was a bug where the decoder read more bytes than it reported, causing the next
        // element to be read incorrectly.

        // given
        #[derive(
            fuels_macros::Tokenizable, fuels_macros::Parameterize, Clone, PartialEq, Debug,
        )]
        #[FuelsCorePath = "crate"]
        #[FuelsTypesPath = "crate::types"]
        struct Test {
            name: AsciiString,
            age: u64,
        }

        let input = Test {
            name: AsciiString::new("Alice".to_owned()).unwrap(),
            age: 42,
        };

        let encoded = ABIEncoder::default()
            .encode(&[input.clone().into_token()])
            .unwrap();

        // when
        let decoded = try_from_bytes::<Test>(&encoded, DecoderConfig::default()).unwrap();

        // then
        assert_eq!(decoded, input);
    }
}