From 67c0549e9df75d7cc43dcb815d42eeb80272ee81 Mon Sep 17 00:00:00 2001 From: Kaya Gokalp Date: Fri, 27 Sep 2024 13:40:09 -0700 Subject: [PATCH 1/9] feat: configurable declaration file --- .../forc-client/src/util/configurables.rs | 106 ++++++++++++++++++ forc-plugins/forc-client/src/util/mod.rs | 1 + 2 files changed, 107 insertions(+) create mode 100644 forc-plugins/forc-client/src/util/configurables.rs diff --git a/forc-plugins/forc-client/src/util/configurables.rs b/forc-plugins/forc-client/src/util/configurables.rs new file mode 100644 index 00000000000..1bd19be21d1 --- /dev/null +++ b/forc-plugins/forc-client/src/util/configurables.rs @@ -0,0 +1,106 @@ +use fuel_abi_types::abi::program::{ConcreteTypeId, ProgramABI}; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, path::Path}; + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ConfigurableDeclarations { + #[serde(flatten)] + declarations: HashMap, +} + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ConfigurableDeclaration { + /// Name of the configurable field. + config_type: String, + /// Value of the configurable field. + value: String, +} + +impl ConfigurableDeclaration { + pub fn new(config_type: String, value: String) -> Self { + Self { config_type, value } + } +} + +impl ConfigurableDeclarations { + pub fn new(declarations: HashMap) -> Self { + Self { declarations } + } + + /// Read `ConfigurableDeclarations` from json file at given `path`. + pub fn from_file(path: &Path) -> anyhow::Result { + let decls = std::fs::read_to_string(path).map_err(|e| { + anyhow::anyhow!( + "failed to read to configurable manifest from {}, error: {}", + path.display(), + e + ) + })?; + let decls: ConfigurableDeclarations = serde_json::from_str(&decls)?; + Ok(decls) + } +} + +impl TryFrom for ConfigurableDeclarations { + type Error = anyhow::Error; + + fn try_from(value: ProgramABI) -> Result { + let concrete_type_lookup: HashMap<&ConcreteTypeId, &str> = value + .concrete_types + .iter() + .map(|conc_type| (&conc_type.concrete_type_id, conc_type.type_field.as_str())) + .collect(); + + let configurables = value + .configurables + .unwrap_or_default() + .iter() + .map(|configurable| { + let config_name = configurable.name.as_str(); + let config_concrete_type_id = &configurable.concrete_type_id; + let config_type_str: &str = concrete_type_lookup + .get(config_concrete_type_id) + .ok_or_else(|| { + anyhow::anyhow!("missing {config_name} type declaration in program abi.") + })?; + + let decl = + ConfigurableDeclaration::new(config_type_str.to_string(), "".to_string()); + + Ok((config_name.to_string(), decl)) + }) + .collect::>>()?; + + Ok(Self::new(configurables)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_configurable_decl() { + let decl = r#"{ "configType": "Type A", "value": "Value" }"#; + let decl_parsed: ConfigurableDeclaration = serde_json::from_str(decl).unwrap(); + + assert_eq!(decl_parsed.config_type, "Type A".to_string()); + assert_eq!(decl_parsed.value, "Value".to_string()) + } + + #[test] + fn test_configurable_decls() { + let decls = r#"{ "configName": {"configType": "Name", "value": "Value"} }"#; + let decls_parsed: ConfigurableDeclarations = serde_json::from_str(decls).unwrap(); + + assert_eq!(decls_parsed.declarations.len(), 1); + + let decl_parsed = decls_parsed.declarations.iter().nth(0).unwrap(); + + assert_eq!(decl_parsed.0, "configName"); + assert_eq!(decl_parsed.1.config_type, "Name"); + assert_eq!(decl_parsed.1.value, "Value"); + } +} diff --git a/forc-plugins/forc-client/src/util/mod.rs b/forc-plugins/forc-client/src/util/mod.rs index 2e58fc41c79..c5856270e4f 100644 --- a/forc-plugins/forc-client/src/util/mod.rs +++ b/forc-plugins/forc-client/src/util/mod.rs @@ -1,5 +1,6 @@ pub mod account; pub mod aws; +pub mod configurables; pub(crate) mod encode; pub(crate) mod node_url; pub(crate) mod pkg; From 4e77ebae117e6d0ed11a76536293a9ca651d9cf7 Mon Sep 17 00:00:00 2001 From: Kaya Gokalp Date: Fri, 27 Sep 2024 14:37:37 -0700 Subject: [PATCH 2/9] enhance encoding capabilities --- forc-plugins/forc-client/src/util/encode.rs | 37 +++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/forc-plugins/forc-client/src/util/encode.rs b/forc-plugins/forc-client/src/util/encode.rs index f1ab46793e4..99aa26fa86d 100644 --- a/forc-plugins/forc-client/src/util/encode.rs +++ b/forc-plugins/forc-client/src/util/encode.rs @@ -14,7 +14,9 @@ pub(crate) enum Type { U16, U32, U64, + U128, U256, + B256, Bool, } @@ -61,6 +63,16 @@ impl Token { let bool_val = value.parse::()?; Ok(Token(fuels_core::types::Token::Bool(bool_val))) } + Type::U128 => { + let u128_val = value.parse::()?; + Ok(Token(fuels_core::types::Token::U128(u128_val))) + } + Type::B256 => { + let s = value.strip_prefix("0x").unwrap_or(value); + let mut res: [u8; 32] = Default::default(); + hex::decode_to_slice(&s, &mut res)?; + Ok(Token(fuels_core::types::Token::B256(res))) + } } } } @@ -75,8 +87,10 @@ impl FromStr for Type { "u16" => Ok(Type::U16), "u32" => Ok(Type::U32), "u64" => Ok(Type::U64), + "u128" => Ok(Type::U128), "u256" => Ok(Type::U256), "bool" => Ok(Type::Bool), + "b256" => Ok(Type::B256), other => anyhow::bail!("{other} type is not supported."), } } @@ -92,14 +106,29 @@ mod tests { let u16_token = Token::from_type_and_value(&Type::U16, "1").unwrap(); let u32_token = Token::from_type_and_value(&Type::U32, "1").unwrap(); let u64_token = Token::from_type_and_value(&Type::U64, "1").unwrap(); + let u128_token = Token::from_type_and_value(&Type::U128, "1").unwrap(); + let u256_token = Token::from_type_and_value(&Type::U256, "1").unwrap(); + let b256_token = Token::from_type_and_value( + &Type::B256, + "0x0000000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(); let bool_token = Token::from_type_and_value(&Type::Bool, "true").unwrap(); - let generated_tokens = [u8_token, u16_token, u32_token, u64_token, bool_token]; + let generated_tokens = [ + u8_token, u16_token, u32_token, u64_token, u128_token, u256_token, b256_token, + bool_token, + ]; let expected_tokens = [ Token(fuels_core::types::Token::U8(1)), Token(fuels_core::types::Token::U16(1)), Token(fuels_core::types::Token::U32(1)), Token(fuels_core::types::Token::U64(1)), + Token(fuels_core::types::Token::U128(1)), + Token(fuels_core::types::Token::U256(fuels::types::U256([ + 1, 0, 0, 0, + ]))), + Token(fuels_core::types::Token::B256([0; 32])), Token(fuels_core::types::Token::Bool(true)), ]; @@ -114,7 +143,9 @@ mod tests { #[test] fn test_type_generation_success() { - let possible_type_list = ["()", "u8", "u16", "u32", "u64", "u256", "bool"]; + let possible_type_list = [ + "()", "u8", "u16", "u32", "u64", "u128", "u256", "b256", "bool", + ]; let types = possible_type_list .iter() .map(|type_str| Type::from_str(type_str)) @@ -127,7 +158,9 @@ mod tests { Type::U16, Type::U32, Type::U64, + Type::U128, Type::U256, + Type::B256, Type::Bool, ]; assert_eq!(types, expected_types) From 982b93f6cf1e6e70bbf4a7f1acd5710dd3d231d8 Mon Sep 17 00:00:00 2001 From: Kaya Gokalp Date: Fri, 27 Sep 2024 23:08:13 -0700 Subject: [PATCH 3/9] add string slice and tests --- forc-plugins/forc-client/src/util/encode.rs | 111 +++++++++++++++++++- 1 file changed, 106 insertions(+), 5 deletions(-) diff --git a/forc-plugins/forc-client/src/util/encode.rs b/forc-plugins/forc-client/src/util/encode.rs index 99aa26fa86d..817e1c89107 100644 --- a/forc-plugins/forc-client/src/util/encode.rs +++ b/forc-plugins/forc-client/src/util/encode.rs @@ -1,5 +1,6 @@ use anyhow::Context; use fuel_abi_types::abi::full_program::FullTypeApplication; +use fuels::types::StaticStringToken; use std::str::FromStr; use sway_types::u256::U256; @@ -18,6 +19,8 @@ pub(crate) enum Type { U256, B256, Bool, + String, + StringSlice(usize), } impl TryFrom<&FullTypeApplication> for Type { @@ -55,7 +58,7 @@ impl Token { Ok(Token(fuels_core::types::Token::U64(u64_val))) } Type::U256 => { - let v = value.parse::().context("u256 literal out of range")?; + let v = value.parse::().context("Invalid value for U256")?; let bytes = v.to_be_bytes(); Ok(Token(fuels_core::types::Token::U256(bytes.into()))) } @@ -70,9 +73,17 @@ impl Token { Type::B256 => { let s = value.strip_prefix("0x").unwrap_or(value); let mut res: [u8; 32] = Default::default(); - hex::decode_to_slice(&s, &mut res)?; + hex::decode_to_slice(&s, &mut res).context("Invalid value for B256")?; Ok(Token(fuels_core::types::Token::B256(res))) } + Type::String => { + let s = value.to_string(); + Ok(Token(fuels_core::types::Token::String(s))) + } + Type::StringSlice(len) => { + let s = StaticStringToken::new(value.to_string(), Some(*len)); + Ok(Token(fuels_core::types::Token::StringSlice(s))) + } } } } @@ -91,7 +102,23 @@ impl FromStr for Type { "u256" => Ok(Type::U256), "bool" => Ok(Type::Bool), "b256" => Ok(Type::B256), - other => anyhow::bail!("{other} type is not supported."), + "String" => Ok(Type::String), + other => { + // Use a regular expression to check for string slice syntax + let re = forc_util::Regex::new(r"^str\[(\d+)\]$").expect("Invalid regex pattern"); + if let Some(captures) = re.captures(other) { + // Extract the number inside the brackets + let len = captures + .get(1) + .ok_or_else(|| anyhow::anyhow!("Invalid string slice length"))? + .as_str() + .parse::() + .map_err(|_| anyhow::anyhow!("Invalid number for string slice length"))?; + Ok(Type::StringSlice(len)) + } else { + anyhow::bail!("{other} type is not supported.") + } + } } } } @@ -99,6 +126,8 @@ impl FromStr for Type { #[cfg(test)] mod tests { use super::*; + use fuel_abi_types::abi::full_program::{FullTypeApplication, FullTypeDeclaration}; + use fuels::types::StaticStringToken; #[test] fn test_token_generation_success() { @@ -114,10 +143,21 @@ mod tests { ) .unwrap(); let bool_token = Token::from_type_and_value(&Type::Bool, "true").unwrap(); + let string_token = Token::from_type_and_value(&Type::String, "Hello, World!").unwrap(); + let string_slice_token = + Token::from_type_and_value(&Type::StringSlice(5), "Hello").unwrap(); let generated_tokens = [ - u8_token, u16_token, u32_token, u64_token, u128_token, u256_token, b256_token, + u8_token, + u16_token, + u32_token, + u64_token, + u128_token, + u256_token, + b256_token, bool_token, + string_token, + string_slice_token, ]; let expected_tokens = [ Token(fuels_core::types::Token::U8(1)), @@ -130,6 +170,12 @@ mod tests { ]))), Token(fuels_core::types::Token::B256([0; 32])), Token(fuels_core::types::Token::Bool(true)), + Token(fuels_core::types::Token::String( + "Hello, World!".to_string(), + )), + Token(fuels_core::types::Token::StringSlice( + StaticStringToken::new("Hello".to_string(), Some(5)), + )), ]; assert_eq!(generated_tokens, expected_tokens) @@ -141,10 +187,26 @@ mod tests { Token::from_type_and_value(&Type::U8, "false").unwrap(); } + #[test] + #[should_panic(expected = "attempt to subtract with overflow")] + fn test_token_generation_fail_u256_out_of_range() { + Token::from_type_and_value( + &Type::U256, + "115792089237316195423570985008687907853269984665640564039457584007913129639937", + ) + .unwrap(); + } + + #[test] + #[should_panic(expected = "Invalid value for B256")] + fn test_token_generation_fail_b256_invalid_length() { + Token::from_type_and_value(&Type::B256, "0x123").unwrap(); + } + #[test] fn test_type_generation_success() { let possible_type_list = [ - "()", "u8", "u16", "u32", "u64", "u128", "u256", "b256", "bool", + "()", "u8", "u16", "u32", "u64", "u128", "u256", "b256", "bool", "String", "str[5]", ]; let types = possible_type_list .iter() @@ -162,6 +224,8 @@ mod tests { Type::U256, Type::B256, Type::Bool, + Type::String, + Type::StringSlice(5), ]; assert_eq!(types, expected_types) } @@ -172,4 +236,41 @@ mod tests { let invalid_type_str = "u2"; Type::from_str(invalid_type_str).unwrap(); } + + #[test] + #[should_panic(expected = "str[abc] type is not supported.")] + fn test_type_generation_fail_invalid_string_slice() { + let invalid_type_str = "str[abc]"; + Type::from_str(invalid_type_str).unwrap(); + } + + #[test] + fn test_try_from_full_type_application() { + let type_application = FullTypeApplication { + type_decl: FullTypeDeclaration { + type_field: "u8".to_string(), + components: Default::default(), + type_parameters: Default::default(), + }, + name: "none".to_string(), + type_arguments: vec![], + }; + let generated_type = Type::try_from(&type_application).unwrap(); + assert_eq!(generated_type, Type::U8); + } + + #[test] + #[should_panic(expected = "str[abc] type is not supported.")] + fn test_try_from_full_type_application_invalid_string_slice() { + let type_application = FullTypeApplication { + type_decl: FullTypeDeclaration { + type_field: "str[abc]".to_string(), + components: Default::default(), + type_parameters: Default::default(), + }, + name: "none".to_string(), + type_arguments: vec![], + }; + Type::try_from(&type_application).unwrap(); + } } From 80241b74d739e98bb48f5f38b27145864c39cfab Mon Sep 17 00:00:00 2001 From: Kaya Gokalp Date: Sat, 28 Sep 2024 00:33:52 -0700 Subject: [PATCH 4/9] apply configurable changes --- forc-plugins/forc-client/src/cmd/deploy.rs | 8 ++++ forc-plugins/forc-client/src/op/deploy.rs | 39 +++++++++++++++++-- .../forc-client/src/util/configurables.rs | 24 ++++++++---- 3 files changed, 61 insertions(+), 10 deletions(-) diff --git a/forc-plugins/forc-client/src/cmd/deploy.rs b/forc-plugins/forc-client/src/cmd/deploy.rs index 5553a2fe06b..828db886a4b 100644 --- a/forc-plugins/forc-client/src/cmd/deploy.rs +++ b/forc-plugins/forc-client/src/cmd/deploy.rs @@ -91,4 +91,12 @@ pub struct Command { /// AWS KMS signer arn. If present forc-deploy will automatically use AWS KMS signer instead of forc-wallet. #[clap(long)] pub aws_kms_signer: Option, + + /// Path to configurable override file. + #[clap(long)] + pub override_configurable_slots: Option, + + /// Generate configurable slots file from abi. + #[clap(long)] + pub generate_configurable_slots_file: Option, } diff --git a/forc-plugins/forc-client/src/op/deploy.rs b/forc-plugins/forc-client/src/op/deploy.rs index a5c71a41f79..ef66667f884 100644 --- a/forc-plugins/forc-client/src/op/deploy.rs +++ b/forc-plugins/forc-client/src/op/deploy.rs @@ -3,6 +3,7 @@ use crate::{ constants::TX_SUBMIT_TIMEOUT_MS, util::{ account::ForcClientAccount, + configurables::ConfigurableDeclarations, node_url::get_node_url, pkg::{built_pkgs, create_proxy_contract, update_proxy_address_in_manifest}, target::Target, @@ -29,19 +30,23 @@ use fuels::{ types::{bech32::Bech32ContractId, transaction_builders::Blob}, }; use fuels_accounts::{provider::Provider, Account, ViewOnlyAccount}; -use fuels_core::types::{transaction::TxPolicies, transaction_builders::CreateTransactionBuilder}; +use fuels_core::{ + codec::ABIEncoder, + types::{transaction::TxPolicies, transaction_builders::CreateTransactionBuilder}, +}; use futures::FutureExt; use pkg::{manifest::build_profile::ExperimentalFlags, BuildProfile, BuiltPackage}; use serde::{Deserialize, Serialize}; use std::{ collections::BTreeMap, + fs, path::{Path, PathBuf}, str::FromStr, sync::Arc, time::Duration, }; -use sway_core::language::parsed::TreeType; use sway_core::BuildTarget; +use sway_core::{asm_generation::ProgramABI, language::parsed::TreeType}; /// Maximum contract size allowed for a single contract. If the target /// contract size is bigger than this amount, forc-deploy will automatically @@ -513,9 +518,37 @@ pub async fn deploy_pkg( let node_url = provider.url(); let client = FuelClient::new(node_url)?; - let bytecode = &compiled.bytecode.bytes; + let mut bytecode = compiled.bytecode.bytes.clone(); let storage_slots = resolve_storage_slots(command, compiled)?; + if let Some(config_slots) = &command.override_configurable_slots { + let mut config_values_with_offset: Vec<_> = vec![]; + let config_decls = ConfigurableDeclarations::from_file(&PathBuf::from_str(config_slots)?)?; + for decl in config_decls.declarations.values() { + let config_type = crate::util::encode::Type::from_str(&decl.config_type)?; + let token = crate::util::encode::Token::from_type_and_value(&config_type, &decl.value)?; + let abi_encoder = ABIEncoder::new(Default::default()); + let encoded_val = abi_encoder.encode(&[token.0])?; + let offset = decl.offset as usize; + config_values_with_offset.push((offset, encoded_val)); + } + // sort config decls based on the offsets. + config_values_with_offset.sort_by(|a, b| a.0.cmp(&b.0)); + + for (offset, val) in config_values_with_offset { + bytecode[offset..offset + val.len()].copy_from_slice(&val); + } + } else if let Some(path) = &command.generate_configurable_slots_file { + let program_abi = &compiled.program_abi; + if let ProgramABI::Fuel(program_abi) = program_abi { + let config_decls = ConfigurableDeclarations::try_from(program_abi.clone())?; + let config_decls_str = serde_json::to_string(&config_decls)?; + fs::write(path, config_decls_str)?; + } else { + bail!("Only Fuel ABI configurable generation is supported"); + } + } + let contract = Contract::from(bytecode.clone()); let root = contract.root(); let state_root = Contract::initial_state_root(storage_slots.iter()); diff --git a/forc-plugins/forc-client/src/util/configurables.rs b/forc-plugins/forc-client/src/util/configurables.rs index 1bd19be21d1..2332f805940 100644 --- a/forc-plugins/forc-client/src/util/configurables.rs +++ b/forc-plugins/forc-client/src/util/configurables.rs @@ -6,21 +6,27 @@ use std::{collections::HashMap, path::Path}; #[serde(rename_all = "camelCase")] pub struct ConfigurableDeclarations { #[serde(flatten)] - declarations: HashMap, + pub declarations: HashMap, } #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ConfigurableDeclaration { /// Name of the configurable field. - config_type: String, + pub config_type: String, + /// Ofset of the configurable field. + pub offset: u64, /// Value of the configurable field. - value: String, + pub value: String, } impl ConfigurableDeclaration { - pub fn new(config_type: String, value: String) -> Self { - Self { config_type, value } + pub fn new(config_type: String, offset: u64, value: String) -> Self { + Self { + config_type, + offset, + value, + } } } @@ -65,9 +71,13 @@ impl TryFrom for ConfigurableDeclarations { .ok_or_else(|| { anyhow::anyhow!("missing {config_name} type declaration in program abi.") })?; + let offset = configurable.offset; - let decl = - ConfigurableDeclaration::new(config_type_str.to_string(), "".to_string()); + let decl = ConfigurableDeclaration::new( + config_type_str.to_string(), + offset, + "".to_string(), + ); Ok((config_name.to_string(), decl)) }) From cce45e6e5d17bc5dd1ab68e4544c237d6865f74b Mon Sep 17 00:00:00 2001 From: Kaya Gokalp Date: Sat, 28 Sep 2024 02:48:28 -0700 Subject: [PATCH 5/9] get str working --- forc-plugins/forc-client/src/op/deploy.rs | 2 -- .../forc-client/src/util/configurables.rs | 6 +++-- forc-plugins/forc-client/src/util/encode.rs | 24 ++++++++----------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/forc-plugins/forc-client/src/op/deploy.rs b/forc-plugins/forc-client/src/op/deploy.rs index ef66667f884..36318d4d165 100644 --- a/forc-plugins/forc-client/src/op/deploy.rs +++ b/forc-plugins/forc-client/src/op/deploy.rs @@ -532,8 +532,6 @@ pub async fn deploy_pkg( let offset = decl.offset as usize; config_values_with_offset.push((offset, encoded_val)); } - // sort config decls based on the offsets. - config_values_with_offset.sort_by(|a, b| a.0.cmp(&b.0)); for (offset, val) in config_values_with_offset { bytecode[offset..offset + val.len()].copy_from_slice(&val); diff --git a/forc-plugins/forc-client/src/util/configurables.rs b/forc-plugins/forc-client/src/util/configurables.rs index 2332f805940..05af962d8b7 100644 --- a/forc-plugins/forc-client/src/util/configurables.rs +++ b/forc-plugins/forc-client/src/util/configurables.rs @@ -93,16 +93,17 @@ mod tests { #[test] fn test_configurable_decl() { - let decl = r#"{ "configType": "Type A", "value": "Value" }"#; + let decl = r#"{ "configType": "Type A", "offset": 120, "value": "Value" }"#; let decl_parsed: ConfigurableDeclaration = serde_json::from_str(decl).unwrap(); assert_eq!(decl_parsed.config_type, "Type A".to_string()); + assert_eq!(decl_parsed.offset, 120); assert_eq!(decl_parsed.value, "Value".to_string()) } #[test] fn test_configurable_decls() { - let decls = r#"{ "configName": {"configType": "Name", "value": "Value"} }"#; + let decls = r#"{ "configName": {"configType": "Name", "offset": 120, "value": "Value"} }"#; let decls_parsed: ConfigurableDeclarations = serde_json::from_str(decls).unwrap(); assert_eq!(decls_parsed.declarations.len(), 1); @@ -111,6 +112,7 @@ mod tests { assert_eq!(decl_parsed.0, "configName"); assert_eq!(decl_parsed.1.config_type, "Name"); + assert_eq!(decl_parsed.1.offset, 120); assert_eq!(decl_parsed.1.value, "Value"); } } diff --git a/forc-plugins/forc-client/src/util/encode.rs b/forc-plugins/forc-client/src/util/encode.rs index 817e1c89107..d467b6ac34b 100644 --- a/forc-plugins/forc-client/src/util/encode.rs +++ b/forc-plugins/forc-client/src/util/encode.rs @@ -1,8 +1,7 @@ use anyhow::Context; use fuel_abi_types::abi::full_program::FullTypeApplication; -use fuels::types::StaticStringToken; +use fuels::types::{Bits256, StaticStringToken, U256}; use std::str::FromStr; -use sway_types::u256::U256; /// A wrapper around fuels_core::types::Token, which enables serde de/serialization. #[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)] @@ -20,7 +19,7 @@ pub(crate) enum Type { B256, Bool, String, - StringSlice(usize), + StringArray(usize), } impl TryFrom<&FullTypeApplication> for Type { @@ -59,8 +58,7 @@ impl Token { } Type::U256 => { let v = value.parse::().context("Invalid value for U256")?; - let bytes = v.to_be_bytes(); - Ok(Token(fuels_core::types::Token::U256(bytes.into()))) + Ok(Token(fuels_core::types::Token::U256(v))) } Type::Bool => { let bool_val = value.parse::()?; @@ -71,18 +69,16 @@ impl Token { Ok(Token(fuels_core::types::Token::U128(u128_val))) } Type::B256 => { - let s = value.strip_prefix("0x").unwrap_or(value); - let mut res: [u8; 32] = Default::default(); - hex::decode_to_slice(&s, &mut res).context("Invalid value for B256")?; - Ok(Token(fuels_core::types::Token::B256(res))) + let bits256 = Bits256::from_hex_str(value)?; + Ok(Token(fuels_core::types::Token::B256(bits256.0))) } Type::String => { let s = value.to_string(); Ok(Token(fuels_core::types::Token::String(s))) } - Type::StringSlice(len) => { + Type::StringArray(len) => { let s = StaticStringToken::new(value.to_string(), Some(*len)); - Ok(Token(fuels_core::types::Token::StringSlice(s))) + Ok(Token(fuels_core::types::Token::StringArray(s))) } } } @@ -114,7 +110,7 @@ impl FromStr for Type { .as_str() .parse::() .map_err(|_| anyhow::anyhow!("Invalid number for string slice length"))?; - Ok(Type::StringSlice(len)) + Ok(Type::StringArray(len)) } else { anyhow::bail!("{other} type is not supported.") } @@ -145,7 +141,7 @@ mod tests { let bool_token = Token::from_type_and_value(&Type::Bool, "true").unwrap(); let string_token = Token::from_type_and_value(&Type::String, "Hello, World!").unwrap(); let string_slice_token = - Token::from_type_and_value(&Type::StringSlice(5), "Hello").unwrap(); + Token::from_type_and_value(&Type::StringArray(5), "Hello").unwrap(); let generated_tokens = [ u8_token, @@ -225,7 +221,7 @@ mod tests { Type::B256, Type::Bool, Type::String, - Type::StringSlice(5), + Type::StringArray(5), ]; assert_eq!(types, expected_types) } From 6e07703eb6591a0eee61be03c28a30258f8e6c15 Mon Sep 17 00:00:00 2001 From: Sophie Date: Sat, 28 Sep 2024 12:09:57 -0700 Subject: [PATCH 6/9] update tests --- .../forc-client/src/util/configurables.rs | 4 +- forc-plugins/forc-client/src/util/encode.rs | 80 ++++++++++++++----- 2 files changed, 64 insertions(+), 20 deletions(-) diff --git a/forc-plugins/forc-client/src/util/configurables.rs b/forc-plugins/forc-client/src/util/configurables.rs index 05af962d8b7..e14d3ff6a67 100644 --- a/forc-plugins/forc-client/src/util/configurables.rs +++ b/forc-plugins/forc-client/src/util/configurables.rs @@ -14,7 +14,7 @@ pub struct ConfigurableDeclarations { pub struct ConfigurableDeclaration { /// Name of the configurable field. pub config_type: String, - /// Ofset of the configurable field. + /// Offset of the configurable field. pub offset: u64, /// Value of the configurable field. pub value: String, @@ -108,7 +108,7 @@ mod tests { assert_eq!(decls_parsed.declarations.len(), 1); - let decl_parsed = decls_parsed.declarations.iter().nth(0).unwrap(); + let decl_parsed = decls_parsed.declarations.iter().next().unwrap(); assert_eq!(decl_parsed.0, "configName"); assert_eq!(decl_parsed.1.config_type, "Name"); diff --git a/forc-plugins/forc-client/src/util/encode.rs b/forc-plugins/forc-client/src/util/encode.rs index d467b6ac34b..80a74048aad 100644 --- a/forc-plugins/forc-client/src/util/encode.rs +++ b/forc-plugins/forc-client/src/util/encode.rs @@ -140,7 +140,7 @@ mod tests { .unwrap(); let bool_token = Token::from_type_and_value(&Type::Bool, "true").unwrap(); let string_token = Token::from_type_and_value(&Type::String, "Hello, World!").unwrap(); - let string_slice_token = + let string_array_token = Token::from_type_and_value(&Type::StringArray(5), "Hello").unwrap(); let generated_tokens = [ @@ -153,7 +153,7 @@ mod tests { b256_token, bool_token, string_token, - string_slice_token, + string_array_token, ]; let expected_tokens = [ Token(fuels_core::types::Token::U8(1)), @@ -169,7 +169,7 @@ mod tests { Token(fuels_core::types::Token::String( "Hello, World!".to_string(), )), - Token(fuels_core::types::Token::StringSlice( + Token(fuels_core::types::Token::StringArray( StaticStringToken::new("Hello".to_string(), Some(5)), )), ]; @@ -178,25 +178,69 @@ mod tests { } #[test] - #[should_panic] fn test_token_generation_fail_type_mismatch() { - Token::from_type_and_value(&Type::U8, "false").unwrap(); - } - - #[test] - #[should_panic(expected = "attempt to subtract with overflow")] - fn test_token_generation_fail_u256_out_of_range() { - Token::from_type_and_value( - &Type::U256, - "115792089237316195423570985008687907853269984665640564039457584007913129639937", - ) - .unwrap(); + for (arg_type, value, error_msg) in [ + (Type::U8, "false", "invalid digit found in string"), + (Type::U16, "false", "invalid digit found in string"), + (Type::U32, "false", "invalid digit found in string"), + (Type::U64, "false", "invalid digit found in string"), + (Type::U128, "false", "invalid digit found in string"), + (Type::U256, "false", "Invalid value for U256"), + (Type::B256, "false", "Odd number of digits"), + (Type::B256, "0x123", "Odd number of digits"), + ( + Type::Bool, + "Hello", + "provided string was not `true` or `false`", + ), + ] { + assert_eq!( + Token::from_type_and_value(&arg_type, value) + .expect_err("should panic") + .to_string(), + error_msg + ); + } } #[test] - #[should_panic(expected = "Invalid value for B256")] - fn test_token_generation_fail_b256_invalid_length() { - Token::from_type_and_value(&Type::B256, "0x123").unwrap(); + fn test_token_generation_fail_out_of_range() { + for (arg_type, value, error_msg) in [ + (Type::U8, "256", "number too large to fit in target type"), + (Type::U16, "65536", "number too large to fit in target type"), + ( + Type::U32, + "4294967296", + "number too large to fit in target type", + ), + ( + Type::U64, + "18446744073709551616", + "number too large to fit in target type", + ), + ( + Type::U128, + "340282366920938463463374607431768211456", + "number too large to fit in target type", + ), + ( + Type::U256, + "115792089237316195423570985008687907853269984665640564039457584007913129639936", + "Invalid value for U256", + ), + ( + Type::B256, + "0x10000000000000000000000000000000000000000000000000000000000000000", + "Odd number of digits", + ), + ] { + assert_eq!( + Token::from_type_and_value(&arg_type, value) + .expect_err("should panic") + .to_string(), + error_msg + ); + } } #[test] From c8bea826daafbfbd2b562c03555e896752fcb72e Mon Sep 17 00:00:00 2001 From: Sophie Date: Sat, 28 Sep 2024 13:13:20 -0700 Subject: [PATCH 7/9] move --configurable-override-file to build cmd --- Cargo.lock | 1 + forc-pkg/src/pkg.rs | 13 ++++++++++++ forc-plugins/forc-client/src/cmd/deploy.rs | 20 ++++++++++--------- forc-plugins/forc-client/src/op/deploy.rs | 16 +++------------ forc-plugins/forc-client/src/op/run/mod.rs | 1 + forc-plugins/forc-client/src/util/mod.rs | 1 - forc-util/Cargo.toml | 1 + .../util => forc-util/src}/configurables.rs | 0 forc-util/src/lib.rs | 1 + forc/src/cli/commands/test.rs | 1 + forc/src/cli/shared.rs | 3 +++ forc/src/ops/forc_build.rs | 1 + forc/src/ops/forc_contract_id.rs | 1 + forc/src/ops/forc_predicate_root.rs | 1 + test/src/e2e_vm_tests/harness.rs | 1 + 15 files changed, 39 insertions(+), 23 deletions(-) rename {forc-plugins/forc-client/src/util => forc-util/src}/configurables.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index b0c7e8122fe..b3cf2582ba4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2964,6 +2964,7 @@ dependencies = [ "dirs 5.0.1", "fd-lock", "forc-tracing 0.64.0", + "fuel-abi-types", "fuel-tx", "hex", "paste", diff --git a/forc-pkg/src/pkg.rs b/forc-pkg/src/pkg.rs index 2dbbc45f20d..4dec37de12b 100644 --- a/forc-pkg/src/pkg.rs +++ b/forc-pkg/src/pkg.rs @@ -11,6 +11,7 @@ use crate::{ use anyhow::{anyhow, bail, Context, Error, Result}; use byte_unit::{Byte, UnitType}; use forc_tracing::{println_action_green, println_warning}; +use forc_util::configurables::ConfigurableDeclarations; use forc_util::{ default_output_directory, find_file_name, kebab_to_snake_case, print_compiling, print_on_failure, print_warnings, @@ -266,6 +267,8 @@ pub struct PrintOpts { pub ir: PrintIr, /// Output build errors and warnings in reverse order. pub reverse_order: bool, + /// Generate a JSON file at the given path to use for overriding configurable slots. + pub configurable_override_file: Option, } #[derive(Default, Clone)] @@ -2149,6 +2152,7 @@ pub fn build_with_options(build_options: &BuildOpts) -> Result { build_target, member_filter, experimental, + print: print_opts, .. } = &build_options; @@ -2229,6 +2233,15 @@ pub fn build_with_options(build_options: &BuildOpts) -> Result { if let Some(outfile) = &debug_outfile { built_package.write_debug_info(outfile.as_ref())?; } + if let Some(path) = &print_opts.configurable_override_file { + if let ProgramABI::Fuel(program_abi) = &built_package.program_abi { + let config_decls = ConfigurableDeclarations::try_from(program_abi.clone())?; + let config_decls_str = serde_json::to_string_pretty(&config_decls)?; + fs::write(path, config_decls_str)?; + } else { + bail!("Only Fuel ABI configurable generation is supported"); + } + } built_package.write_output(minify, &pkg_manifest.project.name, &output_dir)?; built_workspace.push(Arc::new(built_package)); } diff --git a/forc-plugins/forc-client/src/cmd/deploy.rs b/forc-plugins/forc-client/src/cmd/deploy.rs index 828db886a4b..9cd17276041 100644 --- a/forc-plugins/forc-client/src/cmd/deploy.rs +++ b/forc-plugins/forc-client/src/cmd/deploy.rs @@ -84,19 +84,21 @@ pub struct Command { #[clap(long, verbatim_doc_comment, name = "JSON_FILE_PATH")] pub override_storage_slots: Option, + /// Override configurable slot initialization. + /// + /// By default, configurable slots are initialized with the values defined in the configurable block in + /// the sway code. You can override the initialization by providing the file path to a JSON file + /// containing the overridden values. + /// + /// Use forc build --generate_configurable_slots_file to generate the file. + #[clap(long, name = "JSON_FILE_PATH")] + pub override_configurable_slots: Option, + /// Disable the "new encoding" feature #[clap(long)] pub no_encoding_v1: bool, /// AWS KMS signer arn. If present forc-deploy will automatically use AWS KMS signer instead of forc-wallet. - #[clap(long)] + #[clap(long, name = "AWS_KMS_ARN")] pub aws_kms_signer: Option, - - /// Path to configurable override file. - #[clap(long)] - pub override_configurable_slots: Option, - - /// Generate configurable slots file from abi. - #[clap(long)] - pub generate_configurable_slots_file: Option, } diff --git a/forc-plugins/forc-client/src/op/deploy.rs b/forc-plugins/forc-client/src/op/deploy.rs index 36318d4d165..bcfc01b28d8 100644 --- a/forc-plugins/forc-client/src/op/deploy.rs +++ b/forc-plugins/forc-client/src/op/deploy.rs @@ -3,7 +3,6 @@ use crate::{ constants::TX_SUBMIT_TIMEOUT_MS, util::{ account::ForcClientAccount, - configurables::ConfigurableDeclarations, node_url::get_node_url, pkg::{built_pkgs, create_proxy_contract, update_proxy_address_in_manifest}, target::Target, @@ -17,7 +16,7 @@ use anyhow::{bail, Context, Result}; use forc_pkg::manifest::GenericManifestFile; use forc_pkg::{self as pkg, PackageManifestFile}; use forc_tracing::{println_action_green, println_warning}; -use forc_util::default_output_directory; +use forc_util::{configurables::ConfigurableDeclarations, default_output_directory}; use forc_wallet::utils::default_wallet_path; use fuel_core_client::client::types::{ChainInfo, TransactionStatus}; use fuel_core_client::client::FuelClient; @@ -39,14 +38,13 @@ use pkg::{manifest::build_profile::ExperimentalFlags, BuildProfile, BuiltPackage use serde::{Deserialize, Serialize}; use std::{ collections::BTreeMap, - fs, path::{Path, PathBuf}, str::FromStr, sync::Arc, time::Duration, }; +use sway_core::language::parsed::TreeType; use sway_core::BuildTarget; -use sway_core::{asm_generation::ProgramABI, language::parsed::TreeType}; /// Maximum contract size allowed for a single contract. If the target /// contract size is bigger than this amount, forc-deploy will automatically @@ -536,15 +534,6 @@ pub async fn deploy_pkg( for (offset, val) in config_values_with_offset { bytecode[offset..offset + val.len()].copy_from_slice(&val); } - } else if let Some(path) = &command.generate_configurable_slots_file { - let program_abi = &compiled.program_abi; - if let ProgramABI::Fuel(program_abi) = program_abi { - let config_decls = ConfigurableDeclarations::try_from(program_abi.clone())?; - let config_decls_str = serde_json::to_string(&config_decls)?; - fs::write(path, config_decls_str)?; - } else { - bail!("Only Fuel ABI configurable generation is supported"); - } } let contract = Contract::from(bytecode.clone()); @@ -682,6 +671,7 @@ fn build_opts_from_cmd(cmd: &cmd::Deploy) -> pkg::BuildOpts { bytecode_spans: false, ir: cmd.print.ir(), reverse_order: cmd.print.reverse_order, + configurable_override_file: cmd.print.configurable_override_file.clone(), }, time_phases: cmd.print.time_phases, metrics_outfile: cmd.print.metrics_outfile.clone(), diff --git a/forc-plugins/forc-client/src/op/run/mod.rs b/forc-plugins/forc-client/src/op/run/mod.rs index 5e58835fca0..3e55cacc848 100644 --- a/forc-plugins/forc-client/src/op/run/mod.rs +++ b/forc-plugins/forc-client/src/op/run/mod.rs @@ -257,6 +257,7 @@ fn build_opts_from_cmd(cmd: &cmd::Run) -> pkg::BuildOpts { bytecode_spans: false, ir: cmd.print.ir(), reverse_order: cmd.print.reverse_order, + configurable_override_file: cmd.print.configurable_override_file.clone(), }, minify: pkg::MinifyOpts { json_abi: cmd.minify.json_abi, diff --git a/forc-plugins/forc-client/src/util/mod.rs b/forc-plugins/forc-client/src/util/mod.rs index c5856270e4f..2e58fc41c79 100644 --- a/forc-plugins/forc-client/src/util/mod.rs +++ b/forc-plugins/forc-client/src/util/mod.rs @@ -1,6 +1,5 @@ pub mod account; pub mod aws; -pub mod configurables; pub(crate) mod encode; pub(crate) mod node_url; pub(crate) mod pkg; diff --git a/forc-util/Cargo.toml b/forc-util/Cargo.toml index 47e702cec70..97d96cabb65 100644 --- a/forc-util/Cargo.toml +++ b/forc-util/Cargo.toml @@ -16,6 +16,7 @@ clap = { workspace = true, features = ["cargo", "derive", "env"] } dirs.workspace = true fd-lock.workspace = true forc-tracing.workspace = true +fuel-abi-types.workspace = true fuel-tx = { workspace = true, features = ["serde"], optional = true } hex.workspace = true paste.workspace = true diff --git a/forc-plugins/forc-client/src/util/configurables.rs b/forc-util/src/configurables.rs similarity index 100% rename from forc-plugins/forc-client/src/util/configurables.rs rename to forc-util/src/configurables.rs diff --git a/forc-util/src/lib.rs b/forc-util/src/lib.rs index a630f741023..5eb318137c1 100644 --- a/forc-util/src/lib.rs +++ b/forc-util/src/lib.rs @@ -23,6 +23,7 @@ use sway_error::{ use sway_types::{LineCol, LineColRange, SourceEngine, Span}; use sway_utils::constants; +pub mod configurables; pub mod fs_locking; pub mod restricted; diff --git a/forc/src/cli/commands/test.rs b/forc/src/cli/commands/test.rs index 5ffa1c51d75..34d6378229a 100644 --- a/forc/src/cli/commands/test.rs +++ b/forc/src/cli/commands/test.rs @@ -243,6 +243,7 @@ fn opts_from_cmd(cmd: Command) -> forc_test::TestOpts { bytecode_spans: false, ir: cmd.build.print.ir(), reverse_order: cmd.build.print.reverse_order, + configurable_override_file: cmd.build.print.configurable_override_file.clone(), }, time_phases: cmd.build.print.time_phases, metrics_outfile: cmd.build.print.metrics_outfile, diff --git a/forc/src/cli/shared.rs b/forc/src/cli/shared.rs index 28b38465df3..54ce3e5b011 100644 --- a/forc/src/cli/shared.rs +++ b/forc/src/cli/shared.rs @@ -105,6 +105,9 @@ pub struct Print { /// Output compilation metrics into the specified file. #[clap(long)] pub metrics_outfile: Option, + /// Generate a JSON file at the given path to use for overriding configurable slots. + #[clap(long, name = "OUTPUT_PATH")] + pub configurable_override_file: Option, } impl Print { diff --git a/forc/src/ops/forc_build.rs b/forc/src/ops/forc_build.rs index 49c0c27f955..a890401d314 100644 --- a/forc/src/ops/forc_build.rs +++ b/forc/src/ops/forc_build.rs @@ -28,6 +28,7 @@ fn opts_from_cmd(cmd: BuildCommand) -> pkg::BuildOpts { bytecode_spans: false, ir: cmd.build.print.ir(), reverse_order: cmd.build.print.reverse_order, + configurable_override_file: cmd.build.print.configurable_override_file, }, time_phases: cmd.build.print.time_phases, metrics_outfile: cmd.build.print.metrics_outfile, diff --git a/forc/src/ops/forc_contract_id.rs b/forc/src/ops/forc_contract_id.rs index f81441637a5..9744936c55e 100644 --- a/forc/src/ops/forc_contract_id.rs +++ b/forc/src/ops/forc_contract_id.rs @@ -63,6 +63,7 @@ fn build_opts_from_cmd(cmd: &ContractIdCommand) -> pkg::BuildOpts { bytecode_spans: false, ir: cmd.print.ir(), reverse_order: cmd.print.reverse_order, + configurable_override_file: cmd.print.configurable_override_file.clone(), }, time_phases: cmd.print.time_phases, metrics_outfile: cmd.print.metrics_outfile.clone(), diff --git a/forc/src/ops/forc_predicate_root.rs b/forc/src/ops/forc_predicate_root.rs index 1381aec392e..d5cfb9f7bda 100644 --- a/forc/src/ops/forc_predicate_root.rs +++ b/forc/src/ops/forc_predicate_root.rs @@ -32,6 +32,7 @@ fn build_opts_from_cmd(cmd: PredicateRootCommand) -> pkg::BuildOpts { bytecode_spans: false, ir: cmd.print.ir(), reverse_order: cmd.print.reverse_order, + configurable_override_file: cmd.print.configurable_override_file.clone(), }, time_phases: cmd.print.time_phases, metrics_outfile: cmd.print.metrics_outfile, diff --git a/test/src/e2e_vm_tests/harness.rs b/test/src/e2e_vm_tests/harness.rs index e2a3236db07..b0fadcec377 100644 --- a/test/src/e2e_vm_tests/harness.rs +++ b/test/src/e2e_vm_tests/harness.rs @@ -288,6 +288,7 @@ pub(crate) async fn compile_to_bytes(file_name: &str, run_config: &RunConfig) -> bytecode_spans: run_config.print_bytecode, ir: run_config.print_ir.clone(), reverse_order: false, + configurable_override_file: None, }, pkg: forc_pkg::PkgOpts { path: Some(format!( From b08cc771980aff3d3552e5d7142091fc4411b3f5 Mon Sep 17 00:00:00 2001 From: Kaya Gokalp Date: Sat, 28 Sep 2024 16:31:50 -0700 Subject: [PATCH 8/9] test: add e2e test for checking config override --- forc-plugins/forc-client/src/cmd/deploy.rs | 2 +- .../contract_with_configurables/.gitignore | 2 + .../contract_with_configurables/Forc.toml | 9 ++ .../contract_with_configurables-abi.json | 136 ++++++++++++++++++ .../override-configurables.json | 42 ++++++ .../contract_with_configurables/src/main.sw | 22 +++ forc-plugins/forc-client/tests/deploy.rs | 76 +++++++++- 7 files changed, 286 insertions(+), 3 deletions(-) create mode 100644 forc-plugins/forc-client/test/data/contract_with_configurables/.gitignore create mode 100644 forc-plugins/forc-client/test/data/contract_with_configurables/Forc.toml create mode 100644 forc-plugins/forc-client/test/data/contract_with_configurables/contract_with_configurables-abi.json create mode 100644 forc-plugins/forc-client/test/data/contract_with_configurables/override-configurables.json create mode 100644 forc-plugins/forc-client/test/data/contract_with_configurables/src/main.sw diff --git a/forc-plugins/forc-client/src/cmd/deploy.rs b/forc-plugins/forc-client/src/cmd/deploy.rs index 9cd17276041..d1955951436 100644 --- a/forc-plugins/forc-client/src/cmd/deploy.rs +++ b/forc-plugins/forc-client/src/cmd/deploy.rs @@ -91,7 +91,7 @@ pub struct Command { /// containing the overridden values. /// /// Use forc build --generate_configurable_slots_file to generate the file. - #[clap(long, name = "JSON_FILE_PATH")] + #[clap(long, verbatim_doc_comment)] pub override_configurable_slots: Option, /// Disable the "new encoding" feature diff --git a/forc-plugins/forc-client/test/data/contract_with_configurables/.gitignore b/forc-plugins/forc-client/test/data/contract_with_configurables/.gitignore new file mode 100644 index 00000000000..77d3844f58c --- /dev/null +++ b/forc-plugins/forc-client/test/data/contract_with_configurables/.gitignore @@ -0,0 +1,2 @@ +out +target diff --git a/forc-plugins/forc-client/test/data/contract_with_configurables/Forc.toml b/forc-plugins/forc-client/test/data/contract_with_configurables/Forc.toml new file mode 100644 index 00000000000..a30b1f64490 --- /dev/null +++ b/forc-plugins/forc-client/test/data/contract_with_configurables/Forc.toml @@ -0,0 +1,9 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "contract_with_configurables" + +[dependencies] +std = { path = "../../../../../sway-lib-std/" } + diff --git a/forc-plugins/forc-client/test/data/contract_with_configurables/contract_with_configurables-abi.json b/forc-plugins/forc-client/test/data/contract_with_configurables/contract_with_configurables-abi.json new file mode 100644 index 00000000000..a7076a012bd --- /dev/null +++ b/forc-plugins/forc-client/test/data/contract_with_configurables/contract_with_configurables-abi.json @@ -0,0 +1,136 @@ +{ + "programType": "contract", + "specVersion": "1", + "encodingVersion": "1", + "concreteTypes": [ + { + "type": "(bool, u8, u16, u32, u64, u256, b256, str[4])", + "concreteTypeId": "bb312f998479ccee7bd35192143d546be33c1d6865813eca0b67e3feb9c30a09", + "metadataTypeId": 0 + }, + { + "type": "b256", + "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + }, + { + "type": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + }, + { + "type": "str[4]", + "concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a" + }, + { + "type": "u16", + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + }, + { + "type": "u256", + "concreteTypeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e" + }, + { + "type": "u32", + "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" + }, + { + "type": "u64", + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + }, + { + "type": "u8", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ], + "metadataTypes": [ + { + "type": "(_, _, _, _, _, _, _, _)", + "metadataTypeId": 0, + "components": [ + { + "name": "__tuple_element", + "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + }, + { + "name": "__tuple_element", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + }, + { + "name": "__tuple_element", + "typeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + }, + { + "name": "__tuple_element", + "typeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" + }, + { + "name": "__tuple_element", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + }, + { + "name": "__tuple_element", + "typeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e" + }, + { + "name": "__tuple_element", + "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + }, + { + "name": "__tuple_element", + "typeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a" + } + ] + } + ], + "functions": [ + { + "inputs": [], + "name": "return_configurables", + "output": "bb312f998479ccee7bd35192143d546be33c1d6865813eca0b67e3feb9c30a09", + "attributes": null + } + ], + "loggedTypes": [], + "messagesTypes": [], + "configurables": [ + { + "name": "BOOL", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "offset": 4528 + }, + { + "name": "U8", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "offset": 4600 + }, + { + "name": "U16", + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", + "offset": 4544 + }, + { + "name": "U32", + "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", + "offset": 4584 + }, + { + "name": "U64", + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", + "offset": 4592 + }, + { + "name": "U256", + "concreteTypeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e", + "offset": 4552 + }, + { + "name": "B256", + "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", + "offset": 4496 + }, + { + "name": "STR_4", + "concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a", + "offset": 4536 + } + ] +} \ No newline at end of file diff --git a/forc-plugins/forc-client/test/data/contract_with_configurables/override-configurables.json b/forc-plugins/forc-client/test/data/contract_with_configurables/override-configurables.json new file mode 100644 index 00000000000..2b5906a6350 --- /dev/null +++ b/forc-plugins/forc-client/test/data/contract_with_configurables/override-configurables.json @@ -0,0 +1,42 @@ +{ + "U16": { + "configType": "u16", + "offset": 4544, + "value": "2" + }, + "BOOL": { + "configType": "bool", + "offset": 4528, + "value": "false" + }, + "STR_4": { + "configType": "str[4]", + "offset": 4536, + "value": "test" + }, + "U256": { + "configType": "u256", + "offset": 4552, + "value": "0x0000000000000000000000000000000000000000000000000000000000000005" + }, + "U32": { + "configType": "u32", + "offset": 4584, + "value": "3" + }, + "U64": { + "configType": "u64", + "offset": 4592, + "value": "4" + }, + "B256": { + "configType": "b256", + "offset": 4496, + "value": "0x0606060606060606060606060606060606060606060606060606060606060606" + }, + "U8": { + "configType": "u8", + "offset": 4600, + "value": "1" + } +} diff --git a/forc-plugins/forc-client/test/data/contract_with_configurables/src/main.sw b/forc-plugins/forc-client/test/data/contract_with_configurables/src/main.sw new file mode 100644 index 00000000000..e1e0fabf564 --- /dev/null +++ b/forc-plugins/forc-client/test/data/contract_with_configurables/src/main.sw @@ -0,0 +1,22 @@ +contract; + +configurable { + BOOL: bool = true, + U8: u8 = 8, + U16: u16 = 16, + U32: u32 = 32, + U64: u64 = 63, + U256: u256 = 0x0000000000000000000000000000000000000000000000000000000000000008u256, + B256: b256 = 0x0101010101010101010101010101010101010101010101010101010101010101, + STR_4: str[4] = __to_str_array("fuel"), +} + +abi TestContract { + fn return_configurables() -> (bool, u8, u16, u32, u64, u256, b256, str[4]); +} + +impl TestContract for Contract { + fn return_configurables() -> (bool, u8, u16, u32, u64, u256, b256, str[4]) { + (BOOL, U8, U16, U32, U64, U256, B256, STR_4) + } +} diff --git a/forc-plugins/forc-client/tests/deploy.rs b/forc-plugins/forc-client/tests/deploy.rs index eb3c8e259a8..71310c70ae1 100644 --- a/forc-plugins/forc-client/tests/deploy.rs +++ b/forc-plugins/forc-client/tests/deploy.rs @@ -5,12 +5,12 @@ use forc_client::{ util::{account::ForcClientAccount, tx::update_proxy_contract_target}, NodeTarget, }; -use forc_pkg::manifest::Proxy; +use forc_pkg::{manifest::Proxy, BuildProfile}; use fuel_crypto::SecretKey; use fuel_tx::{ContractId, Salt}; use fuels::{ macros::abigen, - types::{transaction::TxPolicies, AsciiString, Bits256}, + types::{transaction::TxPolicies, AsciiString, Bits256, SizedAsciiString}, }; use fuels_accounts::{provider::Provider, wallet::WalletUnlocked, Account}; use portpicker::Port; @@ -793,3 +793,75 @@ async fn chunked_deploy_with_proxy_re_routes_call() { node.kill().unwrap(); } + +#[tokio::test] +async fn configurable_override() { + let (mut node, port) = run_node(); + let tmp_dir = tempdir().unwrap(); + let project_dir = test_data_path().join("contract_with_configurables"); + copy_dir(&project_dir, tmp_dir.path()).unwrap(); + patch_manifest_file_with_path_std(tmp_dir.path()).unwrap(); + + let pkg = Pkg { + path: Some(tmp_dir.path().display().to_string()), + ..Default::default() + }; + + let override_config_file = tmp_dir.path().join("override-configurables.json"); + + let node_url = format!("http://127.0.0.1:{}/v1/graphql", port); + let target = NodeTarget { + node_url: Some(node_url.clone()), + target: None, + testnet: false, + }; + let cmd = cmd::Deploy { + pkg, + salt: Some(vec![format!("{}", Salt::default())]), + node: target, + default_signer: true, + override_configurable_slots: Some(format!("{}", override_config_file.display())), + build_profile: BuildProfile::RELEASE.to_string(), + ..Default::default() + }; + let deployed_contract = deploy(cmd).await.unwrap().remove(0); + + let provider = Provider::connect(&node_url).await.unwrap(); + let secret_key = SecretKey::from_str(forc_client::constants::DEFAULT_PRIVATE_KEY).unwrap(); + let wallet_unlocked = WalletUnlocked::new_from_private_key(secret_key, Some(provider)); + let contract_id = deployed_contract.id; + abigen!(Contract( + name = "ContractWithConfigurables", + abi = "forc-plugins/forc-client/test/data/contract_with_configurables/contract_with_configurables-abi.json" + )); + + let instance = ContractWithConfigurables::new(contract_id, wallet_unlocked); + + let (b_val, u8_val, u16_val, u32_val, u64_val, u256_val, b256_val, str_arr_val) = instance + .methods() + .return_configurables() + .call() + .await + .unwrap() + .value; + + assert_eq!(b_val, false); + assert_eq!(u8_val, 1); + assert_eq!(u16_val, 2); + assert_eq!(u32_val, 3); + assert_eq!(u64_val, 4); + let expected_u256 = fuels::types::U256::from_str( + "0x0000000000000000000000000000000000000000000000000000000000000005", + ) + .unwrap(); + assert_eq!(u256_val, expected_u256); + let expected_b256 = fuels::types::Bits256::from_hex_str( + "0x0606060606060606060606060606060606060606060606060606060606060606", + ) + .unwrap(); + assert_eq!(b256_val, expected_b256); + let expected_str_arr = SizedAsciiString::new("test".to_string()).unwrap(); + assert_eq!(str_arr_val, expected_str_arr); + + node.kill().unwrap(); +} From f1f6a2f8572c9e7ed535421add2de1e4589ed506 Mon Sep 17 00:00:00 2001 From: Kaya Gokalp Date: Sat, 28 Sep 2024 16:39:31 -0700 Subject: [PATCH 9/9] clippy --- forc-plugins/forc-client/tests/deploy.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forc-plugins/forc-client/tests/deploy.rs b/forc-plugins/forc-client/tests/deploy.rs index 71310c70ae1..7f976664777 100644 --- a/forc-plugins/forc-client/tests/deploy.rs +++ b/forc-plugins/forc-client/tests/deploy.rs @@ -845,7 +845,7 @@ async fn configurable_override() { .unwrap() .value; - assert_eq!(b_val, false); + assert!(!b_val); assert_eq!(u8_val, 1); assert_eq!(u16_val, 2); assert_eq!(u32_val, 3);