From 8df71c30e069a59da9d3e43cafbdbfb8e67b189d Mon Sep 17 00:00:00 2001 From: hal3e Date: Tue, 7 May 2024 23:19:17 +0200 Subject: [PATCH] feat!: remove legacy encoding (#1338) closes: https://github.com/FuelLabs/fuels-rs/issues/1318 This PR removes all code related to the legacy encoding. --- .github/workflows/ci.yml | 2 +- docs/src/calling-contracts/low-level-calls.md | 2 +- docs/src/codec/encoding.md | 5 - examples/codec/src/lib.rs | 6 +- examples/contracts/src/lib.rs | 5 +- packages/fuels-accounts/src/predicate.rs | 10 +- .../abigen/bindings/predicate.rs | 2 +- .../program_bindings/abigen/configurables.rs | 6 +- packages/fuels-core/Cargo.toml | 1 - packages/fuels-core/src/codec.rs | 31 +- packages/fuels-core/src/codec/abi_decoder.rs | 6 +- packages/fuels-core/src/codec/abi_encoder.rs | 251 ++------------- .../src/codec/abi_encoder/bounded_encoder.rs | 30 +- .../configurables_bounded_encoder.rs | 291 ----------------- .../fuels-core/src/codec/function_selector.rs | 1 - packages/fuels-core/src/types.rs | 1 - packages/fuels-core/src/types/param_types.rs | 2 - .../src/types/param_types/enum_variants.rs | 117 ------- .../src/types/param_types/param_type.rs | 300 ++---------------- .../src/types/transaction_builders.rs | 140 ++------ .../fuels-core/src/types/unresolved_bytes.rs | 97 ------ .../fuels-core/src/types/wrappers/input.rs | 10 +- packages/fuels-core/src/utils/offsets.rs | 71 +---- packages/fuels-programs/Cargo.toml | 1 - packages/fuels-programs/src/call_utils.rs | 2 - packages/fuels-programs/src/contract.rs | 53 +--- packages/fuels-programs/src/script_calls.rs | 18 +- packages/fuels/Forc.toml | 7 +- packages/fuels/tests/bindings.rs | 11 +- packages/fuels/tests/configurables.rs | 82 +++-- packages/fuels/tests/contracts.rs | 38 --- .../tests/contracts/configurables/src/main.sw | 19 +- .../needs_custom_decoder/src/main.sw | 9 +- packages/fuels/tests/logs.rs | 6 +- .../Forc.toml | 2 +- .../src/main.sw | 0 .../src/main.sw | 9 +- packages/fuels/tests/predicates.rs | 78 ++++- .../predicate_configurables/src/main.sw | 8 +- packages/fuels/tests/providers.rs | 2 +- packages/fuels/tests/scripts.rs | 54 ---- .../scripts/script_configurables/src/main.sw | 18 +- .../script_needs_custom_decoder/src/main.sw | 9 +- packages/fuels/tests/types_predicates.rs | 9 +- packages/fuels/tests/types_scripts.rs | 2 +- packages/wasm-tests/src/lib.rs | 4 +- 46 files changed, 318 insertions(+), 1510 deletions(-) delete mode 100644 packages/fuels-core/src/codec/abi_encoder/configurables_bounded_encoder.rs delete mode 100644 packages/fuels-core/src/types/param_types/enum_variants.rs delete mode 100644 packages/fuels-core/src/types/unresolved_bytes.rs rename packages/fuels/tests/logs/{script_experimental_logs => script_heap_logs}/Forc.toml (73%) rename packages/fuels/tests/logs/{script_experimental_logs => script_heap_logs}/src/main.sw (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ea2577c45..7bfba9bb6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ env: FUEL_CORE_PATCH_BRANCH: RUST_VERSION: 1.76.0 FORC_VERSION: 0.56.0 - FORC_PATCH_BRANCH: "" + FORC_PATCH_BRANCH: "xunilrj/new-encoding-configurables" FORC_PATCH_REVISION: "" NEXTEST_HIDE_PROGRESS_BAR: "true" NEXTEST_STATUS_LEVEL: "fail" diff --git a/docs/src/calling-contracts/low-level-calls.md b/docs/src/calling-contracts/low-level-calls.md index cbae4b26d..40843a0c7 100644 --- a/docs/src/calling-contracts/low-level-calls.md +++ b/docs/src/calling-contracts/low-level-calls.md @@ -15,7 +15,7 @@ Your caller contract should call `std::low_level_call::call_with_function_select {{#include ../../../packages/fuels/tests/contracts/low_level_caller/src/main.sw:low_level_call_contract}} ``` -On the SDK side, you can construct an encoded function selector using the `fuels::core::fn_selector!` macro, and encoded calldata using the `fuels::core::calldata!` macro. +On the SDK side, you can construct an encoded function selector using `fuels::core::encode_fn_selector`, and encoded calldata using the `fuels::core::calldata!` macro. E.g. to call the following function on the target contract: diff --git a/docs/src/codec/encoding.md b/docs/src/codec/encoding.md index 5a2df9a2c..94f82a42f 100644 --- a/docs/src/codec/encoding.md +++ b/docs/src/codec/encoding.md @@ -8,17 +8,12 @@ Encoding is done via the [`ABIEncoder`](https://docs.rs/fuels/latest/fuels/core/ {{#include ../../../examples/codec/src/lib.rs:encoding_example}} ``` -Note that the return type of `encode` is `UnresolvedBytes`. The encoding cannot be finished until we know at which memory address this data is to be loaded. If you don't use heap types (`::std::vec::Vec`, `::fuels::types::Bytes`, `::std::string::String`), then you can safely `.resolve(0)` to get the encoded bytes. - There is also a shortcut-macro that can encode multiple types which implement [`Tokenizable`](https://docs.rs/fuels/latest/fuels/core/traits/trait.Tokenizable.html): ```rust,ignore {{#include ../../../examples/codec/src/lib.rs:encoding_example_w_macro}} ``` -> Note: -> The above example will call `.resolve(0)`. Don't use it if you're encoding heap types. - ## Configuring the encoder The encoder can be configured to limit its resource expenditure: diff --git a/examples/codec/src/lib.rs b/examples/codec/src/lib.rs index 19b05d764..cee21e610 100644 --- a/examples/codec/src/lib.rs +++ b/examples/codec/src/lib.rs @@ -11,7 +11,6 @@ mod tests { use fuels::{ core::{codec::ABIEncoder, traits::Tokenizable}, macros::Tokenizable, - types::unresolved_bytes::UnresolvedBytes, }; #[derive(Tokenizable)] @@ -20,9 +19,7 @@ mod tests { } let instance = MyStruct { field: 101 }; - let encoded: UnresolvedBytes = ABIEncoder::default().encode(&[instance.into_token()])?; - let load_memory_address: u64 = 0x100; - let _: Vec = encoded.resolve(load_memory_address); + let _encoded: Vec = ABIEncoder::default().encode(&[instance.into_token()])?; //ANCHOR_END: encoding_example Ok(()) @@ -110,7 +107,6 @@ mod tests { ABIEncoder::new(EncoderConfig { max_depth: 5, max_tokens: 100, - max_total_enum_width: 10_000, }); // ANCHOR_END: configuring_the_encoder diff --git a/examples/contracts/src/lib.rs b/examples/contracts/src/lib.rs index ce48389cf..ac4d9010c 100644 --- a/examples/contracts/src/lib.rs +++ b/examples/contracts/src/lib.rs @@ -113,7 +113,7 @@ mod tests { .await?; // ANCHOR_END: contract_call_cost_estimation - let expected_gas = 2206; + let expected_gas = 2229; assert_eq!(transaction_cost.gas_used, expected_gas); @@ -610,7 +610,7 @@ mod tests { .await?; // ANCHOR_END: multi_call_cost_estimation - let expected_gas = 3378; + let expected_gas = 3413; assert_eq!(transaction_cost.gas_used, expected_gas); @@ -898,7 +898,6 @@ mod tests { .with_encoder_config(EncoderConfig { max_depth: 10, max_tokens: 2_000, - max_total_enum_width: 10_000, }) .methods() .initialize_counter(42) diff --git a/packages/fuels-accounts/src/predicate.rs b/packages/fuels-accounts/src/predicate.rs index 0f76a0aff..7983a7878 100644 --- a/packages/fuels-accounts/src/predicate.rs +++ b/packages/fuels-accounts/src/predicate.rs @@ -3,7 +3,7 @@ use std::{fmt::Debug, fs}; #[cfg(feature = "std")] use fuels_core::types::{input::Input, AssetId}; use fuels_core::{ - types::{bech32::Bech32Address, errors::Result, unresolved_bytes::UnresolvedBytes}, + types::{bech32::Bech32Address, errors::Result}, Configurables, }; @@ -16,7 +16,7 @@ use crate::{provider::Provider, Account, ViewOnlyAccount}; pub struct Predicate { address: Bech32Address, code: Vec, - data: UnresolvedBytes, + data: Vec, #[cfg(feature = "std")] provider: Option, } @@ -26,11 +26,11 @@ impl Predicate { &self.address } - pub fn code(&self) -> &Vec { + pub fn code(&self) -> &[u8] { &self.code } - pub fn data(&self) -> &UnresolvedBytes { + pub fn data(&self) -> &[u8] { &self.data } @@ -53,7 +53,7 @@ impl Predicate { } } - pub fn with_data(mut self, data: UnresolvedBytes) -> Self { + pub fn with_data(mut self, data: Vec) -> Self { self.data = data; self } diff --git a/packages/fuels-code-gen/src/program_bindings/abigen/bindings/predicate.rs b/packages/fuels-code-gen/src/program_bindings/abigen/bindings/predicate.rs index c0cb4788c..c4137509e 100644 --- a/packages/fuels-code-gen/src/program_bindings/abigen/bindings/predicate.rs +++ b/packages/fuels-code-gen/src/program_bindings/abigen/bindings/predicate.rs @@ -63,7 +63,7 @@ fn expand_fn(abi: &FullProgramABI) -> Result { self.encoder.encode(&#arg_tokens) }; let output_type = quote! { - ::fuels::types::errors::Result<::fuels::types::unresolved_bytes::UnresolvedBytes> + ::fuels::types::errors::Result<::std::vec::Vec> }; generator diff --git a/packages/fuels-code-gen/src/program_bindings/abigen/configurables.rs b/packages/fuels-code-gen/src/program_bindings/abigen/configurables.rs index 32a4fdd30..82edbb918 100644 --- a/packages/fuels-code-gen/src/program_bindings/abigen/configurables.rs +++ b/packages/fuels-code-gen/src/program_bindings/abigen/configurables.rs @@ -51,7 +51,7 @@ fn generate_struct_decl(configurable_struct_name: &Ident) -> TokenStream { #[derive(Clone, Debug, Default)] pub struct #configurable_struct_name { offsets_with_data: ::std::vec::Vec<(u64, ::std::vec::Vec)>, - encoder: ::fuels::core::codec::ConfigurablesEncoder, + encoder: ::fuels::core::codec::ABIEncoder, } } } @@ -66,7 +66,7 @@ fn generate_struct_impl( impl #configurable_struct_name { pub fn new(encoder_config: ::fuels::core::codec::EncoderConfig) -> Self { Self { - encoder: ::fuels::core::codec::ConfigurablesEncoder::new(encoder_config), + encoder: ::fuels::core::codec::ABIEncoder::new(encoder_config), ..::std::default::Default::default() } } @@ -88,7 +88,7 @@ fn generate_builder_methods(resolved_configurables: &[ResolvedConfigurable]) -> #[allow(non_snake_case)] // Generate the `with_XXX` methods for setting the configurables pub fn #name(mut self, value: #ttype) -> ::fuels::prelude::Result { - let encoded = #encoder_code?.resolve(0); + let encoded = #encoder_code?; self.offsets_with_data.push((#offset, encoded)); ::fuels::prelude::Result::Ok(self) } diff --git a/packages/fuels-core/Cargo.toml b/packages/fuels-core/Cargo.toml index e3f61cb71..2460673dc 100644 --- a/packages/fuels-core/Cargo.toml +++ b/packages/fuels-core/Cargo.toml @@ -30,7 +30,6 @@ thiserror = { workspace = true, default-features = false } uint = { workspace = true, default-features = false } [dev-dependencies] -fuels-macros = { workspace = true } tokio = { workspace = true, features = ["test-util", "macros"] } [features] diff --git a/packages/fuels-core/src/codec.rs b/packages/fuels-core/src/codec.rs index 54f6d1aa8..bd58c7ea4 100644 --- a/packages/fuels-core/src/codec.rs +++ b/packages/fuels-core/src/codec.rs @@ -33,19 +33,8 @@ mod tests { }; #[test] - fn can_convert_bytes_into_tuple() -> Result<()> { - 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())?; - - assert_eq!(the_tuple, (1, 2)); - - Ok(()) - } - - #[test] - fn can_convert_all_from_bool_to_u64() -> Result<()> { - let bytes: Vec = vec![0xFF; WORD_SIZE]; + fn convert_all_from_bool_to_u64() -> Result<()> { + let bytes = [255; WORD_SIZE]; macro_rules! test_decode { ($($for_type: ident),*) => { @@ -57,14 +46,26 @@ mod tests { } assert!(try_from_bytes::(&bytes, DecoderConfig::default())?); + test_decode!(u8, u16, u32, u64); Ok(()) } #[test] - fn can_convert_native_types() -> Result<()> { - let bytes = [0xFF; 32]; + fn convert_bytes_into_tuple() -> Result<()> { + let tuple_in_bytes = [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2]; + + let the_tuple: (u64, u32) = try_from_bytes(&tuple_in_bytes, DecoderConfig::default())?; + + assert_eq!(the_tuple, (1, 2)); + + Ok(()) + } + + #[test] + fn convert_native_types() -> Result<()> { + let bytes = [255; 32]; macro_rules! test_decode { ($($for_type: ident),*) => { diff --git a/packages/fuels-core/src/codec/abi_decoder.rs b/packages/fuels-core/src/codec/abi_decoder.rs index 79eac7741..8c8b05480 100644 --- a/packages/fuels-core/src/codec/abi_decoder.rs +++ b/packages/fuels-core/src/codec/abi_decoder.rs @@ -343,8 +343,10 @@ mod tests { generics: vec![], }]; - // "0" discriminant and 42 enum value - let data = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42]; + let data = [ + 0, 0, 0, 0, 0, 0, 0, 0, // discriminant + 0, 0, 0, 42, // u32 + ]; let decoded = ABIDecoder::default().decode_multiple(&types, &data)?; diff --git a/packages/fuels-core/src/codec/abi_encoder.rs b/packages/fuels-core/src/codec/abi_encoder.rs index af32a6b29..e599de096 100644 --- a/packages/fuels-core/src/codec/abi_encoder.rs +++ b/packages/fuels-core/src/codec/abi_encoder.rs @@ -1,13 +1,10 @@ mod bounded_encoder; -mod configurables_bounded_encoder; use std::default::Default; use crate::{ - codec::abi_encoder::{ - bounded_encoder::BoundedEncoder, configurables_bounded_encoder::ConfigurablesBoundedEncoder, - }, - types::{errors::Result, unresolved_bytes::UnresolvedBytes, Token}, + codec::abi_encoder::bounded_encoder::BoundedEncoder, + types::{errors::Result, Token}, }; #[derive(Debug, Clone, Copy)] @@ -18,9 +15,6 @@ pub struct EncoderConfig { /// Every encoded argument will increase the token count. Encoding will fail if the current /// token count becomes greater than `max_tokens` configured here. pub max_tokens: usize, - /// The total memory size of the top-level token must fit in the available memory of the - /// system. - pub max_total_enum_width: usize, } // ANCHOR: default_encoder_config @@ -29,7 +23,6 @@ impl Default for EncoderConfig { Self { max_depth: 45, max_tokens: 10_000, - max_total_enum_width: 10_000, } } } @@ -45,27 +38,10 @@ impl ABIEncoder { Self { config } } - /// Encodes `Token`s in `args` following the ABI specs defined + /// Encodes `Token`s 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, false).encode(args) - } -} - -#[derive(Default, Clone, Debug)] -pub struct ConfigurablesEncoder { - pub config: EncoderConfig, -} - -impl ConfigurablesEncoder { - pub fn new(config: EncoderConfig) -> Self { - Self { config } - } - - /// 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 { - ConfigurablesBoundedEncoder::new(self.config, true).encode(args) + pub fn encode(&self, tokens: &[Token]) -> Result> { + BoundedEncoder::new(self.config).encode(tokens) } } @@ -73,11 +49,8 @@ impl ConfigurablesEncoder { mod tests { use std::slice; - use itertools::chain; - use super::*; use crate::{ - constants::WORD_SIZE, to_named, types::{ errors::Error, @@ -97,7 +70,7 @@ mod tests { Token::U256(U256::MAX), ]; - let result = ABIEncoder::default().encode(&tokens)?.resolve(0); + let result = ABIEncoder::default().encode(&tokens)?; let expected = [ 255, // u8 @@ -119,7 +92,7 @@ mod tests { fn encode_bool() -> Result<()> { let token = Token::Bool(true); - let result = ABIEncoder::default().encode(&[token])?.resolve(0); + let result = ABIEncoder::default().encode(&[token])?; let expected = [1]; @@ -136,7 +109,7 @@ mod tests { ]; let token = Token::B256(data); - let result = ABIEncoder::default().encode(&[token])?.resolve(0); + let result = ABIEncoder::default().encode(&[token])?; assert_eq!(result, data); @@ -147,7 +120,7 @@ mod tests { fn encode_bytes() -> Result<()> { let token = Token::Bytes([255, 0, 1, 2, 3, 4, 5].to_vec()); - let result = ABIEncoder::default().encode(&[token])?.resolve(0); + let result = ABIEncoder::default().encode(&[token])?; let expected = [ 0, 0, 0, 0, 0, 0, 0, 7, // len @@ -163,7 +136,7 @@ mod tests { fn encode_string() -> Result<()> { let token = Token::String("This is a full sentence".to_string()); - let result = ABIEncoder::default().encode(&[token])?.resolve(0); + let result = ABIEncoder::default().encode(&[token])?; let expected = [ 0, 0, 0, 0, 0, 0, 0, 23, // len @@ -180,7 +153,7 @@ mod tests { fn encode_raw_slice() -> Result<()> { let token = Token::RawSlice([255, 0, 1, 2, 3, 4, 5].to_vec()); - let result = ABIEncoder::default().encode(&[token])?.resolve(0); + let result = ABIEncoder::default().encode(&[token])?; let expected = [ 0, 0, 0, 0, 0, 0, 0, 7, // len @@ -199,7 +172,7 @@ mod tests { Some(23), )); - let result = ABIEncoder::default().encode(&[token])?.resolve(0); + let result = ABIEncoder::default().encode(&[token])?; let expected = [ 84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 117, 108, 108, 32, 115, 101, 110, @@ -218,7 +191,7 @@ mod tests { None, )); - let result = ABIEncoder::default().encode(&[token])?.resolve(0); + let result = ABIEncoder::default().encode(&[token])?; let expected = [ 0, 0, 0, 0, 0, 0, 0, 23, // len @@ -235,7 +208,7 @@ mod tests { fn encode_tuple() -> Result<()> { let token = Token::Tuple(vec![Token::U32(255), Token::Bool(true)]); - let result = ABIEncoder::default().encode(&[token])?.resolve(0); + let result = ABIEncoder::default().encode(&[token])?; let expected = [ 0, 0, 0, 255, //u32 @@ -251,7 +224,7 @@ mod tests { fn encode_array() -> Result<()> { let token = Token::Tuple(vec![Token::U32(255), Token::U32(128)]); - let result = ABIEncoder::default().encode(&[token])?.resolve(0); + let result = ABIEncoder::default().encode(&[token])?; let expected = [ 0, 0, 0, 255, //u32 @@ -263,35 +236,6 @@ mod tests { Ok(()) } - // The encoding follows the ABI specs defined [here](https://github.com/FuelLabs/fuel-specs/blob/master/specs/protocol/abi.md) - #[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 - // 256b of space or 4 WORDS because that is the space needed to fit the - // largest variant(B256). - let types = to_named(&[ParamType::B256, ParamType::U64]); - let enum_variants = EnumVariants::new(types)?; - let enum_selector = Box::new((1, Token::U64(42), enum_variants)); - - let encoded = ConfigurablesEncoder::default() - .encode(slice::from_ref(&Token::Enum(enum_selector)))? - .resolve(0); - - let enum_discriminant_enc = vec![0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1]; - let u64_enc = vec![0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a]; - let enum_padding = vec![0x0; 24]; - - // notice the ordering, first the discriminant, then the necessary - // padding and then the value itself. - let expected: Vec = [enum_discriminant_enc, enum_padding, u64_enc] - .into_iter() - .flatten() - .collect(); - - assert_eq!(hex::encode(expected), hex::encode(encoded)); - Ok(()) - } - #[test] fn encode_enum_with_deeply_nested_types() -> Result<()> { /* @@ -344,9 +288,7 @@ mod tests { let top_level_enum_token = Token::Enum(Box::new((0, struct_a_token, top_level_enum_variants))); - let result = ABIEncoder::default() - .encode(slice::from_ref(&top_level_enum_token))? - .resolve(0); + let result = ABIEncoder::default().encode(slice::from_ref(&top_level_enum_token))?; let expected = [ 0, 0, 0, 0, 0, 0, 0, 0, // TopLevelEnum::v1 discriminant @@ -370,7 +312,7 @@ mod tests { ]), ]); - let result = ABIEncoder::default().encode(&[token])?.resolve(0); + let result = ABIEncoder::default().encode(&[token])?; let expected = [ 0, 10, // u16 @@ -400,7 +342,7 @@ mod tests { )); let tokens = vec![foo, arr_u8, b256, str_arr]; - let result = ABIEncoder::default().encode(&tokens)?.resolve(0); + let result = ABIEncoder::default().encode(&tokens)?; let expected = [ 0, 10, // foo.x == 10u16 @@ -429,116 +371,23 @@ mod tests { let types = to_named(&[ParamType::Unit, ParamType::Unit]); let enum_selector = Box::new((1, Token::Unit, EnumVariants::new(types)?)); - let actual = ABIEncoder::default() - .encode(&[Token::Enum(enum_selector)])? - .resolve(0); + let actual = ABIEncoder::default().encode(&[Token::Enum(enum_selector)])?; assert_eq!(actual, expected); Ok(()) } - #[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]; - - let actual = ConfigurablesEncoder::default() - .encode(&[Token::Struct(vec![Token::Unit, Token::U32(5)])])? - .resolve(0); - - assert_eq!(actual, expected); - Ok(()) - } - - #[test] - fn enums_with_units_are_correctly_padded() -> Result<()> { - let discriminant = vec![0, 0, 0, 0, 0, 0, 0, 1]; - let padding = vec![0; 32]; - let expected: Vec = [discriminant, padding].into_iter().flatten().collect(); - - let types = to_named(&[ParamType::B256, ParamType::Unit]); - let enum_selector = Box::new((1, Token::Unit, EnumVariants::new(types)?)); - - let actual = ConfigurablesEncoder::default() - .encode(&[Token::Enum(enum_selector)])? - .resolve(0); - - assert_eq!(actual, expected); - Ok(()) - } - - #[test] - fn vector_has_ptr_cap_len_and_then_data() -> Result<()> { - // arrange - let offset: u8 = 150; - let token = Token::Vector(vec![Token::U64(5)]); - - // act - let result = ConfigurablesEncoder::default() - .encode(&[token])? - .resolve(offset as u64); - - // assert - let ptr = [0, 0, 0, 0, 0, 0, 0, 3 * WORD_SIZE as u8 + offset]; - let cap = [0, 0, 0, 0, 0, 0, 0, 1]; - let len = [0, 0, 0, 0, 0, 0, 0, 1]; - let data = [0, 0, 0, 0, 0, 0, 0, 5]; - - let expected = chain!(ptr, cap, len, data).collect::>(); - - assert_eq!(result, expected); - - Ok(()) - } - - #[test] - fn data_from_two_vectors_aggregated_at_the_end() -> Result<()> { - // arrange - let offset: u8 = 40; - let vec_1 = Token::Vector(vec![Token::U64(5)]); - let vec_2 = Token::Vector(vec![Token::U64(6)]); - - // act - let result = ConfigurablesEncoder::default() - .encode(&[vec_1, vec_2])? - .resolve(offset as u64); - - // assert - let vec1_data_offset = 6 * WORD_SIZE as u8 + offset; - let vec1_ptr = [0, 0, 0, 0, 0, 0, 0, vec1_data_offset]; - let vec1_cap = [0, 0, 0, 0, 0, 0, 0, 1]; - let vec1_len = [0, 0, 0, 0, 0, 0, 0, 1]; - let vec1_data = [0, 0, 0, 0, 0, 0, 0, 5]; - - let vec2_data_offset = vec1_data_offset + vec1_data.len() as u8; - let vec2_ptr = [0, 0, 0, 0, 0, 0, 0, vec2_data_offset]; - let vec2_cap = [0, 0, 0, 0, 0, 0, 0, 1]; - let vec2_len = [0, 0, 0, 0, 0, 0, 0, 1]; - let vec2_data = [0, 0, 0, 0, 0, 0, 0, 6]; - - let expected = chain!( - vec1_ptr, vec1_cap, vec1_len, vec2_ptr, vec2_cap, vec2_len, vec1_data, vec2_data, - ) - .collect::>(); - - assert_eq!(result, expected); - - Ok(()) - } - #[test] fn vec_in_enum() -> Result<()> { // arrange - let offset = 40; let types = to_named(&[ParamType::B256, ParamType::Vector(Box::new(ParamType::U64))]); let variants = EnumVariants::new(types)?; let selector = (1, Token::Vector(vec![Token::U64(5)]), variants); let token = Token::Enum(Box::new(selector)); // act - let result = ABIEncoder::default() - .encode(&[token])? - .resolve(offset as u64); + let result = ABIEncoder::default().encode(&[token])?; // assert let expected = [ @@ -554,7 +403,6 @@ mod tests { #[test] fn enum_in_vec() -> Result<()> { // arrange - let offset = 40; let types = to_named(&[ParamType::B256, ParamType::U8]); let variants = EnumVariants::new(types)?; let selector = (1, Token::U8(8), variants); @@ -563,9 +411,7 @@ mod tests { let vec_token = Token::Vector(vec![enum_token]); // act - let result = ABIEncoder::default() - .encode(&[vec_token])? - .resolve(offset as u64); + let result = ABIEncoder::default().encode(&[vec_token])?; // assert let expected = [ @@ -581,13 +427,10 @@ mod tests { #[test] fn vec_in_struct() -> Result<()> { // arrange - let offset = 40; let token = Token::Struct(vec![Token::Vector(vec![Token::U64(5)]), Token::U8(9)]); // act - let result = ABIEncoder::default() - .encode(&[token])? - .resolve(offset as u64); + let result = ABIEncoder::default().encode(&[token])?; // assert let expected = [ @@ -603,13 +446,10 @@ mod tests { #[test] fn vec_in_vec() -> Result<()> { // arrange - let offset = 40; let token = Token::Vector(vec![Token::Vector(vec![Token::U8(5), Token::U8(6)])]); // act - let result = ABIEncoder::default() - .encode(&[token])? - .resolve(offset as u64); + let result = ABIEncoder::default().encode(&[token])?; // assert let expected = [ @@ -622,27 +462,6 @@ mod tests { Ok(()) } - #[test] - fn capacity_overflow_is_caught() -> Result<()> { - let token = Token::Enum(Box::new(( - 1, - Token::String("".to_string()), - EnumVariants::new(to_named(&[ - ParamType::StringArray(18446742977385549567), - ParamType::U8, - ]))?, - ))); - let capacity_overflow_error = ConfigurablesEncoder::default() - .encode(&[token]) - .unwrap_err(); - - assert!(capacity_overflow_error - .to_string() - .contains("Try increasing maximum total enum width")); - - Ok(()) - } - #[test] fn max_depth_surpassed() { const MAX_DEPTH: usize = 2; @@ -660,30 +479,6 @@ mod tests { }); } - #[test] - fn encoder_for_configurables_optimizes_top_level_u8() { - // given - let encoder = ConfigurablesEncoder::default(); - - // when - let encoded = encoder.encode(&[Token::U8(255)]).unwrap().resolve(0); - - // then - assert_eq!(encoded, vec![255]); - } - - #[test] - fn encoder_for_configurables_optimizes_top_level_bool() { - // given - let encoder = ConfigurablesEncoder::default(); - - // when - let encoded = encoder.encode(&[Token::Bool(true)]).unwrap().resolve(0); - - // then - assert_eq!(encoded, vec![1]); - } - fn assert_encoding_failed(config: EncoderConfig, token: Token, msg: &str) { let encoder = ABIEncoder::new(config); 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 852499f96..5afe8904d 100644 --- a/packages/fuels-core/src/codec/abi_encoder/bounded_encoder.rs +++ b/packages/fuels-core/src/codec/abi_encoder/bounded_encoder.rs @@ -3,11 +3,7 @@ use crate::{ utils::{CodecDirection, CounterWithLimit}, EncoderConfig, }, - types::{ - errors::Result, - unresolved_bytes::{Data, UnresolvedBytes}, - EnumSelector, StaticStringToken, Token, U256, - }, + types::{errors::Result, EnumSelector, StaticStringToken, Token, U256}, }; pub(crate) struct BoundedEncoder { @@ -16,7 +12,7 @@ pub(crate) struct BoundedEncoder { } impl BoundedEncoder { - pub(crate) fn new(config: EncoderConfig, _unused: bool) -> Self { + pub(crate) fn new(config: EncoderConfig) -> Self { let depth_tracker = CounterWithLimit::new(config.max_depth, "depth", CodecDirection::Encoding); let token_tracker = @@ -27,13 +23,7 @@ impl BoundedEncoder { } } - 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> { + pub fn encode(&mut self, tokens: &[Token]) -> Result> { let mut data = vec![]; for token in tokens.iter() { @@ -72,16 +62,10 @@ impl BoundedEncoder { 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::Tuple(arg_tuple) => self.run_w_depth_tracking(|ctx| ctx.encode(arg_tuple))?, + Token::Array(arg_array) => self.run_w_depth_tracking(|ctx| ctx.encode(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::Struct(arg_struct) => self.run_w_depth_tracking(|ctx| ctx.encode(arg_struct))?, Token::Enum(arg_enum) => self.run_w_depth_tracking(|ctx| ctx.encode_enum(arg_enum))?, }; @@ -110,7 +94,7 @@ impl BoundedEncoder { } fn encode_vector(&mut self, data: &[Token]) -> Result> { - let encoded_data = self.encode_tokens(data)?; + let encoded_data = self.encode(data)?; Ok([Self::encode_length(data.len() as u64), encoded_data].concat()) } diff --git a/packages/fuels-core/src/codec/abi_encoder/configurables_bounded_encoder.rs b/packages/fuels-core/src/codec/abi_encoder/configurables_bounded_encoder.rs deleted file mode 100644 index b932dac86..000000000 --- a/packages/fuels-core/src/codec/abi_encoder/configurables_bounded_encoder.rs +++ /dev/null @@ -1,291 +0,0 @@ -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 ConfigurablesBoundedEncoder { - used_for_configurables: bool, - depth_tracker: CounterWithLimit, - token_tracker: CounterWithLimit, - max_total_enum_width: usize, -} - -impl ConfigurablesBoundedEncoder { - 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 { - let padded = crate::types::pad_string(arg_string.get_encodable_str()?)?; - Ok(Data::Inline(padded)) - } - - 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) -> Result<()> { - let padded_length = padded_len_usize(data.len()) - .ok_or_else(|| error!(Codec, "data length exceeds maximum allowed length"))?; - - data.resize(padded_length, 0); - - Ok(()) -} diff --git a/packages/fuels-core/src/codec/function_selector.rs b/packages/fuels-core/src/codec/function_selector.rs index e33b91d04..d60780916 100644 --- a/packages/fuels-core/src/codec/function_selector.rs +++ b/packages/fuels-core/src/codec/function_selector.rs @@ -10,7 +10,6 @@ pub fn encode_fn_selector(name: &str) -> Vec { macro_rules! calldata { ( $($arg: expr),* ) => { ::fuels::core::codec::ABIEncoder::default().encode(&[$(::fuels::core::traits::Tokenizable::into_token($arg)),*]) - .map(|ub| ub.resolve(0)) } } diff --git a/packages/fuels-core/src/types.rs b/packages/fuels-core/src/types.rs index 6f6b0e5c6..513c18b5a 100644 --- a/packages/fuels-core/src/types.rs +++ b/packages/fuels-core/src/types.rs @@ -14,7 +14,6 @@ pub mod param_types; mod token; pub mod transaction_builders; pub mod tx_status; -pub mod unresolved_bytes; mod wrappers; pub type ByteArray = [u8; 8]; diff --git a/packages/fuels-core/src/types/param_types.rs b/packages/fuels-core/src/types/param_types.rs index 840cac482..564ac4f75 100644 --- a/packages/fuels-core/src/types/param_types.rs +++ b/packages/fuels-core/src/types/param_types.rs @@ -1,6 +1,4 @@ -mod enum_variants; mod from_type_application; mod param_type; -pub use enum_variants::*; pub use param_type::*; diff --git a/packages/fuels-core/src/types/param_types/enum_variants.rs b/packages/fuels-core/src/types/param_types/enum_variants.rs deleted file mode 100644 index fa82824ce..000000000 --- a/packages/fuels-core/src/types/param_types/enum_variants.rs +++ /dev/null @@ -1,117 +0,0 @@ -use crate::{ - constants::ENUM_DISCRIMINANT_BYTE_WIDTH, - types::{ - errors::{error, Result}, - param_types::{NamedParamType, ParamType}, - }, - utils::checked_round_up_to_word_alignment, -}; - -#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -pub struct EnumVariants { - variants: Vec, -} - -impl EnumVariants { - pub fn new(variants: Vec) -> Result { - if variants.is_empty() { - return Err(error!(Other, "enum variants cannot be empty!")); - } - - Ok(EnumVariants { variants }) - } - - pub fn variants(&self) -> &Vec { - &self.variants - } - - pub fn param_types(&self) -> impl Iterator { - self.variants.iter().map(|(_, param_type)| param_type) - } - - pub fn select_variant(&self, discriminant: u64) -> Result<&NamedParamType> { - self.variants.get(discriminant as usize).ok_or_else(|| { - error!( - Other, - "discriminant `{discriminant}` doesn't point to any variant: {:?}", - self.variants() - ) - }) - } - - pub fn heap_type_variant(&self) -> Option<(u64, &ParamType)> { - self.param_types() - .enumerate() - .find_map(|(d, p)| p.is_extra_receipt_needed(false).then_some((d as u64, p))) - } - - pub fn only_units_inside(&self) -> bool { - self.variants - .iter() - .all(|(_, param_type)| *param_type == ParamType::Unit) - } - - /// Calculates how many bytes are needed to encode an enum. - pub fn compute_enum_width_in_bytes(&self) -> Result { - if self.only_units_inside() { - return Ok(ENUM_DISCRIMINANT_BYTE_WIDTH); - } - - let width = self.param_types().try_fold(0, |a, p| -> Result<_> { - let size = p.compute_encoding_in_bytes()?; - Ok(a.max(size)) - })?; - - checked_round_up_to_word_alignment(width)? - .checked_add(ENUM_DISCRIMINANT_BYTE_WIDTH) - .ok_or_else(|| error!(Other, "enum variants are too wide")) - } - - /// Determines the padding needed for the provided enum variant (based on the width of the - /// biggest variant) and returns it. - pub fn compute_padding_amount_in_bytes(&self, variant_param_type: &ParamType) -> Result { - let enum_width = self.compute_enum_width_in_bytes()?; - // No need to use checked arithmetics since we called `compute_enum_width_in_bytes` - let biggest_variant_width = enum_width - ENUM_DISCRIMINANT_BYTE_WIDTH; - let variant_width = variant_param_type.compute_encoding_in_bytes()?; - Ok(biggest_variant_width - variant_width) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::to_named; - - #[test] - fn test_get_heap_type_variant_discriminant() -> Result<()> { - { - let variants = to_named(&[ - ParamType::U64, - ParamType::Bool, - ParamType::Vector(Box::from(ParamType::U64)), - ]); - let enum_variants = EnumVariants::new(variants)?; - - assert_eq!(enum_variants.heap_type_variant().unwrap().0, 2); - } - { - let variants = to_named(&[ - ParamType::Vector(Box::from(ParamType::U64)), - ParamType::U64, - ParamType::Bool, - ]); - let enum_variants = EnumVariants::new(variants)?; - - assert_eq!(enum_variants.heap_type_variant().unwrap().0, 0); - } - { - let variants = to_named(&[ParamType::U64, ParamType::Bool]); - let enum_variants = EnumVariants::new(variants)?; - - assert!(enum_variants.heap_type_variant().is_none()); - } - - Ok(()) - } -} 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 2f6079aff..513c26eeb 100644 --- a/packages/fuels-core/src/types/param_types/param_type.rs +++ b/packages/fuels-core/src/types/param_types/param_type.rs @@ -1,12 +1,4 @@ -use itertools::chain; - -use crate::{ - checked_round_up_to_word_alignment, - types::{ - errors::{error, Result}, - param_types::EnumVariants, - }, -}; +use crate::types::errors::{error, Result}; pub type NamedParamType = (String, ParamType); @@ -46,287 +38,35 @@ pub enum ReturnLocation { ReturnData, } -impl ParamType { - /// Given a [ParamType], return the number of elements of that [ParamType] that can fit in - /// `available_bytes`: it is the length of the corresponding heap type. - pub fn calculate_num_of_elements( - param_type: &ParamType, - available_bytes: usize, - ) -> Result { - let memory_size = param_type.compute_encoding_in_bytes()?; - if memory_size == 0 { - return Err(error!( - Codec, - "cannot calculate the number of elements because the type is zero-sized" - )); - } +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct EnumVariants { + variants: Vec, +} - let remainder = available_bytes % memory_size; - if remainder != 0 { - return Err(error!( - Codec, - "{remainder} extra bytes detected while decoding heap type" - )); +impl EnumVariants { + pub fn new(variants: Vec) -> Result { + if variants.is_empty() { + return Err(error!(Other, "enum variants cannot be empty!")); } - let num_of_elements = available_bytes - .checked_div(memory_size) - .ok_or_else(|| error!(Codec, "type {param_type:?} has a memory_size of 0"))?; - - Ok(num_of_elements) - } - pub fn children_need_extra_receipts(&self) -> bool { - match self { - ParamType::Array(inner, _) | ParamType::Vector(inner) => { - inner.is_extra_receipt_needed(false) - } - ParamType::Struct { fields, .. } => fields - .iter() - .any(|(_, param_type)| param_type.is_extra_receipt_needed(false)), - ParamType::Enum { enum_variants, .. } => enum_variants - .param_types() - .any(|param_type| param_type.is_extra_receipt_needed(false)), - ParamType::Tuple(inner_types) => inner_types - .iter() - .any(|param_type| param_type.is_extra_receipt_needed(false)), - _ => false, - } + Ok(EnumVariants { variants }) } - pub fn is_extra_receipt_needed(&self, top_level_type: bool) -> bool { - match self { - ParamType::Vector(_) | ParamType::Bytes | ParamType::String => true, - ParamType::Array(inner, _) => inner.is_extra_receipt_needed(false), - ParamType::Struct { - fields, generics, .. - } => chain!(fields.iter().map(|(_, param_type)| param_type), generics,) - .any(|param_type| param_type.is_extra_receipt_needed(false)), - ParamType::Enum { - enum_variants, - generics, - .. - } => chain!(enum_variants.param_types(), generics) - .any(|param_type| param_type.is_extra_receipt_needed(false)), - ParamType::Tuple(elements) => elements - .iter() - .any(|param_type| param_type.is_extra_receipt_needed(false)), - ParamType::RawSlice | ParamType::StringSlice => !top_level_type, - _ => false, - } + pub fn variants(&self) -> &Vec { + &self.variants } - /// Compute the inner memory size of a containing heap type (`Bytes` or `Vec`s). - pub fn heap_inner_element_size(&self, top_level_type: bool) -> Result> { - let heap_bytes_size = match &self { - ParamType::Vector(inner_param_type) => { - Some(inner_param_type.compute_encoding_in_bytes()?) - } - // `Bytes` type is byte-packed in the VM, so it's the size of an u8 - ParamType::Bytes | ParamType::String => Some(std::mem::size_of::()), - ParamType::StringSlice if !top_level_type => { - Some(ParamType::U8.compute_encoding_in_bytes()?) - } - ParamType::RawSlice if !top_level_type => { - Some(ParamType::U64.compute_encoding_in_bytes()?) - } - _ => None, - }; - Ok(heap_bytes_size) + pub fn param_types(&self) -> impl Iterator { + self.variants.iter().map(|(_, param_type)| param_type) } - /// Calculates the number of bytes the VM expects this parameter to be encoded in. - pub fn compute_encoding_in_bytes(&self) -> Result { - let overflow_error = || { + pub fn select_variant(&self, discriminant: u64) -> Result<&NamedParamType> { + self.variants.get(discriminant as usize).ok_or_else(|| { error!( - Codec, - "reached overflow while computing encoding size for {:?}", self + Other, + "discriminant `{discriminant}` doesn't point to any variant: {:?}", + self.variants() ) - }; - match &self { - ParamType::Unit | ParamType::U8 | ParamType::Bool => Ok(1), - ParamType::U16 | ParamType::U32 | ParamType::U64 => Ok(8), - ParamType::U128 | ParamType::RawSlice | ParamType::StringSlice => Ok(16), - ParamType::U256 | ParamType::B256 => Ok(32), - ParamType::Vector(_) | ParamType::Bytes | ParamType::String => Ok(24), - ParamType::Array(param, count) => param - .compute_encoding_in_bytes()? - .checked_mul(*count) - .ok_or_else(overflow_error), - ParamType::StringArray(len) => { - checked_round_up_to_word_alignment(*len).map_err(|_| overflow_error()) - } - ParamType::Tuple(fields) => fields.iter().try_fold(0, |a: usize, param_type| { - let size = - checked_round_up_to_word_alignment(param_type.compute_encoding_in_bytes()?)?; - a.checked_add(size).ok_or_else(overflow_error) - }), - ParamType::Struct { fields, .. } => fields - .iter() - .map(|(_, param_type)| param_type) - .try_fold(0, |a: usize, param_type| { - let size = checked_round_up_to_word_alignment( - param_type.compute_encoding_in_bytes()?, - )?; - a.checked_add(size).ok_or_else(overflow_error) - }), - ParamType::Enum { enum_variants, .. } => enum_variants - .compute_enum_width_in_bytes() - .map_err(|_| overflow_error()), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - checked_round_up_to_word_alignment, constants::WORD_SIZE, to_named, - types::param_types::ParamType, - }; - - const WIDTH_OF_B256: usize = 32; - const WIDTH_OF_U32: usize = 8; - const WIDTH_OF_BOOL: usize = 1; - - #[test] - fn calculate_num_of_elements() -> Result<()> { - let failing_param_type = ParamType::Array(Box::new(ParamType::U16), usize::MAX); - assert!(ParamType::calculate_num_of_elements(&failing_param_type, 0) - .unwrap_err() - .to_string() - .contains("reached overflow")); - - let zero_sized_type = ParamType::Array(Box::new(ParamType::StringArray(0)), 1000); - assert!(ParamType::calculate_num_of_elements(&zero_sized_type, 0) - .unwrap_err() - .to_string() - .contains("the type is zero-sized")); - - assert!(ParamType::calculate_num_of_elements(&ParamType::U16, 9) - .unwrap_err() - .to_string() - .contains("1 extra bytes detected while decoding heap type")); - - Ok(()) - } - - #[test] - fn array_size_dependent_on_num_of_elements() { - const NUM_ELEMENTS: usize = 11; - let param = ParamType::Array(Box::new(ParamType::B256), NUM_ELEMENTS); - - let width = param.compute_encoding_in_bytes().unwrap(); - - let expected = NUM_ELEMENTS * WIDTH_OF_B256; - assert_eq!(expected, width); - } - - #[test] - fn string_size_dependent_on_num_of_elements() { - const NUM_ASCII_CHARS: usize = 9; - let param = ParamType::StringArray(NUM_ASCII_CHARS); - - let width = param.compute_encoding_in_bytes().unwrap(); - - assert_eq!(16, width); - } - - #[test] - fn structs_are_all_elements_combined_with_padding() -> Result<()> { - let inner_struct = ParamType::Struct { - name: "".to_string(), - fields: to_named(&[ParamType::U32, ParamType::U32]), - generics: vec![], - }; - - let a_struct = ParamType::Struct { - name: "".to_string(), - fields: to_named(&[ParamType::B256, ParamType::Bool, inner_struct]), - generics: vec![], - }; - - let width = a_struct.compute_encoding_in_bytes().unwrap(); - - const INNER_STRUCT_WIDTH: usize = WIDTH_OF_U32 * 2; - let expected_width: usize = - WIDTH_OF_B256 + checked_round_up_to_word_alignment(WIDTH_OF_BOOL)? + INNER_STRUCT_WIDTH; - assert_eq!(expected_width, width); - Ok(()) - } - - #[test] - fn enums_are_as_big_as_their_biggest_variant_plus_a_word() -> Result<()> { - let fields = to_named(&[ParamType::B256]); - let inner_struct = ParamType::Struct { - name: "".to_string(), - fields, - generics: vec![], - }; - let types = to_named(&[ParamType::U32, inner_struct]); - let param = ParamType::Enum { - name: "".to_string(), - enum_variants: EnumVariants::new(types)?, - generics: vec![], - }; - - let width = param.compute_encoding_in_bytes().unwrap(); - - const INNER_STRUCT_SIZE: usize = WIDTH_OF_B256; - const EXPECTED_WIDTH: usize = INNER_STRUCT_SIZE + WORD_SIZE; - assert_eq!(EXPECTED_WIDTH, width); - Ok(()) - } - - #[test] - fn tuples_are_just_all_elements_combined() { - let inner_tuple = ParamType::Tuple(vec![ParamType::B256]); - let param = ParamType::Tuple(vec![ParamType::U32, inner_tuple]); - - let width = param.compute_encoding_in_bytes().unwrap(); - - const INNER_TUPLE_WIDTH: usize = WIDTH_OF_B256; - const EXPECTED_WIDTH: usize = WIDTH_OF_U32 + INNER_TUPLE_WIDTH; - assert_eq!(EXPECTED_WIDTH, width); - } - - #[test] - fn test_compute_encoding_in_bytes_overflows() -> Result<()> { - let overflows = |p: ParamType| { - let error = p.compute_encoding_in_bytes().unwrap_err(); - let overflow_error = error!( - Codec, - "reached overflow while computing encoding size for {:?}", p - ); - assert_eq!(error.to_string(), overflow_error.to_string()); - }; - let tuple_with_fields_too_wide = ParamType::Tuple(vec![ - ParamType::StringArray(12514849900987264429), - ParamType::StringArray(7017071859781709229), - ]); - overflows(tuple_with_fields_too_wide); - - let struct_with_fields_too_wide = ParamType::Struct { - name: "".to_string(), - fields: to_named(&[ - ParamType::StringArray(12514849900987264429), - ParamType::StringArray(7017071859781709229), - ]), - generics: vec![], - }; - overflows(struct_with_fields_too_wide); - - let enum_with_variants_too_wide = ParamType::Enum { - name: "".to_string(), - enum_variants: EnumVariants::new(to_named(&[ParamType::StringArray(usize::MAX - 8)]))?, - generics: vec![], - }; - overflows(enum_with_variants_too_wide); - - let array_too_big = ParamType::Array(Box::new(ParamType::U64), usize::MAX); - overflows(array_too_big); - - let string_array_too_big = ParamType::StringArray(usize::MAX); - overflows(string_array_too_big); - Ok(()) + }) } } diff --git a/packages/fuels-core/src/types/transaction_builders.rs b/packages/fuels-core/src/types/transaction_builders.rs index 96288a097..c68e9af00 100644 --- a/packages/fuels-core/src/types/transaction_builders.rs +++ b/packages/fuels-core/src/types/transaction_builders.rs @@ -16,24 +16,22 @@ use fuel_tx::{ Chargeable, ConsensusParameters, Create, Input as FuelInput, Output, Script, StorageSlot, Transaction as FuelTransaction, TransactionFee, TxPointer, UniqueIdentifier, Witness, }; -use fuel_types::{bytes::padded_len_usize, canonical::Serialize, Bytes32, ChainId, Salt}; +use fuel_types::{bytes::padded_len_usize, Bytes32, Salt}; use itertools::Itertools; use crate::{ constants::{SIGNATURE_WITNESS_SIZE, WITNESS_STATIC_SIZE, WORD_SIZE}, - error, offsets, traits::Signer, types::{ bech32::Bech32Address, coin::Coin, coin_type::CoinType, - errors::{error_transaction, Result}, + errors::{error, error_transaction, Result}, input::Input, message::Message, transaction::{ CreateTransaction, EstimablePredicates, ScriptTransaction, Transaction, TxPolicies, }, - unresolved_bytes::UnresolvedBytes, Address, AssetId, ContractId, }, utils::{calculate_witnesses_size, sealed}, @@ -377,18 +375,9 @@ impl_tx_trait!(CreateTransactionBuilder, CreateTransaction); impl ScriptTransactionBuilder { async fn build(self, provider: impl DryRunner) -> Result { - let is_using_predicates = self.is_using_predicates(); - let base_offset = if is_using_predicates { - self.base_offset(provider.consensus_parameters())? - } else { - 0 - }; - - let tx = self.resolve_fuel_tx(base_offset, &provider).await?; - Ok(ScriptTransaction { - tx, - is_using_predicates, + is_using_predicates: self.is_using_predicates(), + tx: self.resolve_fuel_tx(&provider).await?, }) } @@ -470,7 +459,7 @@ impl ScriptTransactionBuilder { Ok(()) } - async fn resolve_fuel_tx(self, base_offset: usize, provider: impl DryRunner) -> Result