From 81fd7f2e4ec44494661bb3101f33ebc2e2bdd7f2 Mon Sep 17 00:00:00 2001 From: hal3e Date: Tue, 16 Apr 2024 09:05:22 +0200 Subject: [PATCH] feature!: make experimental encoding deafault (#1334) This PR makes the experimental encoding default and adds the `legacy_encoding` flag to use the old one. --- .github/workflows/ci.yml | 20 +- examples/contracts/Cargo.toml | 2 +- examples/contracts/src/lib.rs | 12 +- examples/debugging/Cargo.toml | 2 +- examples/debugging/src/lib.rs | 12 +- packages/fuels-core/Cargo.toml | 2 +- packages/fuels-core/src/codec.rs | 4 +- packages/fuels-core/src/codec/abi_decoder.rs | 66 +-- .../src/codec/abi_decoder/bounded_decoder.rs | 425 +++++++----------- .../codec/abi_decoder/decode_as_debug_str.rs | 4 +- ...d_decoder.rs => legacy_bounded_decoder.rs} | 425 +++++++++++------- packages/fuels-core/src/codec/abi_encoder.rs | 102 ++--- .../src/codec/abi_encoder/bounded_encoder.rs | 257 +++-------- .../experimental_bounded_encoder.rs | 133 ------ .../abi_encoder/legacy_bounded_encoder.rs | 288 ++++++++++++ .../fuels-core/src/codec/function_selector.rs | 48 +- packages/fuels-core/src/codec/logs.rs | 4 +- packages/fuels-core/src/types.rs | 4 +- .../src/types/param_types/param_type.rs | 4 +- packages/fuels-programs/Cargo.toml | 2 +- packages/fuels-programs/src/call_utils.rs | 34 +- packages/fuels-programs/src/contract.rs | 6 +- packages/fuels-programs/src/receipt_parser.rs | 12 +- packages/fuels/Cargo.toml | 2 +- packages/fuels/tests/bindings.rs | 28 +- packages/fuels/tests/contracts.rs | 12 +- packages/fuels/tests/from_token.rs | 6 +- packages/fuels/tests/logs.rs | 6 +- packages/fuels/tests/predicates.rs | 8 +- packages/fuels/tests/providers.rs | 4 +- packages/fuels/tests/types_contracts.rs | 6 +- packages/fuels/tests/types_scripts.rs | 2 +- packages/wasm-tests/Cargo.toml | 2 +- packages/wasm-tests/src/lib.rs | 4 +- 34 files changed, 974 insertions(+), 974 deletions(-) rename packages/fuels-core/src/codec/abi_decoder/{experimental_bounded_decoder.rs => legacy_bounded_decoder.rs} (50%) delete mode 100644 packages/fuels-core/src/codec/abi_encoder/experimental_bounded_encoder.rs create mode 100644 packages/fuels-core/src/codec/abi_encoder/legacy_bounded_encoder.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d271b2213c..fb89addeeb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,7 +63,7 @@ jobs: working-directory: packages/fuels - name: Build Sway test projects - run: forc build --terse --error-on-warnings + run: forc build --terse --error-on-warnings --experimental-new-encoding working-directory: packages/fuels - uses: actions/upload-artifact@v2 @@ -82,7 +82,7 @@ jobs: # TODO: To be removed once https://github.com/FuelLabs/fuels-rs/issues/881 is unblocked. - name: Build Sway test projects w type paths - run: forc build --terse --error-on-warnings --json-abi-with-callpaths + run: forc build --terse --error-on-warnings --json-abi-with-callpaths --experimental-new-encoding working-directory: packages/fuels - uses: actions/upload-artifact@v2 @@ -99,15 +99,15 @@ jobs: !packages/fuels/tests/**/Forc.lock !packages/fuels/tests/.gitignore - # TODO: To be removed once experimental encoding is the default - - name: Build Sway test projects w experimental encoding - run: forc build --terse --error-on-warnings --json-abi-with-callpaths --experimental-new-encoding + # TODO: To be removed once legacy encoding is removed + - name: Build Sway test projects w legacy encoding + run: forc build --terse --error-on-warnings --json-abi-with-callpaths working-directory: packages/fuels - uses: actions/upload-artifact@v2 with: retention-days: 2 - name: sway-examples-w-experimental-encoding + name: sway-examples-w-legacy-encoding # cache only the sway build artifacts, skip all src files path: | packages/fuels/tests @@ -205,10 +205,10 @@ jobs: args: - command: check_typos args: - # TODO: To be removed once experimental encoding is the default + # TODO: To be removed once legacy encoding is removed - cargo_command: nextest - args: run --all-targets --features "experimental" --workspace --cargo-quiet - download_sway_artifacts: sway-examples-w-experimental-encoding + args: run --all-targets --features "legacy_encoding" --workspace --cargo-quiet + download_sway_artifacts: sway-examples-w-legacy-encoding install_fuel_core: true steps: - name: Checkout repository @@ -275,7 +275,7 @@ jobs: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh cd packages/wasm-tests wasm-pack test --node - wasm-pack test --node --features experimental + wasm-pack test --node --features legacy_encoding - name: Check for invalid documentation anchors if: ${{ matrix.command == 'check_doc_anchors_valid' }} diff --git a/examples/contracts/Cargo.toml b/examples/contracts/Cargo.toml index dabd063d66..730db72a76 100644 --- a/examples/contracts/Cargo.toml +++ b/examples/contracts/Cargo.toml @@ -17,4 +17,4 @@ tokio = { workspace = true, features = ["full"] } [features] fuel-core-lib = ["fuels/fuel-core-lib"] rocksdb = ["fuels/rocksdb"] -experimental = ["fuels/experimental"] +legacy_encoding = ["fuels/legacy_encoding"] diff --git a/examples/contracts/src/lib.rs b/examples/contracts/src/lib.rs index 7e8a7f556d..0e7c445f45 100644 --- a/examples/contracts/src/lib.rs +++ b/examples/contracts/src/lib.rs @@ -113,10 +113,10 @@ mod tests { .await?; // ANCHOR_END: contract_call_cost_estimation - let expected_gas = if cfg!(feature = "experimental") { - 2065 - } else { + let expected_gas = if cfg!(feature = "legacy_encoding") { 796 + } else { + 2065 }; assert_eq!(transaction_cost.gas_used, expected_gas); @@ -614,9 +614,9 @@ mod tests { .await?; // ANCHOR_END: multi_call_cost_estimation - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let expected_gas = 1172; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let expected_gas = 3532; assert_eq!(transaction_cost.gas_used, expected_gas); @@ -698,7 +698,7 @@ mod tests { } #[tokio::test] - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] async fn low_level_call_example() -> Result<()> { use fuels::{ core::codec::{calldata, fn_selector}, diff --git a/examples/debugging/Cargo.toml b/examples/debugging/Cargo.toml index 5c4f7c89f5..cdb6f9ee7e 100644 --- a/examples/debugging/Cargo.toml +++ b/examples/debugging/Cargo.toml @@ -17,4 +17,4 @@ serde_json = { workspace = true } tokio = { workspace = true, features = ["full"] } [features] -experimental = ["fuels/experimental"] +legacy_encoding = ["fuels/legacy_encoding"] diff --git a/examples/debugging/src/lib.rs b/examples/debugging/src/lib.rs index 2b52562324..8737a5e97f 100644 --- a/examples/debugging/src/lib.rs +++ b/examples/debugging/src/lib.rs @@ -3,7 +3,7 @@ mod tests { use std::collections::HashMap; use fuel_abi_types::abi::program::ProgramABI; - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] use fuels::core::codec::{calldata, fn_selector}; use fuels::{ core::codec::ABIDecoder, @@ -11,7 +11,7 @@ mod tests { types::{errors::Result, param_types::ParamType, SizedAsciiString}, }; - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] #[test] fn get_a_fn_selector() { use fuels::core::{codec::resolve_fn_selector, traits::Parameterize}; @@ -27,7 +27,7 @@ mod tests { // ANCHOR_END: example_fn_selector } - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] #[test] fn a_fn_selector_from_json_abi() -> Result<()> { use fuels::core::codec::resolve_fn_selector; @@ -65,7 +65,7 @@ mod tests { Ok(()) } - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] #[test] fn test_macros() -> Result<()> { let function_selector = fn_selector!(initialize_counter(u64)); @@ -167,9 +167,9 @@ mod tests { let expected_u8 = 1; - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let data = [0, 0, 0, 0, 0, 0, 0, 1]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let data = [1]; assert_eq!( diff --git a/packages/fuels-core/Cargo.toml b/packages/fuels-core/Cargo.toml index 02f0a559ac..a1f1764f4f 100644 --- a/packages/fuels-core/Cargo.toml +++ b/packages/fuels-core/Cargo.toml @@ -37,4 +37,4 @@ tokio = { workspace = true, features = ["test-util", "macros"] } [features] default = ["std"] std = ["dep:fuel-core-client"] -experimental = [] +legacy_encoding = [] diff --git a/packages/fuels-core/src/codec.rs b/packages/fuels-core/src/codec.rs index b33c519707..2e50706d1e 100644 --- a/packages/fuels-core/src/codec.rs +++ b/packages/fuels-core/src/codec.rs @@ -34,9 +34,9 @@ mod tests { #[test] fn can_convert_bytes_into_tuple() -> Result<()> { - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let tuple_in_bytes: Vec = vec![0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let tuple_in_bytes: Vec = vec![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())?; diff --git a/packages/fuels-core/src/codec/abi_decoder.rs b/packages/fuels-core/src/codec/abi_decoder.rs index c32055f752..33d6551b1e 100644 --- a/packages/fuels-core/src/codec/abi_decoder.rs +++ b/packages/fuels-core/src/codec/abi_decoder.rs @@ -1,13 +1,13 @@ -#[cfg(not(feature = "experimental"))] +#[cfg(not(feature = "legacy_encoding"))] mod bounded_decoder; mod decode_as_debug_str; -#[cfg(feature = "experimental")] -mod experimental_bounded_decoder; +#[cfg(feature = "legacy_encoding")] +mod legacy_bounded_decoder; -#[cfg(not(feature = "experimental"))] +#[cfg(not(feature = "legacy_encoding"))] use crate::codec::abi_decoder::bounded_decoder::BoundedDecoder; -#[cfg(feature = "experimental")] -use crate::codec::abi_decoder::experimental_bounded_decoder::BoundedDecoder; +#[cfg(feature = "legacy_encoding")] +use crate::codec::abi_decoder::legacy_bounded_decoder::BoundedDecoder; use crate::{ codec::abi_decoder::decode_as_debug_str::decode_as_debug_str, types::{errors::Result, param_types::ParamType, Token}, @@ -138,7 +138,7 @@ mod tests { ParamType::U256, ]; - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let data = [ 255, // u8 0, 0, 0, 0, 0, 0, 255, 255, // u16 @@ -149,7 +149,7 @@ mod tests { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // u256 ]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let data = [ 255, // u8 255, 255, // u16 @@ -207,13 +207,13 @@ mod tests { #[test] fn decode_string_array() -> Result<()> { let types = vec![ParamType::StringArray(23), ParamType::StringArray(5)]; - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let data = [ 84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 117, 108, 108, 32, 115, 101, 110, 116, 101, 110, 99, 101, 0, //This is a full sentence 72, 101, 108, 108, 111, 0, 0, 0, // Hello ]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let data = [ 84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 117, 108, 108, 32, 115, 101, 110, 116, 101, 110, 99, 101, //This is a full sentence @@ -237,12 +237,12 @@ mod tests { #[test] fn decode_string_slice() -> Result<()> { - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let data = [ 84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 117, 108, 108, 32, 115, 101, 110, 116, 101, 110, 99, 101, //This is a full sentence ]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let data = [ 0, 0, 0, 0, 0, 0, 0, 23, // [length] 84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 117, 108, 108, 32, 115, 101, 110, @@ -263,12 +263,12 @@ mod tests { #[test] fn decode_string() -> Result<()> { - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let data = [ 84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 117, 108, 108, 32, 115, 101, 110, 116, 101, 110, 99, 101, //This is a full sentence ]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let data = [ 0, 0, 0, 0, 0, 0, 0, 23, // [length] 84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 117, 108, 108, 32, 115, 101, 110, @@ -287,12 +287,12 @@ mod tests { #[test] fn decode_tuple() -> Result<()> { let param_type = ParamType::Tuple(vec![ParamType::U32, ParamType::Bool]); - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let data = [ 0, 0, 0, 0, 0, 0, 0, 255, //u32 1, 0, 0, 0, 0, 0, 0, 0, //bool ]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let data = [ 0, 0, 0, 255, //u32 1, //bool @@ -327,9 +327,9 @@ mod tests { // bar: bool, // } - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let data = [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let data = [1, 1]; let param_type = ParamType::Struct { @@ -349,9 +349,9 @@ mod tests { #[test] fn decode_bytes() -> Result<()> { - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let data = [255, 0, 1, 2, 3, 4, 5]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] 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)?; @@ -365,9 +365,9 @@ mod tests { #[test] fn decode_raw_slice() -> Result<()> { - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let data = [255, 0, 1, 2, 3, 4, 5]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] 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)?; @@ -395,9 +395,9 @@ mod tests { }]; // "0" discriminant and 42 enum value - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let data = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let data = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42]; let decoded = ABIDecoder::default().decode_multiple(&types, &data)?; @@ -408,7 +408,7 @@ mod tests { Ok(()) } - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] #[test] fn decoder_will_skip_enum_padding_and_decode_next_arg() -> Result<()> { // struct MyStruct { @@ -493,11 +493,11 @@ mod tests { generics: vec![], }; - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let data = [ 0, 0, 0, 0, 0, 0, 0, 10, 1, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, ]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let data = [0, 10, 1, 1, 2]; let decoded = ABIDecoder::default().decode(&nested_struct, &data)?; @@ -552,7 +552,7 @@ mod tests { let types = [nested_struct, u8_arr, b256]; - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let bytes = [ 0, 0, 0, 0, 0, 0, 0, 10, // u16 1, 0, 0, 0, 0, 0, 0, 0, // bool @@ -562,7 +562,7 @@ mod tests { 152, 244, 172, 69, 123, 168, 248, 39, 67, 243, 30, 147, 11, // b256 ]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let bytes = [ 0, 10, // u16 1, // bool @@ -597,7 +597,7 @@ mod tests { Ok(()) } - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] #[test] fn units_in_structs_are_decoded_as_one_word() -> Result<()> { let data = [ @@ -734,7 +734,7 @@ mod tests { assert!(matches!(result, Err(Error::Codec(_)))); } - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] #[test] fn decoding_enum_with_more_than_one_heap_type_variant_fails() -> Result<()> { let mut param_types = vec![ @@ -774,7 +774,7 @@ mod tests { Ok(()) } - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] #[test] fn enums_w_too_deeply_nested_heap_types_not_allowed() { let variants = to_named(&[ @@ -892,7 +892,7 @@ mod tests { } } - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] #[test] fn vectors_of_zst_are_not_supported() { let param_type = ParamType::Vector(Box::new(ParamType::StringArray(0))); 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 4bc169d504..3a8bbb6d2a 100644 --- a/packages/fuels-core/src/codec/abi_decoder/bounded_decoder.rs +++ b/packages/fuels-core/src/codec/abi_decoder/bounded_decoder.rs @@ -1,7 +1,6 @@ -use std::{convert::TryInto, str}; +use std::{iter::repeat, str}; use crate::{ - checked_round_up_to_word_alignment, codec::{ utils::{CodecDirection, CounterWithLimit}, DecoderConfig, @@ -19,12 +18,17 @@ use crate::{ pub(crate) struct BoundedDecoder { depth_tracker: CounterWithLimit, token_tracker: CounterWithLimit, - config: DecoderConfig, } +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 { pub(crate) fn new(config: DecoderConfig) -> Self { @@ -35,29 +39,11 @@ impl BoundedDecoder { Self { depth_tracker, token_tracker, - config, } } pub(crate) fn decode(&mut self, param_type: &ParamType, bytes: &[u8]) -> Result { - param_type.validate_is_decodable(self.config.max_depth)?; - match param_type { - // Unit, U8 and Bool are returned as u64 from receipt "Return" - ParamType::Unit => Ok(Token::Unit), - ParamType::U8 => Self::decode_u64(bytes).map(|r| { - Token::U8(match r.token { - Token::U64(v) => v as u8, - _ => unreachable!("decode_u64 returning unexpected token"), - }) - }), - ParamType::Bool => Self::decode_u64(bytes).map(|r| { - Token::Bool(match r.token { - Token::U64(v) => v != 0, - _ => unreachable!("decode_u64 returning unexpected token"), - }) - }), - _ => self.decode_param(param_type, bytes).map(|x| x.token), - } + self.decode_param(param_type, bytes).map(|x| x.token) } pub(crate) fn decode_multiple( @@ -65,9 +51,6 @@ impl BoundedDecoder { param_types: &[ParamType], bytes: &[u8], ) -> Result> { - for param_type in param_types { - param_type.validate_is_decodable(self.config.max_depth)?; - } let (tokens, _) = self.decode_params(param_types, bytes)?; Ok(tokens) @@ -78,294 +61,239 @@ impl BoundedDecoder { decoder: impl FnOnce(&mut Self) -> Result, ) -> Result { self.depth_tracker.increase()?; - let res = decoder(self); - self.depth_tracker.decrease(); + res } fn decode_param(&mut self, param_type: &ParamType, bytes: &[u8]) -> Result { self.token_tracker.increase()?; match param_type { - ParamType::Unit => Self::decode_unit(bytes), + 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::Bool => Self::decode_bool(bytes), ParamType::B256 => Self::decode_b256(bytes), + ParamType::Bytes => Self::decode_bytes(bytes), + ParamType::String => Self::decode_std_string(bytes), ParamType::RawSlice => Self::decode_raw_slice(bytes), + ParamType::StringArray(length) => Self::decode_string_array(bytes, *length), ParamType::StringSlice => Self::decode_string_slice(bytes), - ParamType::StringArray(len) => Self::decode_string_array(bytes, *len), - ParamType::Array(ref t, length) => { - self.run_w_depth_tracking(|ctx| ctx.decode_array(t, bytes, *length)) + ParamType::Tuple(param_types) => { + self.run_w_depth_tracking(|ctx| ctx.decode_tuple(param_types, bytes)) + } + ParamType::Array(param_type, length) => { + self.run_w_depth_tracking(|ctx| ctx.decode_array(param_type, bytes, *length)) } + ParamType::Vector(param_type) => { + self.run_w_depth_tracking(|ctx| ctx.decode_vector(param_type, bytes)) + } + ParamType::Struct { fields, .. } => { 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)) } - ParamType::Tuple(types) => { - self.run_w_depth_tracking(|ctx| ctx.decode_tuple(types, bytes)) - } - ParamType::Vector(param_type) => { - // although nested vectors cannot be decoded yet, depth tracking still occurs for future - // proofing - self.run_w_depth_tracking(|ctx| ctx.decode_vector(param_type, bytes)) - } - ParamType::Bytes => Self::decode_bytes(bytes), - ParamType::String => Self::decode_std_string(bytes), } } - fn decode_bytes(bytes: &[u8]) -> Result { + fn decode_unit() -> Result { Ok(Decoded { - token: Token::Bytes(bytes.to_vec()), - bytes_read: bytes.len(), + token: Token::Unit, + bytes_read: 0, }) } - fn decode_std_string(bytes: &[u8]) -> Result { + fn decode_bool(bytes: &[u8]) -> Result { + let value = peek_u8(bytes)? != 0u8; + Ok(Decoded { - token: Token::String(str::from_utf8(bytes)?.to_string()), - bytes_read: bytes.len(), + token: Token::Bool(value), + bytes_read: U8_BYTES_SIZE, }) } - fn decode_vector(&mut self, param_type: &ParamType, bytes: &[u8]) -> Result { - let num_of_elements = ParamType::calculate_num_of_elements(param_type, bytes.len())?; - let (tokens, bytes_read) = - self.decode_params(std::iter::repeat(param_type).take(num_of_elements), bytes)?; - + fn decode_u8(bytes: &[u8]) -> Result { Ok(Decoded { - token: Token::Vector(tokens), - bytes_read, + token: Token::U8(peek_u8(bytes)?), + bytes_read: U8_BYTES_SIZE, }) } - fn decode_tuple(&mut self, param_types: &[ParamType], bytes: &[u8]) -> Result { - let mut tokens = vec![]; - - let mut bytes_read = 0; - - for param_type in param_types.iter() { - // padding has to be taken into account - bytes_read = checked_round_up_to_word_alignment(bytes_read)?; - let res = self.decode_param(param_type, skip(bytes, bytes_read)?)?; - bytes_read += res.bytes_read; - tokens.push(res.token); - } - + fn decode_u16(bytes: &[u8]) -> Result { Ok(Decoded { - token: Token::Tuple(tokens), - bytes_read, + token: Token::U16(peek_u16(bytes)?), + bytes_read: U16_BYTES_SIZE, }) } - fn decode_struct(&mut self, param_types: &[NamedParamType], bytes: &[u8]) -> Result { - let mut tokens = vec![]; - - let mut bytes_read = 0; - - for (_, param_type) in param_types.iter() { - // padding has to be taken into account - bytes_read = checked_round_up_to_word_alignment(bytes_read)?; - let res = self.decode_param(param_type, skip(bytes, bytes_read)?)?; - bytes_read += res.bytes_read; - tokens.push(res.token); - } - + fn decode_u32(bytes: &[u8]) -> Result { Ok(Decoded { - token: Token::Struct(tokens), - bytes_read, + token: Token::U32(peek_u32(bytes)?), + bytes_read: U32_BYTES_SIZE, }) } - fn decode_params<'a>( - &mut self, - param_types: impl IntoIterator, - bytes: &[u8], - ) -> Result<(Vec, usize)> { - let mut results = vec![]; - - let mut bytes_read = 0; - - for param_type in param_types { - let res = self.decode_param(param_type, skip(bytes, bytes_read)?)?; - bytes_read += res.bytes_read; - results.push(res.token); - } - - Ok((results, bytes_read)) - } - - fn decode_array( - &mut self, - param_type: &ParamType, - bytes: &[u8], - length: usize, - ) -> Result { - let (tokens, bytes_read) = - self.decode_params(std::iter::repeat(param_type).take(length), bytes)?; - + fn decode_u64(bytes: &[u8]) -> Result { Ok(Decoded { - token: Token::Array(tokens), - bytes_read, + token: Token::U64(peek_u64(bytes)?), + bytes_read: U64_BYTES_SIZE, }) } - fn decode_raw_slice(bytes: &[u8]) -> Result { + fn decode_u128(bytes: &[u8]) -> Result { Ok(Decoded { - token: Token::RawSlice(bytes.to_vec()), - bytes_read: bytes.len(), + token: Token::U128(peek_u128(bytes)?), + bytes_read: U128_BYTES_SIZE, }) } - fn decode_string_slice(bytes: &[u8]) -> Result { - let decoded = str::from_utf8(bytes)?; - + fn decode_u256(bytes: &[u8]) -> Result { Ok(Decoded { - token: Token::StringSlice(StaticStringToken::new(decoded.into(), None)), - bytes_read: decoded.len(), + token: Token::U256(peek_u256(bytes)?), + bytes_read: U256_BYTES_SIZE, }) } - fn decode_string_array(bytes: &[u8], length: usize) -> Result { - let encoded_str = peek(bytes, length)?; - - let decoded = str::from_utf8(encoded_str)?; - let result = Decoded { - token: Token::StringArray(StaticStringToken::new(decoded.into(), Some(length))), - bytes_read: checked_round_up_to_word_alignment(length)?, - }; - Ok(result) - } - fn decode_b256(bytes: &[u8]) -> Result { Ok(Decoded { - token: Token::B256(*peek_fixed::<32>(bytes)?), + token: Token::B256(*peek_fixed::(bytes)?), bytes_read: B256_BYTES_SIZE, }) } - fn decode_bool(bytes: &[u8]) -> Result { - // Grab last byte of the word and compare it to 0x00 - let b = peek_u8(bytes)? != 0u8; + fn decode_bytes(bytes: &[u8]) -> Result { + let length = peek_length(bytes)?; + let bytes = peek(skip(bytes, LENGTH_BYTES_SIZE)?, length)?; - let result = Decoded { - token: Token::Bool(b), - bytes_read: 1, - }; + Ok(Decoded { + token: Token::Bytes(bytes.to_vec()), + bytes_read: LENGTH_BYTES_SIZE + bytes.len(), + }) + } - Ok(result) + 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_u128(bytes: &[u8]) -> Result { + 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::U128(peek_u128(bytes)?), - bytes_read: U128_BYTES_SIZE, + token: Token::RawSlice(bytes.to_vec()), + bytes_read: LENGTH_BYTES_SIZE + bytes.len(), }) } - fn decode_u256(bytes: &[u8]) -> Result { + 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::U256(peek_u256(bytes)?), - bytes_read: U256_BYTES_SIZE, + token: Token::StringArray(StaticStringToken::new(decoded, Some(length))), + bytes_read: length, }) } - fn decode_u64(bytes: &[u8]) -> Result { + fn decode_string_slice(bytes: &[u8]) -> Result { + let length = peek_length(bytes)?; + let bytes = peek(skip(bytes, LENGTH_BYTES_SIZE)?, length)?; + let decoded = str::from_utf8(bytes)?.to_string(); + Ok(Decoded { - token: Token::U64(peek_u64(bytes)?), - bytes_read: WORD_SIZE, + token: Token::StringSlice(StaticStringToken::new(decoded, None)), + bytes_read: bytes.len(), }) } - fn decode_u32(bytes: &[u8]) -> Result { + 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::U32(peek_u32(bytes)?), - bytes_read: WORD_SIZE, + token: Token::Tuple(tokens), + bytes_read, }) } - fn decode_u16(bytes: &[u8]) -> Result { + fn decode_array( + &mut self, + param_type: &ParamType, + bytes: &[u8], + length: usize, + ) -> Result { + let (tokens, bytes_read) = self.decode_params(repeat(param_type).take(length), bytes)?; + Ok(Decoded { - token: Token::U16(peek_u16(bytes)?), - bytes_read: WORD_SIZE, + token: Token::Array(tokens), + bytes_read, }) } - fn decode_u8(bytes: &[u8]) -> Result { + 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::U8(peek_u8(bytes)?), - bytes_read: 1, + token: Token::Vector(tokens), + bytes_read: LENGTH_BYTES_SIZE + bytes_read, }) } - fn decode_unit(bytes: &[u8]) -> Result { - // We don't need the data, we're doing this purely as a bounds - // check. - peek_fixed::<1>(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::Unit, - bytes_read: 1, + token: Token::Struct(tokens), + bytes_read, }) } - /// The encoding follows the ABI specs defined - /// [here](https://github.com/FuelLabs/fuel-specs/blob/1be31f70c757d8390f74b9e1b3beb096620553eb/specs/protocol/abi.md) - /// - /// # Arguments - /// - /// * `data`: slice of encoded data on whose beginning we're expecting an encoded enum - /// * `variants`: all types that this particular enum type could hold fn decode_enum(&mut self, bytes: &[u8], enum_variants: &EnumVariants) -> Result { - let enum_width_in_bytes = enum_variants.compute_enum_width_in_bytes()?; - - let discriminant = peek_u64(bytes)?; + let discriminant = peek_discriminant(bytes)?; + let variant_bytes = skip(bytes, DISCRIMINANT_BYTES_SIZE)?; let (_, selected_variant) = enum_variants.select_variant(discriminant)?; - let skip_extra_in_bytes = match enum_variants.heap_type_variant() { - Some((heap_type_discriminant, heap_type)) if heap_type_discriminant == discriminant => { - heap_type.compute_encoding_in_bytes()? - } - _ => 0, - }; + let decoded = self.decode_param(selected_variant, variant_bytes)?; - let bytes_to_skip = enum_width_in_bytes - selected_variant.compute_encoding_in_bytes()? - + skip_extra_in_bytes; - - let enum_content_bytes = skip(bytes, bytes_to_skip)?; - let result = - self.decode_token_in_enum(enum_content_bytes, enum_variants, selected_variant)?; - - let selector = Box::new((discriminant, result.token, enum_variants.clone())); Ok(Decoded { - token: Token::Enum(selector), - bytes_read: enum_width_in_bytes, + token: Token::Enum(Box::new(( + discriminant, + decoded.token, + enum_variants.clone(), + ))), + bytes_read: DISCRIMINANT_BYTES_SIZE + decoded.bytes_read, }) } - fn decode_token_in_enum( + fn decode_params<'a>( &mut self, + param_types: impl IntoIterator, bytes: &[u8], - variants: &EnumVariants, - selected_variant: &ParamType, - ) -> Result { - // Enums that contain only Units as variants have only their discriminant encoded. - // Because of this we construct the Token::Unit rather than calling `decode_param` - if variants.only_units_inside() { - Ok(Decoded { - token: Token::Unit, - bytes_read: 0, - }) - } else { - self.decode_param(selected_variant, bytes) + ) -> Result<(Vec, usize)> { + 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; } + + Ok((tokens, bytes_read)) } } @@ -375,77 +303,70 @@ struct Decoded { bytes_read: usize, } -fn peek_u128(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; - Ok(u128::from_be_bytes(*slice)) +fn peek_u8(bytes: &[u8]) -> Result { + let slice = peek_fixed::(bytes)?; + Ok(u8::from_be_bytes(*slice)) } -fn peek_u256(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; - Ok(U256::from(*slice)) +fn peek_u16(bytes: &[u8]) -> Result { + let slice = peek_fixed::(bytes)?; + Ok(u16::from_be_bytes(*slice)) +} + +fn peek_u32(bytes: &[u8]) -> Result { + let slice = peek_fixed::(bytes)?; + Ok(u32::from_be_bytes(*slice)) } fn peek_u64(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; + let slice = peek_fixed::(bytes)?; Ok(u64::from_be_bytes(*slice)) } -fn peek_u32(bytes: &[u8]) -> Result { - const BYTES: usize = std::mem::size_of::(); +fn peek_u128(bytes: &[u8]) -> Result { + let slice = peek_fixed::(bytes)?; + Ok(u128::from_be_bytes(*slice)) +} - let slice = peek_fixed::(bytes)?; - let bytes = slice[WORD_SIZE - BYTES..] - .try_into() - .expect("peek_u32: You must use a slice containing exactly 4B"); - Ok(u32::from_be_bytes(bytes)) +fn peek_u256(bytes: &[u8]) -> Result { + let slice = peek_fixed::(bytes)?; + Ok(U256::from(*slice)) } -fn peek_u16(bytes: &[u8]) -> Result { - const BYTES: usize = std::mem::size_of::(); +fn peek_length(bytes: &[u8]) -> Result { + let slice = peek_fixed::(bytes)?; - let slice = peek_fixed::(bytes)?; - let bytes = slice[WORD_SIZE - BYTES..] + u64::from_be_bytes(*slice) .try_into() - .expect("peek_u16: You must use a slice containing exactly 2B"); - Ok(u16::from_be_bytes(bytes)) + .map_err(|_| error!(Other, "could not convert `u64` to `usize`")) } -fn peek_u8(bytes: &[u8]) -> Result { - const BYTES: usize = std::mem::size_of::(); +fn peek_discriminant(bytes: &[u8]) -> Result { + let slice = peek_fixed::(bytes)?; + Ok(u64::from_be_bytes(*slice)) +} - let slice = peek_fixed::<1>(bytes)?; - let bytes = slice[1 - BYTES..] - .try_into() - .expect("peek_u8: You must use a slice containing exactly 1B"); - Ok(u8::from_be_bytes(bytes)) +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(<&[u8; LEN]>::try_from(slice_w_correct_length) - .expect("peek(data,len) must return a slice of length `len` or error out")) -} - -fn peek(data: &[u8], len: usize) -> Result<&[u8]> { - if len > data.len() { - Err(error!( - Codec, - "tried to read {len} bytes from response but only had {} remaining!", - data.len() - )) - } else { - Ok(&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]> { - if num_bytes > slice.len() { - Err(error!( + (num_bytes <= slice.len()) + .then_some(&slice[num_bytes..]) + .ok_or(error!( Codec, - "tried to consume {num_bytes} bytes from response but only had {} remaining!", + "tried to consume `{num_bytes}` bytes but only had `{}` remaining!", slice.len() )) - } else { - Ok(&slice[num_bytes..]) - } } 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 75f1082a1f..53dc1c5bb8 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 @@ -104,7 +104,7 @@ mod tests { }, }; - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] #[test] fn param_type_decode_debug() -> Result<()> { let decoder = ABIDecoder::default(); @@ -261,7 +261,7 @@ mod tests { Ok(()) } - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] #[test] fn param_type_decode_debug() -> Result<()> { let decoder = ABIDecoder::default(); diff --git a/packages/fuels-core/src/codec/abi_decoder/experimental_bounded_decoder.rs b/packages/fuels-core/src/codec/abi_decoder/legacy_bounded_decoder.rs similarity index 50% rename from packages/fuels-core/src/codec/abi_decoder/experimental_bounded_decoder.rs rename to packages/fuels-core/src/codec/abi_decoder/legacy_bounded_decoder.rs index 3a8bbb6d2a..4bc169d504 100644 --- a/packages/fuels-core/src/codec/abi_decoder/experimental_bounded_decoder.rs +++ b/packages/fuels-core/src/codec/abi_decoder/legacy_bounded_decoder.rs @@ -1,6 +1,7 @@ -use std::{iter::repeat, str}; +use std::{convert::TryInto, str}; use crate::{ + checked_round_up_to_word_alignment, codec::{ utils::{CodecDirection, CounterWithLimit}, DecoderConfig, @@ -18,17 +19,12 @@ use crate::{ pub(crate) struct BoundedDecoder { depth_tracker: CounterWithLimit, token_tracker: CounterWithLimit, + config: DecoderConfig, } -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 { pub(crate) fn new(config: DecoderConfig) -> Self { @@ -39,11 +35,29 @@ impl BoundedDecoder { Self { depth_tracker, token_tracker, + config, } } pub(crate) fn decode(&mut self, param_type: &ParamType, bytes: &[u8]) -> Result { - self.decode_param(param_type, bytes).map(|x| x.token) + param_type.validate_is_decodable(self.config.max_depth)?; + match param_type { + // Unit, U8 and Bool are returned as u64 from receipt "Return" + ParamType::Unit => Ok(Token::Unit), + ParamType::U8 => Self::decode_u64(bytes).map(|r| { + Token::U8(match r.token { + Token::U64(v) => v as u8, + _ => unreachable!("decode_u64 returning unexpected token"), + }) + }), + ParamType::Bool => Self::decode_u64(bytes).map(|r| { + Token::Bool(match r.token { + Token::U64(v) => v != 0, + _ => unreachable!("decode_u64 returning unexpected token"), + }) + }), + _ => self.decode_param(param_type, bytes).map(|x| x.token), + } } pub(crate) fn decode_multiple( @@ -51,6 +65,9 @@ impl BoundedDecoder { param_types: &[ParamType], bytes: &[u8], ) -> Result> { + for param_type in param_types { + param_type.validate_is_decodable(self.config.max_depth)?; + } let (tokens, _) = self.decode_params(param_types, bytes)?; Ok(tokens) @@ -61,239 +78,294 @@ impl BoundedDecoder { decoder: impl FnOnce(&mut Self) -> Result, ) -> Result { self.depth_tracker.increase()?; + let res = decoder(self); - self.depth_tracker.decrease(); + self.depth_tracker.decrease(); res } fn decode_param(&mut self, param_type: &ParamType, bytes: &[u8]) -> Result { self.token_tracker.increase()?; match param_type { - ParamType::Unit => Self::decode_unit(), - ParamType::Bool => Self::decode_bool(bytes), + ParamType::Unit => Self::decode_unit(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::Bool => Self::decode_bool(bytes), ParamType::B256 => Self::decode_b256(bytes), - ParamType::Bytes => Self::decode_bytes(bytes), - ParamType::String => Self::decode_std_string(bytes), ParamType::RawSlice => Self::decode_raw_slice(bytes), - ParamType::StringArray(length) => Self::decode_string_array(bytes, *length), ParamType::StringSlice => Self::decode_string_slice(bytes), - ParamType::Tuple(param_types) => { - self.run_w_depth_tracking(|ctx| ctx.decode_tuple(param_types, bytes)) - } - ParamType::Array(param_type, length) => { - self.run_w_depth_tracking(|ctx| ctx.decode_array(param_type, bytes, *length)) + ParamType::StringArray(len) => Self::decode_string_array(bytes, *len), + ParamType::Array(ref t, length) => { + self.run_w_depth_tracking(|ctx| ctx.decode_array(t, bytes, *length)) } - ParamType::Vector(param_type) => { - self.run_w_depth_tracking(|ctx| ctx.decode_vector(param_type, bytes)) - } - ParamType::Struct { fields, .. } => { 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)) } + ParamType::Tuple(types) => { + self.run_w_depth_tracking(|ctx| ctx.decode_tuple(types, bytes)) + } + ParamType::Vector(param_type) => { + // although nested vectors cannot be decoded yet, depth tracking still occurs for future + // proofing + self.run_w_depth_tracking(|ctx| ctx.decode_vector(param_type, bytes)) + } + ParamType::Bytes => Self::decode_bytes(bytes), + ParamType::String => Self::decode_std_string(bytes), } } - fn decode_unit() -> Result { + fn decode_bytes(bytes: &[u8]) -> Result { Ok(Decoded { - token: Token::Unit, - bytes_read: 0, + token: Token::Bytes(bytes.to_vec()), + bytes_read: bytes.len(), }) } - fn decode_bool(bytes: &[u8]) -> Result { - let value = peek_u8(bytes)? != 0u8; - + fn decode_std_string(bytes: &[u8]) -> Result { Ok(Decoded { - token: Token::Bool(value), - bytes_read: U8_BYTES_SIZE, + token: Token::String(str::from_utf8(bytes)?.to_string()), + bytes_read: bytes.len(), }) } - fn decode_u8(bytes: &[u8]) -> Result { - Ok(Decoded { - token: Token::U8(peek_u8(bytes)?), - bytes_read: U8_BYTES_SIZE, - }) - } + fn decode_vector(&mut self, param_type: &ParamType, bytes: &[u8]) -> Result { + let num_of_elements = ParamType::calculate_num_of_elements(param_type, bytes.len())?; + let (tokens, bytes_read) = + self.decode_params(std::iter::repeat(param_type).take(num_of_elements), bytes)?; - fn decode_u16(bytes: &[u8]) -> Result { Ok(Decoded { - token: Token::U16(peek_u16(bytes)?), - bytes_read: U16_BYTES_SIZE, + token: Token::Vector(tokens), + bytes_read, }) } - fn decode_u32(bytes: &[u8]) -> Result { + fn decode_tuple(&mut self, param_types: &[ParamType], bytes: &[u8]) -> Result { + let mut tokens = vec![]; + + let mut bytes_read = 0; + + for param_type in param_types.iter() { + // padding has to be taken into account + bytes_read = checked_round_up_to_word_alignment(bytes_read)?; + let res = self.decode_param(param_type, skip(bytes, bytes_read)?)?; + bytes_read += res.bytes_read; + tokens.push(res.token); + } + Ok(Decoded { - token: Token::U32(peek_u32(bytes)?), - bytes_read: U32_BYTES_SIZE, + token: Token::Tuple(tokens), + bytes_read, }) } - fn decode_u64(bytes: &[u8]) -> Result { + fn decode_struct(&mut self, param_types: &[NamedParamType], bytes: &[u8]) -> Result { + let mut tokens = vec![]; + + let mut bytes_read = 0; + + for (_, param_type) in param_types.iter() { + // padding has to be taken into account + bytes_read = checked_round_up_to_word_alignment(bytes_read)?; + let res = self.decode_param(param_type, skip(bytes, bytes_read)?)?; + bytes_read += res.bytes_read; + tokens.push(res.token); + } + Ok(Decoded { - token: Token::U64(peek_u64(bytes)?), - bytes_read: U64_BYTES_SIZE, + token: Token::Struct(tokens), + bytes_read, }) } - fn decode_u128(bytes: &[u8]) -> Result { - Ok(Decoded { - token: Token::U128(peek_u128(bytes)?), - bytes_read: U128_BYTES_SIZE, - }) + fn decode_params<'a>( + &mut self, + param_types: impl IntoIterator, + bytes: &[u8], + ) -> Result<(Vec, usize)> { + let mut results = vec![]; + + let mut bytes_read = 0; + + for param_type in param_types { + let res = self.decode_param(param_type, skip(bytes, bytes_read)?)?; + bytes_read += res.bytes_read; + results.push(res.token); + } + + Ok((results, bytes_read)) } - fn decode_u256(bytes: &[u8]) -> Result { + fn decode_array( + &mut self, + param_type: &ParamType, + bytes: &[u8], + length: usize, + ) -> Result { + let (tokens, bytes_read) = + self.decode_params(std::iter::repeat(param_type).take(length), bytes)?; + Ok(Decoded { - token: Token::U256(peek_u256(bytes)?), - bytes_read: U256_BYTES_SIZE, + token: Token::Array(tokens), + bytes_read, }) } - fn decode_b256(bytes: &[u8]) -> Result { + fn decode_raw_slice(bytes: &[u8]) -> Result { Ok(Decoded { - token: Token::B256(*peek_fixed::(bytes)?), - bytes_read: B256_BYTES_SIZE, + token: Token::RawSlice(bytes.to_vec()), + bytes_read: bytes.len(), }) } - fn decode_bytes(bytes: &[u8]) -> Result { - let length = peek_length(bytes)?; - let bytes = peek(skip(bytes, LENGTH_BYTES_SIZE)?, length)?; + fn decode_string_slice(bytes: &[u8]) -> Result { + let decoded = str::from_utf8(bytes)?; Ok(Decoded { - token: Token::Bytes(bytes.to_vec()), - bytes_read: LENGTH_BYTES_SIZE + bytes.len(), + token: Token::StringSlice(StaticStringToken::new(decoded.into(), None)), + bytes_read: decoded.len(), }) } - fn decode_std_string(bytes: &[u8]) -> Result { - let length = peek_length(bytes)?; - let bytes = peek(skip(bytes, LENGTH_BYTES_SIZE)?, length)?; + fn decode_string_array(bytes: &[u8], length: usize) -> Result { + let encoded_str = peek(bytes, length)?; + + let decoded = str::from_utf8(encoded_str)?; + let result = Decoded { + token: Token::StringArray(StaticStringToken::new(decoded.into(), Some(length))), + bytes_read: checked_round_up_to_word_alignment(length)?, + }; + Ok(result) + } + fn decode_b256(bytes: &[u8]) -> Result { Ok(Decoded { - token: Token::String(str::from_utf8(bytes)?.to_string()), - bytes_read: LENGTH_BYTES_SIZE + bytes.len(), + token: Token::B256(*peek_fixed::<32>(bytes)?), + bytes_read: B256_BYTES_SIZE, }) } - fn decode_raw_slice(bytes: &[u8]) -> Result { - let length = peek_length(bytes)?; - let bytes = peek(skip(bytes, LENGTH_BYTES_SIZE)?, length)?; + fn decode_bool(bytes: &[u8]) -> Result { + // Grab last byte of the word and compare it to 0x00 + let b = peek_u8(bytes)? != 0u8; + + let result = Decoded { + token: Token::Bool(b), + bytes_read: 1, + }; + + Ok(result) + } + fn decode_u128(bytes: &[u8]) -> Result { Ok(Decoded { - token: Token::RawSlice(bytes.to_vec()), - bytes_read: LENGTH_BYTES_SIZE + bytes.len(), + token: Token::U128(peek_u128(bytes)?), + bytes_read: U128_BYTES_SIZE, }) } - fn decode_string_array(bytes: &[u8], length: usize) -> Result { - let bytes = peek(bytes, length)?; - let decoded = str::from_utf8(bytes)?.to_string(); - + fn decode_u256(bytes: &[u8]) -> Result { Ok(Decoded { - token: Token::StringArray(StaticStringToken::new(decoded, Some(length))), - bytes_read: length, + token: Token::U256(peek_u256(bytes)?), + bytes_read: U256_BYTES_SIZE, }) } - fn decode_string_slice(bytes: &[u8]) -> Result { - let length = peek_length(bytes)?; - let bytes = peek(skip(bytes, LENGTH_BYTES_SIZE)?, length)?; - let decoded = str::from_utf8(bytes)?.to_string(); - + fn decode_u64(bytes: &[u8]) -> Result { Ok(Decoded { - token: Token::StringSlice(StaticStringToken::new(decoded, None)), - bytes_read: bytes.len(), + token: Token::U64(peek_u64(bytes)?), + bytes_read: WORD_SIZE, }) } - fn decode_tuple(&mut self, param_types: &[ParamType], bytes: &[u8]) -> Result { - let (tokens, bytes_read) = self.decode_params(param_types, bytes)?; - + fn decode_u32(bytes: &[u8]) -> Result { Ok(Decoded { - token: Token::Tuple(tokens), - bytes_read, + token: Token::U32(peek_u32(bytes)?), + bytes_read: WORD_SIZE, }) } - fn decode_array( - &mut self, - param_type: &ParamType, - bytes: &[u8], - length: usize, - ) -> Result { - let (tokens, bytes_read) = self.decode_params(repeat(param_type).take(length), bytes)?; - + fn decode_u16(bytes: &[u8]) -> Result { Ok(Decoded { - token: Token::Array(tokens), - bytes_read, + token: Token::U16(peek_u16(bytes)?), + bytes_read: WORD_SIZE, }) } - 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)?; - + fn decode_u8(bytes: &[u8]) -> Result { Ok(Decoded { - token: Token::Vector(tokens), - bytes_read: LENGTH_BYTES_SIZE + bytes_read, + token: Token::U8(peek_u8(bytes)?), + bytes_read: 1, }) } - fn decode_struct(&mut self, fields: &[NamedParamType], bytes: &[u8]) -> Result { - let (tokens, bytes_read) = self.decode_params(fields.iter().map(|(_, pt)| pt), bytes)?; - + fn decode_unit(bytes: &[u8]) -> Result { + // We don't need the data, we're doing this purely as a bounds + // check. + peek_fixed::<1>(bytes)?; Ok(Decoded { - token: Token::Struct(tokens), - bytes_read, + token: Token::Unit, + bytes_read: 1, }) } + /// The encoding follows the ABI specs defined + /// [here](https://github.com/FuelLabs/fuel-specs/blob/1be31f70c757d8390f74b9e1b3beb096620553eb/specs/protocol/abi.md) + /// + /// # Arguments + /// + /// * `data`: slice of encoded data on whose beginning we're expecting an encoded enum + /// * `variants`: all types that this particular enum type could hold fn decode_enum(&mut self, bytes: &[u8], enum_variants: &EnumVariants) -> Result { - let discriminant = peek_discriminant(bytes)?; - let variant_bytes = skip(bytes, DISCRIMINANT_BYTES_SIZE)?; + let enum_width_in_bytes = enum_variants.compute_enum_width_in_bytes()?; + + let discriminant = peek_u64(bytes)?; let (_, selected_variant) = enum_variants.select_variant(discriminant)?; - let decoded = self.decode_param(selected_variant, variant_bytes)?; + let skip_extra_in_bytes = match enum_variants.heap_type_variant() { + Some((heap_type_discriminant, heap_type)) if heap_type_discriminant == discriminant => { + heap_type.compute_encoding_in_bytes()? + } + _ => 0, + }; + let bytes_to_skip = enum_width_in_bytes - selected_variant.compute_encoding_in_bytes()? + + skip_extra_in_bytes; + + let enum_content_bytes = skip(bytes, bytes_to_skip)?; + let result = + self.decode_token_in_enum(enum_content_bytes, enum_variants, selected_variant)?; + + let selector = Box::new((discriminant, result.token, enum_variants.clone())); Ok(Decoded { - token: Token::Enum(Box::new(( - discriminant, - decoded.token, - enum_variants.clone(), - ))), - bytes_read: DISCRIMINANT_BYTES_SIZE + decoded.bytes_read, + token: Token::Enum(selector), + bytes_read: enum_width_in_bytes, }) } - fn decode_params<'a>( + fn decode_token_in_enum( &mut self, - param_types: impl IntoIterator, bytes: &[u8], - ) -> Result<(Vec, usize)> { - 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; + variants: &EnumVariants, + selected_variant: &ParamType, + ) -> Result { + // Enums that contain only Units as variants have only their discriminant encoded. + // Because of this we construct the Token::Unit rather than calling `decode_param` + if variants.only_units_inside() { + Ok(Decoded { + token: Token::Unit, + bytes_read: 0, + }) + } else { + self.decode_param(selected_variant, bytes) } - - Ok((tokens, bytes_read)) } } @@ -303,26 +375,6 @@ struct Decoded { 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)) -} - -fn peek_u32(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; - Ok(u32::from_be_bytes(*slice)) -} - -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)) @@ -333,40 +385,67 @@ fn peek_u256(bytes: &[u8]) -> Result { Ok(U256::from(*slice)) } -fn peek_length(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; +fn peek_u64(bytes: &[u8]) -> Result { + let slice = peek_fixed::(bytes)?; + Ok(u64::from_be_bytes(*slice)) +} + +fn peek_u32(bytes: &[u8]) -> Result { + const BYTES: usize = std::mem::size_of::(); - u64::from_be_bytes(*slice) + let slice = peek_fixed::(bytes)?; + let bytes = slice[WORD_SIZE - BYTES..] .try_into() - .map_err(|_| error!(Other, "could not convert `u64` to `usize`")) + .expect("peek_u32: You must use a slice containing exactly 4B"); + Ok(u32::from_be_bytes(bytes)) } -fn peek_discriminant(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; - Ok(u64::from_be_bytes(*slice)) +fn peek_u16(bytes: &[u8]) -> Result { + const BYTES: usize = std::mem::size_of::(); + + let slice = peek_fixed::(bytes)?; + let bytes = slice[WORD_SIZE - BYTES..] + .try_into() + .expect("peek_u16: You must use a slice containing exactly 2B"); + Ok(u16::from_be_bytes(bytes)) } -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_u8(bytes: &[u8]) -> Result { + const BYTES: usize = std::mem::size_of::(); + + let slice = peek_fixed::<1>(bytes)?; + let bytes = slice[1 - BYTES..] + .try_into() + .expect("peek_u8: You must use a slice containing exactly 1B"); + Ok(u8::from_be_bytes(bytes)) } 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")) + Ok(<&[u8; LEN]>::try_from(slice_w_correct_length) + .expect("peek(data,len) must return a slice of length `len` or error out")) +} + +fn peek(data: &[u8], len: usize) -> Result<&[u8]> { + if len > data.len() { + Err(error!( + Codec, + "tried to read {len} bytes from response but only had {} remaining!", + data.len() + )) + } else { + Ok(&data[..len]) + } } fn skip(slice: &[u8], num_bytes: usize) -> Result<&[u8]> { - (num_bytes <= slice.len()) - .then_some(&slice[num_bytes..]) - .ok_or(error!( + if num_bytes > slice.len() { + Err(error!( Codec, - "tried to consume `{num_bytes}` bytes but only had `{}` remaining!", + "tried to consume {num_bytes} bytes from response but only had {} remaining!", slice.len() )) + } else { + Ok(&slice[num_bytes..]) + } } diff --git a/packages/fuels-core/src/codec/abi_encoder.rs b/packages/fuels-core/src/codec/abi_encoder.rs index ce292fa614..258c43bac4 100644 --- a/packages/fuels-core/src/codec/abi_encoder.rs +++ b/packages/fuels-core/src/codec/abi_encoder.rs @@ -1,13 +1,13 @@ +#[cfg(not(feature = "legacy_encoding"))] mod bounded_encoder; -#[cfg(feature = "experimental")] -mod experimental_bounded_encoder; +mod legacy_bounded_encoder; use std::default::Default; -#[cfg(feature = "experimental")] -use crate::codec::abi_encoder::experimental_bounded_encoder::ExperimentalBoundedEncoder; +#[cfg(not(feature = "legacy_encoding"))] +use crate::codec::abi_encoder::bounded_encoder::BoundedEncoder; use crate::{ - codec::abi_encoder::bounded_encoder::BoundedEncoder, + codec::abi_encoder::legacy_bounded_encoder::LegacyBoundedEncoder, types::{errors::Result, unresolved_bytes::UnresolvedBytes, Token}, }; @@ -49,10 +49,10 @@ impl ABIEncoder { /// Encodes `Token`s in `args` following the ABI specs defined /// [here](https://github.com/FuelLabs/fuel-specs/blob/master/specs/protocol/abi.md) pub fn encode(&self, args: &[Token]) -> Result { - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] + let res = LegacyBoundedEncoder::new(self.config, false).encode(args); + #[cfg(not(feature = "legacy_encoding"))] let res = BoundedEncoder::new(self.config, false).encode(args); - #[cfg(feature = "experimental")] - let res = ExperimentalBoundedEncoder::new(self.config, false).encode(args); res } @@ -71,7 +71,7 @@ impl ConfigurablesEncoder { /// Encodes `Token`s in `args` following the ABI specs defined /// [here](https://github.com/FuelLabs/fuel-specs/blob/master/specs/protocol/abi.md) pub fn encode(&self, args: &[Token]) -> Result { - BoundedEncoder::new(self.config, true).encode(args) + LegacyBoundedEncoder::new(self.config, true).encode(args) } } @@ -79,11 +79,11 @@ impl ConfigurablesEncoder { mod tests { use std::slice; - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] use itertools::chain; use super::*; - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] use crate::constants::WORD_SIZE; use crate::{ to_named, @@ -94,9 +94,9 @@ mod tests { }, }; - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] const VEC_METADATA_SIZE: usize = 3 * WORD_SIZE; - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] const DISCRIMINANT_SIZE: usize = WORD_SIZE; #[test] @@ -112,7 +112,7 @@ mod tests { let result = ABIEncoder::default().encode(&tokens)?.resolve(0); - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let expected = [ 255, 0, 0, 0, 0, 0, 0, 0, // u8 0, 0, 0, 0, 0, 0, 255, 255, // u16 @@ -123,7 +123,7 @@ mod tests { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // u256 ]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let expected = [ 255, // u8 255, 255, // u16 @@ -146,9 +146,9 @@ mod tests { let result = ABIEncoder::default().encode(&[token])?.resolve(0); - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let expected = [0, 0, 0, 0, 0, 0, 0, 1]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let expected = [1]; assert_eq!(result, expected); @@ -177,14 +177,14 @@ mod tests { let result = ABIEncoder::default().encode(&[token])?.resolve(0); - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let expected = [ 0, 0, 0, 0, 0, 0, 0, 24, // ptr 0, 0, 0, 0, 0, 0, 0, 8, // cap 0, 0, 0, 0, 0, 0, 0, 7, // len 255, 0, 1, 2, 3, 4, 5, 0, // data ]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let expected = [ 0, 0, 0, 0, 0, 0, 0, 7, // len 255, 0, 1, 2, 3, 4, 5, // data @@ -201,7 +201,7 @@ mod tests { let result = ABIEncoder::default().encode(&[token])?.resolve(0); - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let expected = [ 0, 0, 0, 0, 0, 0, 0, 24, // ptr 0, 0, 0, 0, 0, 0, 0, 24, // cap @@ -209,7 +209,7 @@ mod tests { 84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 117, 108, 108, 32, 115, 101, 110, 116, 101, 110, 99, 101, 0, //This is a full sentence ]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let expected = [ 0, 0, 0, 0, 0, 0, 0, 23, // len 84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 117, 108, 108, 32, 115, 101, 110, @@ -227,13 +227,13 @@ mod tests { let result = ABIEncoder::default().encode(&[token])?.resolve(0); - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let expected = [ 0, 0, 0, 0, 0, 0, 0, 16, // cap 0, 0, 0, 0, 0, 0, 0, 7, // len 255, 0, 1, 2, 3, 4, 5, 0, // data ]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let expected = [ 0, 0, 0, 0, 0, 0, 0, 7, // len 255, 0, 1, 2, 3, 4, 5, // data @@ -253,12 +253,12 @@ mod tests { let result = ABIEncoder::default().encode(&[token])?.resolve(0); - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let expected = [ 84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 117, 108, 108, 32, 115, 101, 110, 116, 101, 110, 99, 101, 0, //This is a full sentence ]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let expected = [ 84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 117, 108, 108, 32, 115, 101, 110, 116, 101, 110, 99, 101, //This is a full sentence @@ -278,14 +278,14 @@ mod tests { let result = ABIEncoder::default().encode(&[token])?.resolve(0); - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let expected = [ 0, 0, 0, 0, 0, 0, 0, 16, // ptr 0, 0, 0, 0, 0, 0, 0, 23, // len 84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 117, 108, 108, 32, 115, 101, 110, 116, 101, 110, 99, 101, //This is a full sentence ]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let expected = [ 0, 0, 0, 0, 0, 0, 0, 23, // len 84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 117, 108, 108, 32, 115, 101, 110, @@ -303,12 +303,12 @@ mod tests { let result = ABIEncoder::default().encode(&[token])?.resolve(0); - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let expected = [ 0, 0, 0, 0, 0, 0, 0, 255, //u32 1, 0, 0, 0, 0, 0, 0, 0, //bool ]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let expected = [ 0, 0, 0, 255, //u32 1, //bool @@ -325,12 +325,12 @@ mod tests { let result = ABIEncoder::default().encode(&[token])?.resolve(0); - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let expected = [ 0, 0, 0, 0, 0, 0, 0, 255, //u32 0, 0, 0, 0, 0, 0, 0, 128, //u32 ]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let expected = [ 0, 0, 0, 255, //u32 0, 0, 0, 128, //u32 @@ -342,7 +342,7 @@ mod tests { } // The encoding follows the ABI specs defined [here](https://github.com/FuelLabs/fuel-specs/blob/master/specs/protocol/abi.md) - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] #[test] fn enums_are_sized_to_fit_the_biggest_variant() -> Result<()> { // Our enum has two variants: B256, and U64. So the enum will set aside @@ -427,7 +427,7 @@ mod tests { .encode(slice::from_ref(&top_level_enum_token))? .resolve(0); - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let expected = [ 0, 0, 0, 0, 0, 0, 0, 0, // TopLevelEnum::v1 discriminant 0, 0, 0, 0, 0, 0, 0, 1, // DeeperEnum::v2 discriminant @@ -435,7 +435,7 @@ mod tests { 0, 0, 0, 0, 0, 0, // DeeperEnum padding 0, 0, 0, 0, 0, 0, 44, 68, // StructA.some_number ]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let expected = [ 0, 0, 0, 0, 0, 0, 0, 0, // TopLevelEnum::v1 discriminant 0, 0, 0, 0, 0, 0, 0, 1, // DeeperEnum::v2 discriminant @@ -460,7 +460,7 @@ mod tests { let result = ABIEncoder::default().encode(&[token])?.resolve(0); - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let expected = [ 0, 0, 0, 0, 0, 0, 0, 10, // u16 1, // bool @@ -468,7 +468,7 @@ mod tests { 1, 2, // [u8, u8] 0, 0, 0, 0, 0, 0, // padding ]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let expected = [ 0, 10, // u16 1, // bool @@ -499,7 +499,7 @@ mod tests { let result = ABIEncoder::default().encode(&tokens)?.resolve(0); - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let expected = [ 0, 0, 0, 0, 0, 0, 0, 10, // foo.x == 10u16 1, // foo.y.a == true @@ -517,7 +517,7 @@ mod tests { 84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 117, 108, 108, 32, 115, 101, 110, 116, 101, 110, 99, 101, 0, // str[23] ]; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let expected = [ 0, 10, // foo.x == 10u16 1, // foo.y.a == true @@ -554,7 +554,7 @@ mod tests { Ok(()) } - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] #[test] fn units_in_composite_types_are_encoded_in_one_word() -> Result<()> { let expected = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5]; @@ -567,7 +567,7 @@ mod tests { Ok(()) } - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] #[test] fn enums_with_units_are_correctly_padded() -> Result<()> { let discriminant = vec![0, 0, 0, 0, 0, 0, 0, 1]; @@ -585,7 +585,7 @@ mod tests { Ok(()) } - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] #[test] fn vector_has_ptr_cap_len_and_then_data() -> Result<()> { // arrange @@ -610,7 +610,7 @@ mod tests { Ok(()) } - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] #[test] fn data_from_two_vectors_aggregated_at_the_end() -> Result<()> { // arrange @@ -661,7 +661,7 @@ mod tests { .resolve(offset as u64); // assert - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let expected = { let discriminant = vec![0, 0, 0, 0, 0, 0, 0, 1]; @@ -684,7 +684,7 @@ mod tests { ) .collect::>() }; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let expected = [ 0, 0, 0, 0, 0, 0, 0, 1, // enum dicsriminant 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 5, // vec[len, u64] @@ -712,7 +712,7 @@ mod tests { .resolve(offset as u64); // assert - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let expected = { const PADDING: usize = std::mem::size_of::<[u8; 32]>() - WORD_SIZE; @@ -725,7 +725,7 @@ mod tests { chain!(vec1_ptr, vec1_cap, vec1_len, vec1_data).collect::>() }; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let expected = [ 0, 0, 0, 0, 0, 0, 0, 1, // vec len 0, 0, 0, 0, 0, 0, 0, 1, 8, // enum discriminant and u8 value @@ -748,7 +748,7 @@ mod tests { .resolve(offset as u64); // assert - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let expected = { let vec1_ptr = ((VEC_METADATA_SIZE + WORD_SIZE + offset) as u64) .to_be_bytes() @@ -759,7 +759,7 @@ mod tests { chain!(vec1_ptr, vec1_cap, vec1_len, [9], [0; 7], vec1_data).collect::>() }; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let expected = [ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 5, // vec[len, u64] 9, // u8 @@ -782,7 +782,7 @@ mod tests { .resolve(offset as u64); // assert - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let expected = { let vec1_data_offset = (VEC_METADATA_SIZE + offset) as u64; let vec1_ptr = vec1_data_offset.to_be_bytes().to_vec(); @@ -800,7 +800,7 @@ mod tests { chain!(vec1_ptr, vec1_cap, vec1_len, vec1_data).collect::>() }; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let expected = [ 0, 0, 0, 0, 0, 0, 0, 1, // vec1 len 0, 0, 0, 0, 0, 0, 0, 2, 5, 6, // vec2 [len, u8, u8] @@ -811,7 +811,7 @@ mod tests { Ok(()) } - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] #[test] fn capacity_overflow_is_caught() -> Result<()> { let token = Token::Enum(Box::new(( diff --git a/packages/fuels-core/src/codec/abi_encoder/bounded_encoder.rs b/packages/fuels-core/src/codec/abi_encoder/bounded_encoder.rs index 7d2087d5f6..852499f969 100644 --- a/packages/fuels-core/src/codec/abi_encoder/bounded_encoder.rs +++ b/packages/fuels-core/src/codec/abi_encoder/bounded_encoder.rs @@ -1,29 +1,22 @@ -use fuel_types::bytes::padded_len_usize; - use crate::{ - checked_round_up_to_word_alignment, codec::{ utils::{CodecDirection, CounterWithLimit}, EncoderConfig, }, - error, types::{ errors::Result, - pad_u16, pad_u32, unresolved_bytes::{Data, UnresolvedBytes}, EnumSelector, StaticStringToken, Token, U256, }, }; pub(crate) struct BoundedEncoder { - used_for_configurables: bool, depth_tracker: CounterWithLimit, token_tracker: CounterWithLimit, - max_total_enum_width: usize, } impl BoundedEncoder { - pub(crate) fn new(config: EncoderConfig, used_for_configurables: bool) -> Self { + pub(crate) fn new(config: EncoderConfig, _unused: bool) -> Self { let depth_tracker = CounterWithLimit::new(config.max_depth, "depth", CodecDirection::Encoding); let token_tracker = @@ -31,57 +24,21 @@ impl BoundedEncoder { Self { depth_tracker, token_tracker, - max_total_enum_width: config.max_total_enum_width, - used_for_configurables, } } pub fn encode(&mut self, args: &[Token]) -> Result { - // Checking that the tokens can be encoded is not done here, because it would require - // going through the whole array of tokens, which can be pretty inefficient. - let data = if args.len() == 1 { - match args[0] { - Token::U8(arg_u8) if self.used_for_configurables => { - vec![Self::encode_u8_as_byte(arg_u8)] - } - Token::U8(arg_u8) => vec![Self::encode_u8_as_u64(arg_u8)], - Token::Bool(arg_bool) if self.used_for_configurables => { - vec![Self::encode_bool_as_byte(arg_bool)] - } - Token::Bool(arg_bool) => { - vec![Self::encode_bool_as_u64(arg_bool)] - } - _ => self.encode_tokens(args, true)?, - } - } else { - self.encode_tokens(args, true)? - }; + let data = vec![Data::Inline(self.encode_tokens(args)?)]; Ok(UnresolvedBytes::new(data)) } - fn encode_tokens(&mut self, tokens: &[Token], word_aligned: bool) -> Result> { - let mut offset_in_bytes = 0; + fn encode_tokens(&mut self, tokens: &[Token]) -> Result> { let mut data = vec![]; - for token in tokens { - self.token_tracker.increase()?; - let mut new_data = self.encode_token(token)?; - offset_in_bytes += new_data.iter().map(Data::size_in_bytes).sum::(); - - data.append(&mut new_data); - - if word_aligned { - let padding = vec![ - 0u8; - checked_round_up_to_word_alignment(offset_in_bytes)? - - offset_in_bytes - ]; - if !padding.is_empty() { - offset_in_bytes += padding.len(); - data.push(Data::Inline(padding)); - } - } + for token in tokens.iter() { + let new_data = self.encode_token(token)?; + data.extend(new_data); } Ok(data) @@ -89,200 +46,88 @@ impl BoundedEncoder { fn run_w_depth_tracking( &mut self, - encoder: impl FnOnce(&mut Self) -> Result>, - ) -> Result> { + encoder: impl FnOnce(&mut Self) -> Result>, + ) -> Result> { self.depth_tracker.increase()?; - let res = encoder(self); - self.depth_tracker.decrease(); + res } - fn encode_token(&mut self, arg: &Token) -> Result> { + fn encode_token(&mut self, arg: &Token) -> Result> { + self.token_tracker.increase()?; let encoded_token = match arg { - Token::Unit => vec![Self::encode_unit()], - Token::U8(arg_u8) => vec![Self::encode_u8_as_byte(*arg_u8)], - Token::U16(arg_u16) => vec![Self::encode_u16(*arg_u16)], - Token::U32(arg_u32) => vec![Self::encode_u32(*arg_u32)], - Token::U64(arg_u64) => vec![Self::encode_u64(*arg_u64)], - Token::U128(arg_u128) => vec![Self::encode_u128(*arg_u128)], - Token::U256(arg_u256) => vec![Self::encode_u256(*arg_u256)], - Token::Bool(arg_bool) => vec![Self::encode_bool_as_byte(*arg_bool)], - Token::B256(arg_bits256) => vec![Self::encode_b256(arg_bits256)], - Token::RawSlice(data) => Self::encode_raw_slice(data.clone())?, + Token::Unit => vec![], + Token::Bool(arg_bool) => vec![u8::from(*arg_bool)], + Token::U8(arg_u8) => vec![*arg_u8], + Token::U16(arg_u16) => arg_u16.to_be_bytes().to_vec(), + Token::U32(arg_u32) => arg_u32.to_be_bytes().to_vec(), + Token::U64(arg_u64) => arg_u64.to_be_bytes().to_vec(), + Token::U128(arg_u128) => arg_u128.to_be_bytes().to_vec(), + Token::U256(arg_u256) => Self::encode_u256(*arg_u256), + Token::B256(arg_bits256) => arg_bits256.to_vec(), + Token::Bytes(data) => Self::encode_bytes(data.to_vec())?, + Token::String(string) => Self::encode_bytes(string.clone().into_bytes())?, + Token::RawSlice(data) => Self::encode_bytes(data.clone())?, + Token::StringArray(arg_string) => Self::encode_string_array(arg_string)?, Token::StringSlice(arg_string) => Self::encode_string_slice(arg_string)?, - Token::StringArray(arg_string) => vec![Self::encode_string_array(arg_string)?], + Token::Tuple(arg_tuple) => { + self.run_w_depth_tracking(|ctx| ctx.encode_tokens(arg_tuple))? + } Token::Array(arg_array) => { - self.run_w_depth_tracking(|ctx| ctx.encode_array(arg_array))? + self.run_w_depth_tracking(|ctx| ctx.encode_tokens(arg_array))? } + Token::Vector(data) => self.run_w_depth_tracking(|ctx| ctx.encode_vector(data))?, Token::Struct(arg_struct) => { - self.run_w_depth_tracking(|ctx| ctx.encode_struct(arg_struct))? + self.run_w_depth_tracking(|ctx| ctx.encode_tokens(arg_struct))? } Token::Enum(arg_enum) => self.run_w_depth_tracking(|ctx| ctx.encode_enum(arg_enum))?, - Token::Tuple(arg_tuple) => { - self.run_w_depth_tracking(|ctx| ctx.encode_tuple(arg_tuple))? - } - Token::Vector(data) => self.run_w_depth_tracking(|ctx| ctx.encode_vector(data))?, - Token::Bytes(data) => Self::encode_bytes(data.to_vec())?, - // `String` in Sway has the same memory layout as the bytes type - Token::String(string) => Self::encode_bytes(string.clone().into_bytes())?, }; Ok(encoded_token) } - fn encode_unit() -> Data { - Data::Inline(vec![0u8]) - } - - fn encode_tuple(&mut self, arg_tuple: &[Token]) -> Result> { - self.encode_tokens(arg_tuple, true) - } - - fn encode_struct(&mut self, subcomponents: &[Token]) -> Result> { - self.encode_tokens(subcomponents, true) - } - - fn encode_array(&mut self, arg_array: &[Token]) -> Result> { - self.encode_tokens(arg_array, false) - } - - fn encode_b256(arg_bits256: &[u8; 32]) -> Data { - Data::Inline(arg_bits256.to_vec()) - } - - fn encode_bool_as_byte(arg_bool: bool) -> Data { - Data::Inline(vec![u8::from(arg_bool)]) - } - - fn encode_bool_as_u64(arg_bool: bool) -> Data { - Data::Inline(vec![0, 0, 0, 0, 0, 0, 0, u8::from(arg_bool)]) - } - - fn encode_u128(arg_u128: u128) -> Data { - Data::Inline(arg_u128.to_be_bytes().to_vec()) - } - - fn encode_u256(arg_u256: U256) -> Data { + fn encode_u256(arg_u256: U256) -> Vec { let mut bytes = [0u8; 32]; arg_u256.to_big_endian(&mut bytes); - Data::Inline(bytes.to_vec()) - } - - fn encode_u64(arg_u64: u64) -> Data { - Data::Inline(arg_u64.to_be_bytes().to_vec()) - } - - fn encode_u32(arg_u32: u32) -> Data { - Data::Inline(pad_u32(arg_u32).to_vec()) - } - fn encode_u16(arg_u16: u16) -> Data { - Data::Inline(pad_u16(arg_u16).to_vec()) + bytes.to_vec() } - fn encode_u8_as_byte(arg_u8: u8) -> Data { - Data::Inline(vec![arg_u8]) - } - - fn encode_u8_as_u64(arg_u8: u8) -> Data { - Data::Inline(vec![0, 0, 0, 0, 0, 0, 0, arg_u8]) - } - - fn encode_enum(&mut self, selector: &EnumSelector) -> Result> { - let (discriminant, token_within_enum, variants) = selector; - - let mut encoded_enum = vec![Self::encode_discriminant(*discriminant)]; - - // Enums that contain only Units as variants have only their discriminant encoded. - if !variants.only_units_inside() { - let (_, variant_param_type) = variants.select_variant(*discriminant)?; - let enum_width_in_bytes = variants.compute_enum_width_in_bytes()?; - - if enum_width_in_bytes > self.max_total_enum_width { - return Err(error!( - Codec, - "cannot encode enum with variants: {variants:?}. It is `{enum_width_in_bytes}` bytes wide. Try increasing maximum total enum width." - )); - } - let padding_amount = variants.compute_padding_amount_in_bytes(variant_param_type)?; - - encoded_enum.push(Data::Inline(vec![0; padding_amount])); - - let token_data = self.encode_token(token_within_enum)?; - encoded_enum.extend(token_data); - } + fn encode_bytes(data: Vec) -> Result> { + let len = data.len(); - Ok(encoded_enum) + Ok([Self::encode_length(len as u64), data].concat()) } - fn encode_discriminant(discriminant: u64) -> Data { - Self::encode_u64(discriminant) + fn encode_string_array(arg_string: &StaticStringToken) -> Result> { + Ok(arg_string.get_encodable_str()?.as_bytes().to_vec()) } - fn encode_vector(&mut self, data: &[Token]) -> Result> { - let encoded_data = self.encode_tokens(data, false)?; - let cap = data.len() as u64; - let len = data.len() as u64; - - // A vector is expected to be encoded as 3 WORDs -- a ptr, a cap and a - // len. This means that we must place the encoded vector elements - // somewhere else. Hence the use of Data::Dynamic which will, when - // resolved, leave behind in its place only a pointer to the actual - // data. - Ok(vec![ - Data::Dynamic(encoded_data), - Self::encode_u64(cap), - Self::encode_u64(len), - ]) + fn encode_string_slice(arg_string: &StaticStringToken) -> Result> { + Self::encode_bytes(arg_string.get_encodable_str()?.as_bytes().to_vec()) } - fn encode_raw_slice(mut data: Vec) -> Result> { - let len = data.len(); - - zeropad_to_word_alignment(&mut data); - - let encoded_data = vec![Data::Inline(data)]; + fn encode_vector(&mut self, data: &[Token]) -> Result> { + let encoded_data = self.encode_tokens(data)?; - Ok(vec![ - Data::Dynamic(encoded_data), - Self::encode_u64(len as u64), - ]) + Ok([Self::encode_length(data.len() as u64), encoded_data].concat()) } - fn encode_string_slice(arg_string: &StaticStringToken) -> Result> { - let encodable_str = arg_string.get_encodable_str()?; + fn encode_enum(&mut self, selector: &EnumSelector) -> Result> { + let (discriminant, token_within_enum, _) = selector; + let encoded_discriminant = Self::encode_discriminant(*discriminant); + let encoded_token = self.encode_token(token_within_enum)?; - let encoded_data = Data::Inline(encodable_str.as_bytes().to_vec()); - let len = Self::encode_u64(encodable_str.len() as u64); - - Ok(vec![Data::Dynamic(vec![encoded_data]), len]) + Ok([encoded_discriminant, encoded_token].concat()) } - fn encode_string_array(arg_string: &StaticStringToken) -> Result { - Ok(Data::Inline(crate::types::pad_string( - arg_string.get_encodable_str()?, - ))) + fn encode_length(len: u64) -> Vec { + len.to_be_bytes().to_vec() } - fn encode_bytes(mut data: Vec) -> Result> { - let len = data.len(); - - zeropad_to_word_alignment(&mut data); - - let cap = data.len() as u64; - let encoded_data = vec![Data::Inline(data)]; - - Ok(vec![ - Data::Dynamic(encoded_data), - Self::encode_u64(cap), - Self::encode_u64(len as u64), - ]) + fn encode_discriminant(discriminant: u64) -> Vec { + discriminant.to_be_bytes().to_vec() } } - -fn zeropad_to_word_alignment(data: &mut Vec) { - let padded_length = padded_len_usize(data.len()); - data.resize(padded_length, 0); -} diff --git a/packages/fuels-core/src/codec/abi_encoder/experimental_bounded_encoder.rs b/packages/fuels-core/src/codec/abi_encoder/experimental_bounded_encoder.rs deleted file mode 100644 index 7c1db32868..0000000000 --- a/packages/fuels-core/src/codec/abi_encoder/experimental_bounded_encoder.rs +++ /dev/null @@ -1,133 +0,0 @@ -use crate::{ - codec::{ - utils::{CodecDirection, CounterWithLimit}, - EncoderConfig, - }, - types::{ - errors::Result, - unresolved_bytes::{Data, UnresolvedBytes}, - EnumSelector, StaticStringToken, Token, U256, - }, -}; - -pub(crate) struct ExperimentalBoundedEncoder { - depth_tracker: CounterWithLimit, - token_tracker: CounterWithLimit, -} - -impl ExperimentalBoundedEncoder { - pub(crate) fn new(config: EncoderConfig, _unused: bool) -> Self { - let depth_tracker = - CounterWithLimit::new(config.max_depth, "depth", CodecDirection::Encoding); - let token_tracker = - CounterWithLimit::new(config.max_tokens, "token", CodecDirection::Encoding); - Self { - depth_tracker, - token_tracker, - } - } - - pub fn encode(&mut self, args: &[Token]) -> Result { - let data = vec![Data::Inline(self.encode_tokens(args)?)]; - - Ok(UnresolvedBytes::new(data)) - } - - fn encode_tokens(&mut self, tokens: &[Token]) -> Result> { - let mut data = vec![]; - - for token in tokens.iter() { - let new_data = self.encode_token(token)?; - data.extend(new_data); - } - - Ok(data) - } - - fn run_w_depth_tracking( - &mut self, - encoder: impl FnOnce(&mut Self) -> Result>, - ) -> Result> { - self.depth_tracker.increase()?; - let res = encoder(self); - self.depth_tracker.decrease(); - - res - } - - fn encode_token(&mut self, arg: &Token) -> Result> { - self.token_tracker.increase()?; - let encoded_token = match arg { - Token::Unit => vec![], - Token::Bool(arg_bool) => vec![u8::from(*arg_bool)], - Token::U8(arg_u8) => vec![*arg_u8], - Token::U16(arg_u16) => arg_u16.to_be_bytes().to_vec(), - Token::U32(arg_u32) => arg_u32.to_be_bytes().to_vec(), - Token::U64(arg_u64) => arg_u64.to_be_bytes().to_vec(), - Token::U128(arg_u128) => arg_u128.to_be_bytes().to_vec(), - Token::U256(arg_u256) => Self::encode_u256(*arg_u256), - Token::B256(arg_bits256) => arg_bits256.to_vec(), - Token::Bytes(data) => Self::encode_bytes(data.to_vec())?, - Token::String(string) => Self::encode_bytes(string.clone().into_bytes())?, - Token::RawSlice(data) => Self::encode_bytes(data.clone())?, - Token::StringArray(arg_string) => Self::encode_string_array(arg_string)?, - Token::StringSlice(arg_string) => Self::encode_string_slice(arg_string)?, - Token::Tuple(arg_tuple) => { - self.run_w_depth_tracking(|ctx| ctx.encode_tokens(arg_tuple))? - } - Token::Array(arg_array) => { - self.run_w_depth_tracking(|ctx| ctx.encode_tokens(arg_array))? - } - Token::Vector(data) => self.run_w_depth_tracking(|ctx| ctx.encode_vector(data))?, - Token::Struct(arg_struct) => { - self.run_w_depth_tracking(|ctx| ctx.encode_tokens(arg_struct))? - } - Token::Enum(arg_enum) => self.run_w_depth_tracking(|ctx| ctx.encode_enum(arg_enum))?, - }; - - Ok(encoded_token) - } - - fn encode_u256(arg_u256: U256) -> Vec { - let mut bytes = [0u8; 32]; - arg_u256.to_big_endian(&mut bytes); - - bytes.to_vec() - } - - fn encode_bytes(data: Vec) -> Result> { - let len = data.len(); - - Ok([Self::encode_length(len as u64), data].concat()) - } - - fn encode_string_array(arg_string: &StaticStringToken) -> Result> { - Ok(arg_string.get_encodable_str()?.as_bytes().to_vec()) - } - - fn encode_string_slice(arg_string: &StaticStringToken) -> Result> { - Self::encode_bytes(arg_string.get_encodable_str()?.as_bytes().to_vec()) - } - - fn encode_vector(&mut self, data: &[Token]) -> Result> { - let encoded_data = self.encode_tokens(data)?; - - Ok([Self::encode_length(data.len() as u64), encoded_data].concat()) - } - - fn encode_enum(&mut self, selector: &EnumSelector) -> Result> { - let (discriminant, token_within_enum, _) = selector; - let encoded_discriminant = Self::encode_discriminant(*discriminant); - let encoded_token = self.encode_token(token_within_enum)?; - - Ok([encoded_discriminant, encoded_token].concat()) - } - - fn encode_length(len: u64) -> Vec { - len.to_be_bytes().to_vec() - } - - fn encode_discriminant(discriminant: u64) -> Vec { - discriminant.to_be_bytes().to_vec() - } -} diff --git a/packages/fuels-core/src/codec/abi_encoder/legacy_bounded_encoder.rs b/packages/fuels-core/src/codec/abi_encoder/legacy_bounded_encoder.rs new file mode 100644 index 0000000000..f33541e5d2 --- /dev/null +++ b/packages/fuels-core/src/codec/abi_encoder/legacy_bounded_encoder.rs @@ -0,0 +1,288 @@ +use fuel_types::bytes::padded_len_usize; + +use crate::{ + checked_round_up_to_word_alignment, + codec::{ + utils::{CodecDirection, CounterWithLimit}, + EncoderConfig, + }, + error, + types::{ + errors::Result, + pad_u16, pad_u32, + unresolved_bytes::{Data, UnresolvedBytes}, + EnumSelector, StaticStringToken, Token, U256, + }, +}; + +pub(crate) struct LegacyBoundedEncoder { + used_for_configurables: bool, + depth_tracker: CounterWithLimit, + token_tracker: CounterWithLimit, + max_total_enum_width: usize, +} + +impl LegacyBoundedEncoder { + pub(crate) fn new(config: EncoderConfig, used_for_configurables: bool) -> Self { + let depth_tracker = + CounterWithLimit::new(config.max_depth, "depth", CodecDirection::Encoding); + let token_tracker = + CounterWithLimit::new(config.max_tokens, "token", CodecDirection::Encoding); + Self { + depth_tracker, + token_tracker, + max_total_enum_width: config.max_total_enum_width, + used_for_configurables, + } + } + + pub fn encode(&mut self, args: &[Token]) -> Result { + // Checking that the tokens can be encoded is not done here, because it would require + // going through the whole array of tokens, which can be pretty inefficient. + let data = if args.len() == 1 { + match args[0] { + Token::U8(arg_u8) if self.used_for_configurables => { + vec![Self::encode_u8_as_byte(arg_u8)] + } + Token::U8(arg_u8) => vec![Self::encode_u8_as_u64(arg_u8)], + Token::Bool(arg_bool) if self.used_for_configurables => { + vec![Self::encode_bool_as_byte(arg_bool)] + } + Token::Bool(arg_bool) => { + vec![Self::encode_bool_as_u64(arg_bool)] + } + _ => self.encode_tokens(args, true)?, + } + } else { + self.encode_tokens(args, true)? + }; + + Ok(UnresolvedBytes::new(data)) + } + + fn encode_tokens(&mut self, tokens: &[Token], word_aligned: bool) -> Result> { + let mut offset_in_bytes = 0; + let mut data = vec![]; + + for token in tokens { + self.token_tracker.increase()?; + let mut new_data = self.encode_token(token)?; + offset_in_bytes += new_data.iter().map(Data::size_in_bytes).sum::(); + + data.append(&mut new_data); + + if word_aligned { + let padding = vec![ + 0u8; + checked_round_up_to_word_alignment(offset_in_bytes)? + - offset_in_bytes + ]; + if !padding.is_empty() { + offset_in_bytes += padding.len(); + data.push(Data::Inline(padding)); + } + } + } + + Ok(data) + } + + fn run_w_depth_tracking( + &mut self, + encoder: impl FnOnce(&mut Self) -> Result>, + ) -> Result> { + self.depth_tracker.increase()?; + + let res = encoder(self); + + self.depth_tracker.decrease(); + res + } + + fn encode_token(&mut self, arg: &Token) -> Result> { + let encoded_token = match arg { + Token::Unit => vec![Self::encode_unit()], + Token::U8(arg_u8) => vec![Self::encode_u8_as_byte(*arg_u8)], + Token::U16(arg_u16) => vec![Self::encode_u16(*arg_u16)], + Token::U32(arg_u32) => vec![Self::encode_u32(*arg_u32)], + Token::U64(arg_u64) => vec![Self::encode_u64(*arg_u64)], + Token::U128(arg_u128) => vec![Self::encode_u128(*arg_u128)], + Token::U256(arg_u256) => vec![Self::encode_u256(*arg_u256)], + Token::Bool(arg_bool) => vec![Self::encode_bool_as_byte(*arg_bool)], + Token::B256(arg_bits256) => vec![Self::encode_b256(arg_bits256)], + Token::RawSlice(data) => Self::encode_raw_slice(data.clone())?, + Token::StringSlice(arg_string) => Self::encode_string_slice(arg_string)?, + Token::StringArray(arg_string) => vec![Self::encode_string_array(arg_string)?], + Token::Array(arg_array) => { + self.run_w_depth_tracking(|ctx| ctx.encode_array(arg_array))? + } + Token::Struct(arg_struct) => { + self.run_w_depth_tracking(|ctx| ctx.encode_struct(arg_struct))? + } + Token::Enum(arg_enum) => self.run_w_depth_tracking(|ctx| ctx.encode_enum(arg_enum))?, + Token::Tuple(arg_tuple) => { + self.run_w_depth_tracking(|ctx| ctx.encode_tuple(arg_tuple))? + } + Token::Vector(data) => self.run_w_depth_tracking(|ctx| ctx.encode_vector(data))?, + Token::Bytes(data) => Self::encode_bytes(data.to_vec())?, + // `String` in Sway has the same memory layout as the bytes type + Token::String(string) => Self::encode_bytes(string.clone().into_bytes())?, + }; + + Ok(encoded_token) + } + + fn encode_unit() -> Data { + Data::Inline(vec![0u8]) + } + + fn encode_tuple(&mut self, arg_tuple: &[Token]) -> Result> { + self.encode_tokens(arg_tuple, true) + } + + fn encode_struct(&mut self, subcomponents: &[Token]) -> Result> { + self.encode_tokens(subcomponents, true) + } + + fn encode_array(&mut self, arg_array: &[Token]) -> Result> { + self.encode_tokens(arg_array, false) + } + + fn encode_b256(arg_bits256: &[u8; 32]) -> Data { + Data::Inline(arg_bits256.to_vec()) + } + + fn encode_bool_as_byte(arg_bool: bool) -> Data { + Data::Inline(vec![u8::from(arg_bool)]) + } + + fn encode_bool_as_u64(arg_bool: bool) -> Data { + Data::Inline(vec![0, 0, 0, 0, 0, 0, 0, u8::from(arg_bool)]) + } + + fn encode_u128(arg_u128: u128) -> Data { + Data::Inline(arg_u128.to_be_bytes().to_vec()) + } + + fn encode_u256(arg_u256: U256) -> Data { + let mut bytes = [0u8; 32]; + arg_u256.to_big_endian(&mut bytes); + Data::Inline(bytes.to_vec()) + } + + fn encode_u64(arg_u64: u64) -> Data { + Data::Inline(arg_u64.to_be_bytes().to_vec()) + } + + fn encode_u32(arg_u32: u32) -> Data { + Data::Inline(pad_u32(arg_u32).to_vec()) + } + + fn encode_u16(arg_u16: u16) -> Data { + Data::Inline(pad_u16(arg_u16).to_vec()) + } + + fn encode_u8_as_byte(arg_u8: u8) -> Data { + Data::Inline(vec![arg_u8]) + } + + fn encode_u8_as_u64(arg_u8: u8) -> Data { + Data::Inline(vec![0, 0, 0, 0, 0, 0, 0, arg_u8]) + } + + fn encode_enum(&mut self, selector: &EnumSelector) -> Result> { + let (discriminant, token_within_enum, variants) = selector; + + let mut encoded_enum = vec![Self::encode_discriminant(*discriminant)]; + + // Enums that contain only Units as variants have only their discriminant encoded. + if !variants.only_units_inside() { + let (_, variant_param_type) = variants.select_variant(*discriminant)?; + let enum_width_in_bytes = variants.compute_enum_width_in_bytes()?; + + if enum_width_in_bytes > self.max_total_enum_width { + return Err(error!( + Codec, + "cannot encode enum with variants: {variants:?}. It is `{enum_width_in_bytes}` bytes wide. Try increasing maximum total enum width." + )); + } + let padding_amount = variants.compute_padding_amount_in_bytes(variant_param_type)?; + + encoded_enum.push(Data::Inline(vec![0; padding_amount])); + + let token_data = self.encode_token(token_within_enum)?; + encoded_enum.extend(token_data); + } + + Ok(encoded_enum) + } + + fn encode_discriminant(discriminant: u64) -> Data { + Self::encode_u64(discriminant) + } + + fn encode_vector(&mut self, data: &[Token]) -> Result> { + let encoded_data = self.encode_tokens(data, false)?; + let cap = data.len() as u64; + let len = data.len() as u64; + + // A vector is expected to be encoded as 3 WORDs -- a ptr, a cap and a + // len. This means that we must place the encoded vector elements + // somewhere else. Hence the use of Data::Dynamic which will, when + // resolved, leave behind in its place only a pointer to the actual + // data. + Ok(vec![ + Data::Dynamic(encoded_data), + Self::encode_u64(cap), + Self::encode_u64(len), + ]) + } + + fn encode_raw_slice(mut data: Vec) -> Result> { + let len = data.len(); + + zeropad_to_word_alignment(&mut data); + + let encoded_data = vec![Data::Inline(data)]; + + Ok(vec![ + Data::Dynamic(encoded_data), + Self::encode_u64(len as u64), + ]) + } + + fn encode_string_slice(arg_string: &StaticStringToken) -> Result> { + let encodable_str = arg_string.get_encodable_str()?; + + let encoded_data = Data::Inline(encodable_str.as_bytes().to_vec()); + let len = Self::encode_u64(encodable_str.len() as u64); + + Ok(vec![Data::Dynamic(vec![encoded_data]), len]) + } + + fn encode_string_array(arg_string: &StaticStringToken) -> Result { + Ok(Data::Inline(crate::types::pad_string( + arg_string.get_encodable_str()?, + ))) + } + + fn encode_bytes(mut data: Vec) -> Result> { + let len = data.len(); + + zeropad_to_word_alignment(&mut data); + + let cap = data.len() as u64; + let encoded_data = vec![Data::Inline(data)]; + + Ok(vec![ + Data::Dynamic(encoded_data), + Self::encode_u64(cap), + Self::encode_u64(len as u64), + ]) + } +} + +fn zeropad_to_word_alignment(data: &mut Vec) { + let padded_length = padded_len_usize(data.len()); + data.resize(padded_length, 0); +} diff --git a/packages/fuels-core/src/codec/function_selector.rs b/packages/fuels-core/src/codec/function_selector.rs index 1b5a55be88..2bb1fbd7dd 100644 --- a/packages/fuels-core/src/codec/function_selector.rs +++ b/packages/fuels-core/src/codec/function_selector.rs @@ -1,13 +1,13 @@ -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] use sha2::{Digest, Sha256}; -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] use crate::types::param_types::NamedParamType; use crate::types::param_types::ParamType; -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] use crate::types::ByteArray; -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] /// Given a function name and its inputs will return a ByteArray representing /// the function selector as specified in the Fuel specs. pub fn resolve_fn_selector(name: &str, inputs: &[ParamType]) -> ByteArray { @@ -16,7 +16,7 @@ pub fn resolve_fn_selector(name: &str, inputs: &[ParamType]) -> ByteArray { first_four_bytes_of_sha256_hash(&fn_signature) } -#[cfg(feature = "experimental")] +#[cfg(not(feature = "legacy_encoding"))] //TODO: remove `_inputs` once the new encoding stabilizes //https://github.com/FuelLabs/fuels-rs/issues/1318 pub fn resolve_fn_selector(name: &str, _inputs: &[ParamType]) -> Vec { @@ -26,19 +26,19 @@ pub fn resolve_fn_selector(name: &str, _inputs: &[ParamType]) -> Vec { [len.to_be_bytes().to_vec(), bytes].concat() } -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] fn resolve_fn_signature(name: &str, inputs: &[ParamType]) -> String { let fn_args = resolve_args(inputs); format!("{name}({fn_args})") } -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] fn resolve_args(args: &[ParamType]) -> String { args.iter().map(resolve_arg).collect::>().join(",") } -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] fn resolve_named_args(args: &[NamedParamType]) -> String { args.iter() .map(|(_, param_type)| resolve_arg(param_type)) @@ -46,7 +46,7 @@ fn resolve_named_args(args: &[NamedParamType]) -> String { .join(",") } -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] fn resolve_arg(arg: &ParamType) -> String { match &arg { ParamType::U8 => "u8".to_owned(), @@ -106,7 +106,7 @@ fn resolve_arg(arg: &ParamType) -> String { } } -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] /// Hashes an encoded function selector using SHA256 and returns the first 4 bytes. /// The function selector has to have been already encoded following the ABI specs defined /// [here](https://github.com/FuelLabs/fuel-specs/blob/1be31f70c757d8390f74b9e1b3beb096620553eb/specs/protocol/abi.md) @@ -144,7 +144,7 @@ macro_rules! calldata { pub use calldata; -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] #[cfg(test)] mod tests { use super::*; @@ -308,7 +308,7 @@ mod tests { } #[test] - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] fn encode_function_signature() { let fn_signature = "entry_one(u64)"; @@ -318,7 +318,7 @@ mod tests { } #[test] - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] fn encode_function_with_u32_type() { let fn_signature = "entry_one(u32)"; @@ -330,7 +330,7 @@ mod tests { } #[test] - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] fn encode_function_with_u32_type_multiple_args() { let fn_signature = "takes_two(u32,u32)"; @@ -342,7 +342,7 @@ mod tests { } #[test] - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] fn encode_function_with_u64_type() { let fn_signature = "entry_one(u64)"; @@ -354,7 +354,7 @@ mod tests { } #[test] - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] fn encode_function_with_bool_type() { let fn_signature = "bool_check(bool)"; @@ -366,7 +366,7 @@ mod tests { } #[test] - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] fn encode_function_with_two_different_type() { let fn_signature = "takes_two_types(u32,bool)"; @@ -378,7 +378,7 @@ mod tests { } #[test] - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] fn encode_function_with_bits256_type() { let fn_signature = "takes_bits256(b256)"; @@ -390,7 +390,7 @@ mod tests { } #[test] - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] fn encode_function_with_array_type() { let fn_signature = "takes_integer_array(u8[3])"; @@ -402,7 +402,7 @@ mod tests { } #[test] - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] fn encode_function_with_string_array_type() { let fn_signature = "takes_string(str[23])"; @@ -414,7 +414,7 @@ mod tests { } #[test] - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] fn encode_function_with_string_slice_type() { let fn_signature = "takes_string(str)"; @@ -426,7 +426,7 @@ mod tests { } #[test] - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] fn encode_function_with_struct() { let fn_signature = "takes_my_struct(MyStruct)"; @@ -438,7 +438,7 @@ mod tests { } #[test] - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] fn encode_function_with_enum() { let fn_signature = "takes_my_enum(MyEnum)"; @@ -450,7 +450,7 @@ mod tests { } #[test] - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] fn encode_comprehensive_function() { let fn_signature = "long_function(Foo,u8[2],b256,str[23])"; diff --git a/packages/fuels-core/src/codec/logs.rs b/packages/fuels-core/src/codec/logs.rs index 7ca63bc1c4..87fafc2cf8 100644 --- a/packages/fuels-core/src/codec/logs.rs +++ b/packages/fuels-core/src/codec/logs.rs @@ -31,7 +31,7 @@ impl LogFormatter { decoder_config: DecoderConfig, bytes: &[u8], ) -> Result { - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] Self::can_decode_log_with_type::()?; let token = ABIDecoder::new(decoder_config).decode(&T::param_type(), bytes)?; @@ -39,7 +39,7 @@ impl LogFormatter { Ok(format!("{:?}", T::from_token(token)?)) } - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] fn can_decode_log_with_type() -> Result<()> { use crate::types::param_types::ParamType; diff --git a/packages/fuels-core/src/types.rs b/packages/fuels-core/src/types.rs index 836b57246e..2cb686b1cc 100644 --- a/packages/fuels-core/src/types.rs +++ b/packages/fuels-core/src/types.rs @@ -17,9 +17,9 @@ pub mod unresolved_bytes; mod wrappers; pub type ByteArray = [u8; 8]; -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] pub type Selector = ByteArray; -#[cfg(feature = "experimental")] +#[cfg(not(feature = "legacy_encoding"))] pub type Selector = Vec; /// Converts a u16 to a right aligned array of 8 bytes. diff --git a/packages/fuels-core/src/types/param_types/param_type.rs b/packages/fuels-core/src/types/param_types/param_type.rs index ddca1530e4..bb59515f64 100644 --- a/packages/fuels-core/src/types/param_types/param_type.rs +++ b/packages/fuels-core/src/types/param_types/param_type.rs @@ -50,7 +50,7 @@ impl ParamType { // Depending on the type, the returned value will be stored // either in `Return` or `ReturnData`. pub fn get_return_location(&self) -> ReturnLocation { - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] match self { Self::Unit | Self::U8 | Self::U16 | Self::U32 | Self::U64 | Self::Bool => { ReturnLocation::Return @@ -59,7 +59,7 @@ impl ParamType { _ => ReturnLocation::ReturnData, } - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] ReturnLocation::ReturnData } diff --git a/packages/fuels-programs/Cargo.toml b/packages/fuels-programs/Cargo.toml index 9dc5ff1ff8..cb2b0af09a 100644 --- a/packages/fuels-programs/Cargo.toml +++ b/packages/fuels-programs/Cargo.toml @@ -29,4 +29,4 @@ tempfile = "3.8.1" [features] default = ["std"] std = ["fuels-core/std", "fuels-accounts/std"] -experimental = ["fuels-core/experimental"] +legacy_encoding = ["fuels-core/legacy_encoding"] diff --git a/packages/fuels-programs/src/call_utils.rs b/packages/fuels-programs/src/call_utils.rs index 6fb9da9873..d55edb3cd8 100644 --- a/packages/fuels-programs/src/call_utils.rs +++ b/packages/fuels-programs/src/call_utils.rs @@ -255,7 +255,7 @@ pub(crate) fn get_instructions( }) } -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] /// Returns script data, consisting of the following items in the given order: /// 1. Amount to be forwarded `(1 * `[`WORD_SIZE`]`)` /// 2. Asset ID to be forwarded ([`AssetId::LEN`]) @@ -341,7 +341,7 @@ pub(crate) fn build_script_data_from_contract_calls( Ok((script_data, param_offsets)) } -#[cfg(feature = "experimental")] +#[cfg(not(feature = "legacy_encoding"))] /// Returns script data, consisting of the following items in the given order: /// 1. Amount to be forwarded `(1 * `[`WORD_SIZE`]`)` /// 2. Asset ID to be forwarded ([`AssetId::LEN`]) @@ -459,14 +459,14 @@ pub(crate) fn get_single_call_instructions( None => instructions.push(op::call(0x10, 0x11, 0x12, RegId::CGAS)), }; - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] instructions.extend(extract_heap_data(_output_param_type)?); #[allow(clippy::iter_cloned_collect)] Ok(instructions.into_iter().collect::>()) } -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] fn extract_heap_data(param_type: &ParamType) -> Result> { match param_type { ParamType::Enum { enum_variants, .. } => { @@ -502,7 +502,7 @@ fn extract_heap_data(param_type: &ParamType) -> Result Vec { mod test { use std::slice; - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] use fuel_types::bytes::WORD_SIZE; use fuels_accounts::wallet::WalletUnlocked; use fuels_core::types::{ @@ -686,7 +686,7 @@ mod test { coin::{Coin, CoinStatus}, coin_type::CoinType, }; - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] use fuels_core::{codec::ABIEncoder, types::Token}; use rand::Rng; @@ -698,9 +698,9 @@ mod test { ContractCall { contract_id: random_bech32_contract_id(), encoded_args: Ok(Default::default()), - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] encoded_selector: [0; 8], - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] encoded_selector: [0; 8].to_vec(), call_parameters: Default::default(), compute_custom_input_offset: false, @@ -721,7 +721,7 @@ mod test { Bech32ContractId::new("fuel", rand::thread_rng().gen::<[u8; 32]>()) } - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] #[tokio::test] async fn test_script_data() { // Arrange @@ -751,9 +751,9 @@ mod test { let calls: Vec = (0..NUM_CALLS) .map(|i| ContractCall { contract_id: contract_ids[i].clone(), - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] encoded_selector: selectors[i], - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] encoded_selector: selectors[i].to_vec(), encoded_args: Ok(args[i].clone()), call_parameters: CallParameters::new(i as u64, asset_ids[i], i as u64), @@ -1078,10 +1078,10 @@ mod test { const BASE_INSTRUCTION_COUNT: usize = 5; // 2 instructions (movi and lw) added in get_single_call_instructions when gas_offset is set const GAS_OFFSET_INSTRUCTION_COUNT: usize = 2; - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] // 4 instructions (lw, lw, muli, retd) added by extract_data_receipt const EXTRACT_DATA_RECEIPT_INSTRUCTION_COUNT: usize = 4; - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] // 4 instructions (movi, lw, jnef, retd) added by extract_heap_data const EXTRACT_HEAP_DATA_INSTRUCTION_COUNT: usize = 4; @@ -1103,7 +1103,7 @@ mod test { ); } - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] #[test] fn test_with_heap_type() { let output_params = vec![ @@ -1123,7 +1123,7 @@ mod test { } } - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] #[test] fn test_with_gas_offset_and_heap_type() { let mut call = ContractCall::new_with_random_id(); @@ -1140,7 +1140,7 @@ mod test { ); } - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] #[test] fn test_with_enum_with_heap_and_non_heap_variant() { let variant_sets = vec![ diff --git a/packages/fuels-programs/src/contract.rs b/packages/fuels-programs/src/contract.rs index bc4bd51ea3..e55e9cfa31 100644 --- a/packages/fuels-programs/src/contract.rs +++ b/packages/fuels-programs/src/contract.rs @@ -726,9 +726,9 @@ pub fn method_hash( let tx_policies = TxPolicies::default(); let call_parameters = CallParameters::default(); - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let compute_custom_input_offset = should_compute_custom_input_offset(args); - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let compute_custom_input_offset = true; let unresolved_bytes = ABIEncoder::new(encoder_config).encode(args); @@ -759,7 +759,7 @@ pub fn method_hash( // If the data passed into the contract method is an integer or a // boolean, then the data itself should be passed. Otherwise, it // should simply pass a pointer to the data in memory. -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] fn should_compute_custom_input_offset(args: &[Token]) -> bool { args.len() > 1 || args.iter().any(|t| { diff --git a/packages/fuels-programs/src/receipt_parser.rs b/packages/fuels-programs/src/receipt_parser.rs index 4c61ca21c7..88faca19ab 100644 --- a/packages/fuels-programs/src/receipt_parser.rs +++ b/packages/fuels-programs/src/receipt_parser.rs @@ -43,7 +43,7 @@ impl ReceiptParser { // During a script execution, the script's contract id is the **null** contract id .unwrap_or_else(ContractId::zeroed); - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] output_param.validate_is_decodable(self.decoder.config.max_depth)?; let data = self @@ -65,9 +65,9 @@ impl ReceiptParser { output_param: &ParamType, contract_id: &ContractId, ) -> Option> { - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let extra_receipts_needed = output_param.is_extra_receipt_needed(true); - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let extra_receipts_needed = false; match output_param.get_return_location() { @@ -324,9 +324,9 @@ mod tests { let contract_id = target_contract(); let mut receipts = expected_receipts.clone(); - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] receipts.push(get_return_receipt(contract_id, RECEIPT_VAL)); - #[cfg(feature = "experimental")] // all data is returned as RETD + #[cfg(not(feature = "legacy_encoding"))] // all data is returned as RETD receipts.push(get_return_data_receipt( contract_id, &RECEIPT_VAL.to_be_bytes(), @@ -343,7 +343,7 @@ mod tests { Ok(()) } - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] #[tokio::test] async fn receipt_parser_extract_return_data_heap() -> Result<()> { let expected_receipts = get_relevant_receipts(); diff --git a/packages/fuels/Cargo.toml b/packages/fuels/Cargo.toml index 99b296b25f..5b58b1ddb7 100644 --- a/packages/fuels/Cargo.toml +++ b/packages/fuels/Cargo.toml @@ -40,7 +40,7 @@ tai64 = { workspace = true } [features] default = ["std", "fuels-test-helpers?/fuels-accounts", "coin-cache"] coin-cache = ["fuels-accounts/coin-cache"] -experimental = ["fuels-core/experimental", "fuels-programs/experimental"] +legacy_encoding = ["fuels-core/legacy_encoding", "fuels-programs/legacy_encoding"] # The crates enabled via `dep:` below are not currently wasm compatible, as # such they are only available if `std` is enabled. The `dep:` syntax was diff --git a/packages/fuels/tests/bindings.rs b/packages/fuels/tests/bindings.rs index 8ca5011778..1736599b2b 100644 --- a/packages/fuels/tests/bindings.rs +++ b/packages/fuels/tests/bindings.rs @@ -1,14 +1,14 @@ -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] use std::slice; use std::str::FromStr; use fuels::prelude::*; -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] use fuels::{ core::{codec::ABIEncoder, traits::Tokenizable}, types::{Bits256, EvmAddress}, }; -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] use sha2::{Digest, Sha256}; pub fn null_contract_id() -> Bech32ContractId { @@ -17,7 +17,7 @@ pub fn null_contract_id() -> Bech32ContractId { .unwrap() } -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] #[tokio::test] async fn compile_bindings_from_contract_file() { // Generates the bindings from an ABI definition in a JSON file @@ -49,7 +49,7 @@ async fn compile_bindings_from_contract_file() { assert_eq!("000000005f68ee3d000000000000002a", encoded); } -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] #[tokio::test] async fn compile_bindings_from_inline_contract() -> Result<()> { // Generates the bindings from the an ABI definition inline. @@ -110,7 +110,7 @@ async fn compile_bindings_from_inline_contract() -> Result<()> { Ok(()) } -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] #[tokio::test] async fn compile_bindings_array_input() -> Result<()> { // Generates the bindings from the an ABI definition inline. @@ -188,7 +188,7 @@ async fn compile_bindings_array_input() -> Result<()> { Ok(()) } -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] #[tokio::test] async fn compile_bindings_bool_array_input() -> Result<()> { // Generates the bindings from the an ABI definition inline. @@ -263,7 +263,7 @@ async fn compile_bindings_bool_array_input() -> Result<()> { Ok(()) } -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] #[tokio::test] async fn compile_bindings_string_input() -> Result<()> { // Generates the bindings from the an ABI definition inline. @@ -334,7 +334,7 @@ async fn compile_bindings_string_input() -> Result<()> { Ok(()) } -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] #[tokio::test] async fn compile_bindings_b256_input() -> Result<()> { // Generates the bindings from the an ABI definition inline. @@ -406,7 +406,7 @@ async fn compile_bindings_b256_input() -> Result<()> { Ok(()) } -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] #[tokio::test] async fn compile_bindings_evm_address_input() -> Result<()> { abigen!(Contract( @@ -477,7 +477,7 @@ async fn compile_bindings_evm_address_input() -> Result<()> { Ok(()) } -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] #[tokio::test] async fn compile_bindings_struct_input() -> Result<()> { // Generates the bindings from the an ABI definition inline. @@ -580,7 +580,7 @@ async fn compile_bindings_struct_input() -> Result<()> { Ok(()) } -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] #[tokio::test] async fn compile_bindings_nested_struct_input() -> Result<()> { // Generates the bindings from the an ABI definition inline. @@ -689,7 +689,7 @@ async fn compile_bindings_nested_struct_input() -> Result<()> { Ok(()) } -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] #[tokio::test] async fn compile_bindings_enum_input() -> Result<()> { // Generates the bindings from the an ABI definition inline. @@ -777,7 +777,7 @@ async fn compile_bindings_enum_input() -> Result<()> { Ok(()) } -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] #[tokio::test] async fn shared_types() -> Result<()> { setup_program_test!( diff --git a/packages/fuels/tests/contracts.rs b/packages/fuels/tests/contracts.rs index 49edcf9a34..9f8d8719cc 100644 --- a/packages/fuels/tests/contracts.rs +++ b/packages/fuels/tests/contracts.rs @@ -1,5 +1,5 @@ use fuel_tx::ContractParameters; -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] use fuels::core::codec::{calldata, fn_selector}; use fuels::{ core::codec::{DecoderConfig, EncoderConfig}, @@ -277,14 +277,14 @@ async fn test_contract_call_fee_estimation() -> Result<()> { let tolerance = Some(0.2); let block_horizon = Some(1); - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let expected_gas_used = 955; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let expected_gas_used = 960; - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let expected_metered_bytes_size = 784; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let expected_metered_bytes_size = 824; let estimated_transaction_cost = contract_instance @@ -1231,7 +1231,7 @@ async fn multi_call_from_calls_with_different_account_types() -> Result<()> { } #[tokio::test] -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] async fn low_level_call() -> Result<()> { use fuels::types::SizedAsciiString; diff --git a/packages/fuels/tests/from_token.rs b/packages/fuels/tests/from_token.rs index 73fef9c9b2..39fc40a039 100644 --- a/packages/fuels/tests/from_token.rs +++ b/packages/fuels/tests/from_token.rs @@ -1,7 +1,7 @@ use std::str::FromStr; use fuels::prelude::*; -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] use fuels::{core::traits::Tokenizable, types::Token}; pub fn null_contract_id() -> Bech32ContractId { @@ -10,7 +10,7 @@ pub fn null_contract_id() -> Bech32ContractId { .unwrap() } -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] #[tokio::test] async fn create_struct_from_decoded_tokens() -> Result<()> { // Generates the bindings from the an ABI definition inline. @@ -105,7 +105,7 @@ async fn create_struct_from_decoded_tokens() -> Result<()> { Ok(()) } -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] #[tokio::test] async fn create_nested_struct_from_decoded_tokens() -> Result<()> { // Generates the bindings from the an ABI definition inline. diff --git a/packages/fuels/tests/logs.rs b/packages/fuels/tests/logs.rs index 5b92fc9302..948bd1de09 100644 --- a/packages/fuels/tests/logs.rs +++ b/packages/fuels/tests/logs.rs @@ -1453,7 +1453,7 @@ async fn can_configure_decoder_for_script_log_decoding() -> Result<()> { // String slices cannot be decoded from logs as they are encoded as ptr, len // TODO: Once https://github.com/FuelLabs/sway/issues/5110 is resolved we can remove this #[tokio::test] -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] async fn string_slice_log() -> Result<()> { setup_program_test!( Wallets("wallet"), @@ -1486,7 +1486,7 @@ async fn string_slice_log() -> Result<()> { } #[tokio::test] -#[cfg(feature = "experimental")] +#[cfg(not(feature = "legacy_encoding"))] async fn contract_experimental_log() -> Result<()> { use fuels_core::types::AsciiString; @@ -1545,7 +1545,7 @@ async fn contract_experimental_log() -> Result<()> { } #[tokio::test] -#[cfg(feature = "experimental")] +#[cfg(not(feature = "legacy_encoding"))] async fn script_experimental_log() -> Result<()> { use fuels_core::types::AsciiString; diff --git a/packages/fuels/tests/predicates.rs b/packages/fuels/tests/predicates.rs index 3c54e4cd6a..3c14853e4e 100644 --- a/packages/fuels/tests/predicates.rs +++ b/packages/fuels/tests/predicates.rs @@ -814,12 +814,12 @@ async fn predicate_can_access_manually_added_witnesses() -> Result<()> { .build(&provider) .await?; - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let witness = ABIEncoder::default() .encode(&[64u8.into_token()])? .resolve(0); - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let witness = ABIEncoder::default() .encode(&[64u64.into_token()])? // u64 because this is VM memory .resolve(0); @@ -893,12 +893,12 @@ async fn tx_id_not_changed_after_adding_witnesses() -> Result<()> { let tx_id = tx.id(provider.chain_id()); - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let witness = ABIEncoder::default() .encode(&[64u8.into_token()])? .resolve(0); - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let witness = ABIEncoder::default() .encode(&[64u64.into_token()])? // u64 because this is VM memory .resolve(0); diff --git a/packages/fuels/tests/providers.rs b/packages/fuels/tests/providers.rs index 73d3add5fc..9e280904b2 100644 --- a/packages/fuels/tests/providers.rs +++ b/packages/fuels/tests/providers.rs @@ -330,9 +330,9 @@ async fn test_gas_forwarded_defaults_to_tx_limit() -> Result<()> { ); // The gas used by the script to call a contract and forward remaining gas limit. - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let gas_used_by_script = 364; - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let gas_used_by_script = 856; let gas_limit = 225_883; let response = contract_instance diff --git a/packages/fuels/tests/types_contracts.rs b/packages/fuels/tests/types_contracts.rs index 15fc90a1eb..6a01c5ffec 100644 --- a/packages/fuels/tests/types_contracts.rs +++ b/packages/fuels/tests/types_contracts.rs @@ -1872,7 +1872,7 @@ async fn test_composite_types_in_vec_output() -> Result<()> { Ok(()) } -#[cfg(not(feature = "experimental"))] +#[cfg(feature = "legacy_encoding")] #[tokio::test] async fn test_nested_vector_methods_fail() -> Result<()> { // This is just an E2E test of the method `ParamType::contains_nested_heap_types`, hence it's @@ -2166,7 +2166,7 @@ async fn test_heap_type_in_enums() -> Result<()> { assert!(resp.value.is_none()); } - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] { // If the LW(RET) instruction was not executed only conditionally, then the FuelVM would OOM. let _ = contract_methods @@ -2187,7 +2187,7 @@ async fn test_heap_type_in_enums() -> Result<()> { Ok(()) } -#[cfg(feature = "experimental")] +#[cfg(not(feature = "legacy_encoding"))] #[tokio::test] async fn nested_heap_types() -> Result<()> { setup_program_test!( diff --git a/packages/fuels/tests/types_scripts.rs b/packages/fuels/tests/types_scripts.rs index 9456be0b29..ec317e7283 100644 --- a/packages/fuels/tests/types_scripts.rs +++ b/packages/fuels/tests/types_scripts.rs @@ -323,7 +323,7 @@ async fn script_handles_std_string() -> Result<()> { Ok(()) } -#[cfg(feature = "experimental")] +#[cfg(not(feature = "legacy_encoding"))] #[tokio::test] async fn nested_heap_types() -> Result<()> { setup_program_test!( diff --git a/packages/wasm-tests/Cargo.toml b/packages/wasm-tests/Cargo.toml index 330a9f78a6..62b23f81b1 100644 --- a/packages/wasm-tests/Cargo.toml +++ b/packages/wasm-tests/Cargo.toml @@ -23,4 +23,4 @@ getrandom = { version = "0.2.11", features = ["js"] } wasm-bindgen-test = "0.3.39" [features] -experimental = ["fuels-core/experimental"] +legacy_encoding = ["fuels-core/legacy_encoding"] diff --git a/packages/wasm-tests/src/lib.rs b/packages/wasm-tests/src/lib.rs index c6f7f4f250..a644aec004 100644 --- a/packages/wasm-tests/src/lib.rs +++ b/packages/wasm-tests/src/lib.rs @@ -116,14 +116,14 @@ mod tests { .encode(&[original.clone().into_token()])? .resolve(0); - #[cfg(not(feature = "experimental"))] + #[cfg(feature = "legacy_encoding")] let expected_bytes = [ 0, 0, 0, 0, 0, 0, 0, 1, // enum discriminant 0, 0, 0, 0, 0, 0, 0, 123, 0, 0, 0, 0, 0, 0, 0, 0, // SomeStruct ] .to_vec(); - #[cfg(feature = "experimental")] + #[cfg(not(feature = "legacy_encoding"))] let expected_bytes = [ 0, 0, 0, 0, 0, 0, 0, 1, // enum discriminant 0, 0, 0, 123, 0, // SomeStruct