From 9f34af8112b40e6acb2f51f178160891cc13e82b Mon Sep 17 00:00:00 2001 From: hal3e Date: Tue, 7 Jan 2025 11:55:54 +0100 Subject: [PATCH] Work in progress --- e2e/tests/configurables.rs | 67 ++++++++- .../abigen/bindings/contract.rs | 6 +- .../program_bindings/abigen/configurables.rs | 138 +++--------------- packages/fuels-core/src/lib.rs | 103 ++++++++----- packages/fuels/src/lib.rs | 2 +- 5 files changed, 155 insertions(+), 161 deletions(-) diff --git a/e2e/tests/configurables.rs b/e2e/tests/configurables.rs index 0d7ddc76a..aa9e00e72 100644 --- a/e2e/tests/configurables.rs +++ b/e2e/tests/configurables.rs @@ -1,7 +1,7 @@ use fuels::{ - core::codec::EncoderConfig, + core::{codec::EncoderConfig, ConfigurablesReader}, prelude::*, - types::{Bits256, SizedAsciiString, U256}, + types::{AsciiString, Bits256, SizedAsciiString, U256}, }; use test_case::test_case; @@ -392,3 +392,66 @@ async fn configurable_encoder_config_is_applied() { .contains("token limit `1` reached while encoding. Try increasing it"),) } } + +#[tokio::test] +async fn contract_configurables_reader() -> Result<()> { + abigen!(Contract( + name = "MyContract", + abi = "e2e/sway/contracts/dyn_configurables/out/release/dyn_configurables-abi.json" + )); + + let configurables_reader = MyContractConfigurablesReader::load_from( + "sway/contracts/dyn_configurables/out/release/dyn_configurables.bin", + )?; + + let some_bool = configurables_reader.BOOL()?; + let some_u8 = configurables_reader.U8()?; + let some_str = configurables_reader.STR()?; + let some_str2 = configurables_reader.STR_2()?; + let some_str3 = configurables_reader.STR_3()?; + let some_last_u8 = configurables_reader.LAST_U8()?; + + assert!(some_bool); + assert_eq!(some_u8, 8); + assert_eq!(some_str, "sway"); + assert_eq!(some_str2, "forc"); + assert_eq!(some_str3, "fuel"); + assert_eq!(some_last_u8, 16); + + // let offset = 2555; + // let some_u8: u8 = fuels::core::ConfigurablesReader::load_from( + // "sway/contracts/dyn_configurables/out/release/dyn_configurables.bin", + // )? + // .decode_direct(offset)?; + + // let offset = 2555; + // let some_str: fuels::types::AsciiString = fuels::core::ConfigurablesReader::load_from( + // "sway/contracts/dyn_configurables/out/release/dyn_configurables.bin", + // )? + // .decode_indirect(offset)?; + + Ok(()) +} + +#[tokio::test] +async fn contract_configurables_reader_manual() -> Result<()> { + let configurables_reader = ConfigurablesReader::load_from( + "sway/contracts/dyn_configurables/out/release/dyn_configurables.bin", + )?; + + let some_bool: bool = configurables_reader.decode_direct(3264)?; + let some_u8: u8 = configurables_reader.decode_direct(3304)?; + let some_str: AsciiString = configurables_reader.decode_indirect(3280)?; + let some_str2: AsciiString = configurables_reader.decode_indirect(3288)?; + let some_str3: AsciiString = configurables_reader.decode_indirect(3296)?; + let some_last_u8: u8 = configurables_reader.decode_direct(3272)?; + + assert!(some_bool); + assert_eq!(some_u8, 8); + assert_eq!(some_str, "sway"); + assert_eq!(some_str2, "forc"); + assert_eq!(some_str3, "fuel"); + assert_eq!(some_last_u8, 16); + + Ok(()) +} diff --git a/packages/fuels-code-gen/src/program_bindings/abigen/bindings/contract.rs b/packages/fuels-code-gen/src/program_bindings/abigen/bindings/contract.rs index 95ae6bc2b..e92b904ea 100644 --- a/packages/fuels-code-gen/src/program_bindings/abigen/bindings/contract.rs +++ b/packages/fuels-code-gen/src/program_bindings/abigen/bindings/contract.rs @@ -9,7 +9,7 @@ use crate::{ abigen::{ bindings::function_generator::FunctionGenerator, configurables::{ - generate_code_for_configurable_constants, generate_code_for_configurable_constants2, + generate_code_for_configurable_constants, generate_code_for_configurable_reader, }, logs::log_formatters_instantiation_code, }, @@ -38,9 +38,9 @@ pub(crate) fn contract_bindings( let constant_configuration_code = generate_code_for_configurable_constants(&configuration_struct_name, &abi.configurables)?; - let configuration_struct_name2 = ident(&format!("{name}Configurables2")); + let configuration_struct_name2 = ident(&format!("{name}ConfigurablesReader")); let constant_configuration_code2 = - generate_code_for_configurable_constants2(&configuration_struct_name2, &abi.configurables)?; + generate_code_for_configurable_reader(&configuration_struct_name2, &abi.configurables)?; let code = quote! { #[derive(Debug, Clone)] 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 c53622f70..964d74d4d 100644 --- a/packages/fuels-code-gen/src/program_bindings/abigen/configurables.rs +++ b/packages/fuels-code-gen/src/program_bindings/abigen/configurables.rs @@ -147,7 +147,7 @@ fn generate_from_impl(configurable_struct_name: &Ident) -> TokenStream { } } -pub(crate) fn generate_code_for_configurable_constants2( +pub(crate) fn generate_code_for_configurable_reader( configurable_struct_name: &Ident, configurables: &[FullConfigurable], ) -> Result { @@ -156,8 +156,9 @@ pub(crate) fn generate_code_for_configurable_constants2( .map(ResolvedConfigurable::new) .collect::>>()?; - let struct_decl = generate_struct_decl2(configurable_struct_name); - let struct_impl = generate_struct_impl2(configurable_struct_name, &resolved_configurables); + let struct_decl = generate_struct_decl_reader(configurable_struct_name); + let struct_impl = + generate_struct_impl_reader(configurable_struct_name, &resolved_configurables); Ok(quote! { #struct_decl @@ -165,100 +166,29 @@ pub(crate) fn generate_code_for_configurable_constants2( }) } -fn generate_struct_decl2(configurable_struct_name: &Ident) -> TokenStream { +fn generate_struct_decl_reader(configurable_struct_name: &Ident) -> TokenStream { quote! { - #[derive(Clone, Debug, Default)] + #[derive(Clone, Debug)] pub struct #configurable_struct_name { - configurables: ::std::collections::HashMap, - encoder: ::fuels::core::codec::ABIEncoder, - decoder: ::fuels::core::codec::ABIDecoder, + reader: ::fuels::core::ConfigurablesReader, } } } -fn generate_struct_impl2( +fn generate_struct_impl_reader( configurable_struct_name: &Ident, resolved_configurables: &[ResolvedConfigurable], ) -> TokenStream { - let methods = generate_methods2(resolved_configurables); - - let code_to_load_configurables = resolved_configurables.iter().map( - |ResolvedConfigurable { - ttype, - offset, - indirect, - .. - }| { - let decoder_code = generate_decoder_code2(ttype, *offset as usize); - quote! { - let configurable = if #indirect { - let offset_from_data_section = Self::extract_usize_at_offset(&binary, #offset as usize)?; - ::std::dbg!(&offset_from_data_section); - let data_offset = data_section_offset + offset_from_data_section; - - let token = self.decoder.decode(&<#ttype as ::fuels::core::traits::Parameterize>::param_type(), &binary[data_offset..])?; - let encoded = self.encoder.encode(&[token])?; //TODO: @hal3e remove the encoding - - ::fuels::core::Configurable::Indirect{ - offset: #offset, - data_offset: data_offset as u64, - data: encoded, - } - } - else { - let token = #decoder_code?; - let encoded = self.encoder.encode(&[token])?; //TODO: @hal3e remove the encoding - //and get the num of loaded bytes - ::fuels::core::Configurable::Direct{ - offset: #offset, - data: encoded, - } - }; - - configurables.insert(#offset, configurable); - } - }, - ); + let methods = generate_methods_reader(resolved_configurables); quote! { impl #configurable_struct_name { - pub fn load_from(mut self, + pub fn load_from( binary_filepath: impl ::std::convert::AsRef<::std::path::Path>, ) -> ::fuels::prelude::Result { - let binary_filepath = binary_filepath.as_ref(); - - let binary = ::std::fs::read(binary_filepath).map_err(|e| { - ::std::io::Error::new( - e.kind(), - format!("failed to read binary: {binary_filepath:?}: {e}"), - ) - })?; - - let mut configurables = ::std::collections::HashMap::new(); - let data_section_offset = Self::extract_usize_at_offset(&binary, 8)?; - ::std::dbg!(&data_section_offset); + let reader = ::fuels::core::ConfigurablesReader::load_from(binary_filepath)?; - - #(#code_to_load_configurables)* - - self.configurables = configurables; - - ::fuels::prelude::Result::Ok(self) - } - - fn extract_usize_at_offset(binary: &[u8], offset: usize) -> ::fuels::prelude::Result { - if binary.len() < (offset + 8) { - return ::std::result::Result::Err(::fuels::types::errors::error!( - Other, - "given binary is too short to contain a data offset, len: {}", - binary.len() - )); - } - let data_offset = - <&[u8] as ::std::convert::TryInto<[u8; 8]>>::try_into(&binary[offset..(offset + 8)]).expect("checked above"); - - - ::fuels::prelude::Result::Ok(u64::from_be_bytes(data_offset) as usize) + ::fuels::prelude::Result::Ok(Self{reader}) } #methods @@ -266,37 +196,27 @@ fn generate_struct_impl2( } } -fn generate_methods2(resolved_configurables: &[ResolvedConfigurable]) -> TokenStream { +fn generate_methods_reader(resolved_configurables: &[ResolvedConfigurable]) -> TokenStream { let methods = resolved_configurables.iter().map( |ResolvedConfigurable { name, ttype, offset, - .. + indirect, }| { - let encoder_code = generate_encoder_code2(ttype); let name = safe_ident(name); - let with_name = safe_ident(&format!("with_{}", name)); - quote! { - // Generate the `with_XXX` methods for setting the configurables - #[allow(non_snake_case)] - pub fn #with_name(mut self, value: #ttype) -> ::fuels::prelude::Result { - let encoded = #encoder_code?; - let mut configurable = self.configurables.get(&#offset).expect("is there").clone(); - configurable.set_data(encoded); - self.configurables.insert(#offset, configurable); - ::fuels::prelude::Result::Ok(self) - } + let reader_code = if *indirect { + quote! { self.reader.decode_indirect(#offset as usize) } + } else { + quote! { self.reader.decode_direct(#offset as usize) } + }; + quote! { // Generate the `XXX` methods for getting the configurables #[allow(non_snake_case)] - pub fn #name(&self) -> #ttype { - let configurable = self.configurables.get(&#offset).expect("is there"); - - let token = self.decoder.decode(&<#ttype as ::fuels::core::traits::Parameterize>::param_type(), configurable.data()).expect("is ok"); - - <#ttype as ::fuels::core::traits::Tokenizable>::from_token(token).expect("is ok") + pub fn #name(&self) -> ::fuels::prelude::Result<#ttype> { + #reader_code } } @@ -307,17 +227,3 @@ fn generate_methods2(resolved_configurables: &[ResolvedConfigurable]) -> TokenSt #(#methods)* } } - -fn generate_encoder_code2(ttype: &ResolvedType) -> TokenStream { - quote! { - self.encoder.encode(&[ - <#ttype as ::fuels::core::traits::Tokenizable>::into_token(value) - ]) - } -} - -fn generate_decoder_code2(ttype: &ResolvedType, offset: usize) -> TokenStream { - quote! { - self.decoder.decode(&<#ttype as ::fuels::core::traits::Parameterize>::param_type(), &binary[#offset..]) - } -} diff --git a/packages/fuels-core/src/lib.rs b/packages/fuels-core/src/lib.rs index f9dd8d919..aa8597c75 100644 --- a/packages/fuels-core/src/lib.rs +++ b/packages/fuels-core/src/lib.rs @@ -3,12 +3,12 @@ pub mod traits; pub mod types; mod utils; -use std::{collections::HashMap, iter}; +use std::{collections::HashMap, iter, path::Path}; -use codec::ABIEncoder; +use codec::{try_from_bytes, ABIEncoder, DecoderConfig}; use itertools::Itertools; use offsets::{extract_data_offset, extract_offset_at}; -use traits::Tokenizable; +use traits::{Parameterize, Tokenizable}; pub use utils::*; use crate::types::errors::Result; @@ -16,33 +16,58 @@ use crate::types::errors::Result; type OffsetWithData = (u64, Vec); type OffsetWithSlice<'a> = (u64, &'a [u8]); -#[derive(Debug, Clone)] -pub enum Configurable { - //TODO:hal3e make private - Direct { - offset: u64, - data: Vec, - }, - Indirect { - offset: u64, - data_offset: u64, - data: Vec, - }, +#[derive(Debug, Clone)] //TODO: hal3e test this +pub struct ConfigurablesReader { + binary: Vec, + decoder_config: DecoderConfig, } -impl Configurable { - pub fn data(&self) -> &[u8] { - match self { - Configurable::Direct { data, .. } => data, - Configurable::Indirect { data, .. } => data, +impl ConfigurablesReader { + pub fn load(binary: Vec) -> Self { + Self { + binary, + decoder_config: DecoderConfig::default(), } } - pub fn set_data(&mut self, new_data: Vec) { - match self { - Configurable::Direct { data, .. } => *data = new_data, - Configurable::Indirect { data, .. } => *data = new_data, - } + pub fn load_from(binary_filepath: impl AsRef) -> Result { + let binary_filepath = binary_filepath.as_ref(); + + let binary = std::fs::read(binary_filepath).map_err(|e| { + std::io::Error::new( + e.kind(), + format!("failed to read binary: {binary_filepath:?}: {e}"), + ) + })?; + + Ok(Self { + binary, + decoder_config: DecoderConfig::default(), + }) + } + + pub fn with_decoder_config(mut self, decoder_config: DecoderConfig) -> Self { + self.decoder_config = decoder_config; + + self + } + + pub fn decode_direct(&self, offset: usize) -> Result { + check_binary_len(&self.binary, offset)?; + + try_from_bytes(&self.binary[offset..], self.decoder_config) + } + + pub fn decode_indirect(&self, offset: usize) -> Result { + let data_offset = extract_data_offset(&self.binary)?; + let dyn_offset = extract_offset_at(&self.binary, offset)?; + + check_binary_len(&self.binary, data_offset + dyn_offset)?; + + try_from_bytes( + &self.binary[data_offset + dyn_offset..], + self.decoder_config, + ) } } @@ -177,8 +202,8 @@ impl Configurables { .expect("is there as we created the sorted vec"); let end_offset = sorted_dyn_offsets[idx + 1]; // is there as we created the sorted vec - Self::check_binary_len(binary, dyn_offset)?; - Self::check_binary_len(binary, end_offset)?; + check_binary_len(binary, dyn_offset)?; + check_binary_len(binary, end_offset)?; let data = &binary[dyn_offset..end_offset]; @@ -224,25 +249,25 @@ impl Configurables { fn write(binary: &mut [u8], offset: usize, data: &[u8]) -> Result<()> { let data_len = data.len(); - Self::check_binary_len(binary, offset + data_len)?; + check_binary_len(binary, offset + data_len)?; binary[offset..offset + data.len()].copy_from_slice(data); Ok(()) } +} - fn check_binary_len(binary: &[u8], offset: usize) -> Result<()> { - if binary.len() < offset { - return Err(crate::error!( - Other, - "configurables: given binary with len: `{}` is too short for offset:`{}`", - binary.len(), - offset - )); - } - - Ok(()) +fn check_binary_len(binary: &[u8], offset: usize) -> Result<()> { + if binary.len() < offset { + return Err(crate::error!( + Other, + "configurables: given binary with len: `{}` is too short for offset:`{}`", + binary.len(), + offset + )); } + + Ok(()) } #[cfg(test)] diff --git a/packages/fuels/src/lib.rs b/packages/fuels/src/lib.rs index 13df820f5..ef8f1a062 100644 --- a/packages/fuels/src/lib.rs +++ b/packages/fuels/src/lib.rs @@ -38,7 +38,7 @@ pub mod programs { } pub mod core { - pub use fuels_core::{codec, constants, offsets, traits, Configurable, Configurables}; + pub use fuels_core::{codec, constants, offsets, traits, Configurables, ConfigurablesReader}; } pub mod crypto {