From d5d5003b5362288b1f658a52bd6a41e0f4a2f002 Mon Sep 17 00:00:00 2001 From: Hannes Karppila Date: Mon, 6 Jan 2025 06:57:21 +0200 Subject: [PATCH] Make ABIDecoder accept std::io::Read --- e2e/tests/debug_utils.rs | 16 +- examples/contracts/src/lib.rs | 2 +- examples/debugging/src/lib.rs | 8 +- packages/fuels-core/src/codec.rs | 14 +- packages/fuels-core/src/codec/abi_decoder.rs | 86 +++-- .../src/codec/abi_decoder/bounded_decoder.rs | 361 +++++------------- .../codec/abi_decoder/decode_as_debug_str.rs | 46 +-- .../fuels-core/src/codec/abi_formatter.rs | 11 +- packages/fuels-core/src/codec/logs.rs | 4 +- .../src/calls/receipt_parser.rs | 4 +- 10 files changed, 198 insertions(+), 354 deletions(-) diff --git a/e2e/tests/debug_utils.rs b/e2e/tests/debug_utils.rs index c834069249..e3c81f524c 100644 --- a/e2e/tests/debug_utils.rs +++ b/e2e/tests/debug_utils.rs @@ -71,7 +71,7 @@ async fn can_debug_single_call_tx() -> Result<()> { assert_eq!( decoder.decode_fn_args( &call_description.decode_fn_selector().unwrap(), - &call_description.encoded_args + call_description.encoded_args.as_slice() )?, vec!["AllStruct { some_struct: SomeStruct { field: 2, field_2: true } }"] ); @@ -115,7 +115,7 @@ async fn can_debug_single_call_tx() -> Result<()> { assert_eq!( decoder.decode_fn_args( &call_description.decode_fn_selector().unwrap(), - &call_description.encoded_args + call_description.encoded_args.as_slice() )?, vec!["AllStruct { some_struct: SomeStruct { field: 2, field_2: true } }"] ); @@ -214,7 +214,7 @@ async fn can_debug_multi_call_tx() -> Result<()> { assert_eq!( decoder.decode_fn_args( &call_description.decode_fn_selector().unwrap(), - &call_description.encoded_args + call_description.encoded_args.as_slice() )?, vec!["AllStruct { some_struct: SomeStruct { field: 2, field_2: true } }"] ); @@ -229,7 +229,7 @@ async fn can_debug_multi_call_tx() -> Result<()> { assert!(call_description.gas_forwarded.is_none()); assert_eq!( - decoder.decode_fn_args(&fn_selector, &call_description.encoded_args)?, + decoder.decode_fn_args(&fn_selector, call_description.encoded_args.as_slice())?, vec!["AllStruct { some_struct: SomeStruct { field: 2, field_2: true } }", "MemoryAddress { contract_id: std::contract_id::ContractId { bits: Bits256([77, 127, 224, 17, 182, 42, 211, 241, 46, 156, 74, 204, 31, 156, 188, 77, 183, 63, 55, 80, 119, 142, 192, 75, 130, 205, 208, 253, 25, 104, 22, 171]) }, function_selector: 123, function_data: 456 }"] ); } @@ -286,7 +286,7 @@ async fn can_debug_multi_call_tx() -> Result<()> { assert_eq!( decoder.decode_fn_args( &call_description.decode_fn_selector().unwrap(), - &call_description.encoded_args + call_description.encoded_args.as_slice() )?, vec!["AllStruct { some_struct: SomeStruct { field: 2, field_2: true } }"] ); @@ -303,7 +303,7 @@ async fn can_debug_multi_call_tx() -> Result<()> { assert_eq!(call_description.gas_forwarded, Some(25)); assert_eq!( - decoder.decode_fn_args(&call_description.decode_fn_selector().unwrap(), &call_description.encoded_args)?, + decoder.decode_fn_args(&call_description.decode_fn_selector().unwrap(), call_description.encoded_args.as_slice())?, vec!["AllStruct { some_struct: SomeStruct { field: 2, field_2: true } }", "MemoryAddress { contract_id: std::contract_id::ContractId { bits: Bits256([77, 127, 224, 17, 182, 42, 211, 241, 46, 156, 74, 204, 31, 156, 188, 77, 183, 63, 55, 80, 119, 142, 192, 75, 130, 205, 208, 253, 25, 104, 22, 171]) }, function_selector: 123, function_data: 456 }"] ); } @@ -345,7 +345,7 @@ async fn can_debug_sway_script() -> Result<()> { }; assert_eq!( - decoder.decode_fn_args("main", &desc.data)?, + decoder.decode_fn_args("main", desc.data.as_slice())?, vec!["MyStruct { number: 10, boolean: false }"] ); @@ -455,7 +455,7 @@ async fn can_detect_a_loader_script_w_data_section() -> Result<()> { )?)?; assert_eq!( - decoder.decode_fn_args("main", &script.data)?, + decoder.decode_fn_args("main", script.data.as_slice())?, vec!["MyStruct { number: 10, boolean: false }"] ); diff --git a/examples/contracts/src/lib.rs b/examples/contracts/src/lib.rs index 1698ae3cc8..ec3c88f33b 100644 --- a/examples/contracts/src/lib.rs +++ b/examples/contracts/src/lib.rs @@ -1189,7 +1189,7 @@ mod tests { let call = &calls[0]; let fn_selector = call.decode_fn_selector()?; - let decoded_args = abi_formatter.decode_fn_args(&fn_selector, &call.encoded_args)?; + let decoded_args = abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?; eprintln!( "The script called: {fn_selector}({})", diff --git a/examples/debugging/src/lib.rs b/examples/debugging/src/lib.rs index 1e1b8a19c6..756e21b652 100644 --- a/examples/debugging/src/lib.rs +++ b/examples/debugging/src/lib.rs @@ -67,7 +67,7 @@ mod tests { assert_eq!( format!("{expected_struct:?}"), - decoder.decode_as_debug_str(¶m_type, &[0, 0, 0, 0, 0, 0, 0, 123])? + decoder.decode_as_debug_str(¶m_type, [0, 0, 0, 0, 0, 0, 0, 123].as_slice())? ); } { @@ -83,7 +83,7 @@ mod tests { assert_eq!( format!("{expected_struct:?}"), - decoder.decode_as_debug_str(¶m_type, &[97, 98, 99])? + decoder.decode_as_debug_str(¶m_type, [97, 98, 99].as_slice())? ); } { @@ -97,7 +97,7 @@ mod tests { format!("{expected_enum:?}"), decoder.decode_as_debug_str( ¶m_type, - &[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 10] + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 10].as_slice() )? ); } @@ -117,7 +117,7 @@ mod tests { assert_eq!( format!("{expected_u8}"), - decoder.decode_as_debug_str(¶m_type, &[1])? + decoder.decode_as_debug_str(¶m_type, [1].as_slice())? ); } diff --git a/packages/fuels-core/src/codec.rs b/packages/fuels-core/src/codec.rs index af83cbf11a..e4dfc31cdc 100644 --- a/packages/fuels-core/src/codec.rs +++ b/packages/fuels-core/src/codec.rs @@ -5,6 +5,8 @@ mod function_selector; mod logs; mod utils; +use std::io::Read; + pub use abi_decoder::*; pub use abi_encoder::*; pub use abi_formatter::*; @@ -17,7 +19,7 @@ use crate::{ }; /// Decodes `bytes` into type `T` following the schema defined by T's `Parameterize` impl -pub fn try_from_bytes(bytes: &[u8], decoder_config: DecoderConfig) -> Result +pub fn try_from_bytes(bytes: R, decoder_config: DecoderConfig) -> Result where T: Parameterize + Tokenizable, { @@ -41,13 +43,13 @@ mod tests { macro_rules! test_decode { ($($for_type: ident),*) => { $(assert_eq!( - try_from_bytes::<$for_type>(&bytes, DecoderConfig::default())?, + try_from_bytes::<$for_type, _>(bytes.as_slice(), DecoderConfig::default())?, $for_type::MAX );)* }; } - assert!(try_from_bytes::(&bytes, DecoderConfig::default())?); + assert!(try_from_bytes::(bytes.as_slice(), DecoderConfig::default())?); test_decode!(u8, u16, u32, u64); @@ -58,7 +60,7 @@ mod tests { 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())?; + let the_tuple: (u64, u32) = try_from_bytes(tuple_in_bytes.as_slice(), DecoderConfig::default())?; assert_eq!(the_tuple, (1, 2)); @@ -72,7 +74,7 @@ mod tests { macro_rules! test_decode { ($($for_type: ident),*) => { $(assert_eq!( - try_from_bytes::<$for_type>(&bytes, DecoderConfig::default())?, + try_from_bytes::<$for_type, _>(bytes.as_slice(), DecoderConfig::default())?, $for_type::new(bytes.as_slice().try_into()?) );)* }; @@ -109,7 +111,7 @@ mod tests { .unwrap(); // when - let decoded = try_from_bytes::(&encoded, DecoderConfig::default()).unwrap(); + let decoded = try_from_bytes::(encoded.as_slice(), DecoderConfig::default()).unwrap(); // then assert_eq!(decoded, input); diff --git a/packages/fuels-core/src/codec/abi_decoder.rs b/packages/fuels-core/src/codec/abi_decoder.rs index 6ee0740a2e..4fee827f80 100644 --- a/packages/fuels-core/src/codec/abi_decoder.rs +++ b/packages/fuels-core/src/codec/abi_decoder.rs @@ -1,6 +1,8 @@ mod bounded_decoder; mod decode_as_debug_str; +use std::io::Read; + use crate::{ codec::abi_decoder::{ bounded_decoder::BoundedDecoder, decode_as_debug_str::decode_as_debug_str, @@ -59,8 +61,8 @@ impl ABIDecoder { /// /// assert_eq!(u64::from_token(token).unwrap(), 7u64); /// ``` - pub fn decode(&self, param_type: &ParamType, bytes: &[u8]) -> Result { - BoundedDecoder::new(self.config).decode(param_type, bytes) + pub fn decode(&self, param_type: &ParamType, mut bytes: impl Read) -> Result { + BoundedDecoder::new(self.config).decode(param_type, &mut bytes) } /// Same as `decode` but decodes multiple `ParamType`s in one go. @@ -73,12 +75,16 @@ impl ABIDecoder { /// let decoder = ABIDecoder::default(); /// let data: &[u8] = &[7, 8]; /// - /// let tokens = decoder.decode_multiple(&[ParamType::U8, ParamType::U8], &data).unwrap(); + /// let tokens = decoder.decode_multiple(&[ParamType::U8, ParamType::U8], data.as_slice()).unwrap(); /// /// assert_eq!(tokens, vec![Token::U8(7), Token::U8(8)]); /// ``` - pub fn decode_multiple(&self, param_types: &[ParamType], bytes: &[u8]) -> Result> { - BoundedDecoder::new(self.config).decode_multiple(param_types, bytes) + pub fn decode_multiple( + &self, + param_types: &[ParamType], + mut bytes: impl Read, + ) -> Result> { + BoundedDecoder::new(self.config).decode_multiple(param_types, &mut bytes) } /// Decodes `bytes` following the schema described in `param_type` into its respective debug @@ -102,17 +108,17 @@ impl ABIDecoder { /// /// assert_eq!(debug_string, format!("{expected_value}")); /// ``` - pub fn decode_as_debug_str(&self, param_type: &ParamType, bytes: &[u8]) -> Result { - let token = BoundedDecoder::new(self.config).decode(param_type, bytes)?; + pub fn decode_as_debug_str(&self, param_type: &ParamType, mut bytes: impl Read) -> Result { + let token = BoundedDecoder::new(self.config).decode(param_type, &mut bytes)?; decode_as_debug_str(param_type, &token) } pub fn decode_multiple_as_debug_str( &self, param_types: &[ParamType], - bytes: &[u8], + mut bytes: impl Read, ) -> Result> { - let token = BoundedDecoder::new(self.config).decode_multiple(param_types, bytes)?; + let token = BoundedDecoder::new(self.config).decode_multiple(param_types, &mut bytes)?; token .into_iter() .zip(param_types) @@ -157,7 +163,7 @@ mod tests { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // u256 ]; - let decoded = ABIDecoder::default().decode_multiple(&types, &data)?; + let decoded = ABIDecoder::default().decode_multiple(&types, data.as_slice())?; let expected = vec![ Token::U8(u8::MAX), @@ -177,7 +183,7 @@ mod tests { let types = vec![ParamType::Bool, ParamType::Bool]; let data = [1, 0]; - let decoded = ABIDecoder::default().decode_multiple(&types, &data)?; + let decoded = ABIDecoder::default().decode_multiple(&types, data.as_slice())?; let expected = vec![Token::Bool(true), Token::Bool(false)]; @@ -193,7 +199,7 @@ mod tests { 152, 244, 172, 69, 123, 168, 248, 39, 67, 243, 30, 147, 11, ]; - let decoded = ABIDecoder::default().decode(&ParamType::B256, &data)?; + let decoded = ABIDecoder::default().decode(&ParamType::B256, data.as_slice())?; assert_eq!(decoded, Token::B256(data)); @@ -209,7 +215,7 @@ mod tests { 72, 101, 108, 108, 111, // Hello ]; - let decoded = ABIDecoder::default().decode_multiple(&types, &data)?; + let decoded = ABIDecoder::default().decode_multiple(&types, data.as_slice())?; let expected = vec![ Token::StringArray(StaticStringToken::new( @@ -232,7 +238,7 @@ mod tests { 116, 101, 110, 99, 101, //This is a full sentence ]; - let decoded = ABIDecoder::default().decode(&ParamType::StringSlice, &data)?; + let decoded = ABIDecoder::default().decode(&ParamType::StringSlice, data.as_slice())?; let expected = Token::StringSlice(StaticStringToken::new( "This is a full sentence".into(), @@ -252,7 +258,7 @@ mod tests { 116, 101, 110, 99, 101, //This is a full sentence ]; - let decoded = ABIDecoder::default().decode(&ParamType::String, &data)?; + let decoded = ABIDecoder::default().decode(&ParamType::String, data.as_slice())?; let expected = Token::String("This is a full sentence".to_string()); @@ -269,7 +275,7 @@ mod tests { 1, //bool ]; - let result = ABIDecoder::default().decode(¶m_type, &data)?; + let result = ABIDecoder::default().decode(¶m_type, data.as_slice())?; let expected = Token::Tuple(vec![Token::U32(255), Token::Bool(true)]); @@ -283,7 +289,7 @@ mod tests { let types = vec![ParamType::Array(Box::new(ParamType::U8), 2)]; let data = [255, 42]; - let decoded = ABIDecoder::default().decode_multiple(&types, &data)?; + let decoded = ABIDecoder::default().decode_multiple(&types, data.as_slice())?; let expected = vec![Token::Array(vec![Token::U8(255), Token::U8(42)])]; assert_eq!(decoded, expected); @@ -306,7 +312,7 @@ mod tests { generics: vec![], }; - let decoded = ABIDecoder::default().decode(¶m_type, &data)?; + let decoded = ABIDecoder::default().decode(¶m_type, data.as_slice())?; let expected = Token::Struct(vec![Token::U8(1), Token::Bool(true)]); @@ -319,7 +325,7 @@ mod tests { fn decode_bytes() -> Result<()> { let data = [0, 0, 0, 0, 0, 0, 0, 7, 255, 0, 1, 2, 3, 4, 5]; - let decoded = ABIDecoder::default().decode(&ParamType::Bytes, &data)?; + let decoded = ABIDecoder::default().decode(&ParamType::Bytes, data.as_slice())?; let expected = Token::Bytes([255, 0, 1, 2, 3, 4, 5].to_vec()); @@ -332,7 +338,7 @@ mod tests { fn decode_raw_slice() -> Result<()> { let data = [0, 0, 0, 0, 0, 0, 0, 7, 255, 0, 1, 2, 3, 4, 5]; - let decoded = ABIDecoder::default().decode(&ParamType::RawSlice, &data)?; + let decoded = ABIDecoder::default().decode(&ParamType::RawSlice, data.as_slice())?; let expected = Token::RawSlice([255, 0, 1, 2, 3, 4, 5].to_vec()); @@ -361,7 +367,7 @@ mod tests { 0, 0, 0, 42, // u32 ]; - let decoded = ABIDecoder::default().decode_multiple(&types, &data)?; + let decoded = ABIDecoder::default().decode_multiple(&types, data.as_slice())?; let expected = vec![Token::Enum(Box::new((0, Token::U32(42), inner_enum_types)))]; assert_eq!(decoded, expected); @@ -400,7 +406,7 @@ mod tests { let data = [0, 10, 1, 1, 2]; - let decoded = ABIDecoder::default().decode(&nested_struct, &data)?; + let decoded = ABIDecoder::default().decode(&nested_struct, data.as_slice())?; let my_nested_struct = vec![ Token::U16(10), @@ -461,7 +467,7 @@ mod tests { 152, 244, 172, 69, 123, 168, 248, 39, 67, 243, 30, 147, 11, // b256 ]; - let decoded = ABIDecoder::default().decode_multiple(&types, &bytes)?; + let decoded = ABIDecoder::default().decode_multiple(&types, bytes.as_slice())?; // Expected tokens let foo = Token::Struct(vec![ @@ -497,7 +503,7 @@ mod tests { generics: vec![], }; - let result = ABIDecoder::default().decode(&enum_w_only_units, &data)?; + let result = ABIDecoder::default().decode(&enum_w_only_units, data.as_slice())?; let expected_enum = Token::Enum(Box::new((1, Token::Unit, enum_variants))); assert_eq!(result, expected_enum); @@ -516,7 +522,7 @@ mod tests { generics: vec![], }; - let result = ABIDecoder::default().decode(&enum_type, &data); + let result = ABIDecoder::default().decode(&enum_type, data.as_slice()); let error = result.expect_err("should have resulted in an error"); @@ -529,8 +535,8 @@ mod tests { #[test] pub fn division_by_zero() { let param_type = Vec::<[u16; 0]>::param_type(); - let result = ABIDecoder::default().decode(¶m_type, &[]); - assert!(matches!(result, Err(Error::Codec(_)))); + let result = ABIDecoder::default().decode(¶m_type, [].as_slice()); + assert!(matches!(result, Err(Error::IO(_)))); } #[test] @@ -554,10 +560,10 @@ mod tests { .unwrap(), generics: vec![U16], }, - &[], + [].as_slice(), ); - assert!(matches!(result, Err(Error::Codec(_)))); + assert!(matches!(result, Err(Error::IO(_)))); } #[test] @@ -572,16 +578,16 @@ mod tests { enum_variants: EnumVariants::new(to_named(&[param_type])).unwrap(), generics: vec![U16], }, - &[], + [].as_slice(), ); - assert!(matches!(result, Err(Error::Codec(_)))); + assert!(matches!(result, Err(Error::IO(_)))); } #[test] pub fn capacity_overflow() { let result = ABIDecoder::default().decode( &Array(Box::new(Array(Box::new(Tuple(vec![])), usize::MAX)), 1), - &[], + [].as_slice(), ); assert!(matches!(result, Err(Error::Codec(_)))); } @@ -592,15 +598,15 @@ mod tests { for _ in 0..13500 { param_type = Vector(Box::new(param_type)); } - let result = ABIDecoder::default().decode(¶m_type, &[]); - assert!(matches!(result, Err(Error::Codec(_)))); + let result = ABIDecoder::default().decode(¶m_type, [].as_slice()); + assert!(matches!(result, Err(Error::IO(_)))); } #[test] pub fn capacity_malloc() { let param_type = Array(Box::new(U8), usize::MAX); - let result = ABIDecoder::default().decode(¶m_type, &[]); - assert!(matches!(result, Err(Error::Codec(_)))); + let result = ABIDecoder::default().decode(¶m_type, [].as_slice()); + assert!(matches!(result, Err(Error::IO(_)))); } #[test] @@ -618,7 +624,7 @@ mod tests { .iter() .map(|fun| fun(MAX_DEPTH + 1)) .for_each(|param_type| { - assert_decoding_failed_w_data(config, ¶m_type, &msg, &data); + assert_decoding_failed_w_data(config, ¶m_type, &msg, data.as_slice()); }) } @@ -647,7 +653,7 @@ mod tests { } }) .for_each(|param_type| { - ABIDecoder::new(config).decode(¶m_type, &data).unwrap(); + ABIDecoder::new(config).decode(¶m_type, data.as_slice()).unwrap(); }) } @@ -700,10 +706,10 @@ mod tests { let param_type = ParamType::Array(Box::new(ParamType::StringArray(0)), 2); let decoder = ABIDecoder::new(config); - decoder.decode(¶m_type, &[]).unwrap(); + decoder.decode(¶m_type, [].as_slice()).unwrap(); // when - let result = decoder.decode(¶m_type, &[]); + let result = decoder.decode(¶m_type, [].as_slice()); // then result.expect("element count to be reset"); diff --git a/packages/fuels-core/src/codec/abi_decoder/bounded_decoder.rs b/packages/fuels-core/src/codec/abi_decoder/bounded_decoder.rs index 9c5f85374c..3d1259c9dc 100644 --- a/packages/fuels-core/src/codec/abi_decoder/bounded_decoder.rs +++ b/packages/fuels-core/src/codec/abi_decoder/bounded_decoder.rs @@ -1,11 +1,10 @@ -use std::{iter::repeat, str}; +use std::{io::Read, iter::repeat, marker::PhantomData, str}; use crate::{ codec::{ utils::{CodecDirection, CounterWithLimit}, DecoderConfig, }, - constants::WORD_SIZE, types::{ errors::{error, Result}, param_types::{EnumVariants, NamedParamType, ParamType}, @@ -15,22 +14,14 @@ use crate::{ /// Is used to decode bytes into `Token`s from which types implementing `Tokenizable` can be /// instantiated. Implements decoding limits to control resource usage. -pub(crate) struct BoundedDecoder { +pub(crate) struct BoundedDecoder { depth_tracker: CounterWithLimit, token_tracker: CounterWithLimit, + /// We use a struct-level generic type to avoid https://github.com/rust-lang/rust/issues/50043 + _read: PhantomData, } -const U8_BYTES_SIZE: usize = 1; -const U16_BYTES_SIZE: usize = 2; -const U32_BYTES_SIZE: usize = 4; -const U64_BYTES_SIZE: usize = WORD_SIZE; -const U128_BYTES_SIZE: usize = 2 * WORD_SIZE; -const U256_BYTES_SIZE: usize = 4 * WORD_SIZE; -const B256_BYTES_SIZE: usize = 4 * WORD_SIZE; -const LENGTH_BYTES_SIZE: usize = WORD_SIZE; -const DISCRIMINANT_BYTES_SIZE: usize = WORD_SIZE; - -impl BoundedDecoder { +impl BoundedDecoder { pub(crate) fn new(config: DecoderConfig) -> Self { let depth_tracker = CounterWithLimit::new(config.max_depth, "depth", CodecDirection::Decoding); @@ -39,27 +30,26 @@ impl BoundedDecoder { Self { depth_tracker, token_tracker, + _read: PhantomData, } } - pub(crate) fn decode(&mut self, param_type: &ParamType, bytes: &[u8]) -> Result { - self.decode_param(param_type, bytes).map(|x| x.token) + pub(crate) fn decode(&mut self, param_type: &ParamType, bytes: &mut R) -> Result { + self.decode_param(param_type, bytes) } pub(crate) fn decode_multiple( &mut self, param_types: &[ParamType], - bytes: &[u8], + bytes: &mut R, ) -> Result> { - let (tokens, _) = self.decode_params(param_types, bytes)?; - - Ok(tokens) + self.decode_params(param_types, bytes) } fn run_w_depth_tracking( &mut self, - decoder: impl FnOnce(&mut Self) -> Result, - ) -> Result { + decoder: impl FnOnce(&mut Self) -> Result, + ) -> Result { self.depth_tracker.increase()?; let res = decoder(self); self.depth_tracker.decrease(); @@ -67,21 +57,21 @@ impl BoundedDecoder { res } - fn decode_param(&mut self, param_type: &ParamType, bytes: &[u8]) -> Result { + fn decode_param(&mut self, param_type: &ParamType, bytes: &mut R) -> Result { self.token_tracker.increase()?; match param_type { - ParamType::Unit => Self::decode_unit(), - ParamType::Bool => Self::decode_bool(bytes), - ParamType::U8 => Self::decode_u8(bytes), - ParamType::U16 => Self::decode_u16(bytes), - ParamType::U32 => Self::decode_u32(bytes), - ParamType::U64 => Self::decode_u64(bytes), - ParamType::U128 => Self::decode_u128(bytes), - ParamType::U256 => Self::decode_u256(bytes), - ParamType::B256 => Self::decode_b256(bytes), - ParamType::Bytes => Self::decode_bytes(bytes), + ParamType::Unit => Ok(Token::Unit), + ParamType::Bool => decode(bytes, |[value]| Token::Bool(value != 0)), + ParamType::U8 => decode(bytes, |[value]| Token::U8(value)), + ParamType::U16 => decode(bytes, |value| Token::U16(u16::from_be_bytes(value))), + ParamType::U32 => decode(bytes, |value| Token::U32(u32::from_be_bytes(value))), + ParamType::U64 => decode(bytes, |value| Token::U64(u64::from_be_bytes(value))), + ParamType::U128 => decode(bytes, |value| Token::U128(u128::from_be_bytes(value))), + ParamType::U256 => decode(bytes, |value| Token::U256(U256::from(value))), + ParamType::B256 => decode(bytes, |value| Token::B256(value)), + ParamType::Bytes => Ok(Token::Bytes(decode_slice(bytes)?)), ParamType::String => Self::decode_std_string(bytes), - ParamType::RawSlice => Self::decode_raw_slice(bytes), + ParamType::RawSlice => Ok(Token::RawSlice(decode_slice(bytes)?)), ParamType::StringArray(length) => Self::decode_string_array(bytes, *length), ParamType::StringSlice => Self::decode_string_slice(bytes), ParamType::Tuple(param_types) => { @@ -98,277 +88,120 @@ impl BoundedDecoder { self.run_w_depth_tracking(|ctx| ctx.decode_struct(fields, bytes)) } ParamType::Enum { enum_variants, .. } => { - self.run_w_depth_tracking(|ctx| ctx.decode_enum(bytes, enum_variants)) + self.run_w_depth_tracking(|ctx| ctx.decode_enum(enum_variants, bytes)) } } } - fn decode_unit() -> Result { - Ok(Decoded { - token: Token::Unit, - bytes_read: 0, - }) - } - - fn decode_bool(bytes: &[u8]) -> Result { - let value = peek_u8(bytes)? != 0u8; - - Ok(Decoded { - token: Token::Bool(value), - bytes_read: U8_BYTES_SIZE, - }) - } - - fn decode_u8(bytes: &[u8]) -> Result { - Ok(Decoded { - token: Token::U8(peek_u8(bytes)?), - bytes_read: U8_BYTES_SIZE, - }) - } - - fn decode_u16(bytes: &[u8]) -> Result { - Ok(Decoded { - token: Token::U16(peek_u16(bytes)?), - bytes_read: U16_BYTES_SIZE, - }) - } - - fn decode_u32(bytes: &[u8]) -> Result { - Ok(Decoded { - token: Token::U32(peek_u32(bytes)?), - bytes_read: U32_BYTES_SIZE, - }) - } - - fn decode_u64(bytes: &[u8]) -> Result { - Ok(Decoded { - token: Token::U64(peek_u64(bytes)?), - bytes_read: U64_BYTES_SIZE, - }) - } - - fn decode_u128(bytes: &[u8]) -> Result { - Ok(Decoded { - token: Token::U128(peek_u128(bytes)?), - bytes_read: U128_BYTES_SIZE, - }) + fn decode_std_string(bytes: &mut R) -> Result { + let data = decode_slice(bytes)?; + let string = str::from_utf8(&data)?.to_string(); + Ok(Token::String(string)) } - fn decode_u256(bytes: &[u8]) -> Result { - Ok(Decoded { - token: Token::U256(peek_u256(bytes)?), - bytes_read: U256_BYTES_SIZE, - }) + fn decode_string_array(bytes: &mut R, length: usize) -> Result { + let data = decode_sized(bytes, length)?; + let decoded = str::from_utf8(&data)?.to_string(); + Ok(Token::StringArray(StaticStringToken::new( + decoded, + Some(length), + ))) } - fn decode_b256(bytes: &[u8]) -> Result { - Ok(Decoded { - token: Token::B256(*peek_fixed::(bytes)?), - bytes_read: B256_BYTES_SIZE, - }) - } - - fn decode_bytes(bytes: &[u8]) -> Result { - let length = peek_length(bytes)?; - let bytes = peek(skip(bytes, LENGTH_BYTES_SIZE)?, length)?; - - Ok(Decoded { - token: Token::Bytes(bytes.to_vec()), - bytes_read: LENGTH_BYTES_SIZE + bytes.len(), - }) - } - - fn decode_std_string(bytes: &[u8]) -> Result { - let length = peek_length(bytes)?; - let bytes = peek(skip(bytes, LENGTH_BYTES_SIZE)?, length)?; - - Ok(Decoded { - token: Token::String(str::from_utf8(bytes)?.to_string()), - bytes_read: LENGTH_BYTES_SIZE + bytes.len(), - }) - } - - fn decode_raw_slice(bytes: &[u8]) -> Result { - let length = peek_length(bytes)?; - let bytes = peek(skip(bytes, LENGTH_BYTES_SIZE)?, length)?; - - Ok(Decoded { - token: Token::RawSlice(bytes.to_vec()), - bytes_read: LENGTH_BYTES_SIZE + bytes.len(), - }) - } - - fn decode_string_array(bytes: &[u8], length: usize) -> Result { - let bytes = peek(bytes, length)?; - let decoded = str::from_utf8(bytes)?.to_string(); - - Ok(Decoded { - token: Token::StringArray(StaticStringToken::new(decoded, Some(length))), - bytes_read: length, - }) - } - - fn decode_string_slice(bytes: &[u8]) -> Result { - let length = peek_length(bytes)?; - // skipping over the previously read length - let content_bytes = skip(bytes, LENGTH_BYTES_SIZE)?; - let string_bytes = peek(content_bytes, length)?; - let decoded = str::from_utf8(string_bytes)?.to_string(); - - Ok(Decoded { - token: Token::StringSlice(StaticStringToken::new(decoded, None)), - bytes_read: string_bytes.len() + LENGTH_BYTES_SIZE, - }) + fn decode_string_slice(bytes: &mut R) -> Result { + let data = decode_slice(bytes)?; + let decoded = str::from_utf8(&data)?.to_string(); + Ok(Token::StringSlice(StaticStringToken::new(decoded, None))) } - fn decode_tuple(&mut self, param_types: &[ParamType], bytes: &[u8]) -> Result { - let (tokens, bytes_read) = self.decode_params(param_types, bytes)?; - - Ok(Decoded { - token: Token::Tuple(tokens), - bytes_read, - }) + fn decode_tuple(&mut self, param_types: &[ParamType], bytes: &mut R) -> Result { + Ok(Token::Tuple(self.decode_params(param_types, bytes)?)) } fn decode_array( &mut self, param_type: &ParamType, - bytes: &[u8], + bytes: &mut R, length: usize, - ) -> Result { - let (tokens, bytes_read) = self.decode_params(repeat(param_type).take(length), bytes)?; - - Ok(Decoded { - token: Token::Array(tokens), - bytes_read, - }) + ) -> Result { + Ok(Token::Array( + self.decode_params(repeat(param_type).take(length), bytes)?, + )) } - fn decode_vector(&mut self, param_type: &ParamType, bytes: &[u8]) -> Result { - let length = peek_length(bytes)?; - let bytes = skip(bytes, LENGTH_BYTES_SIZE)?; - let (tokens, bytes_read) = self.decode_params(repeat(param_type).take(length), bytes)?; - - Ok(Decoded { - token: Token::Vector(tokens), - bytes_read: LENGTH_BYTES_SIZE + bytes_read, - }) + fn decode_vector(&mut self, param_type: &ParamType, bytes: &mut R) -> Result { + let length = decode_len(bytes)?; + Ok(Token::Vector( + self.decode_params(repeat(param_type).take(length), bytes)?, + )) } - fn decode_struct(&mut self, fields: &[NamedParamType], bytes: &[u8]) -> Result { - let (tokens, bytes_read) = self.decode_params(fields.iter().map(|(_, pt)| pt), bytes)?; - - Ok(Decoded { - token: Token::Struct(tokens), - bytes_read, - }) + fn decode_struct(&mut self, fields: &[NamedParamType], bytes: &mut R) -> Result { + Ok(Token::Struct( + self.decode_params(fields.iter().map(|(_, pt)| pt), bytes)?, + )) } - fn decode_enum(&mut self, bytes: &[u8], enum_variants: &EnumVariants) -> Result { - let discriminant = peek_discriminant(bytes)?; - let variant_bytes = skip(bytes, DISCRIMINANT_BYTES_SIZE)?; + fn decode_enum( + &mut self, + enum_variants: &EnumVariants, + bytes: &mut R, + ) -> Result { + let discriminant = decode(bytes, |value| u64::from_be_bytes(value))?; let (_, selected_variant) = enum_variants.select_variant(discriminant)?; - let decoded = self.decode_param(selected_variant, variant_bytes)?; + let decoded = self.decode_param(selected_variant, bytes)?; - Ok(Decoded { - token: Token::Enum(Box::new(( - discriminant, - decoded.token, - enum_variants.clone(), - ))), - bytes_read: DISCRIMINANT_BYTES_SIZE + decoded.bytes_read, - }) + Ok(Token::Enum(Box::new(( + discriminant, + decoded, + enum_variants.clone(), + )))) } fn decode_params<'a>( &mut self, param_types: impl IntoIterator, - bytes: &[u8], - ) -> Result<(Vec, usize)> { + bytes: &mut R, + ) -> Result> { let mut tokens = vec![]; - let mut bytes_read = 0; - for param_type in param_types { - let decoded = self.decode_param(param_type, skip(bytes, bytes_read)?)?; - tokens.push(decoded.token); - bytes_read += decoded.bytes_read; + tokens.push(self.decode_param(param_type, bytes)?); } - - Ok((tokens, bytes_read)) + Ok(tokens) } } -#[derive(Debug, Clone)] -struct Decoded { - token: Token, - bytes_read: usize, -} - -fn peek_u8(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; - Ok(u8::from_be_bytes(*slice)) -} - -fn peek_u16(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; - Ok(u16::from_be_bytes(*slice)) +/// Decodes a fixed-size array of bytes using a converter function. +fn decode( + bytes: &mut R, + f: impl FnOnce([u8; SIZE]) -> Out, +) -> Result { + let mut buffer = [0u8; SIZE]; + bytes.read_exact(&mut buffer)?; + Ok(f(buffer)) } -fn peek_u32(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; - Ok(u32::from_be_bytes(*slice)) +/// Reads a byte array with known size. +fn decode_sized(bytes: &mut R, len: usize) -> Result> { + let mut data = vec![0; len]; + bytes.read_exact(&mut data)?; + Ok(data) } -fn peek_u64(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; - Ok(u64::from_be_bytes(*slice)) -} - -fn peek_u128(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; - Ok(u128::from_be_bytes(*slice)) -} - -fn peek_u256(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; - Ok(U256::from(*slice)) -} - -fn peek_length(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; - - u64::from_be_bytes(*slice) +/// Decodes a length prefix. +fn decode_len(bytes: &mut R) -> Result { + let len_u64 = decode(bytes, |value| u64::from_be_bytes(value))?; + let len: usize = len_u64 .try_into() - .map_err(|_| error!(Other, "could not convert `u64` to `usize`")) + .map_err(|_| error!(Other, "could not convert `u64` to `usize`"))?; + Ok(len) } -fn peek_discriminant(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; - Ok(u64::from_be_bytes(*slice)) -} - -fn peek(data: &[u8], len: usize) -> Result<&[u8]> { - (len <= data.len()).then(|| &data[..len]).ok_or(error!( - Codec, - "tried to read `{len}` bytes but only had `{}` remaining!", - data.len() - )) -} - -fn peek_fixed(data: &[u8]) -> Result<&[u8; LEN]> { - let slice_w_correct_length = peek(data, LEN)?; - Ok(slice_w_correct_length - .try_into() - .expect("peek(data, len) must return a slice of length `len` or error out")) -} - -fn skip(slice: &[u8], num_bytes: usize) -> Result<&[u8]> { - (num_bytes <= slice.len()) - .then_some(&slice[num_bytes..]) - .ok_or(error!( - Codec, - "tried to consume `{num_bytes}` bytes but only had `{}` remaining!", - slice.len() - )) +/// Decodes a size-prefixed slice. +fn decode_slice(bytes: &mut R) -> Result> { + let len = decode_len(bytes)?; + let mut data = vec![0; len]; + bytes.read_exact(&mut data)?; + Ok(data) } diff --git a/packages/fuels-core/src/codec/abi_decoder/decode_as_debug_str.rs b/packages/fuels-core/src/codec/abi_decoder/decode_as_debug_str.rs index 0c462eb6ca..270a2cd1e1 100644 --- a/packages/fuels-core/src/codec/abi_decoder/decode_as_debug_str.rs +++ b/packages/fuels-core/src/codec/abi_decoder/decode_as_debug_str.rs @@ -110,34 +110,34 @@ mod tests { { assert_eq!( format!("{:?}", true), - decoder.decode_as_debug_str(&bool::param_type(), &[1])? + decoder.decode_as_debug_str(&bool::param_type(), [1].as_slice())? ); assert_eq!( format!("{:?}", 128u8), - decoder.decode_as_debug_str(&u8::param_type(), &[128])? + decoder.decode_as_debug_str(&u8::param_type(), [128].as_slice())? ); assert_eq!( format!("{:?}", 256u16), - decoder.decode_as_debug_str(&u16::param_type(), &[1, 0])? + decoder.decode_as_debug_str(&u16::param_type(), [1, 0].as_slice())? ); assert_eq!( format!("{:?}", 512u32), - decoder.decode_as_debug_str(&u32::param_type(), &[0, 0, 2, 0])? + decoder.decode_as_debug_str(&u32::param_type(), [0, 0, 2, 0].as_slice())? ); assert_eq!( format!("{:?}", 1024u64), - decoder.decode_as_debug_str(&u64::param_type(), &[0, 0, 0, 0, 0, 0, 4, 0])? + decoder.decode_as_debug_str(&u64::param_type(), [0, 0, 0, 0, 0, 0, 4, 0].as_slice())? ); assert_eq!( format!("{:?}", 1024u128), decoder.decode_as_debug_str( &u128::param_type(), - &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0] + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0].as_slice() )? ); @@ -145,10 +145,10 @@ mod tests { format!("{:?}", U256::from(2048)), decoder.decode_as_debug_str( &U256::param_type(), - &[ + [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0 - ] + ].as_slice() )? ); } @@ -163,10 +163,10 @@ mod tests { format!("{bits256:?}"), decoder.decode_as_debug_str( &Bits256::param_type(), - &[ + [ 239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74 - ] + ].as_slice() )? ); @@ -174,11 +174,11 @@ mod tests { format!("{:?}", Bytes(bytes.to_vec())), decoder.decode_as_debug_str( &Bytes::param_type(), - &[ + [ 0, 0, 0, 0, 0, 0, 0, 32, 239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74 - ] + ].as_slice() )? ); @@ -186,11 +186,11 @@ mod tests { format!("{:?}", RawSlice(bytes.to_vec())), decoder.decode_as_debug_str( &RawSlice::param_type(), - &[ + [ 0, 0, 0, 0, 0, 0, 0, 32, 239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74 - ] + ].as_slice() )? ); @@ -198,10 +198,10 @@ mod tests { format!("{:?}", EvmAddress::from(bits256)), decoder.decode_as_debug_str( &EvmAddress::param_type(), - &[ + [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 166, 225, 89, 161, 16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74 - ] + ].as_slice() )? ); } @@ -210,7 +210,7 @@ mod tests { format!("{:?}", AsciiString::new("Fuel".to_string())?), decoder.decode_as_debug_str( &AsciiString::param_type(), - &[0, 0, 0, 0, 0, 0, 0, 4, 70, 117, 101, 108] + [0, 0, 0, 0, 0, 0, 0, 4, 70, 117, 101, 108].as_slice() )? ); @@ -218,7 +218,7 @@ mod tests { format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?), decoder.decode_as_debug_str( &SizedAsciiString::<4>::param_type(), - &[70, 117, 101, 108, 0, 0, 0, 0] + [70, 117, 101, 108, 0, 0, 0, 0].as_slice() )? ); @@ -226,21 +226,21 @@ mod tests { format!("{}", "Fuel"), decoder.decode_as_debug_str( &String::param_type(), - &[0, 0, 0, 0, 0, 0, 0, 4, 70, 117, 101, 108] + [0, 0, 0, 0, 0, 0, 0, 4, 70, 117, 101, 108].as_slice() )? ); } { assert_eq!( format!("{:?}", (1, 2)), - decoder.decode_as_debug_str(&<(u8, u8)>::param_type(), &[1, 2])? + decoder.decode_as_debug_str(&<(u8, u8)>::param_type(), [1, 2].as_slice())? ); assert_eq!( format!("{:?}", [3, 4]), decoder.decode_as_debug_str( &<[u64; 2]>::param_type(), - &[0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4] + [0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4].as_slice() )? ); } @@ -249,7 +249,7 @@ mod tests { format!("{:?}", Some(42)), decoder.decode_as_debug_str( &>::param_type(), - &[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 42] + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 42].as_slice() )? ); @@ -257,7 +257,7 @@ mod tests { format!("{:?}", Err::(42u64)), decoder.decode_as_debug_str( &>::param_type(), - &[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 42] + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 42].as_slice() )? ); } diff --git a/packages/fuels-core/src/codec/abi_formatter.rs b/packages/fuels-core/src/codec/abi_formatter.rs index 42a19d3c67..1b2ca55bd0 100644 --- a/packages/fuels-core/src/codec/abi_formatter.rs +++ b/packages/fuels-core/src/codec/abi_formatter.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{collections::HashMap, io::Read}; use fuel_abi_types::abi::unified_program::UnifiedProgramABI; use itertools::Itertools; @@ -75,7 +75,7 @@ impl ABIFormatter { Self::from_abi(parsed_abi) } - pub fn decode_fn_args(&self, fn_name: &str, data: &[u8]) -> Result> { + pub fn decode_fn_args(&self, fn_name: &str, data: R) -> Result> { let args = self .functions .get(fn_name) @@ -84,7 +84,10 @@ impl ABIFormatter { self.decoder.decode_multiple_as_debug_str(args, data) } - pub fn decode_configurables(&self, configurable_data: &[u8]) -> Result> { + pub fn decode_configurables( + &self, + configurable_data: R, + ) -> Result> { let param_types = self .configurables .iter() @@ -116,7 +119,7 @@ mod tests { let decoder = ABIFormatter::from_abi(UnifiedProgramABI::default()).unwrap(); // when - let err = decoder.decode_fn_args("non_existent_fn", &[]).unwrap_err(); + let err = decoder.decode_fn_args("non_existent_fn", [].as_slice()).unwrap_err(); // then let Error::Codec(err) = err else { diff --git a/packages/fuels-core/src/codec/logs.rs b/packages/fuels-core/src/codec/logs.rs index 9c8ae7b6f1..1561748a67 100644 --- a/packages/fuels-core/src/codec/logs.rs +++ b/packages/fuels-core/src/codec/logs.rs @@ -173,8 +173,8 @@ impl LogDecoder { .extract_log_id_and_data() .filter_map(|(log_id, bytes)| { target_ids.contains(&log_id).then(|| { - let token = - ABIDecoder::new(self.decoder_config).decode(&T::param_type(), &bytes)?; + let token = ABIDecoder::new(self.decoder_config) + .decode(&T::param_type(), bytes.as_slice())?; T::from_token(token) }) diff --git a/packages/fuels-programs/src/calls/receipt_parser.rs b/packages/fuels-programs/src/calls/receipt_parser.rs index 7027ea9d30..ae165627bc 100644 --- a/packages/fuels-programs/src/calls/receipt_parser.rs +++ b/packages/fuels-programs/src/calls/receipt_parser.rs @@ -41,7 +41,7 @@ impl ReceiptParser { .extract_contract_call_data(contract_id.into()) .ok_or_else(|| Self::missing_receipts_error(output_param))?; - self.decoder.decode(output_param, &data) + self.decoder.decode(output_param, data.as_slice()) } pub fn parse_script(self, output_param: &ParamType) -> Result { @@ -49,7 +49,7 @@ impl ReceiptParser { .extract_script_data() .ok_or_else(|| Self::missing_receipts_error(output_param))?; - self.decoder.decode(output_param, &data) + self.decoder.decode(output_param, data.as_slice()) } fn missing_receipts_error(output_param: &ParamType) -> Error {