From c6022f31d6fecf2e30e844f18f064067e33d89d3 Mon Sep 17 00:00:00 2001 From: hal3e Date: Tue, 6 Dec 2022 13:21:39 +0100 Subject: [PATCH 01/14] feat: add predicates abigen --- .github/workflows/ci.yml | 4 +- examples/predicates/src/lib.rs | 4 +- packages/fuels-abigen-macro/src/lib.rs | 49 ++-- packages/fuels-contract/src/predicate.rs | 7 +- packages/fuels-core/src/code_gen/abigen.rs | 226 +++++++++++++++--- .../fuels-core/src/code_gen/functions_gen.rs | 47 +++- packages/fuels-signers/src/wallet.rs | 5 +- packages/fuels/src/lib.rs | 2 +- packages/fuels/tests/predicates.rs | 161 ++++++++++++- .../predicates/predicate_struct/src/main.sw | 5 +- .../predicates/predicate_u32/src/main.sw | 5 +- 11 files changed, 427 insertions(+), 88 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b67fc92e7f..8f709728b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ env: FUEL_CORE_VERSION: 0.14.1 RUST_VERSION: 1.64.0 FORC_VERSION: 0.31.1 - FORC_PATCH_BRANCH: "" + FORC_PATCH_BRANCH: "mohammadfawaz/predicate_data" FORC_PATCH_REVISION: "" jobs: @@ -52,7 +52,7 @@ jobs: else if [[ -n $FORC_PATCH_BRANCH ]]; then cargo install forc forc-fmt --git https://github.com/FuelLabs/sway --branch $FORC_PATCH_BRANCH - else + else cargo install forc forc-fmt --git https://github.com/FuelLabs/sway --rev $FORC_PATCH_REVISION fi fi diff --git a/examples/predicates/src/lib.rs b/examples/predicates/src/lib.rs index f6c42523a5..a888eb888d 100644 --- a/examples/predicates/src/lib.rs +++ b/examples/predicates/src/lib.rs @@ -102,7 +102,7 @@ mod tests { amount_to_predicate, asset_id, receiver.address(), - Some(predicate_data), + predicate_data, TxParameters::default(), ) .await?; @@ -188,7 +188,7 @@ mod tests { amount_to_unlock, AssetId::default(), second_wallet.address(), - Some(predicate_data), + predicate_data, TxParameters::default(), ) .await?; diff --git a/packages/fuels-abigen-macro/src/lib.rs b/packages/fuels-abigen-macro/src/lib.rs index 1368c8bc3e..4241c3e423 100644 --- a/packages/fuels-abigen-macro/src/lib.rs +++ b/packages/fuels-abigen-macro/src/lib.rs @@ -1,4 +1,4 @@ -use fuels_core::code_gen::abigen::Abigen; +use fuels_core::code_gen::abigen::{Abigen, AbigenType}; use inflector::Inflector; use proc_macro::TokenStream; use proc_macro2::Span; @@ -13,11 +13,11 @@ use syn::{ /// Abigen proc macro definition and helper functions/types. #[proc_macro] pub fn abigen(input: TokenStream) -> TokenStream { - let args = parse_macro_input!(input as Spanned); + let args = parse_macro_input!(input as Spanned); - Abigen::new(&args.name, &args.abi) + Abigen::new(&args.name, &args.abi, AbigenType::Contract) .unwrap() - .expand_contract() + .expand() .unwrap() .into() } @@ -25,23 +25,35 @@ pub fn abigen(input: TokenStream) -> TokenStream { /// Abigen proc macro definition and helper functions/types for scripts #[proc_macro] pub fn script_abigen(input: TokenStream) -> TokenStream { - let args = parse_macro_input!(input as Spanned); + let args = parse_macro_input!(input as Spanned); - Abigen::new(&args.name, &args.abi) + Abigen::new(&args.name, &args.abi, AbigenType::Script) .unwrap() - .expand_script() + .expand() + .unwrap() + .into() +} + +/// Abigen proc macro definition and helper functions/types for scripts +#[proc_macro] +pub fn predicate_abigen(input: TokenStream) -> TokenStream { + let args = parse_macro_input!(input as Spanned); + + Abigen::new(&args.name, &args.abi, AbigenType::Predicate) + .unwrap() + .expand() .unwrap() .into() } #[proc_macro] pub fn wasm_abigen(input: TokenStream) -> TokenStream { - let args = parse_macro_input!(input as Spanned); + let args = parse_macro_input!(input as Spanned); - Abigen::new(&args.name, &args.abi) + Abigen::new(&args.name, &args.abi, AbigenType::Contract) .unwrap() .no_std() - .expand_contract() + .expand() .unwrap() .into() } @@ -86,11 +98,12 @@ pub fn setup_contract_test(input: TokenStream) -> TokenStream { let storage_path = compiled_file_path("-storage_slots.json", "the storage slots file"); let contract_struct_name = args.instance_name.to_class_case(); - let mut abigen_token_stream: TokenStream = Abigen::new(&contract_struct_name, abi_path) - .unwrap() - .expand_contract() - .unwrap() - .into(); + let mut abigen_token_stream: TokenStream = + Abigen::new(&contract_struct_name, abi_path, AbigenType::Contract) + .unwrap() + .expand() + .unwrap() + .into(); // Generate random salt for contract deployment let mut rng = StdRng::from_entropy(); @@ -180,12 +193,12 @@ impl Deref for Spanned { /// Contract procedural macro arguments. #[cfg_attr(test, derive(Debug, Eq, PartialEq))] -pub(crate) struct ContractArgs { +pub(crate) struct AbigenArgs { name: String, abi: String, } -impl ParseInner for ContractArgs { +impl ParseInner for AbigenArgs { fn spanned_parse(input: ParseStream) -> ParseResult<(Span, Self)> { // read the contract name let name = input.parse::()?.to_string(); @@ -201,7 +214,7 @@ impl ParseInner for ContractArgs { input.parse::()?; } - Ok((span, ContractArgs { name, abi })) + Ok((span, AbigenArgs { name, abi })) } } diff --git a/packages/fuels-contract/src/predicate.rs b/packages/fuels-contract/src/predicate.rs index a75a68134b..993a545195 100644 --- a/packages/fuels-contract/src/predicate.rs +++ b/packages/fuels-contract/src/predicate.rs @@ -1,9 +1,6 @@ use fuel_gql_client::fuel_tx::{Address, Contract}; -use fuels_core::abi_encoder::ABIEncoder; -use fuels_core::Tokenizable; -use fuels_types::errors::Error; - -use fuels_types::bech32::Bech32Address; +use fuels_core::{abi_encoder::ABIEncoder, Tokenizable}; +use fuels_types::{bech32::Bech32Address, errors::Error}; pub struct Predicate { address: Bech32Address, diff --git a/packages/fuels-core/src/code_gen/abigen.rs b/packages/fuels-core/src/code_gen/abigen.rs index 4536a0db96..159d996ab2 100644 --- a/packages/fuels-core/src/code_gen/abigen.rs +++ b/packages/fuels-core/src/code_gen/abigen.rs @@ -5,7 +5,10 @@ use super::{ }; use crate::{ - code_gen::{bindings::ContractBindings, functions_gen::generate_script_main_function}, + code_gen::{ + bindings::ContractBindings, + functions_gen::{generate_predicate_encode_function, generate_script_main_function}, + }, source::Source, utils::ident, }; @@ -19,6 +22,12 @@ use proc_macro2::TokenStream; use quote::quote; use std::collections::HashMap; +pub enum AbigenType { + Contract, + Script, + Predicate, +} + pub struct Abigen { /// Format the code using a locally installed copy of `rustfmt`. rustfmt: bool, @@ -30,11 +39,17 @@ pub struct Abigen { abi: ProgramABI, types: HashMap, + + abigen_type: AbigenType, } impl Abigen { /// Creates a new contract with the given ABI JSON source. - pub fn new>(contract_name: &str, abi_source: S) -> Result { + pub fn new>( + instance_name: &str, + abi_source: S, + abigen_type: AbigenType, + ) -> Result { let source = Source::parse(abi_source).expect("failed to parse JSON ABI"); let json_abi_str = source.get().expect("failed to parse JSON ABI from string"); @@ -43,9 +58,10 @@ impl Abigen { Ok(Self { types: Abigen::get_types(&parsed_abi), abi: parsed_abi, - name: contract_name.to_string(), + name: instance_name.to_string(), rustfmt: true, no_std: false, + abigen_type, }) } @@ -62,6 +78,14 @@ impl Abigen { Ok(ContractBindings { tokens, rustfmt }) } + pub fn expand(&self) -> Result { + match self.abigen_type { + AbigenType::Contract => self.expand_contract(), + AbigenType::Script => self.expand_script(), + AbigenType::Predicate => self.expand_predicate(), + } + } + /// Entry point of the Abigen's expansion logic. /// The high-level goal of this function is to expand* a contract defined as a JSON ABI /// into type-safe bindings of that contract that can be used after it is brought into @@ -83,7 +107,7 @@ impl Abigen { let resolved_logs = self.resolve_logs(); let log_id_param_type_pairs = generate_log_id_param_type_pairs(&resolved_logs); - let includes = self.includes(false); + let includes = self.includes(); let code = if self.no_std { quote! {} @@ -166,7 +190,7 @@ impl Abigen { let name = ident(&self.name); let name_mod = ident(&format!("{}_mod", self.name.to_string().to_snake_case())); - let includes = self.includes(true); + let includes = self.includes(); let resolved_logs = self.resolve_logs(); let log_id_param_type_pairs = generate_log_id_param_type_pairs(&resolved_logs); @@ -217,52 +241,161 @@ impl Abigen { }) } + /// Expand a predicate into type-safe Rust bindings based on its ABI. See `expand_contract` for + /// more details. + pub fn expand_predicate(&self) -> Result { + let name = ident(&self.name); + let name_mod = ident(&format!("{}_mod", self.name.to_string().to_snake_case())); + + let includes = self.includes(); + + let encode_data_function = self.predicate_function()?; + let code = if self.no_std { + quote! {} + } else { + quote! { + #[derive(Debug)] + pub struct #name{ + address: Bech32Address, + code: Vec, + data: Vec + } + + impl #name { + pub fn new(code: Vec) -> Self { + let address: Address = (*Contract::root_from_code(&code)).into(); + Self { + address: address.into(), + code, + data: vec![] + } + } + + pub fn load_from(file_path: &str) -> Result { + Ok(Self::new(std::fs::read(file_path)?)) + } + + pub fn address(&self) -> &Bech32Address { + &self.address + } + + pub fn code(&self) -> Vec { + self.code.clone() + } + + pub fn data(&self) -> Vec { + self.data.clone() + } + + pub async fn receive_from_wallet(&self, wallet: &WalletUnlocked, amount:u64, asset_id: AssetId, tx_parameters: Option) -> Result<(String, Vec), SDKError> { + let tx_parameters = tx_parameters.unwrap_or(TxParameters::default()); + wallet + .transfer( + self.address(), + amount, + asset_id, + tx_parameters + ) + .await + } + + pub async fn spend_to_wallet(&self, wallet: &WalletUnlocked, amount:u64, asset_id: AssetId, tx_parameters: Option) -> Result, SDKError> { + let tx_parameters = tx_parameters.unwrap_or(TxParameters::default()); + wallet + .receive_from_predicate( + self.address(), + self.code(), + amount, + asset_id, + self.data(), + tx_parameters + ) + .await + } + + + + + #encode_data_function + } + } + }; + + let abi_structs = self.abi_structs()?; + let abi_enums = self.abi_enums()?; + Ok(quote! { + pub use #name_mod::*; + + #[allow(clippy::too_many_arguments)] + pub mod #name_mod { + #![allow(clippy::enum_variant_names)] + #![allow(dead_code)] + + #includes + + #code + + #abi_structs + #abi_enums + + } + }) + } + /// Generates the includes necessary for the abigen. - fn includes(&self, is_script: bool) -> TokenStream { + fn includes(&self) -> TokenStream { if self.no_std { quote! { use alloc::{vec, vec::Vec}; - use fuels_core::code_gen::function_selector::resolve_fn_selector; - use fuels_core::types::*; - use fuels_core::{EnumSelector, Parameterize, Tokenizable, Token, Identity, try_from_bytes}; - use fuels_types::enum_variants::EnumVariants; - use fuels_types::errors::Error as SDKError; - use fuels_types::param_types::ParamType; + use fuels_core::{ + code_gen::function_selector::resolve_fn_selector, try_from_bytes, types::*, + EnumSelector, Identity, Parameterize, Token, Tokenizable, + }; + use fuels_types::{ + enum_variants::EnumVariants, errors::Error as SDKError, + param_types::ParamType, + }; } } else { - let specific_includes = if is_script { - quote! { - use fuels::contract::script_calls::{ScriptCallHandler, ScriptCall}; - use fuels::core::abi_encoder::ABIEncoder; - use fuels::core::parameters::TxParameters; - use std::marker::PhantomData; - } - } else { - quote! { + let specific_includes = match self.abigen_type { + AbigenType::Contract => quote! { use fuels::contract::contract::{ - Contract, - ContractCallHandler, - get_decoded_output + get_decoded_output, Contract, ContractCallHandler, + }; + use fuels::core::{ + abi_decoder::ABIDecoder, code_gen::function_selector::resolve_fn_selector, + EnumSelector, Identity, StringToken, }; - use fuels::core::abi_decoder::ABIDecoder; - use fuels::core::code_gen::function_selector::resolve_fn_selector; - use fuels::core::{EnumSelector, StringToken, Identity}; use fuels::types::ResolvedLog; use std::str::FromStr; - } + }, + AbigenType::Script => quote! { + use fuels::{ + contract::script_calls::{ScriptCall, ScriptCallHandler}, + core::{abi_encoder::ABIEncoder, parameters::TxParameters}, + }; + use std::marker::PhantomData; + }, + AbigenType::Predicate => quote! { + use fuels::{ + core::{abi_encoder::ABIEncoder, parameters::TxParameters}, + tx::{Contract, AssetId}, + }; + }, }; quote! { use fuels::contract::logs::LogDecoder; - use fuels::core::types::*; - use fuels::core::{Tokenizable, Token, Parameterize, try_from_bytes}; + use fuels::core::{ + code_gen::get_logs_hashmap, try_from_bytes, types::*, Parameterize, Token, + Tokenizable, + }; use fuels::signers::WalletUnlocked; - use fuels::types::enum_variants::EnumVariants; - use fuels::types::errors::Error as SDKError; - use fuels::types::param_types::ParamType; - use fuels::tx::{ContractId, Address, Receipt}; - use fuels::types::bech32::Bech32ContractId; - use std::collections::{HashSet, HashMap}; - use fuels::core::code_gen::get_logs_hashmap; + use fuels::tx::{Address, ContractId, Receipt}; + use fuels::types::{ + bech32::{Bech32ContractId, Bech32Address}, enum_variants::EnumVariants, + errors::Error as SDKError, param_types::ParamType, + }; + use std::collections::{HashMap, HashSet}; #specific_includes } } @@ -296,6 +429,25 @@ impl Abigen { } } + pub fn predicate_function(&self) -> Result { + let functions = self + .abi + .functions + .iter() + .filter(|function| function.name == "main") + .collect::>(); + + if let [main_function] = functions.as_slice() { + let tokenized_function = + generate_predicate_encode_function(main_function, &self.types)?; + Ok(quote! { #tokenized_function }) + } else { + Err(Error::CompilationError( + "The script must have one function named `main` to compile!".to_string(), + )) + } + } + fn abi_structs(&self) -> Result { let mut structs = TokenStream::new(); diff --git a/packages/fuels-core/src/code_gen/functions_gen.rs b/packages/fuels-core/src/code_gen/functions_gen.rs index faa6edb42a..b69f81c0ea 100644 --- a/packages/fuels-core/src/code_gen/functions_gen.rs +++ b/packages/fuels-core/src/code_gen/functions_gen.rs @@ -80,12 +80,6 @@ pub fn generate_script_main_function( main_function_abi: &ABIFunction, types: &HashMap, ) -> Result { - if main_function_abi.name != "main" { - return Err(Error::InvalidData( - "Script `main` function name can not be different from `main`".into(), - )); - } - let output_type_resolved = resolve_fn_output_type(main_function_abi, types)?; let output_params = single_param_type_call(&output_type_resolved); let output_type: TokenStream = output_type_resolved.into(); @@ -126,6 +120,47 @@ pub fn generate_script_main_function( }) } +pub fn generate_predicate_encode_function( + main_function_abi: &ABIFunction, + types: &HashMap, +) -> Result { + let args = function_arguments(main_function_abi, types)?; + + // TODO(hal3e): enable support for vector inputs + if args.iter().any(|c| c.field_type.uses_vectors()) { + return Err(Error::CompilationError( + "Predicate main function contains a vector in its argument types. This currently isn't supported." + .to_string(), + )); + } + let arg_names = args.iter().map(|component| &component.field_name); + + let arg_declarations = args.iter().map(|component| { + let name = &component.field_name; + let field_type: TokenStream = (&component.field_type).into(); + quote! { #name: #field_type } + }); + + let doc = expand_doc("Run the predicate's encode function with the provided arguments"); + + let name = safe_ident("encode_data"); + + Ok(quote! { + #doc + pub fn #name(&self #(,#arg_declarations)*) -> Self{ + let arg_name_tokens = [#(#arg_names.into_token()),*]; + let data = ABIEncoder::encode(&arg_name_tokens) + .expect("Cannot encode predicate data").resolve(0); + + Self { + address: self.address.clone(), + code: self.code.clone(), + data + } + } + }) +} + fn resolve_fn_output_type( function: &ABIFunction, types: &HashMap, diff --git a/packages/fuels-signers/src/wallet.rs b/packages/fuels-signers/src/wallet.rs index c39c1c3541..8d004fe466 100644 --- a/packages/fuels-signers/src/wallet.rs +++ b/packages/fuels-signers/src/wallet.rs @@ -664,7 +664,7 @@ impl WalletUnlocked { amount: u64, asset_id: AssetId, to: &Bech32Address, - data: Option>, + predicate_data: Vec, tx_parameters: TxParameters, ) -> Result, Error> { let spendable_predicate_resources = self @@ -679,7 +679,6 @@ impl WalletUnlocked { .map(|resource| resource.amount()) .sum(); - let predicate_data = data.unwrap_or_default(); let inputs = spendable_predicate_resources .into_iter() .map(|resource| match resource { @@ -748,7 +747,7 @@ impl WalletUnlocked { predicate_code: Vec, amount: u64, asset_id: AssetId, - predicate_data: Option>, + predicate_data: Vec, tx_parameters: TxParameters, ) -> Result, Error> { self.spend_predicate( diff --git a/packages/fuels/src/lib.rs b/packages/fuels/src/lib.rs index 11a7392813..f5afc1799b 100644 --- a/packages/fuels/src/lib.rs +++ b/packages/fuels/src/lib.rs @@ -74,7 +74,7 @@ pub mod prelude { pub use super::core::Identity; pub use super::core::{Token, Tokenizable}; pub use super::fuel_node::*; - pub use super::fuels_abigen::{abigen, script_abigen, setup_contract_test}; + pub use super::fuels_abigen::{abigen, predicate_abigen, script_abigen, setup_contract_test}; pub use super::signers::provider::*; pub use super::signers::{wallet::generate_mnemonic_phrase, Signer, Wallet, WalletUnlocked}; pub use super::test_helpers::Config; diff --git a/packages/fuels/tests/predicates.rs b/packages/fuels/tests/predicates.rs index f28ff95d46..0ca91fc4e9 100644 --- a/packages/fuels/tests/predicates.rs +++ b/packages/fuels/tests/predicates.rs @@ -2,6 +2,27 @@ use fuels::core::abi_encoder::ABIEncoder; use fuels::prelude::*; use std::str::FromStr; +async fn setup_predicate_test2( + num_coins: u64, + coin_amount: u64, +) -> Result<(WalletUnlocked, WalletUnlocked, AssetId), Error> { + let mut wallets = launch_custom_provider_and_get_wallets( + WalletsConfig::new(Some(2), Some(num_coins), Some(coin_amount)), + Some(Config { + utxo_validation: true, + ..Config::local_node() + }), + None, + ) + .await; + + let sender = wallets.pop().unwrap(); + let receiver = wallets.pop().unwrap(); + let asset_id = AssetId::default(); + + Ok((sender, receiver, asset_id)) +} + async fn setup_predicate_test( file_path: &str, num_coins: u64, @@ -57,7 +78,7 @@ async fn can_call_no_arg_predicate_returns_true() -> Result<(), Error> { predicate.code(), amount_to_predicate, asset_id, - None, + vec![], TxParameters::default(), ) .await?; @@ -108,7 +129,7 @@ async fn can_call_no_arg_predicate_returns_false() -> Result<(), Error> { predicate.code(), amount_to_predicate, asset_id, - None, + vec![], TxParameters::default(), ) .await @@ -123,6 +144,7 @@ async fn can_call_no_arg_predicate_returns_false() -> Result<(), Error> { .get_asset_balance(predicate.address(), asset_id) .await?; assert_eq!(predicate_balance, amount_to_predicate); + Ok(()) } @@ -161,7 +183,7 @@ async fn can_call_predicate_with_u32_data() -> Result<(), Error> { predicate.code(), amount_to_predicate, asset_id, - Some(predicate_data), + predicate_data, TxParameters::default(), ) .await @@ -187,7 +209,7 @@ async fn can_call_predicate_with_u32_data() -> Result<(), Error> { predicate.code(), amount_to_predicate, asset_id, - Some(predicate_data), + predicate_data, TxParameters::default(), ) .await?; @@ -242,7 +264,7 @@ async fn can_call_predicate_with_address_data() -> Result<(), Error> { predicate.code(), amount_to_predicate, asset_id, - Some(predicate_data), + predicate_data, TxParameters::default(), ) .await?; @@ -297,7 +319,7 @@ async fn can_call_predicate_with_struct_data() -> Result<(), Error> { predicate.code(), amount_to_predicate, asset_id, - Some(predicate_data), + predicate_data, TxParameters::default(), ) .await @@ -323,7 +345,7 @@ async fn can_call_predicate_with_struct_data() -> Result<(), Error> { predicate.code(), amount_to_predicate, asset_id, - Some(predicate_data), + predicate_data, TxParameters::default(), ) .await?; @@ -383,7 +405,7 @@ async fn predicate_with_multiple_coins() -> Result<(), Error> { predicate.code(), amount_to_predicate, asset_id, - None, + vec![], TxParameters::new(Some(1), None, None), ) .await?; @@ -402,3 +424,126 @@ async fn predicate_with_multiple_coins() -> Result<(), Error> { assert_eq!(predicate_balance, 10); Ok(()) } + +async fn assert_address_balance( + address: &Bech32Address, + provider: &Provider, + asset_id: AssetId, + amount: u64, +) { + let balance = provider + .get_asset_balance(address, asset_id) + .await + .expect("Could not retrieve balnace"); + assert_eq!(balance, amount); +} + +#[tokio::test] +async fn can_call_predicate_with_u32_data_new() -> Result<(), Error> { + let initial_balance = 16; + let (sender, receiver, asset_id) = setup_predicate_test2(1, initial_balance).await?; + let provider = receiver.get_provider()?; + let amount = 8; + + predicate_abigen!( + MyPredicate, + "packages/fuels/tests/predicates/predicate_u32/out/debug/predicate_u32-abi.json" + ); + + let predicate = + MyPredicate::load_from("tests/predicates/predicate_u32/out/debug/predicate_u32.bin")?; + + predicate + .receive_from_wallet(&sender, amount, asset_id, None) + .await?; + + // The provider has received the funds + assert_address_balance(predicate.address(), provider, asset_id, amount).await; + + // Run predicate with wrong data + predicate + .encode_data(1077) + .spend_to_wallet(&receiver, amount, asset_id, None) + .await + .expect_err("Should error"); + + // No funds were transferred + assert_address_balance(receiver.address(), provider, asset_id, initial_balance).await; + + predicate + .encode_data(1078) + .spend_to_wallet(&receiver, amount, asset_id, None) + .await?; + + // The provider has spent the funds + assert_address_balance(predicate.address(), provider, asset_id, 0).await; + + // No funds were transfered + assert_address_balance( + receiver.address(), + provider, + asset_id, + initial_balance + amount, + ) + .await; + + Ok(()) +} + +#[tokio::test] +async fn can_call_predicate_with_struct_data_new() -> Result<(), Error> { + let initial_balance = 16; + let (sender, receiver, asset_id) = setup_predicate_test2(1, initial_balance).await?; + let provider = receiver.get_provider()?; + let amount = 8; + + predicate_abigen!( + MyPredicate, + "packages/fuels/tests/predicates/predicate_struct/out/debug/predicate_struct-abi.json" + ); + + let predicate = + MyPredicate::load_from("tests/predicates/predicate_struct/out/debug/predicate_struct.bin")?; + + predicate + .receive_from_wallet(&sender, amount, asset_id, None) + .await?; + + // The provider has received the funds + assert_address_balance(predicate.address(), provider, asset_id, amount).await; + + // Run predicate with wrong data + predicate + .encode_data(Validation { + has_account: false, + total_complete: 10, + }) + .spend_to_wallet(&receiver, amount, asset_id, None) + .await + .expect_err("Should error"); + + // No funds were transferred + assert_address_balance(receiver.address(), provider, asset_id, initial_balance).await; + + predicate + .encode_data(Validation { + has_account: true, + total_complete: 100, + }) + .spend_to_wallet(&receiver, amount, asset_id, None) + .await?; + + // The provider has spent the funds + assert_address_balance(predicate.address(), provider, asset_id, 0).await; + + // No funds were transfered + assert_address_balance( + receiver.address(), + provider, + asset_id, + initial_balance + amount, + ) + .await; + + Ok(()) +} diff --git a/packages/fuels/tests/predicates/predicate_struct/src/main.sw b/packages/fuels/tests/predicates/predicate_struct/src/main.sw index 85d63c178a..349dc4f2d6 100644 --- a/packages/fuels/tests/predicates/predicate_struct/src/main.sw +++ b/packages/fuels/tests/predicates/predicate_struct/src/main.sw @@ -7,10 +7,9 @@ struct Validation { total_complete: u64, } -fn main() -> bool { - let received: Validation = input_predicate_data(0); +fn main(input: Validation) -> bool { let expected_has_account: bool = true; let expected_total_complete: u64 = 100; - received.has_account == expected_has_account && received.total_complete == expected_total_complete + input.has_account == expected_has_account && input.total_complete == expected_total_complete } diff --git a/packages/fuels/tests/predicates/predicate_u32/src/main.sw b/packages/fuels/tests/predicates/predicate_u32/src/main.sw index bf0b0a6ae1..f019dc8198 100644 --- a/packages/fuels/tests/predicates/predicate_u32/src/main.sw +++ b/packages/fuels/tests/predicates/predicate_u32/src/main.sw @@ -2,9 +2,8 @@ predicate; use std::inputs::input_predicate_data; -fn main() -> bool { - let received: u32 = input_predicate_data(0); +fn main(input: u32) -> bool { let expected: u32 = 1078; - received == expected + input == expected } From 26e3c0db372e1c1b03157f60937f3f5f0cb6388a Mon Sep 17 00:00:00 2001 From: hal3e Date: Wed, 7 Dec 2022 11:05:32 +0100 Subject: [PATCH 02/14] fix comment --- packages/fuels/tests/predicates.rs | 4 ++-- packages/fuels/tests/predicates/predicate_struct/src/main.sw | 2 -- packages/fuels/tests/predicates/predicate_u32/src/main.sw | 2 -- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/fuels/tests/predicates.rs b/packages/fuels/tests/predicates.rs index 0ca91fc4e9..cf9dd64bf3 100644 --- a/packages/fuels/tests/predicates.rs +++ b/packages/fuels/tests/predicates.rs @@ -478,7 +478,7 @@ async fn can_call_predicate_with_u32_data_new() -> Result<(), Error> { // The provider has spent the funds assert_address_balance(predicate.address(), provider, asset_id, 0).await; - // No funds were transfered + // Funds were transferred assert_address_balance( receiver.address(), provider, @@ -536,7 +536,7 @@ async fn can_call_predicate_with_struct_data_new() -> Result<(), Error> { // The provider has spent the funds assert_address_balance(predicate.address(), provider, asset_id, 0).await; - // No funds were transfered + // Funds were transferred assert_address_balance( receiver.address(), provider, diff --git a/packages/fuels/tests/predicates/predicate_struct/src/main.sw b/packages/fuels/tests/predicates/predicate_struct/src/main.sw index 349dc4f2d6..c78baa39dc 100644 --- a/packages/fuels/tests/predicates/predicate_struct/src/main.sw +++ b/packages/fuels/tests/predicates/predicate_struct/src/main.sw @@ -1,7 +1,5 @@ predicate; -use std::inputs::input_predicate_data; - struct Validation { has_account: bool, total_complete: u64, diff --git a/packages/fuels/tests/predicates/predicate_u32/src/main.sw b/packages/fuels/tests/predicates/predicate_u32/src/main.sw index f019dc8198..10626c0f72 100644 --- a/packages/fuels/tests/predicates/predicate_u32/src/main.sw +++ b/packages/fuels/tests/predicates/predicate_u32/src/main.sw @@ -1,7 +1,5 @@ predicate; -use std::inputs::input_predicate_data; - fn main(input: u32) -> bool { let expected: u32 = 1078; From 1cbacf65b7d81e4edd619bde44fb5f33bea0dafa Mon Sep 17 00:00:00 2001 From: hal3e Date: Sat, 10 Dec 2022 12:09:09 +0100 Subject: [PATCH 03/14] use UnresolvedBytes and calculate the offsets --- .../src/contract_calls_utils.rs | 10 + packages/fuels-core/src/code_gen/abigen.rs | 25 +- .../fuels-core/src/code_gen/functions_gen.rs | 19 +- packages/fuels-signers/src/wallet.rs | 42 +- packages/fuels/tests/predicates.rs | 988 ++++++++++-------- .../predicate_vector}/Forc.toml | 2 +- .../predicates/predicate_vector/src/main.sw | 6 + .../scripts/script_tuple_argument/src/main.sw | 24 - 8 files changed, 598 insertions(+), 518 deletions(-) rename packages/fuels/tests/{scripts/script_tuple_argument => predicates/predicate_vector}/Forc.toml (77%) create mode 100644 packages/fuels/tests/predicates/predicate_vector/src/main.sw delete mode 100644 packages/fuels/tests/scripts/script_tuple_argument/src/main.sw diff --git a/packages/fuels-contract/src/contract_calls_utils.rs b/packages/fuels-contract/src/contract_calls_utils.rs index 2a3c3c219e..a78caa303d 100644 --- a/packages/fuels-contract/src/contract_calls_utils.rs +++ b/packages/fuels-contract/src/contract_calls_utils.rs @@ -264,6 +264,16 @@ pub fn get_base_script_offset(consensus_parameters: &ConsensusParameters) -> usi consensus_parameters.tx_offset() + fuel_tx::Script::script_offset_static() } +/// Gets the base offset for a predicate. The offset depends on the `max_inputs` +/// field of the `ConsensusParameters` and the static offset of a transfer script +pub fn get_predicate_offset(consensus_parameters: &ConsensusParameters) -> usize { + // Opcode::LEN is a placeholder for the RET instruction which is + // added for transfer transactions + let opcode_len = Opcode::LEN; + + get_base_script_offset(consensus_parameters) + padded_len_usize(opcode_len) +} + /// Calculates the length of the script based on the number of contract calls it /// has to make and returns the offset at which the script data begins pub(crate) fn get_data_offset( diff --git a/packages/fuels-core/src/code_gen/abigen.rs b/packages/fuels-core/src/code_gen/abigen.rs index 159d996ab2..9a47d5858f 100644 --- a/packages/fuels-core/src/code_gen/abigen.rs +++ b/packages/fuels-core/src/code_gen/abigen.rs @@ -258,7 +258,7 @@ impl Abigen { pub struct #name{ address: Bech32Address, code: Vec, - data: Vec + data: UnresolvedBytes } impl #name { @@ -267,7 +267,7 @@ impl Abigen { Self { address: address.into(), code, - data: vec![] + data: UnresolvedBytes::new() } } @@ -283,10 +283,16 @@ impl Abigen { self.code.clone() } - pub fn data(&self) -> Vec { + pub fn data(&self) -> UnresolvedBytes { self.data.clone() } + /// Compute the predicate data by calculating the predicate offset and resolving the encoded arguments + async fn get_offset(&self, provider: &Provider) -> Result { + let consensus_parameters = provider.consensus_parameters().await?; + Ok(get_predicate_offset(&consensus_parameters) as u64) + } + pub async fn receive_from_wallet(&self, wallet: &WalletUnlocked, amount:u64, asset_id: AssetId, tx_parameters: Option) -> Result<(String, Vec), SDKError> { let tx_parameters = tx_parameters.unwrap_or(TxParameters::default()); wallet @@ -307,15 +313,13 @@ impl Abigen { self.code(), amount, asset_id, - self.data(), - tx_parameters + self.data.clone(), + tx_parameters, + self.get_offset(wallet.get_provider()?).await? ) .await } - - - #encode_data_function } } @@ -378,9 +382,12 @@ impl Abigen { }, AbigenType::Predicate => quote! { use fuels::{ - core::{abi_encoder::ABIEncoder, parameters::TxParameters}, + core::{abi_encoder::{ABIEncoder, UnresolvedBytes}, parameters::TxParameters}, + contract::contract_calls_utils::get_predicate_offset, tx::{Contract, AssetId}, + signers::provider::Provider }; + use fuel_gql_client:: fuel_types::bytes::padded_len_usize; }, }; quote! { diff --git a/packages/fuels-core/src/code_gen/functions_gen.rs b/packages/fuels-core/src/code_gen/functions_gen.rs index b69f81c0ea..799eeda90e 100644 --- a/packages/fuels-core/src/code_gen/functions_gen.rs +++ b/packages/fuels-core/src/code_gen/functions_gen.rs @@ -34,9 +34,7 @@ pub fn expand_function( } let args = function_arguments(function, types)?; - let arg_names = args.iter().map(|component| &component.field_name); - let param_type_calls = param_type_calls(&args); let arg_declarations = args.iter().map(|component| { @@ -85,7 +83,6 @@ pub fn generate_script_main_function( let output_type: TokenStream = output_type_resolved.into(); let args = function_arguments(main_function_abi, types)?; - let arg_names = args.iter().map(|component| &component.field_name); let arg_declarations = args.iter().map(|component| { @@ -95,7 +92,6 @@ pub fn generate_script_main_function( }); let doc = expand_doc("Run the script's `main` function with the provided arguments"); - let name = safe_ident("main"); Ok(quote! { @@ -104,8 +100,8 @@ pub fn generate_script_main_function( let arg_name_tokens = [#(#arg_names.into_token()),*]; let script_binary = std::fs::read(self.binary_filepath.as_str()) .expect("Could not read from binary filepath"); - let encoded_args = ABIEncoder::encode(&arg_name_tokens).expect("Cannot encode script - arguments"); + let encoded_args = ABIEncoder::encode(&arg_name_tokens) + .expect("Cannot encode script arguments"); let provider = self.wallet.get_provider().expect("Provider not set up").clone(); let log_decoder = LogDecoder{logs_map: self.logs_map.clone()}; ScriptCallHandler::new( @@ -125,14 +121,6 @@ pub fn generate_predicate_encode_function( types: &HashMap, ) -> Result { let args = function_arguments(main_function_abi, types)?; - - // TODO(hal3e): enable support for vector inputs - if args.iter().any(|c| c.field_type.uses_vectors()) { - return Err(Error::CompilationError( - "Predicate main function contains a vector in its argument types. This currently isn't supported." - .to_string(), - )); - } let arg_names = args.iter().map(|component| &component.field_name); let arg_declarations = args.iter().map(|component| { @@ -142,7 +130,6 @@ pub fn generate_predicate_encode_function( }); let doc = expand_doc("Run the predicate's encode function with the provided arguments"); - let name = safe_ident("encode_data"); Ok(quote! { @@ -150,7 +137,7 @@ pub fn generate_predicate_encode_function( pub fn #name(&self #(,#arg_declarations)*) -> Self{ let arg_name_tokens = [#(#arg_names.into_token()),*]; let data = ABIEncoder::encode(&arg_name_tokens) - .expect("Cannot encode predicate data").resolve(0); + .expect("Cannot encode predicate data"); Self { address: self.address.clone(), diff --git a/packages/fuels-signers/src/wallet.rs b/packages/fuels-signers/src/wallet.rs index 8d004fe466..550e1c1ed9 100644 --- a/packages/fuels-signers/src/wallet.rs +++ b/packages/fuels-signers/src/wallet.rs @@ -13,8 +13,9 @@ use fuel_gql_client::{ }, fuel_vm::{consts::REG_ONE, prelude::Opcode}, }; -use fuel_types::bytes::WORD_SIZE; +use fuel_types::bytes::{padded_len_usize, WORD_SIZE}; use fuel_types::{Address, MessageId}; +use fuels_core::abi_encoder::UnresolvedBytes; use fuels_core::tx::{field, Chargeable, Script, Transaction, UniqueIdentifier}; use fuels_core::{constants::BASE_ASSET_ID, parameters::TxParameters}; use fuels_types::bech32::{Bech32Address, Bech32ContractId, FUEL_BECH32_HRP}; @@ -656,6 +657,21 @@ impl WalletUnlocked { .and_then(|m| m.message_id()) } + fn get_coin_predicate_data_offset(code_len: usize) -> u64 { + fuel_gql_client::fuel_tx::InputRepr::Coin + .coin_predicate_offset() + .expect("should have the predicate offset") as u64 + + padded_len_usize(code_len) as u64 + } + + fn get_message_predicate_data_offset(message_data_len: usize, code_len: usize) -> u64 { + fuel_gql_client::fuel_tx::InputRepr::Message + .data_offset() + .expect("should have the data offset") as u64 + + padded_len_usize(message_data_len) as u64 + + padded_len_usize(code_len) as u64 + } + #[allow(clippy::too_many_arguments)] pub async fn spend_predicate( &self, @@ -664,8 +680,9 @@ impl WalletUnlocked { amount: u64, asset_id: AssetId, to: &Bech32Address, - predicate_data: Vec, + predicate_data: UnresolvedBytes, tx_parameters: TxParameters, + base_offset: u64, ) -> Result, Error> { let spendable_predicate_resources = self .get_provider()? @@ -679,14 +696,27 @@ impl WalletUnlocked { .map(|resource| resource.amount()) .sum(); + let mut offset = base_offset; + let inputs = spendable_predicate_resources .into_iter() .map(|resource| match resource { Resource::Coin(coin) => { - self.create_coin_predicate(coin, asset_id, code.clone(), predicate_data.clone()) + offset += Self::get_coin_predicate_data_offset(code.len()); + + let data = predicate_data.clone().resolve(offset); + offset += data.len() as u64; + + self.create_coin_predicate(coin, asset_id, code.clone(), data) } Resource::Message(message) => { - self.create_message_predicate(message, code.clone(), predicate_data.clone()) + offset += + Self::get_message_predicate_data_offset(message.data.len(), code.len()); + + let data = predicate_data.clone().resolve(offset); + offset += data.len() as u64; + + self.create_message_predicate(message, code.clone(), data) } }) .collect::>(); @@ -747,8 +777,9 @@ impl WalletUnlocked { predicate_code: Vec, amount: u64, asset_id: AssetId, - predicate_data: Vec, + predicate_data: UnresolvedBytes, tx_parameters: TxParameters, + offset: u64, ) -> Result, Error> { self.spend_predicate( predicate_address, @@ -758,6 +789,7 @@ impl WalletUnlocked { self.address(), predicate_data, tx_parameters, + offset, ) .await } diff --git a/packages/fuels/tests/predicates.rs b/packages/fuels/tests/predicates.rs index cf9dd64bf3..efda1d60fc 100644 --- a/packages/fuels/tests/predicates.rs +++ b/packages/fuels/tests/predicates.rs @@ -1,6 +1,4 @@ -use fuels::core::abi_encoder::ABIEncoder; use fuels::prelude::*; -use std::str::FromStr; async fn setup_predicate_test2( num_coins: u64, @@ -23,407 +21,407 @@ async fn setup_predicate_test2( Ok((sender, receiver, asset_id)) } -async fn setup_predicate_test( - file_path: &str, - num_coins: u64, - coin_amount: u64, -) -> Result<(Predicate, WalletUnlocked, WalletUnlocked, AssetId), Error> { - let predicate = Predicate::load_from(file_path)?; - - let mut wallets = launch_custom_provider_and_get_wallets( - WalletsConfig::new(Some(2), Some(num_coins), Some(coin_amount)), - Some(Config { - utxo_validation: true, - ..Config::local_node() - }), - None, - ) - .await; - - let sender = wallets.pop().unwrap(); - let receiver = wallets.pop().unwrap(); - let asset_id = AssetId::default(); - - Ok((predicate, sender, receiver, asset_id)) -} - -#[tokio::test] -async fn can_call_no_arg_predicate_returns_true() -> Result<(), Error> { - let (predicate, sender, receiver, asset_id) = setup_predicate_test( - "tests/predicates/predicate_true/out/debug/predicate_true.bin", - 1, - 16, - ) - .await?; - let provider = receiver.get_provider()?; - let amount_to_predicate = 2; - - sender - .transfer( - predicate.address(), - amount_to_predicate, - asset_id, - TxParameters::default(), - ) - .await?; - - let receiver_balance_before = provider - .get_asset_balance(receiver.address(), asset_id) - .await?; - assert_eq!(receiver_balance_before, 16); - - receiver - .receive_from_predicate( - predicate.address(), - predicate.code(), - amount_to_predicate, - asset_id, - vec![], - TxParameters::default(), - ) - .await?; - - let receiver_balance_after = provider - .get_asset_balance(receiver.address(), asset_id) - .await?; - assert_eq!( - receiver_balance_before + amount_to_predicate, - receiver_balance_after - ); - - let predicate_balance = provider - .get_asset_balance(predicate.address(), asset_id) - .await?; - assert_eq!(predicate_balance, 0); - Ok(()) -} - -#[tokio::test] -async fn can_call_no_arg_predicate_returns_false() -> Result<(), Error> { - let (predicate, sender, receiver, asset_id) = setup_predicate_test( - "tests/predicates/predicate_false/out/debug/predicate_false.bin", - 1, - 16, - ) - .await?; - let provider = receiver.get_provider()?; - let amount_to_predicate = 4; - - sender - .transfer( - predicate.address(), - amount_to_predicate, - asset_id, - TxParameters::default(), - ) - .await?; - - let receiver_balance_before = provider - .get_asset_balance(receiver.address(), asset_id) - .await?; - assert_eq!(receiver_balance_before, 16); - - receiver - .receive_from_predicate( - predicate.address(), - predicate.code(), - amount_to_predicate, - asset_id, - vec![], - TxParameters::default(), - ) - .await - .expect_err("should error"); - - let receiver_balance_after = provider - .get_asset_balance(receiver.address(), asset_id) - .await?; - assert_eq!(receiver_balance_before, receiver_balance_after); - - let predicate_balance = provider - .get_asset_balance(predicate.address(), asset_id) - .await?; - assert_eq!(predicate_balance, amount_to_predicate); - - Ok(()) -} - -#[tokio::test] -async fn can_call_predicate_with_u32_data() -> Result<(), Error> { - let (predicate, sender, receiver, asset_id) = setup_predicate_test( - "tests/predicates/predicate_u32/out/debug/predicate_u32.bin", - 1, - 16, - ) - .await?; - let provider = receiver.get_provider()?; - let amount_to_predicate = 8; - - sender - .transfer( - predicate.address(), - amount_to_predicate, - asset_id, - TxParameters::default(), - ) - .await?; - - let receiver_balance_before = provider - .get_asset_balance(receiver.address(), asset_id) - .await?; - assert_eq!(receiver_balance_before, 16); - - // invalid predicate data - let predicate_data = ABIEncoder::encode(&[101_u32.into_token()]) - .unwrap() - .resolve(0); - receiver - .receive_from_predicate( - predicate.address(), - predicate.code(), - amount_to_predicate, - asset_id, - predicate_data, - TxParameters::default(), - ) - .await - .expect_err("should error"); - - let receiver_balance_after = provider - .get_asset_balance(receiver.address(), asset_id) - .await?; - assert_eq!(receiver_balance_before, receiver_balance_after); - - let predicate_balance = provider - .get_asset_balance(predicate.address(), asset_id) - .await?; - assert_eq!(predicate_balance, amount_to_predicate); - - // valid predicate data - let predicate_data = ABIEncoder::encode(&[1078_u32.into_token()]) - .unwrap() - .resolve(0); - receiver - .receive_from_predicate( - predicate.address(), - predicate.code(), - amount_to_predicate, - asset_id, - predicate_data, - TxParameters::default(), - ) - .await?; - - let receiver_balance_after = provider - .get_asset_balance(receiver.address(), asset_id) - .await?; - assert_eq!( - receiver_balance_before + amount_to_predicate, - receiver_balance_after - ); - - let predicate_balance = provider - .get_asset_balance(predicate.address(), asset_id) - .await?; - assert_eq!(predicate_balance, 0); - Ok(()) -} - -#[tokio::test] -async fn can_call_predicate_with_address_data() -> Result<(), Error> { - let (predicate, sender, receiver, asset_id) = setup_predicate_test( - "tests/predicates/predicate_address/out/debug/predicate_address.bin", - 1, - 16, - ) - .await?; - let provider = receiver.get_provider()?; - let amount_to_predicate = 16; - - sender - .transfer( - predicate.address(), - amount_to_predicate, - asset_id, - TxParameters::default(), - ) - .await?; - - let receiver_balance_before = provider - .get_asset_balance(receiver.address(), asset_id) - .await?; - assert_eq!(receiver_balance_before, 16); - - let addr = - Address::from_str("0xef86afa9696cf0dc6385e2c407a6e159a1103cefb7e2ae0636fb33d3cb2a9e4a") - .unwrap(); - let predicate_data = ABIEncoder::encode(&[addr.into_token()]).unwrap().resolve(0); - receiver - .receive_from_predicate( - predicate.address(), - predicate.code(), - amount_to_predicate, - asset_id, - predicate_data, - TxParameters::default(), - ) - .await?; - - let receiver_balance_after = provider - .get_asset_balance(receiver.address(), asset_id) - .await?; - assert_eq!( - receiver_balance_before + amount_to_predicate, - receiver_balance_after - ); - - let predicate_balance = provider - .get_asset_balance(predicate.address(), asset_id) - .await?; - assert_eq!(predicate_balance, 0); - Ok(()) -} - -#[tokio::test] -async fn can_call_predicate_with_struct_data() -> Result<(), Error> { - let (predicate, sender, receiver, asset_id) = setup_predicate_test( - "tests/predicates/predicate_struct/out/debug/predicate_struct.bin", - 1, - 16, - ) - .await?; - let provider = receiver.get_provider()?; - let amount_to_predicate = 8; - - sender - .transfer( - predicate.address(), - amount_to_predicate, - asset_id, - TxParameters::default(), - ) - .await?; - - let receiver_balance_before = provider - .get_asset_balance(receiver.address(), asset_id) - .await?; - assert_eq!(receiver_balance_before, 16); - - // invalid predicate data - let predicate_data = ABIEncoder::encode(&[true.into_token(), 55_u32.into_token()]) - .unwrap() - .resolve(0); - receiver - .receive_from_predicate( - predicate.address(), - predicate.code(), - amount_to_predicate, - asset_id, - predicate_data, - TxParameters::default(), - ) - .await - .expect_err("should error"); - - let receiver_balance_after = provider - .get_asset_balance(receiver.address(), asset_id) - .await?; - assert_eq!(receiver_balance_before, receiver_balance_after); - - let predicate_balance = provider - .get_asset_balance(predicate.address(), asset_id) - .await?; - assert_eq!(predicate_balance, amount_to_predicate); - - // valid predicate data - let predicate_data = ABIEncoder::encode(&[true.into_token(), 100_u32.into_token()]) - .unwrap() - .resolve(0); - receiver - .receive_from_predicate( - predicate.address(), - predicate.code(), - amount_to_predicate, - asset_id, - predicate_data, - TxParameters::default(), - ) - .await?; - - let receiver_balance_after = provider - .get_asset_balance(receiver.address(), asset_id) - .await?; - assert_eq!( - receiver_balance_before + amount_to_predicate, - receiver_balance_after - ); - - let predicate_balance = provider - .get_asset_balance(predicate.address(), asset_id) - .await?; - assert_eq!(predicate_balance, 0); - Ok(()) -} - -#[tokio::test] -async fn predicate_with_multiple_coins() -> Result<(), Error> { - let (predicate, sender, receiver, asset_id) = setup_predicate_test( - "tests/predicates/predicate_true/out/debug/predicate_true.bin", - 3, - 100, - ) - .await?; - let provider = receiver.get_provider()?; - let amount_to_predicate = 10; - - sender - .transfer( - predicate.address(), - amount_to_predicate, - asset_id, - TxParameters::new(Some(1), None, None), - ) - .await?; - - sender - .transfer( - predicate.address(), - amount_to_predicate, - asset_id, - TxParameters::new(Some(1), None, None), - ) - .await?; - - let receiver_balance_before = provider - .get_asset_balance(receiver.address(), asset_id) - .await?; - assert_eq!(receiver_balance_before, 300); - - receiver - .receive_from_predicate( - predicate.address(), - predicate.code(), - amount_to_predicate, - asset_id, - vec![], - TxParameters::new(Some(1), None, None), - ) - .await?; - - let receiver_balance_after = provider - .get_asset_balance(receiver.address(), asset_id) - .await?; - assert_eq!( - receiver_balance_before + amount_to_predicate - 1, - receiver_balance_after - ); - - let predicate_balance = provider - .get_asset_balance(predicate.address(), asset_id) - .await?; - assert_eq!(predicate_balance, 10); - Ok(()) -} +// async fn setup_predicate_test( +// file_path: &str, +// num_coins: u64, +// coin_amount: u64, +// ) -> Result<(Predicate, WalletUnlocked, WalletUnlocked, AssetId), Error> { +// let predicate = Predicate::load_from(file_path)?; + +// let mut wallets = launch_custom_provider_and_get_wallets( +// WalletsConfig::new(Some(2), Some(num_coins), Some(coin_amount)), +// Some(Config { +// utxo_validation: true, +// ..Config::local_node() +// }), +// None, +// ) +// .await; + +// let sender = wallets.pop().unwrap(); +// let receiver = wallets.pop().unwrap(); +// let asset_id = AssetId::default(); + +// Ok((predicate, sender, receiver, asset_id)) +// } + +// #[tokio::test] +// async fn can_call_no_arg_predicate_returns_true() -> Result<(), Error> { +// let (predicate, sender, receiver, asset_id) = setup_predicate_test( +// "tests/predicates/predicate_true/out/debug/predicate_true.bin", +// 1, +// 16, +// ) +// .await?; +// let provider = receiver.get_provider()?; +// let amount_to_predicate = 2; + +// sender +// .transfer( +// predicate.address(), +// amount_to_predicate, +// asset_id, +// TxParameters::default(), +// ) +// .await?; + +// let receiver_balance_before = provider +// .get_asset_balance(receiver.address(), asset_id) +// .await?; +// assert_eq!(receiver_balance_before, 16); + +// receiver +// .receive_from_predicate( +// predicate.address(), +// predicate.code(), +// amount_to_predicate, +// asset_id, +// vec![], +// TxParameters::default(), +// ) +// .await?; + +// let receiver_balance_after = provider +// .get_asset_balance(receiver.address(), asset_id) +// .await?; +// assert_eq!( +// receiver_balance_before + amount_to_predicate, +// receiver_balance_after +// ); + +// let predicate_balance = provider +// .get_asset_balance(predicate.address(), asset_id) +// .await?; +// assert_eq!(predicate_balance, 0); +// Ok(()) +// } + +// #[tokio::test] +// async fn can_call_no_arg_predicate_returns_false() -> Result<(), Error> { +// let (predicate, sender, receiver, asset_id) = setup_predicate_test( +// "tests/predicates/predicate_false/out/debug/predicate_false.bin", +// 1, +// 16, +// ) +// .await?; +// let provider = receiver.get_provider()?; +// let amount_to_predicate = 4; + +// sender +// .transfer( +// predicate.address(), +// amount_to_predicate, +// asset_id, +// TxParameters::default(), +// ) +// .await?; + +// let receiver_balance_before = provider +// .get_asset_balance(receiver.address(), asset_id) +// .await?; +// assert_eq!(receiver_balance_before, 16); + +// receiver +// .receive_from_predicate( +// predicate.address(), +// predicate.code(), +// amount_to_predicate, +// asset_id, +// vec![], +// TxParameters::default(), +// ) +// .await +// .expect_err("should error"); + +// let receiver_balance_after = provider +// .get_asset_balance(receiver.address(), asset_id) +// .await?; +// assert_eq!(receiver_balance_before, receiver_balance_after); + +// let predicate_balance = provider +// .get_asset_balance(predicate.address(), asset_id) +// .await?; +// assert_eq!(predicate_balance, amount_to_predicate); + +// Ok(()) +// } + +// #[tokio::test] +// async fn can_call_predicate_with_u32_data() -> Result<(), Error> { +// let (predicate, sender, receiver, asset_id) = setup_predicate_test( +// "tests/predicates/predicate_u32/out/debug/predicate_u32.bin", +// 1, +// 16, +// ) +// .await?; +// let provider = receiver.get_provider()?; +// let amount_to_predicate = 8; + +// sender +// .transfer( +// predicate.address(), +// amount_to_predicate, +// asset_id, +// TxParameters::default(), +// ) +// .await?; + +// let receiver_balance_before = provider +// .get_asset_balance(receiver.address(), asset_id) +// .await?; +// assert_eq!(receiver_balance_before, 16); + +// // invalid predicate data +// let predicate_data = ABIEncoder::encode(&[101_u32.into_token()]) +// .unwrap() +// .resolve(0); +// receiver +// .receive_from_predicate( +// predicate.address(), +// predicate.code(), +// amount_to_predicate, +// asset_id, +// predicate_data, +// TxParameters::default(), +// ) +// .await +// .expect_err("should error"); + +// let receiver_balance_after = provider +// .get_asset_balance(receiver.address(), asset_id) +// .await?; +// assert_eq!(receiver_balance_before, receiver_balance_after); + +// let predicate_balance = provider +// .get_asset_balance(predicate.address(), asset_id) +// .await?; +// assert_eq!(predicate_balance, amount_to_predicate); + +// // valid predicate data +// let predicate_data = ABIEncoder::encode(&[1078_u32.into_token()]) +// .unwrap() +// .resolve(0); +// receiver +// .receive_from_predicate( +// predicate.address(), +// predicate.code(), +// amount_to_predicate, +// asset_id, +// predicate_data, +// TxParameters::default(), +// ) +// .await?; + +// let receiver_balance_after = provider +// .get_asset_balance(receiver.address(), asset_id) +// .await?; +// assert_eq!( +// receiver_balance_before + amount_to_predicate, +// receiver_balance_after +// ); + +// let predicate_balance = provider +// .get_asset_balance(predicate.address(), asset_id) +// .await?; +// assert_eq!(predicate_balance, 0); +// Ok(()) +// } + +// #[tokio::test] +// async fn can_call_predicate_with_address_data() -> Result<(), Error> { +// let (predicate, sender, receiver, asset_id) = setup_predicate_test( +// "tests/predicates/predicate_address/out/debug/predicate_address.bin", +// 1, +// 16, +// ) +// .await?; +// let provider = receiver.get_provider()?; +// let amount_to_predicate = 16; + +// sender +// .transfer( +// predicate.address(), +// amount_to_predicate, +// asset_id, +// TxParameters::default(), +// ) +// .await?; + +// let receiver_balance_before = provider +// .get_asset_balance(receiver.address(), asset_id) +// .await?; +// assert_eq!(receiver_balance_before, 16); + +// let addr = +// Address::from_str("0xef86afa9696cf0dc6385e2c407a6e159a1103cefb7e2ae0636fb33d3cb2a9e4a") +// .unwrap(); +// let predicate_data = ABIEncoder::encode(&[addr.into_token()]).unwrap().resolve(0); +// receiver +// .receive_from_predicate( +// predicate.address(), +// predicate.code(), +// amount_to_predicate, +// asset_id, +// predicate_data, +// TxParameters::default(), +// ) +// .await?; + +// let receiver_balance_after = provider +// .get_asset_balance(receiver.address(), asset_id) +// .await?; +// assert_eq!( +// receiver_balance_before + amount_to_predicate, +// receiver_balance_after +// ); + +// let predicate_balance = provider +// .get_asset_balance(predicate.address(), asset_id) +// .await?; +// assert_eq!(predicate_balance, 0); +// Ok(()) +// } + +// #[tokio::test] +// async fn can_call_predicate_with_struct_data() -> Result<(), Error> { +// let (predicate, sender, receiver, asset_id) = setup_predicate_test( +// "tests/predicates/predicate_struct/out/debug/predicate_struct.bin", +// 1, +// 16, +// ) +// .await?; +// let provider = receiver.get_provider()?; +// let amount_to_predicate = 8; + +// sender +// .transfer( +// predicate.address(), +// amount_to_predicate, +// asset_id, +// TxParameters::default(), +// ) +// .await?; + +// let receiver_balance_before = provider +// .get_asset_balance(receiver.address(), asset_id) +// .await?; +// assert_eq!(receiver_balance_before, 16); + +// // invalid predicate data +// let predicate_data = ABIEncoder::encode(&[true.into_token(), 55_u32.into_token()]) +// .unwrap() +// .resolve(0); +// receiver +// .receive_from_predicate( +// predicate.address(), +// predicate.code(), +// amount_to_predicate, +// asset_id, +// predicate_data, +// TxParameters::default(), +// ) +// .await +// .expect_err("should error"); + +// let receiver_balance_after = provider +// .get_asset_balance(receiver.address(), asset_id) +// .await?; +// assert_eq!(receiver_balance_before, receiver_balance_after); + +// let predicate_balance = provider +// .get_asset_balance(predicate.address(), asset_id) +// .await?; +// assert_eq!(predicate_balance, amount_to_predicate); + +// // valid predicate data +// let predicate_data = ABIEncoder::encode(&[true.into_token(), 100_u32.into_token()]) +// .unwrap() +// .resolve(0); +// receiver +// .receive_from_predicate( +// predicate.address(), +// predicate.code(), +// amount_to_predicate, +// asset_id, +// predicate_data, +// TxParameters::default(), +// ) +// .await?; + +// let receiver_balance_after = provider +// .get_asset_balance(receiver.address(), asset_id) +// .await?; +// assert_eq!( +// receiver_balance_before + amount_to_predicate, +// receiver_balance_after +// ); + +// let predicate_balance = provider +// .get_asset_balance(predicate.address(), asset_id) +// .await?; +// assert_eq!(predicate_balance, 0); +// Ok(()) +// } + +// #[tokio::test] +// async fn predicate_with_multiple_coins() -> Result<(), Error> { +// let (predicate, sender, receiver, asset_id) = setup_predicate_test( +// "tests/predicates/predicate_true/out/debug/predicate_true.bin", +// 3, +// 100, +// ) +// .await?; +// let provider = receiver.get_provider()?; +// let amount_to_predicate = 10; + +// sender +// .transfer( +// predicate.address(), +// amount_to_predicate, +// asset_id, +// TxParameters::new(Some(1), None, None), +// ) +// .await?; + +// sender +// .transfer( +// predicate.address(), +// amount_to_predicate, +// asset_id, +// TxParameters::new(Some(1), None, None), +// ) +// .await?; + +// let receiver_balance_before = provider +// .get_asset_balance(receiver.address(), asset_id) +// .await?; +// assert_eq!(receiver_balance_before, 300); + +// receiver +// .receive_from_predicate( +// predicate.address(), +// predicate.code(), +// amount_to_predicate, +// asset_id, +// vec![], +// TxParameters::new(Some(1), None, None), +// ) +// .await?; + +// let receiver_balance_after = provider +// .get_asset_balance(receiver.address(), asset_id) +// .await?; +// assert_eq!( +// receiver_balance_before + amount_to_predicate - 1, +// receiver_balance_after +// ); + +// let predicate_balance = provider +// .get_asset_balance(predicate.address(), asset_id) +// .await?; +// assert_eq!(predicate_balance, 10); +// Ok(()) +// } async fn assert_address_balance( address: &Bech32Address, @@ -438,86 +436,153 @@ async fn assert_address_balance( assert_eq!(balance, amount); } +// #[tokio::test] +// async fn can_call_predicate_with_u32_data_new() -> Result<(), Error> { +// let initial_balance = 16; +// let (sender, receiver, asset_id) = setup_predicate_test2(1, initial_balance).await?; +// let provider = receiver.get_provider()?; +// let amount = 8; + +// predicate_abigen!( +// MyPredicate, +// "packages/fuels/tests/predicates/predicate_u32/out/debug/predicate_u32-abi.json" +// ); + +// let predicate = +// MyPredicate::load_from("tests/predicates/predicate_u32/out/debug/predicate_u32.bin")?; + +// predicate +// .receive_from_wallet(&sender, amount, asset_id, None) +// .await?; + +// // The predicate has received the funds +// assert_address_balance(predicate.address(), provider, asset_id, amount).await; + +// // Run predicate with wrong data +// predicate +// .encode_data(1077) +// .spend_to_wallet(&receiver, amount, asset_id, None) +// .await +// .expect_err("Should error"); + +// // No funds were transferred +// assert_address_balance(receiver.address(), provider, asset_id, initial_balance).await; + +// predicate +// .encode_data(1078) +// .spend_to_wallet(&receiver, amount, asset_id, None) +// .await?; + +// // The predicate has spent the funds +// assert_address_balance(predicate.address(), provider, asset_id, 0).await; + +// // Funds were transferred +// assert_address_balance( +// receiver.address(), +// provider, +// asset_id, +// initial_balance + amount, +// ) +// .await; + +// Ok(()) +// } + +// #[tokio::test] +// async fn can_call_predicate_with_struct_data_new() -> Result<(), Error> { +// let initial_balance = 16; +// let (sender, receiver, asset_id) = setup_predicate_test2(1, initial_balance).await?; +// let provider = receiver.get_provider()?; +// let amount = 8; + +// predicate_abigen!( +// MyPredicate, +// "packages/fuels/tests/predicates/predicate_struct/out/debug/predicate_struct-abi.json" +// ); + +// let predicate = +// MyPredicate::load_from("tests/predicates/predicate_struct/out/debug/predicate_struct.bin")?; + +// predicate +// .receive_from_wallet(&sender, amount, asset_id, None) +// .await?; + +// // The predicate has received the funds +// assert_address_balance(predicate.address(), provider, asset_id, amount).await; + +// // Run predicate with wrong data +// predicate +// .encode_data(Validation { +// has_account: false, +// total_complete: 10, +// }) +// .spend_to_wallet(&receiver, amount, asset_id, None) +// .await +// .expect_err("Should error"); + +// // No funds were transferred +// assert_address_balance(receiver.address(), provider, asset_id, initial_balance).await; + +// predicate +// .encode_data(Validation { +// has_account: true, +// total_complete: 100, +// }) +// .spend_to_wallet(&receiver, amount, asset_id, None) +// .await?; + +// // The predicate has spent the funds +// assert_address_balance(predicate.address(), provider, asset_id, 0).await; + +// // Funds were transferred +// assert_address_balance( +// receiver.address(), +// provider, +// asset_id, +// initial_balance + amount, +// ) +// .await; + +// Ok(()) +// } + #[tokio::test] -async fn can_call_predicate_with_u32_data_new() -> Result<(), Error> { - let initial_balance = 16; - let (sender, receiver, asset_id) = setup_predicate_test2(1, initial_balance).await?; +async fn can_call_predicate_with_vector() -> Result<(), Error> { + let initial_balance = 10; + let (sender, receiver, asset_id) = setup_predicate_test2(10, 1).await?; let provider = receiver.get_provider()?; let amount = 8; predicate_abigen!( MyPredicate, - "packages/fuels/tests/predicates/predicate_u32/out/debug/predicate_u32-abi.json" + "packages/fuels/tests/predicates/predicate_vector/out/debug/predicate_vector-abi.json" ); let predicate = - MyPredicate::load_from("tests/predicates/predicate_u32/out/debug/predicate_u32.bin")?; + MyPredicate::load_from("tests/predicates/predicate_vector/out/debug/predicate_vector.bin")?; predicate - .receive_from_wallet(&sender, amount, asset_id, None) + .receive_from_wallet(&sender, 2, asset_id, None) .await?; - // The provider has received the funds - assert_address_balance(predicate.address(), provider, asset_id, amount).await; - - // Run predicate with wrong data predicate - .encode_data(1077) - .spend_to_wallet(&receiver, amount, asset_id, None) - .await - .expect_err("Should error"); - - // No funds were transferred - assert_address_balance(receiver.address(), provider, asset_id, initial_balance).await; + .receive_from_wallet(&sender, 2, asset_id, None) + .await?; predicate - .encode_data(1078) - .spend_to_wallet(&receiver, amount, asset_id, None) + .receive_from_wallet(&sender, 2, asset_id, None) .await?; - // The provider has spent the funds - assert_address_balance(predicate.address(), provider, asset_id, 0).await; - - // Funds were transferred - assert_address_balance( - receiver.address(), - provider, - asset_id, - initial_balance + amount, - ) - .await; - - Ok(()) -} - -#[tokio::test] -async fn can_call_predicate_with_struct_data_new() -> Result<(), Error> { - let initial_balance = 16; - let (sender, receiver, asset_id) = setup_predicate_test2(1, initial_balance).await?; - let provider = receiver.get_provider()?; - let amount = 8; - - predicate_abigen!( - MyPredicate, - "packages/fuels/tests/predicates/predicate_struct/out/debug/predicate_struct-abi.json" - ); - - let predicate = - MyPredicate::load_from("tests/predicates/predicate_struct/out/debug/predicate_struct.bin")?; - predicate - .receive_from_wallet(&sender, amount, asset_id, None) + .receive_from_wallet(&sender, 2, asset_id, None) .await?; - // The provider has received the funds + // The predicate has received the funds assert_address_balance(predicate.address(), provider, asset_id, amount).await; // Run predicate with wrong data predicate - .encode_data(Validation { - has_account: false, - total_complete: 10, - }) + .encode_data(2, 4, vec![2, 4, 43]) .spend_to_wallet(&receiver, amount, asset_id, None) .await .expect_err("Should error"); @@ -526,14 +591,11 @@ async fn can_call_predicate_with_struct_data_new() -> Result<(), Error> { assert_address_balance(receiver.address(), provider, asset_id, initial_balance).await; predicate - .encode_data(Validation { - has_account: true, - total_complete: 100, - }) + .encode_data(2, 4, vec![2, 4, 42]) .spend_to_wallet(&receiver, amount, asset_id, None) .await?; - // The provider has spent the funds + // The predicate has spent the funds assert_address_balance(predicate.address(), provider, asset_id, 0).await; // Funds were transferred diff --git a/packages/fuels/tests/scripts/script_tuple_argument/Forc.toml b/packages/fuels/tests/predicates/predicate_vector/Forc.toml similarity index 77% rename from packages/fuels/tests/scripts/script_tuple_argument/Forc.toml rename to packages/fuels/tests/predicates/predicate_vector/Forc.toml index a91bd28040..6972a31e70 100644 --- a/packages/fuels/tests/scripts/script_tuple_argument/Forc.toml +++ b/packages/fuels/tests/predicates/predicate_vector/Forc.toml @@ -2,6 +2,6 @@ authors = ["Fuel Labs "] entry = "main.sw" license = "Apache-2.0" -name = "script_tuple_argument" +name = "predicate_vector" [dependencies] diff --git a/packages/fuels/tests/predicates/predicate_vector/src/main.sw b/packages/fuels/tests/predicates/predicate_vector/src/main.sw new file mode 100644 index 0000000000..cfd07ae47e --- /dev/null +++ b/packages/fuels/tests/predicates/predicate_vector/src/main.sw @@ -0,0 +1,6 @@ +predicate; + +fn main(a: u32, b: u64, c: Vec) -> bool { + let number: u64 = c.get(2).unwrap(); + number == 42 +} diff --git a/packages/fuels/tests/scripts/script_tuple_argument/src/main.sw b/packages/fuels/tests/scripts/script_tuple_argument/src/main.sw deleted file mode 100644 index 850b1e9332..0000000000 --- a/packages/fuels/tests/scripts/script_tuple_argument/src/main.sw +++ /dev/null @@ -1,24 +0,0 @@ -script; - -struct Bim { - bim: u64, -} - -struct Bam { - bam: str[5], -} - -struct Boum { - boum: bool, -} - -fn main(my_tuple: (Bim, Bam, Boum), zim: Bam) -> ((Boum, Bim, Bam), u64) { - ( - ( - Boum { boum: true }, - Bim { bim: 193817 }, - Bam { bam: "hello" }, - ), - 42242, - ) -} From 4a1bfc5d86aed94d6334d24c6c6dbe8ed50ba8db Mon Sep 17 00:00:00 2001 From: hal3e Date: Tue, 13 Dec 2022 01:10:59 +0100 Subject: [PATCH 04/14] add coins and messages tests with dynamic data --- packages/fuels-core/src/code_gen/abigen.rs | 2 +- packages/fuels-signers/src/wallet.rs | 26 +- packages/fuels/tests/predicates.rs | 631 ++++++--------------- 3 files changed, 190 insertions(+), 469 deletions(-) diff --git a/packages/fuels-core/src/code_gen/abigen.rs b/packages/fuels-core/src/code_gen/abigen.rs index 9a47d5858f..767267fc2f 100644 --- a/packages/fuels-core/src/code_gen/abigen.rs +++ b/packages/fuels-core/src/code_gen/abigen.rs @@ -313,7 +313,7 @@ impl Abigen { self.code(), amount, asset_id, - self.data.clone(), + self.data(), tx_parameters, self.get_offset(wallet.get_provider()?).await? ) diff --git a/packages/fuels-signers/src/wallet.rs b/packages/fuels-signers/src/wallet.rs index 550e1c1ed9..8135a2f300 100644 --- a/packages/fuels-signers/src/wallet.rs +++ b/packages/fuels-signers/src/wallet.rs @@ -151,16 +151,15 @@ impl Wallet { amount: u64, witness_index: u8, ) -> Result, Error> { - let spendable = self.get_spendable_resources(asset_id, amount).await?; - let mut inputs = vec![]; - for resource in spendable { - let input = match resource { + Ok(self + .get_spendable_resources(asset_id, amount) + .await? + .into_iter() + .map(|resource| match resource { Resource::Coin(coin) => self.create_coin_input(coin, asset_id, witness_index), Resource::Message(message) => self.create_message_input(message, witness_index), - }; - inputs.push(input); - } - Ok(inputs) + }) + .collect::>()) } fn create_coin_input(&self, coin: Coin, asset_id: AssetId, witness_index: u8) -> Input { @@ -660,14 +659,14 @@ impl WalletUnlocked { fn get_coin_predicate_data_offset(code_len: usize) -> u64 { fuel_gql_client::fuel_tx::InputRepr::Coin .coin_predicate_offset() - .expect("should have the predicate offset") as u64 + .expect("should have predicate offset") as u64 + padded_len_usize(code_len) as u64 } fn get_message_predicate_data_offset(message_data_len: usize, code_len: usize) -> u64 { fuel_gql_client::fuel_tx::InputRepr::Message .data_offset() - .expect("should have the data offset") as u64 + .expect("should have data offset") as u64 + padded_len_usize(message_data_len) as u64 + padded_len_usize(code_len) as u64 } @@ -696,8 +695,9 @@ impl WalletUnlocked { .map(|resource| resource.amount()) .sum(); + // Iterate through the spendable resources and calculate the appropriate offsets + // for the coin or message predicates let mut offset = base_offset; - let inputs = spendable_predicate_resources .into_iter() .map(|resource| match resource { @@ -764,8 +764,8 @@ impl WalletUnlocked { message.sender.into(), message.recipient.into(), message.amount, - 0, - vec![], + message.nonce, + message.data, code, predicate_data, ) diff --git a/packages/fuels/tests/predicates.rs b/packages/fuels/tests/predicates.rs index efda1d60fc..3f0d788176 100644 --- a/packages/fuels/tests/predicates.rs +++ b/packages/fuels/tests/predicates.rs @@ -1,441 +1,223 @@ -use fuels::prelude::*; +use fuels::{ + prelude::*, + tx::AssetId, + types::{coin::Coin, message::Message}, +}; -async fn setup_predicate_test2( - num_coins: u64, - coin_amount: u64, -) -> Result<(WalletUnlocked, WalletUnlocked, AssetId), Error> { - let mut wallets = launch_custom_provider_and_get_wallets( - WalletsConfig::new(Some(2), Some(num_coins), Some(coin_amount)), - Some(Config { - utxo_validation: true, - ..Config::local_node() - }), - None, - ) - .await; +async fn assert_address_balance( + address: &Bech32Address, + provider: &Provider, + asset_id: AssetId, + amount: u64, +) { + let balance = provider + .get_asset_balance(address, asset_id) + .await + .expect("Could not retrieve balance"); + assert_eq!(balance, amount); +} - let sender = wallets.pop().unwrap(); - let receiver = wallets.pop().unwrap(); +fn get_test_coins_and_messages( + address: &Bech32Address, + num_coins: u64, + num_messages: u64, + amount: u64, +) -> (Vec, Vec, AssetId) { let asset_id = AssetId::default(); - - Ok((sender, receiver, asset_id)) + let coins = setup_single_asset_coins(address, asset_id, num_coins, amount); + let messages = (0..num_messages) + .flat_map(|i| { + setup_single_message( + &Bech32Address::default(), + address, + amount, + i, + [104, 97, 108, 51, 101].to_vec(), + ) + }) + .collect(); + + (coins, messages, asset_id) } -// async fn setup_predicate_test( -// file_path: &str, -// num_coins: u64, -// coin_amount: u64, -// ) -> Result<(Predicate, WalletUnlocked, WalletUnlocked, AssetId), Error> { -// let predicate = Predicate::load_from(file_path)?; - -// let mut wallets = launch_custom_provider_and_get_wallets( -// WalletsConfig::new(Some(2), Some(num_coins), Some(coin_amount)), -// Some(Config { -// utxo_validation: true, -// ..Config::local_node() -// }), -// None, -// ) -// .await; +// Setup function used to assign coins and messages to a predicate address +// and create a `receiver` wallet +async fn setup_predicate_test( + predicate_address: &Bech32Address, + num_coins: u64, + num_messages: u64, + amount: u64, +) -> Result<(Provider, u64, WalletUnlocked, u64, AssetId), Error> { + let receiver_num_coins = 1; + let receiver_amount = 1; + let receiver_balance = receiver_num_coins * receiver_amount; -// let sender = wallets.pop().unwrap(); -// let receiver = wallets.pop().unwrap(); -// let asset_id = AssetId::default(); + let predicate_balance = (num_coins + num_messages) * amount; + let mut receiver = WalletUnlocked::new_random(None); -// Ok((predicate, sender, receiver, asset_id)) -// } + let (mut coins, messages, asset_id) = + get_test_coins_and_messages(predicate_address, num_coins, num_messages, amount); -// #[tokio::test] -// async fn can_call_no_arg_predicate_returns_true() -> Result<(), Error> { -// let (predicate, sender, receiver, asset_id) = setup_predicate_test( -// "tests/predicates/predicate_true/out/debug/predicate_true.bin", -// 1, -// 16, -// ) -// .await?; -// let provider = receiver.get_provider()?; -// let amount_to_predicate = 2; - -// sender -// .transfer( -// predicate.address(), -// amount_to_predicate, -// asset_id, -// TxParameters::default(), -// ) -// .await?; + coins.extend(setup_single_asset_coins( + receiver.address(), + asset_id, + receiver_num_coins, + receiver_amount, + )); -// let receiver_balance_before = provider -// .get_asset_balance(receiver.address(), asset_id) -// .await?; -// assert_eq!(receiver_balance_before, 16); - -// receiver -// .receive_from_predicate( -// predicate.address(), -// predicate.code(), -// amount_to_predicate, -// asset_id, -// vec![], -// TxParameters::default(), -// ) -// .await?; + let (provider, _address) = setup_test_provider(coins, messages, None, None).await; + receiver.set_provider(provider.clone()); -// let receiver_balance_after = provider -// .get_asset_balance(receiver.address(), asset_id) -// .await?; -// assert_eq!( -// receiver_balance_before + amount_to_predicate, -// receiver_balance_after -// ); + Ok(( + provider, + predicate_balance, + receiver, + receiver_balance, + asset_id, + )) +} -// let predicate_balance = provider -// .get_asset_balance(predicate.address(), asset_id) -// .await?; -// assert_eq!(predicate_balance, 0); -// Ok(()) -// } +#[tokio::test] +async fn transfer_coins_and_messages_to_predicate() -> Result<(), Error> { + let num_coins = 16; + let num_messages = 32; + let amount = 64; + let total_balance = (num_coins + num_messages) * amount; -// #[tokio::test] -// async fn can_call_no_arg_predicate_returns_false() -> Result<(), Error> { -// let (predicate, sender, receiver, asset_id) = setup_predicate_test( -// "tests/predicates/predicate_false/out/debug/predicate_false.bin", -// 1, -// 16, -// ) -// .await?; -// let provider = receiver.get_provider()?; -// let amount_to_predicate = 4; - -// sender -// .transfer( -// predicate.address(), -// amount_to_predicate, -// asset_id, -// TxParameters::default(), -// ) -// .await?; + let mut wallet = WalletUnlocked::new_random(None); -// let receiver_balance_before = provider -// .get_asset_balance(receiver.address(), asset_id) -// .await?; -// assert_eq!(receiver_balance_before, 16); - -// receiver -// .receive_from_predicate( -// predicate.address(), -// predicate.code(), -// amount_to_predicate, -// asset_id, -// vec![], -// TxParameters::default(), -// ) -// .await -// .expect_err("should error"); + let (coins, messages, asset_id) = + get_test_coins_and_messages(wallet.address(), num_coins, num_messages, amount); -// let receiver_balance_after = provider -// .get_asset_balance(receiver.address(), asset_id) -// .await?; -// assert_eq!(receiver_balance_before, receiver_balance_after); + let (provider, _address) = setup_test_provider(coins, messages, None, None).await; -// let predicate_balance = provider -// .get_asset_balance(predicate.address(), asset_id) -// .await?; -// assert_eq!(predicate_balance, amount_to_predicate); + wallet.set_provider(provider.clone()); -// Ok(()) -// } + predicate_abigen!( + MyPredicate, + "packages/fuels/tests/predicates/predicate_struct/out/debug/predicate_struct-abi.json" + ); -// #[tokio::test] -// async fn can_call_predicate_with_u32_data() -> Result<(), Error> { -// let (predicate, sender, receiver, asset_id) = setup_predicate_test( -// "tests/predicates/predicate_u32/out/debug/predicate_u32.bin", -// 1, -// 16, -// ) -// .await?; -// let provider = receiver.get_provider()?; -// let amount_to_predicate = 8; - -// sender -// .transfer( -// predicate.address(), -// amount_to_predicate, -// asset_id, -// TxParameters::default(), -// ) -// .await?; + let predicate = + MyPredicate::load_from("tests/predicates/predicate_struct/out/debug/predicate_struct.bin")?; -// let receiver_balance_before = provider -// .get_asset_balance(receiver.address(), asset_id) -// .await?; -// assert_eq!(receiver_balance_before, 16); - -// // invalid predicate data -// let predicate_data = ABIEncoder::encode(&[101_u32.into_token()]) -// .unwrap() -// .resolve(0); -// receiver -// .receive_from_predicate( -// predicate.address(), -// predicate.code(), -// amount_to_predicate, -// asset_id, -// predicate_data, -// TxParameters::default(), -// ) -// .await -// .expect_err("should error"); + predicate + .receive_from_wallet(&wallet, total_balance, asset_id, None) + .await?; -// let receiver_balance_after = provider -// .get_asset_balance(receiver.address(), asset_id) -// .await?; -// assert_eq!(receiver_balance_before, receiver_balance_after); + // The predicate has received the funds + assert_address_balance(predicate.address(), &provider, asset_id, total_balance).await; -// let predicate_balance = provider -// .get_asset_balance(predicate.address(), asset_id) -// .await?; -// assert_eq!(predicate_balance, amount_to_predicate); - -// // valid predicate data -// let predicate_data = ABIEncoder::encode(&[1078_u32.into_token()]) -// .unwrap() -// .resolve(0); -// receiver -// .receive_from_predicate( -// predicate.address(), -// predicate.code(), -// amount_to_predicate, -// asset_id, -// predicate_data, -// TxParameters::default(), -// ) -// .await?; + Ok(()) +} -// let receiver_balance_after = provider -// .get_asset_balance(receiver.address(), asset_id) -// .await?; -// assert_eq!( -// receiver_balance_before + amount_to_predicate, -// receiver_balance_after -// ); +#[tokio::test] +async fn spend_predicate_coins_messages_vector_args() -> Result<(), Error> { + predicate_abigen!( + MyPredicate, + "packages/fuels/tests/predicates/predicate_vector/out/debug/predicate_vector-abi.json" + ); -// let predicate_balance = provider -// .get_asset_balance(predicate.address(), asset_id) -// .await?; -// assert_eq!(predicate_balance, 0); -// Ok(()) -// } + let predicate = + MyPredicate::load_from("tests/predicates/predicate_vector/out/debug/predicate_vector.bin")?; -// #[tokio::test] -// async fn can_call_predicate_with_address_data() -> Result<(), Error> { -// let (predicate, sender, receiver, asset_id) = setup_predicate_test( -// "tests/predicates/predicate_address/out/debug/predicate_address.bin", -// 1, -// 16, -// ) -// .await?; -// let provider = receiver.get_provider()?; -// let amount_to_predicate = 16; - -// sender -// .transfer( -// predicate.address(), -// amount_to_predicate, -// asset_id, -// TxParameters::default(), -// ) -// .await?; + let num_coins = 4; + let num_messages = 8; + let amount = 16; + let (provider, predicate_balance, receiver, receiver_balance, asset_id) = + setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?; -// let receiver_balance_before = provider -// .get_asset_balance(receiver.address(), asset_id) -// .await?; -// assert_eq!(receiver_balance_before, 16); - -// let addr = -// Address::from_str("0xef86afa9696cf0dc6385e2c407a6e159a1103cefb7e2ae0636fb33d3cb2a9e4a") -// .unwrap(); -// let predicate_data = ABIEncoder::encode(&[addr.into_token()]).unwrap().resolve(0); -// receiver -// .receive_from_predicate( -// predicate.address(), -// predicate.code(), -// amount_to_predicate, -// asset_id, -// predicate_data, -// TxParameters::default(), -// ) -// .await?; + // Run predicate with wrong data + predicate + .encode_data(2, 4, vec![2, 4, 43]) + .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .await + .expect_err("Should error"); -// let receiver_balance_after = provider -// .get_asset_balance(receiver.address(), asset_id) -// .await?; -// assert_eq!( -// receiver_balance_before + amount_to_predicate, -// receiver_balance_after -// ); + // No funds were transferred + assert_address_balance(receiver.address(), &provider, asset_id, receiver_balance).await; -// let predicate_balance = provider -// .get_asset_balance(predicate.address(), asset_id) -// .await?; -// assert_eq!(predicate_balance, 0); -// Ok(()) -// } + predicate + .encode_data(2, 4, vec![2, 4, 42]) + .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .await?; -// #[tokio::test] -// async fn can_call_predicate_with_struct_data() -> Result<(), Error> { -// let (predicate, sender, receiver, asset_id) = setup_predicate_test( -// "tests/predicates/predicate_struct/out/debug/predicate_struct.bin", -// 1, -// 16, -// ) -// .await?; -// let provider = receiver.get_provider()?; -// let amount_to_predicate = 8; - -// sender -// .transfer( -// predicate.address(), -// amount_to_predicate, -// asset_id, -// TxParameters::default(), -// ) -// .await?; + // The predicate has spent the funds + assert_address_balance(predicate.address(), &provider, asset_id, 0).await; -// let receiver_balance_before = provider -// .get_asset_balance(receiver.address(), asset_id) -// .await?; -// assert_eq!(receiver_balance_before, 16); - -// // invalid predicate data -// let predicate_data = ABIEncoder::encode(&[true.into_token(), 55_u32.into_token()]) -// .unwrap() -// .resolve(0); -// receiver -// .receive_from_predicate( -// predicate.address(), -// predicate.code(), -// amount_to_predicate, -// asset_id, -// predicate_data, -// TxParameters::default(), -// ) -// .await -// .expect_err("should error"); + // Funds were transferred + assert_address_balance( + receiver.address(), + &provider, + asset_id, + receiver_balance + predicate_balance, + ) + .await; -// let receiver_balance_after = provider -// .get_asset_balance(receiver.address(), asset_id) -// .await?; -// assert_eq!(receiver_balance_before, receiver_balance_after); + Ok(()) +} -// let predicate_balance = provider -// .get_asset_balance(predicate.address(), asset_id) -// .await?; -// assert_eq!(predicate_balance, amount_to_predicate); - -// // valid predicate data -// let predicate_data = ABIEncoder::encode(&[true.into_token(), 100_u32.into_token()]) -// .unwrap() -// .resolve(0); -// receiver -// .receive_from_predicate( -// predicate.address(), -// predicate.code(), -// amount_to_predicate, -// asset_id, -// predicate_data, -// TxParameters::default(), -// ) -// .await?; +// #[tokio::test] +// async fn spend_predicate_with_vector_args() -> Result<(), Error> { +// let num_coins = 2; +// let amount = 4; +// let (sender, receiver, provider, asset_id) = +// setup_predicate_coin_test(num_coins, amount).await?; +// let initial_balance = num_coins * amount; -// let receiver_balance_after = provider -// .get_asset_balance(receiver.address(), asset_id) -// .await?; -// assert_eq!( -// receiver_balance_before + amount_to_predicate, -// receiver_balance_after +// let transfer_amount = initial_balance / 2; + +// predicate_abigen!( +// MyPredicate, +// "packages/fuels/tests/predicates/predicate_vector/out/debug/predicate_vector-abi.json" // ); -// let predicate_balance = provider -// .get_asset_balance(predicate.address(), asset_id) -// .await?; -// assert_eq!(predicate_balance, 0); -// Ok(()) -// } +// let predicate = +// MyPredicate::load_from("tests/predicates/predicate_vector/out/debug/predicate_vector.bin")?; -// #[tokio::test] -// async fn predicate_with_multiple_coins() -> Result<(), Error> { -// let (predicate, sender, receiver, asset_id) = setup_predicate_test( -// "tests/predicates/predicate_true/out/debug/predicate_true.bin", -// 3, -// 100, -// ) -// .await?; -// let provider = receiver.get_provider()?; -// let amount_to_predicate = 10; - -// sender -// .transfer( -// predicate.address(), -// amount_to_predicate, -// asset_id, -// TxParameters::new(Some(1), None, None), -// ) +// // Make two transactions so that the predicate holds 2 coins. This is needed to verify +// // that the predicate data offsets are correctly calculated for multiple coins +// predicate +// .receive_from_wallet(&sender, transfer_amount, asset_id, None) // .await?; -// sender -// .transfer( -// predicate.address(), -// amount_to_predicate, -// asset_id, -// TxParameters::new(Some(1), None, None), -// ) +// predicate +// .receive_from_wallet(&sender, transfer_amount, asset_id, None) // .await?; -// let receiver_balance_before = provider -// .get_asset_balance(receiver.address(), asset_id) -// .await?; -// assert_eq!(receiver_balance_before, 300); - -// receiver -// .receive_from_predicate( -// predicate.address(), -// predicate.code(), -// amount_to_predicate, -// asset_id, -// vec![], -// TxParameters::new(Some(1), None, None), -// ) -// .await?; +// // The predicate has received the funds +// assert_address_balance(predicate.address(), &provider, asset_id, initial_balance).await; -// let receiver_balance_after = provider -// .get_asset_balance(receiver.address(), asset_id) -// .await?; -// assert_eq!( -// receiver_balance_before + amount_to_predicate - 1, -// receiver_balance_after -// ); +// // Run predicate with wrong data +// predicate +// .encode_data(2, 4, vec![2, 4, 43]) +// .spend_to_wallet(&receiver, initial_balance, asset_id, None) +// .await +// .expect_err("Should error"); + +// // No funds were transferred +// assert_address_balance(receiver.address(), &provider, asset_id, initial_balance).await; -// let predicate_balance = provider -// .get_asset_balance(predicate.address(), asset_id) +// predicate +// .encode_data(2, 4, vec![2, 4, 42]) +// .spend_to_wallet(&receiver, initial_balance, asset_id, None) // .await?; -// assert_eq!(predicate_balance, 10); + +// // The predicate has spent the funds +// assert_address_balance(predicate.address(), &provider, asset_id, 0).await; + +// // Funds were transferred +// assert_address_balance( +// receiver.address(), +// &provider, +// asset_id, +// initial_balance + transfer_amount * 2, +// ) +// .await; + // Ok(()) // } -async fn assert_address_balance( - address: &Bech32Address, - provider: &Provider, - asset_id: AssetId, - amount: u64, -) { - let balance = provider - .get_asset_balance(address, asset_id) - .await - .expect("Could not retrieve balnace"); - assert_eq!(balance, amount); -} - // #[tokio::test] // async fn can_call_predicate_with_u32_data_new() -> Result<(), Error> { // let initial_balance = 16; @@ -546,66 +328,5 @@ async fn assert_address_balance( // Ok(()) // } -#[tokio::test] -async fn can_call_predicate_with_vector() -> Result<(), Error> { - let initial_balance = 10; - let (sender, receiver, asset_id) = setup_predicate_test2(10, 1).await?; - let provider = receiver.get_provider()?; - let amount = 8; - predicate_abigen!( - MyPredicate, - "packages/fuels/tests/predicates/predicate_vector/out/debug/predicate_vector-abi.json" - ); - - let predicate = - MyPredicate::load_from("tests/predicates/predicate_vector/out/debug/predicate_vector.bin")?; - - predicate - .receive_from_wallet(&sender, 2, asset_id, None) - .await?; - - predicate - .receive_from_wallet(&sender, 2, asset_id, None) - .await?; - - predicate - .receive_from_wallet(&sender, 2, asset_id, None) - .await?; - - predicate - .receive_from_wallet(&sender, 2, asset_id, None) - .await?; - // The predicate has received the funds - assert_address_balance(predicate.address(), provider, asset_id, amount).await; - - // Run predicate with wrong data - predicate - .encode_data(2, 4, vec![2, 4, 43]) - .spend_to_wallet(&receiver, amount, asset_id, None) - .await - .expect_err("Should error"); - - // No funds were transferred - assert_address_balance(receiver.address(), provider, asset_id, initial_balance).await; - - predicate - .encode_data(2, 4, vec![2, 4, 42]) - .spend_to_wallet(&receiver, amount, asset_id, None) - .await?; - - // The predicate has spent the funds - assert_address_balance(predicate.address(), provider, asset_id, 0).await; - - // Funds were transferred - assert_address_balance( - receiver.address(), - provider, - asset_id, - initial_balance + amount, - ) - .await; - - Ok(()) -} From 64c13c5618d3743b2c4a69ba205e697ea0b9329b Mon Sep 17 00:00:00 2001 From: hal3e Date: Wed, 14 Dec 2022 00:29:25 +0100 Subject: [PATCH 05/14] add tests and update docs --- .github/workflows/ci.yml | 12 +- docs/src/SUMMARY.md | 2 +- docs/src/getting-started/predicates.md | 6 +- docs/src/predicates/predicate-data.md | 15 +- docs/src/predicates/send-spend-predicate.md | 16 +- examples/predicates/src/lib.rs | 159 +++-- .../src/contract_calls_utils.rs | 40 +- .../fuels-contract/src/execution_script.rs | 15 +- packages/fuels-contract/src/lib.rs | 1 - packages/fuels-contract/src/predicate.rs | 35 -- packages/fuels-contract/src/script_calls.rs | 4 +- packages/fuels-core/Cargo.toml | 1 + packages/fuels-core/src/code_gen/abigen.rs | 9 - packages/fuels-core/src/lib.rs | 5 + packages/fuels-core/src/offsets.rs | 46 ++ packages/fuels-core/src/types/bits.rs | 13 + packages/fuels-signers/src/wallet.rs | 46 +- packages/fuels/src/lib.rs | 1 - packages/fuels/tests/predicates.rs | 570 ++++++++++++------ .../predicates/predicate_address/src/main.sw | 9 +- .../Forc.toml | 2 +- .../predicates/predicate_basic/src/main.sw | 5 + .../predicate_data_example/.gitignore | 2 - .../predicate_data_example/Forc.toml | 7 - .../predicate_data_example/src/main.sw | 11 - .../Forc.toml | 2 +- .../predicates/predicate_enums/src/main.sw | 20 + .../predicates/predicate_false/src/main.sw | 5 - .../predicates/predicate_generics/Forc.toml | 7 + .../predicates/predicate_generics/src/main.sw | 21 + .../predicate_signatures/src/main.sw | 4 +- .../predicates/predicate_struct/src/main.sw | 13 - .../Forc.toml | 2 +- .../predicates/predicate_structs/src/main.sw | 14 + .../predicates/predicate_true/src/main.sw | 5 - .../Forc.toml | 2 +- .../predicates/predicate_tuple/src/main.sw | 19 + .../predicates/predicate_u32/src/main.sw | 7 - .../predicates/predicate_vectors/Forc.toml | 7 + .../predicates/predicate_vectors/src/main.sw | 60 ++ 40 files changed, 746 insertions(+), 474 deletions(-) delete mode 100644 packages/fuels-contract/src/predicate.rs create mode 100644 packages/fuels-core/src/offsets.rs rename packages/fuels/tests/predicates/{predicate_false => predicate_basic}/Forc.toml (81%) create mode 100644 packages/fuels/tests/predicates/predicate_basic/src/main.sw delete mode 100644 packages/fuels/tests/predicates/predicate_data_example/.gitignore delete mode 100644 packages/fuels/tests/predicates/predicate_data_example/Forc.toml delete mode 100644 packages/fuels/tests/predicates/predicate_data_example/src/main.sw rename packages/fuels/tests/predicates/{predicate_u32 => predicate_enums}/Forc.toml (81%) create mode 100644 packages/fuels/tests/predicates/predicate_enums/src/main.sw delete mode 100644 packages/fuels/tests/predicates/predicate_false/src/main.sw create mode 100644 packages/fuels/tests/predicates/predicate_generics/Forc.toml create mode 100644 packages/fuels/tests/predicates/predicate_generics/src/main.sw delete mode 100644 packages/fuels/tests/predicates/predicate_struct/src/main.sw rename packages/fuels/tests/predicates/{predicate_struct => predicate_structs}/Forc.toml (80%) create mode 100644 packages/fuels/tests/predicates/predicate_structs/src/main.sw delete mode 100644 packages/fuels/tests/predicates/predicate_true/src/main.sw rename packages/fuels/tests/predicates/{predicate_true => predicate_tuple}/Forc.toml (81%) create mode 100644 packages/fuels/tests/predicates/predicate_tuple/src/main.sw delete mode 100644 packages/fuels/tests/predicates/predicate_u32/src/main.sw create mode 100644 packages/fuels/tests/predicates/predicate_vectors/Forc.toml create mode 100644 packages/fuels/tests/predicates/predicate_vectors/src/main.sw diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0454caed22..9e68258013 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,18 +43,16 @@ jobs: - name: Install forc and forc-fmt run: | - if [[ -z $FORC_PATCH_BRANCH && -z $FORC_PATCH_REVISION ]]; then + if [[ -n $FORC_PATCH_BRANCH ]]; then + cargo install forc forc-fmt --git https://github.com/FuelLabs/sway --branch $FORC_PATCH_BRANCH + elif [[ -n $FORC_PATCH_BRANCH ]]; then + cargo install forc forc-fmt --git https://github.com/FuelLabs/sway --rev $FORC_PATCH_REVISION + else curl -sSLf https://github.com/FuelLabs/sway/releases/download/v${{ env.FORC_VERSION }}/forc-binaries-linux_amd64.tar.gz -L -o forc.tar.gz tar -xvf forc.tar.gz chmod +x forc-binaries/forc mv forc-binaries/forc /usr/local/bin/forc mv forc-binaries/forc-fmt /usr/local/bin/forc-fmt - else - if [[ -n $FORC_PATCH_BRANCH ]]; then - cargo install forc forc-fmt --git https://github.com/FuelLabs/sway --branch $FORC_PATCH_BRANCH - else - cargo install forc forc-fmt --git https://github.com/FuelLabs/sway --rev $FORC_PATCH_REVISION - fi fi - name: Build Sway test projects diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 3a0ace8eb4..438b7d5596 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -37,8 +37,8 @@ - [Estimating cost](./calling-contracts/cost-estimation.md) - [Running scripts](./getting-started/running-scripts.md) - [Predicates](./getting-started/predicates.md) - - [Send and spend funds](./predicates/send-spend-predicate.md) - [Predicate data](./predicates/predicate-data.md) + - [Signatures example](./predicates/send-spend-predicate.md) - [Scripts](./scripts/index.md) - [Running scripts](./scripts/scripts.md) - [Types](./getting-started/types.md) diff --git a/docs/src/getting-started/predicates.md b/docs/src/getting-started/predicates.md index a3d0eb365f..f30242ad28 100644 --- a/docs/src/getting-started/predicates.md +++ b/docs/src/getting-started/predicates.md @@ -4,12 +4,12 @@ Predicates, in Sway, are programs that return a Boolean value, and they do not h ## Instantiating predicates -Similar to contracts, once you've written a predicate in Sway and compiled it with `forc build` (read [here](https://fuellabs.github.io/sway/master/introduction/sway_quickstart.html) for more on how to work with Sway), you'll get the predicate binary. Using the binary, you can instantiate a `predicate` as shown in the code snippet below: +Once you've written a predicate in Sway and compiled it with `forc build`, you can use the `predicate_abigen!` to generate all the types specified in the predicate. Additionally, you will get a `predicate` instance with methods for receiving and spending funds and encoding the predicate data. The code snippet below shows how to use the abigen and generate a predicate instance. ```rust,ignore -{{#include ../../../examples/predicates/src/lib.rs:predicate_load_from}} +{{#include ../../../examples/predicates/src/lib.rs:predicate_load}} ``` -The created `predicate` instance has two fields. The predicate `byte code` and the predicate `address`. This address is generated from the byte code and is the same as the `P2SH` address used in Bitcoin. Users can seamlessly send assets to the predicate address as they do for any other address on the chain. To spend the predicate funds, the user has to provide the original `byte code` of the predicate together with the `predicate data`. The `predicate data` will be used when executing the `byte code`, and if the predicate is validated successfully, the funds will be accessible. +The predicate address is generated from the compiled byte code and is the same as the `P2SH` address used in Bitcoin. Users can seamlessly send assets to the predicate address as they do for any other address on the chain. To spend the predicate funds, the user has to provide the original `byte code` of the predicate together with the `predicate data`. The `predicate data` will be used when executing the `byte code`, and if the predicate is validated successfully, the funds can be transferred. In the next section, we show how to interact with a predicate and explore an example where specific signatures are needed to spend the predicate funds. diff --git a/docs/src/predicates/predicate-data.md b/docs/src/predicates/predicate-data.md index e5b55208b9..f7f4041e4b 100644 --- a/docs/src/predicates/predicate-data.md +++ b/docs/src/predicates/predicate-data.md @@ -3,22 +3,19 @@ Let's consider the following predicate example: ```rust,ignore -{{#include ../../../packages/fuels/tests/predicates/predicate_data_example/src/main.sw}} +{{#include ../../../packages/fuels/tests/predicates/predicate_basic/src/main.sw}} ``` -With the Fuel Rust SDK, You can encode and send the predicate data through `Predicate`'s `encode_data()`: +Similarly to contracts and scripts, the `predicate_abigen!` is generating an function that will conveniently encode all the arguments of the main function for us. This function is called `encode_data` and it is accessed through the predicate instance as shown below: ```rust,ignore {{#include ../../../examples/predicates/src/lib.rs:encode_predicate_data}} ``` -Keep on reading for the full example. +Next we will have a look on a complete example on how we can use the SDK to send and receive funds from a predicate. -Notice how this predicate uses `input_predicate_data()`, a way for the predicate code to read the data the caller passed to it. -Like everything else in the FuelVM, this data follows the ABI encoding/decoding specification. When using the Fuel Rust SDK to pass data to this predicate, you must encode it properly. - -Here's how you can do it. First, we set up the wallets, node, and predicate code: +First, we set up the wallets, node, and a predicate instance: ```rust,ignore {{#include ../../../examples/predicates/src/lib.rs:predicate_data_setup}} @@ -32,10 +29,8 @@ Next, we lock some assets in this predicate using the first wallet: Then, we try to unlock the amount and spend it using the second wallet, effectively sending the previously locked value to itself. -The predicate expects the data sent to it to be a `u64` type with the value `42`. +The predicate expects the data sent to it to be two numbers (`u32` and `u64`) whit matching values. ```rust,ignore {{#include ../../../examples/predicates/src/lib.rs:predicate_data_unlock}} ``` - -> **Note:** if the data you're encoding is already a `Vec`, e.g., in the [send and spend examples](./send-spend-predicate.md), then you don't need to call `encode_predicate_data()`, passing it as-is works. diff --git a/docs/src/predicates/send-spend-predicate.md b/docs/src/predicates/send-spend-predicate.md index a3764a01bd..109449fa93 100644 --- a/docs/src/predicates/send-spend-predicate.md +++ b/docs/src/predicates/send-spend-predicate.md @@ -1,14 +1,14 @@ -# Send and spend funds from predicates +# Signatures in predicates example + +This is a more involved example where the predicate accepts three signatures and matches them to three predefined public keys. The `ec_recover_address` function is used to recover the public key from the signatures. If two of three extracted public keys match the predefined public keys, the funds can be spent. Note that the signature order has to match the order of the predefined public keys. -Let's consider the following predicate example: ```rust,ignore {{#include ../../../packages/fuels/tests/predicates/predicate_signatures/src/main.sw}} ``` -This predicate accepts three signatures and matches them to three predefined public keys. The `ec_recover_address` function is used to recover the public key from the signatures. If two of three extracted public keys match the predefined public keys, the funds can be spent. Note that the signature order has to match the order of the predefined public keys. -Let's use the SDK to interact with the predicate. First, let's create three wallets with specific keys. Their hashed public keys are already hard-coded in the predicate. +Let's use the SDK to interact with the predicate. First, let's create three wallets with specific keys. Their hashed public keys are already hard-coded in the predicate. Then we create the receiver wallet which we will use to spend the predicate funds. ```rust,ignore {{#include ../../../examples/predicates/src/lib.rs:predicate_wallets}} @@ -20,16 +20,16 @@ Next, let's add some coins, start a provider and connect it with the wallets. {{#include ../../../examples/predicates/src/lib.rs:predicate_coins}} ``` -Now we can load the predicate binary, and prepare some transaction variables. +Now we can use the predicate abigen, which will create a predicate instance for us. ```rust,ignore {{#include ../../../examples/predicates/src/lib.rs:predicate_load}} ``` -After the predicate address is generated we can send funds to it. Note that we are using the same `transfer` function as we used when sending funds to other wallets. We also make sure that the funds are indeed transferred. +After the predicate instance is generated we can use the `receive_from_wallet` function to transfer funds to the predicate. We also make sure that the funds are indeed transferred. ```rust,ignore -{{#include ../../../examples/predicates/src/lib.rs:predicate_send}} +{{#include ../../../examples/predicates/src/lib.rs:predicate_receive}} ``` To spend the funds that are now locked in the predicate, we have to provide two out of three signatures whose public keys match the ones we defined in the predicate. In this example, the signatures are generated from an array of zeros. @@ -38,7 +38,7 @@ To spend the funds that are now locked in the predicate, we have to provide two {{#include ../../../examples/predicates/src/lib.rs:predicate_signatures}} ``` -After generating the signatures, we can send a transaction to spend the predicate funds. We use the `receiver` wallet as the recipient. We have to provide the predicate byte code and the required signatures. As we provide the correct data, we receive the funds and verify that the amount is correct. +After generating the signatures, we can use the predicate's `encode_data` and `spend_to_wallet` functions to spend the funds. If the provided data is correct the `receiver` wallet will get the funds and we verify that the amount is indeed correct. ```rust,ignore {{#include ../../../examples/predicates/src/lib.rs:predicate_spend}} diff --git a/examples/predicates/src/lib.rs b/examples/predicates/src/lib.rs index a888eb888d..c450e8e60c 100644 --- a/examples/predicates/src/lib.rs +++ b/examples/predicates/src/lib.rs @@ -5,9 +5,7 @@ mod tests { #[tokio::test] async fn predicate_example() -> Result<(), Error> { - use fuels::contract::predicate::Predicate; - use fuels::prelude::*; - use fuels::signers::fuel_crypto::SecretKey; + use fuels::core::vm::prelude::SecretKey; // ANCHOR: predicate_wallets let secret_key1: SecretKey = @@ -28,89 +26,86 @@ mod tests { let mut wallet = WalletUnlocked::new_from_private_key(secret_key1, None); let mut wallet2 = WalletUnlocked::new_from_private_key(secret_key2, None); let mut wallet3 = WalletUnlocked::new_from_private_key(secret_key3, None); - let receiver = WalletUnlocked::new_random(None); + let mut receiver = WalletUnlocked::new_random(None); // ANCHOR_END: predicate_wallets // ANCHOR: predicate_coins - let all_coins = [&wallet, &wallet2, &wallet3] + let asset_id = AssetId::default(); + let num_coins = 32; + let amount = 64; + let initial_balance = amount * num_coins; + let all_coins = [&wallet, &wallet2, &wallet3, &receiver] .iter() .flat_map(|wallet| { - setup_single_asset_coins(wallet.address(), AssetId::default(), 10, 1_000_000) + setup_single_asset_coins(wallet.address(), asset_id, num_coins, amount) }) .collect::>(); - let (provider, _) = setup_test_provider( - all_coins, - vec![], - Some(Config { - utxo_validation: true, - ..Config::local_node() - }), - None, - ) - .await; - - [&mut wallet, &mut wallet2, &mut wallet3] + let (provider, _) = setup_test_provider(all_coins, vec![], None, None).await; + + [&mut wallet, &mut wallet2, &mut wallet3, &mut receiver] .iter_mut() .for_each(|wallet| wallet.set_provider(provider.clone())); // ANCHOR_END: predicate_coins // ANCHOR: predicate_load - // ANCHOR: predicate_load_from - let predicate = Predicate::load_from( + predicate_abigen!( + MyPredicate, + "packages/fuels/tests/predicates/predicate_signatures/out/debug/predicate_signatures-abi.json" + ); + + let predicate = MyPredicate::load_from( "../../packages/fuels/tests/predicates/predicate_signatures/out/debug/predicate_signatures.bin", )?; - - let predicate_code = predicate.code(); - let predicate_address = predicate.address(); - // ANCHOR_END: predicate_load_from - let amount_to_predicate = 1000; - let asset_id = AssetId::default(); // ANCHOR_END: predicate_load - // ANCHOR: predicate_send - wallet - .transfer( - predicate_address, - amount_to_predicate, - asset_id, - TxParameters::default(), - ) + // ANCHOR: predicate_receive + let amount_to_predicate = 512; + + predicate + .receive_from_wallet(&wallet, amount_to_predicate, asset_id, None) .await?; let predicate_balance = provider .get_asset_balance(predicate.address(), asset_id) .await?; assert_eq!(predicate_balance, amount_to_predicate); - // ANCHOR_END: predicate_send + // ANCHOR_END: predicate_receive // ANCHOR: predicate_signatures let data_to_sign = [0; 32]; - let signature1 = wallet.sign_message(data_to_sign).await?.to_vec(); - let signature2 = wallet2.sign_message(data_to_sign).await?.to_vec(); - let signature3 = wallet3.sign_message(data_to_sign).await?.to_vec(); - - let signatures = vec![signature1, signature2, signature3]; + let signature1: B512 = wallet + .sign_message(data_to_sign) + .await? + .as_ref() + .try_into()?; + let signature2: B512 = wallet2 + .sign_message(data_to_sign) + .await? + .as_ref() + .try_into()?; + let signature3: B512 = wallet3 + .sign_message(data_to_sign) + .await? + .as_ref() + .try_into()?; + + let signatures = [signature1, signature2, signature3]; // ANCHOR_END: predicate_signatures // ANCHOR: predicate_spend - let predicate_data = signatures.into_iter().flatten().collect(); - wallet - .spend_predicate( - predicate_address, - predicate_code, - amount_to_predicate, - asset_id, - receiver.address(), - predicate_data, - TxParameters::default(), - ) + predicate + .encode_data(signatures) + .spend_to_wallet(&receiver, amount_to_predicate, asset_id, None) .await?; let receiver_balance_after = provider .get_asset_balance(receiver.address(), asset_id) .await?; - assert_eq!(amount_to_predicate, receiver_balance_after); + assert_eq!( + initial_balance + amount_to_predicate, + receiver_balance_after + ); let predicate_balance = provider .get_asset_balance(predicate.address(), asset_id) @@ -124,79 +119,65 @@ mod tests { #[tokio::test] async fn predicate_data_example() -> Result<(), Error> { // ANCHOR: predicate_data_setup - let provider_config = Config { - utxo_validation: true, - ..Config::local_node() - }; - + let asset_id = AssetId::default(); let wallets_config = WalletsConfig::new_multiple_assets( 2, vec![AssetConfig { - id: AssetId::default(), + id: asset_id, num_coins: 1, coin_amount: 1_000, }], ); - let wallets = - &launch_custom_provider_and_get_wallets(wallets_config, Some(provider_config), None) - .await; + let wallets = &launch_custom_provider_and_get_wallets(wallets_config, None, None).await; let first_wallet = &wallets[0]; let second_wallet = &wallets[1]; - let predicate = Predicate::load_from( "../../packages/fuels/tests/predicates/predicate_data_example/out/debug/predicate_data_example.bin")?; + predicate_abigen!( + MyPredicate, + "packages/fuels/tests/predicates/predicate_basic/out/debug/predicate_basic-abi.json" + ); - let predicate_code = predicate.code(); - let predicate_address = predicate.address(); + let predicate = MyPredicate::load_from( + "../../packages/fuels/tests/predicates/predicate_basic/out/debug/predicate_basic.bin", + )?; // ANCHOR_END: predicate_data_setup // ANCHOR: predicate_data_lock_amount // First wallet transfers amount to predicate. - let _result = first_wallet - .transfer( - predicate_address, - 500, - AssetId::default(), - TxParameters::default(), - ) + predicate + .receive_from_wallet(first_wallet, 500, asset_id, None) .await?; // Check predicate balance. let balance = first_wallet .get_provider()? - .get_asset_balance(predicate_address, AssetId::default()) + .get_asset_balance(predicate.address(), asset_id) .await?; assert_eq!(balance, 500); // ANCHOR_END: predicate_data_lock_amount - - // ANCHOR: predicate_data_unlock - // We use the Predicate's `encode_data()` to encode the data we want to - // send to the predicate. - + // // ANCHOR: encode_predicate_data - let predicate_data: Vec = predicate.encode_data(42_u64)?; + let predicate = predicate.encode_data(4096, 4096); // ANCHOR_END: encode_predicate_data + // ANCHOR: predicate_data_unlock + // We use the Predicate's `encode_data()` to encode the data we want to + // send to the predicate. This is a builder pattern and the function + // returns a new predicate. let amount_to_unlock = 500; - let _result = second_wallet - .spend_predicate( - predicate_address, - predicate_code, - amount_to_unlock, - AssetId::default(), - second_wallet.address(), - predicate_data, - TxParameters::default(), - ) + predicate + .encode_data(4096, 4096) + .spend_to_wallet(second_wallet, amount_to_unlock, asset_id, None) .await?; // Predicate balance is zero. let balance = first_wallet .get_provider()? - .get_asset_balance(predicate_address, AssetId::default()) + .get_asset_balance(predicate.address(), AssetId::default()) .await?; assert_eq!(balance, 0); diff --git a/packages/fuels-contract/src/contract_calls_utils.rs b/packages/fuels-contract/src/contract_calls_utils.rs index a78caa303d..cd2244b420 100644 --- a/packages/fuels-contract/src/contract_calls_utils.rs +++ b/packages/fuels-contract/src/contract_calls_utils.rs @@ -1,7 +1,5 @@ -use fuel_gql_client::fuel_tx::{ - field::Script as ScriptField, ConsensusParameters, Input, Output, TxPointer, UtxoId, -}; -use fuel_gql_client::fuel_types::{bytes::padded_len_usize, Immediate18, Word}; +use fuel_gql_client::fuel_tx::{Input, Output, TxPointer, UtxoId}; +use fuel_gql_client::fuel_types::{Immediate18, Word}; use fuel_gql_client::fuel_vm::{consts::REG_ONE, prelude::Opcode}; use fuel_tx::{AssetId, Bytes32, ContractId}; use fuels_core::constants::BASE_ASSET_ID; @@ -144,7 +142,7 @@ pub(crate) fn build_script_data_from_contract_calls( /// /// Note that these are soft rules as we're picking this addresses simply because they /// non-reserved register. -fn get_single_call_instructions(offsets: &CallOpcodeParamsOffset) -> Vec { +pub(crate) fn get_single_call_instructions(offsets: &CallOpcodeParamsOffset) -> Vec { let instructions = vec![ Opcode::MOVI(0x10, offsets.call_data_offset as Immediate18), Opcode::MOVI(0x11, offsets.gas_forwarded_offset as Immediate18), @@ -258,38 +256,6 @@ fn convert_to_signed_resources(spendable_resources: Vec) -> Vec .collect() } -/// Gets the base offset for a script. The offset depends on the `max_inputs` -/// field of the `ConsensusParameters` and the static offset -pub fn get_base_script_offset(consensus_parameters: &ConsensusParameters) -> usize { - consensus_parameters.tx_offset() + fuel_tx::Script::script_offset_static() -} - -/// Gets the base offset for a predicate. The offset depends on the `max_inputs` -/// field of the `ConsensusParameters` and the static offset of a transfer script -pub fn get_predicate_offset(consensus_parameters: &ConsensusParameters) -> usize { - // Opcode::LEN is a placeholder for the RET instruction which is - // added for transfer transactions - let opcode_len = Opcode::LEN; - - get_base_script_offset(consensus_parameters) + padded_len_usize(opcode_len) -} - -/// Calculates the length of the script based on the number of contract calls it -/// has to make and returns the offset at which the script data begins -pub(crate) fn get_data_offset( - consensus_parameters: &ConsensusParameters, - num_calls: usize, -) -> usize { - // use placeholder for call param offsets, we only care about the length - let len_script = - get_single_call_instructions(&CallOpcodeParamsOffset::default()).len() * num_calls; - - // Opcode::LEN is a placeholder for the RET instruction which is added later - let opcode_len = Opcode::LEN; - - get_base_script_offset(consensus_parameters) + padded_len_usize(len_script + opcode_len) -} - fn generate_contract_inputs(contract_ids: HashSet) -> Vec { contract_ids .into_iter() diff --git a/packages/fuels-contract/src/execution_script.rs b/packages/fuels-contract/src/execution_script.rs index 7907f5cf81..f57726279a 100644 --- a/packages/fuels-contract/src/execution_script.rs +++ b/packages/fuels-contract/src/execution_script.rs @@ -1,10 +1,11 @@ +use crate::contract_calls_utils::{get_single_call_instructions, CallOpcodeParamsOffset}; use anyhow::Result; use std::fmt::Debug; use fuel_gql_client::fuel_tx::{Receipt, Transaction}; use fuel_tx::{AssetId, Checkable, ScriptExecutionResult}; -use fuels_core::parameters::TxParameters; +use fuels_core::{offsets::call_script_data_offset, parameters::TxParameters}; use fuels_signers::provider::Provider; use fuels_signers::{Signer, WalletUnlocked}; @@ -14,8 +15,8 @@ use std::vec; use crate::contract::ContractCall; use crate::contract_calls_utils::{ - build_script_data_from_contract_calls, calculate_required_asset_amounts, get_data_offset, - get_instructions, get_transaction_inputs_outputs, + build_script_data_from_contract_calls, calculate_required_asset_amounts, get_instructions, + get_transaction_inputs_outputs, }; /// [`TransactionExecution`] provides methods to create and call/simulate a transaction that carries @@ -39,7 +40,13 @@ impl ExecutableFuelCall { wallet: &WalletUnlocked, ) -> Result { let consensus_parameters = wallet.get_provider()?.consensus_parameters().await?; - let data_offset = get_data_offset(&consensus_parameters, calls.len()); + + // Calculate instructions lenght for call instructions + // Use placeholder for call param offsets, we only care about the length + let calls_instructions_len = + get_single_call_instructions(&CallOpcodeParamsOffset::default()).len() * calls.len(); + + let data_offset = call_script_data_offset(&consensus_parameters, calls_instructions_len); let (script_data, call_param_offsets) = build_script_data_from_contract_calls(calls, data_offset, tx_parameters.gas_limit); diff --git a/packages/fuels-contract/src/lib.rs b/packages/fuels-contract/src/lib.rs index 26a535407e..c8249a8472 100644 --- a/packages/fuels-contract/src/lib.rs +++ b/packages/fuels-contract/src/lib.rs @@ -3,7 +3,6 @@ pub mod contract; pub mod contract_calls_utils; pub mod execution_script; pub mod logs; -pub mod predicate; pub mod script_calls; pub mod abi_encoder { diff --git a/packages/fuels-contract/src/predicate.rs b/packages/fuels-contract/src/predicate.rs deleted file mode 100644 index 993a545195..0000000000 --- a/packages/fuels-contract/src/predicate.rs +++ /dev/null @@ -1,35 +0,0 @@ -use fuel_gql_client::fuel_tx::{Address, Contract}; -use fuels_core::{abi_encoder::ABIEncoder, Tokenizable}; -use fuels_types::{bech32::Bech32Address, errors::Error}; - -pub struct Predicate { - address: Bech32Address, - code: Vec, -} - -impl Predicate { - pub fn new(code: Vec) -> Self { - let address: Address = (*Contract::root_from_code(&code)).into(); - Self { - address: address.into(), - code, - } - } - - pub fn load_from(file_path: &str) -> Result { - Ok(Predicate::new(std::fs::read(file_path)?)) - } - - pub fn address(&self) -> &Bech32Address { - &self.address - } - - pub fn code(&self) -> Vec { - self.code.clone() - } - - /// Encode the predicate data with the given arguments. - pub fn encode_data(&self, data: D) -> Result, Error> { - Ok(ABIEncoder::encode(&[data.into_token()])?.resolve(0)) - } -} diff --git a/packages/fuels-contract/src/script_calls.rs b/packages/fuels-contract/src/script_calls.rs index 81d421f743..41a6f08a33 100644 --- a/packages/fuels-contract/src/script_calls.rs +++ b/packages/fuels-contract/src/script_calls.rs @@ -2,7 +2,6 @@ use crate::{ abi_encoder::UnresolvedBytes, call_response::FuelCallResponse, contract::get_decoded_output, - contract_calls_utils::get_base_script_offset, execution_script::ExecutableFuelCall, logs::{decode_revert_error, LogDecoder}, }; @@ -12,6 +11,7 @@ use fuel_gql_client::{ }; use fuel_tx::Input; use fuels_core::{ + offsets::base_script_offset, parameters::{CallParameters, TxParameters}, Tokenizable, }; @@ -110,7 +110,7 @@ where /// Compute the script data by calculating the script offset and resolving the encoded arguments async fn compute_script_data(&self) -> Result, Error> { let consensus_parameters = self.provider.consensus_parameters().await?; - let script_offset = get_base_script_offset(&consensus_parameters) + let script_offset = base_script_offset(&consensus_parameters) + padded_len_usize(self.script_call.script_binary.len()); Ok(self.script_call.encoded_args.resolve(script_offset as u64)) diff --git a/packages/fuels-core/Cargo.toml b/packages/fuels-core/Cargo.toml index e77db1a64f..3243e0cc94 100644 --- a/packages/fuels-core/Cargo.toml +++ b/packages/fuels-core/Cargo.toml @@ -12,6 +12,7 @@ description = "Fuel Rust SDK core." Inflector = "0.11" anyhow = "1" fuel-tx = "0.23" +fuel-vm = "0.22" fuel-types = "0.5" fuels-types = { version = "0.32.1", path = "../fuels-types" } hex = { version = "0.4.3", features = ["std"] } diff --git a/packages/fuels-core/src/code_gen/abigen.rs b/packages/fuels-core/src/code_gen/abigen.rs index 767267fc2f..faf16b43ca 100644 --- a/packages/fuels-core/src/code_gen/abigen.rs +++ b/packages/fuels-core/src/code_gen/abigen.rs @@ -287,12 +287,6 @@ impl Abigen { self.data.clone() } - /// Compute the predicate data by calculating the predicate offset and resolving the encoded arguments - async fn get_offset(&self, provider: &Provider) -> Result { - let consensus_parameters = provider.consensus_parameters().await?; - Ok(get_predicate_offset(&consensus_parameters) as u64) - } - pub async fn receive_from_wallet(&self, wallet: &WalletUnlocked, amount:u64, asset_id: AssetId, tx_parameters: Option) -> Result<(String, Vec), SDKError> { let tx_parameters = tx_parameters.unwrap_or(TxParameters::default()); wallet @@ -315,7 +309,6 @@ impl Abigen { asset_id, self.data(), tx_parameters, - self.get_offset(wallet.get_provider()?).await? ) .await } @@ -383,11 +376,9 @@ impl Abigen { AbigenType::Predicate => quote! { use fuels::{ core::{abi_encoder::{ABIEncoder, UnresolvedBytes}, parameters::TxParameters}, - contract::contract_calls_utils::get_predicate_offset, tx::{Contract, AssetId}, signers::provider::Provider }; - use fuel_gql_client:: fuel_types::bytes::padded_len_usize; }, }; quote! { diff --git a/packages/fuels-core/src/lib.rs b/packages/fuels-core/src/lib.rs index de811d666a..b461a9f6e6 100644 --- a/packages/fuels-core/src/lib.rs +++ b/packages/fuels-core/src/lib.rs @@ -13,6 +13,7 @@ pub mod abi_decoder; pub mod abi_encoder; pub mod code_gen; pub mod constants; +pub mod offsets; pub mod parameters; pub mod rustfmt; pub mod source; @@ -25,6 +26,10 @@ pub mod tx { pub use fuel_tx::*; } +pub mod vm { + pub use fuel_vm::*; +} + pub type ByteArray = [u8; 8]; pub type Selector = ByteArray; pub type EnumSelector = (u8, Token, EnumVariants); diff --git a/packages/fuels-core/src/offsets.rs b/packages/fuels-core/src/offsets.rs new file mode 100644 index 0000000000..5ddd6d2626 --- /dev/null +++ b/packages/fuels-core/src/offsets.rs @@ -0,0 +1,46 @@ +use crate::tx::{field::Script, ConsensusParameters, InputRepr}; +use fuel_types::bytes::padded_len_usize; +use fuel_vm::prelude::Opcode; + +/// Gets the base offset for a script. The offset depends on the `max_inputs` +/// field of the `ConsensusParameters` and the static offset +pub fn base_script_offset(consensus_parameters: &ConsensusParameters) -> usize { + consensus_parameters.tx_offset() + fuel_tx::Script::script_offset_static() +} + +/// Gets the base offset for a predicate. The offset depends on the `max_inputs` +/// field of the `ConsensusParameters` and the static offset of a transfer script +pub fn base_predicate_offset(consensus_parameters: &ConsensusParameters) -> usize { + // Opcode::LEN is a placeholder for the RET instruction which is + // added for transfer transactions + let opcode_len = Opcode::LEN; + + base_script_offset(consensus_parameters) + padded_len_usize(opcode_len) +} + +/// Calculates the length of the script based on the number of contract calls it +/// has to make and returns the offset at which the script data begins +pub fn call_script_data_offset( + consensus_parameters: &ConsensusParameters, + calls_instructions_len: usize, +) -> usize { + // Opcode::LEN is a placeholder for the RET instruction which is added later + let opcode_len = Opcode::LEN; + + base_script_offset(consensus_parameters) + padded_len_usize(calls_instructions_len + opcode_len) +} + +pub fn coin_predicate_data_offset(code_len: usize) -> usize { + InputRepr::Coin + .coin_predicate_offset() + .expect("should have predicate offset") + + padded_len_usize(code_len) +} + +pub fn message_predicate_data_offset(message_data_len: usize, code_len: usize) -> usize { + InputRepr::Message + .data_offset() + .expect("should have data offset") + + padded_len_usize(message_data_len) + + padded_len_usize(code_len) +} diff --git a/packages/fuels-core/src/types/bits.rs b/packages/fuels-core/src/types/bits.rs index dee1b26cbe..6f498c0555 100644 --- a/packages/fuels-core/src/types/bits.rs +++ b/packages/fuels-core/src/types/bits.rs @@ -65,6 +65,19 @@ impl From<(Bits256, Bits256)> for B512 { } } +impl TryFrom<&[u8]> for B512 { + type Error = Error; + + fn try_from(slice: &[u8]) -> Result { + Ok(B512 { + bytes: [ + Bits256(slice[0..32].try_into()?), + Bits256(slice[32..].try_into()?), + ], + }) + } +} + impl Parameterize for B512 { fn param_type() -> ParamType { ParamType::Struct { diff --git a/packages/fuels-signers/src/wallet.rs b/packages/fuels-signers/src/wallet.rs index 8135a2f300..09a92ad633 100644 --- a/packages/fuels-signers/src/wallet.rs +++ b/packages/fuels-signers/src/wallet.rs @@ -13,10 +13,13 @@ use fuel_gql_client::{ }, fuel_vm::{consts::REG_ONE, prelude::Opcode}, }; -use fuel_types::bytes::{padded_len_usize, WORD_SIZE}; +use fuel_types::bytes::WORD_SIZE; use fuel_types::{Address, MessageId}; -use fuels_core::abi_encoder::UnresolvedBytes; use fuels_core::tx::{field, Chargeable, Script, Transaction, UniqueIdentifier}; +use fuels_core::{ + abi_encoder::UnresolvedBytes, + offsets::{base_predicate_offset, coin_predicate_data_offset, message_predicate_data_offset}, +}; use fuels_core::{constants::BASE_ASSET_ID, parameters::TxParameters}; use fuels_types::bech32::{Bech32Address, Bech32ContractId, FUEL_BECH32_HRP}; use fuels_types::errors::Error; @@ -656,21 +659,6 @@ impl WalletUnlocked { .and_then(|m| m.message_id()) } - fn get_coin_predicate_data_offset(code_len: usize) -> u64 { - fuel_gql_client::fuel_tx::InputRepr::Coin - .coin_predicate_offset() - .expect("should have predicate offset") as u64 - + padded_len_usize(code_len) as u64 - } - - fn get_message_predicate_data_offset(message_data_len: usize, code_len: usize) -> u64 { - fuel_gql_client::fuel_tx::InputRepr::Message - .data_offset() - .expect("should have data offset") as u64 - + padded_len_usize(message_data_len) as u64 - + padded_len_usize(code_len) as u64 - } - #[allow(clippy::too_many_arguments)] pub async fn spend_predicate( &self, @@ -681,10 +669,9 @@ impl WalletUnlocked { to: &Bech32Address, predicate_data: UnresolvedBytes, tx_parameters: TxParameters, - base_offset: u64, ) -> Result, Error> { - let spendable_predicate_resources = self - .get_provider()? + let predicate = self.get_provider()?; + let spendable_predicate_resources = predicate .get_spendable_resources(predicate_address, asset_id, amount) .await?; @@ -697,24 +684,23 @@ impl WalletUnlocked { // Iterate through the spendable resources and calculate the appropriate offsets // for the coin or message predicates - let mut offset = base_offset; + let mut offset = base_predicate_offset(&predicate.consensus_parameters().await?); let inputs = spendable_predicate_resources .into_iter() .map(|resource| match resource { Resource::Coin(coin) => { - offset += Self::get_coin_predicate_data_offset(code.len()); + offset += coin_predicate_data_offset(code.len()); - let data = predicate_data.clone().resolve(offset); - offset += data.len() as u64; + let data = predicate_data.clone().resolve(offset as u64); + offset += data.len(); self.create_coin_predicate(coin, asset_id, code.clone(), data) } Resource::Message(message) => { - offset += - Self::get_message_predicate_data_offset(message.data.len(), code.len()); + offset += message_predicate_data_offset(message.data.len(), code.len()); - let data = predicate_data.clone().resolve(offset); - offset += data.len() as u64; + let data = predicate_data.clone().resolve(offset as u64); + offset += data.len(); self.create_message_predicate(message, code.clone(), data) } @@ -731,7 +717,7 @@ impl WalletUnlocked { self.add_fee_coins(&mut tx, 0, 0).await?; self.sign_transaction(&mut tx).await?; - self.get_provider()?.send_transaction(&tx).await + predicate.send_transaction(&tx).await } fn create_coin_predicate( @@ -779,7 +765,6 @@ impl WalletUnlocked { asset_id: AssetId, predicate_data: UnresolvedBytes, tx_parameters: TxParameters, - offset: u64, ) -> Result, Error> { self.spend_predicate( predicate_address, @@ -789,7 +774,6 @@ impl WalletUnlocked { self.address(), predicate_data, tx_parameters, - offset, ) .await } diff --git a/packages/fuels/src/lib.rs b/packages/fuels/src/lib.rs index f5afc1799b..6794ffb892 100644 --- a/packages/fuels/src/lib.rs +++ b/packages/fuels/src/lib.rs @@ -65,7 +65,6 @@ pub mod prelude { pub use super::contract::{ contract::{Contract, MultiContractCallHandler}, logs::LogDecoder, - predicate::Predicate, }; pub use super::core::constants::*; pub use super::core::parameters::*; diff --git a/packages/fuels/tests/predicates.rs b/packages/fuels/tests/predicates.rs index 3f0d788176..d71d030591 100644 --- a/packages/fuels/tests/predicates.rs +++ b/packages/fuels/tests/predicates.rs @@ -95,11 +95,11 @@ async fn transfer_coins_and_messages_to_predicate() -> Result<(), Error> { predicate_abigen!( MyPredicate, - "packages/fuels/tests/predicates/predicate_struct/out/debug/predicate_struct-abi.json" + "packages/fuels/tests/predicates/predicate_basic/out/debug/predicate_basic-abi.json" ); let predicate = - MyPredicate::load_from("tests/predicates/predicate_struct/out/debug/predicate_struct.bin")?; + MyPredicate::load_from("tests/predicates/predicate_basic/out/debug/predicate_basic.bin")?; predicate .receive_from_wallet(&wallet, total_balance, asset_id, None) @@ -112,7 +112,259 @@ async fn transfer_coins_and_messages_to_predicate() -> Result<(), Error> { } #[tokio::test] -async fn spend_predicate_coins_messages_vector_args() -> Result<(), Error> { +async fn spend_predicate_coins_messages_basic() -> Result<(), Error> { + predicate_abigen!( + MyPredicate, + "packages/fuels/tests/predicates/predicate_basic/out/debug/predicate_basic-abi.json" + ); + + let predicate = + MyPredicate::load_from("tests/predicates/predicate_basic/out/debug/predicate_basic.bin")?; + + let num_coins = 4; + let num_messages = 8; + let amount = 16; + let (provider, predicate_balance, receiver, receiver_balance, asset_id) = + setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?; + + // Run predicate with wrong data + predicate + .encode_data(4096, 4097) + .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .await + .expect_err("Should error"); + + // No funds were transferred + assert_address_balance(receiver.address(), &provider, asset_id, receiver_balance).await; + + predicate + .encode_data(4096, 4096) + .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .await?; + + // The predicate has spent the funds + assert_address_balance(predicate.address(), &provider, asset_id, 0).await; + + // Funds were transferred + assert_address_balance( + receiver.address(), + &provider, + asset_id, + receiver_balance + predicate_balance, + ) + .await; + + Ok(()) +} + +#[tokio::test] +async fn spend_predicate_coins_messages_address() -> Result<(), Error> { + predicate_abigen!( + MyPredicate, + "packages/fuels/tests/predicates/predicate_address/out/debug/predicate_address-abi.json" + ); + + let predicate = MyPredicate::load_from( + "tests/predicates/predicate_address/out/debug/predicate_address.bin", + )?; + + let num_coins = 4; + let num_messages = 8; + let amount = 16; + let (provider, predicate_balance, receiver, receiver_balance, asset_id) = + setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?; + + let wrong_addr: Address = "0x7f86afa9696cf0dc6385e2c407a6e159a1103cefb7e2ae0636fb33d3cb2a9e4a" + .parse() + .unwrap(); + + // Run predicate with wrong data + predicate + .encode_data(wrong_addr) + .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .await + .expect_err("Should error"); + + // No funds were transferred + assert_address_balance(receiver.address(), &provider, asset_id, receiver_balance).await; + + let addr: Address = "0xef86afa9696cf0dc6385e2c407a6e159a1103cefb7e2ae0636fb33d3cb2a9e4a" + .parse() + .unwrap(); + + predicate + .encode_data(addr) + .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .await?; + + // The predicate has spent the funds + assert_address_balance(predicate.address(), &provider, asset_id, 0).await; + + // Funds were transferred + assert_address_balance( + receiver.address(), + &provider, + asset_id, + receiver_balance + predicate_balance, + ) + .await; + + Ok(()) +} + +#[tokio::test] +async fn spend_predicate_coins_messages_enums() -> Result<(), Error> { + predicate_abigen!( + MyPredicate, + "packages/fuels/tests/predicates/predicate_enums/out/debug/predicate_enums-abi.json" + ); + + let predicate = + MyPredicate::load_from("tests/predicates/predicate_enums/out/debug/predicate_enums.bin")?; + + let num_coins = 4; + let num_messages = 8; + let amount = 16; + let (provider, predicate_balance, receiver, receiver_balance, asset_id) = + setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?; + + // Run predicate with wrong data + predicate + .encode_data(TestEnum::A(32), AnotherTestEnum::A(32)) + .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .await + .expect_err("Should error"); + + // No funds were transferred + assert_address_balance(receiver.address(), &provider, asset_id, receiver_balance).await; + + predicate + .encode_data(TestEnum::A(32), AnotherTestEnum::B(32)) + .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .await?; + + // The predicate has spent the funds + assert_address_balance(predicate.address(), &provider, asset_id, 0).await; + + // Funds were transferred + assert_address_balance( + receiver.address(), + &provider, + asset_id, + receiver_balance + predicate_balance, + ) + .await; + + Ok(()) +} + +#[tokio::test] +async fn spend_predicate_coins_messages_structs() -> Result<(), Error> { + predicate_abigen!( + MyPredicate, + "packages/fuels/tests/predicates/predicate_structs/out/debug/predicate_structs-abi.json" + ); + + let predicate = MyPredicate::load_from( + "tests/predicates/predicate_structs/out/debug/predicate_structs.bin", + )?; + + let num_coins = 4; + let num_messages = 8; + let amount = 16; + let (provider, predicate_balance, receiver, receiver_balance, asset_id) = + setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?; + + // Run predicate with wrong data + predicate + .encode_data( + TestStruct { value: 191 }, + AnotherTestStruct { + value: 63, + number: 127, + }, + ) + .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .await + .expect_err("Should error"); + + // No funds were transferred + assert_address_balance(receiver.address(), &provider, asset_id, receiver_balance).await; + + predicate + .encode_data( + TestStruct { value: 192 }, + AnotherTestStruct { + value: 64, + number: 128, + }, + ) + .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .await?; + + // The predicate has spent the funds + assert_address_balance(predicate.address(), &provider, asset_id, 0).await; + + // Funds were transferred + assert_address_balance( + receiver.address(), + &provider, + asset_id, + receiver_balance + predicate_balance, + ) + .await; + + Ok(()) +} + +#[tokio::test] +async fn spend_predicate_coins_messages_tuple() -> Result<(), Error> { + predicate_abigen!( + MyPredicate, + "packages/fuels/tests/predicates/predicate_tuple/out/debug/predicate_tuple-abi.json" + ); + + let predicate = + MyPredicate::load_from("tests/predicates/predicate_tuple/out/debug/predicate_tuple.bin")?; + + let num_coins = 4; + let num_messages = 8; + let amount = 16; + let (provider, predicate_balance, receiver, receiver_balance, asset_id) = + setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?; + + // Run predicate with wrong data + predicate + .encode_data((15, TestStruct { value: 31 }, TestEnum::Value(63)), 127) + .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .await + .expect_err("Should error"); + + // No funds were transferred + assert_address_balance(receiver.address(), &provider, asset_id, receiver_balance).await; + + predicate + .encode_data((16, TestStruct { value: 32 }, TestEnum::Value(64)), 128) + .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .await?; + + // The predicate has spent the funds + assert_address_balance(predicate.address(), &provider, asset_id, 0).await; + + // Funds were transferred + assert_address_balance( + receiver.address(), + &provider, + asset_id, + receiver_balance + predicate_balance, + ) + .await; + + Ok(()) +} + +#[tokio::test] +async fn spend_predicate_coins_messages_vector() -> Result<(), Error> { predicate_abigen!( MyPredicate, "packages/fuels/tests/predicates/predicate_vector/out/debug/predicate_vector-abi.json" @@ -157,176 +409,148 @@ async fn spend_predicate_coins_messages_vector_args() -> Result<(), Error> { Ok(()) } -// #[tokio::test] -// async fn spend_predicate_with_vector_args() -> Result<(), Error> { -// let num_coins = 2; -// let amount = 4; -// let (sender, receiver, provider, asset_id) = -// setup_predicate_coin_test(num_coins, amount).await?; -// let initial_balance = num_coins * amount; - -// let transfer_amount = initial_balance / 2; - -// predicate_abigen!( -// MyPredicate, -// "packages/fuels/tests/predicates/predicate_vector/out/debug/predicate_vector-abi.json" -// ); - -// let predicate = -// MyPredicate::load_from("tests/predicates/predicate_vector/out/debug/predicate_vector.bin")?; - -// // Make two transactions so that the predicate holds 2 coins. This is needed to verify -// // that the predicate data offsets are correctly calculated for multiple coins -// predicate -// .receive_from_wallet(&sender, transfer_amount, asset_id, None) -// .await?; - -// predicate -// .receive_from_wallet(&sender, transfer_amount, asset_id, None) -// .await?; - -// // The predicate has received the funds -// assert_address_balance(predicate.address(), &provider, asset_id, initial_balance).await; - -// // Run predicate with wrong data -// predicate -// .encode_data(2, 4, vec![2, 4, 43]) -// .spend_to_wallet(&receiver, initial_balance, asset_id, None) -// .await -// .expect_err("Should error"); - -// // No funds were transferred -// assert_address_balance(receiver.address(), &provider, asset_id, initial_balance).await; - -// predicate -// .encode_data(2, 4, vec![2, 4, 42]) -// .spend_to_wallet(&receiver, initial_balance, asset_id, None) -// .await?; - -// // The predicate has spent the funds -// assert_address_balance(predicate.address(), &provider, asset_id, 0).await; - -// // Funds were transferred -// assert_address_balance( -// receiver.address(), -// &provider, -// asset_id, -// initial_balance + transfer_amount * 2, -// ) -// .await; - -// Ok(()) -// } - -// #[tokio::test] -// async fn can_call_predicate_with_u32_data_new() -> Result<(), Error> { -// let initial_balance = 16; -// let (sender, receiver, asset_id) = setup_predicate_test2(1, initial_balance).await?; -// let provider = receiver.get_provider()?; -// let amount = 8; - -// predicate_abigen!( -// MyPredicate, -// "packages/fuels/tests/predicates/predicate_u32/out/debug/predicate_u32-abi.json" -// ); - -// let predicate = -// MyPredicate::load_from("tests/predicates/predicate_u32/out/debug/predicate_u32.bin")?; - -// predicate -// .receive_from_wallet(&sender, amount, asset_id, None) -// .await?; - -// // The predicate has received the funds -// assert_address_balance(predicate.address(), provider, asset_id, amount).await; - -// // Run predicate with wrong data -// predicate -// .encode_data(1077) -// .spend_to_wallet(&receiver, amount, asset_id, None) -// .await -// .expect_err("Should error"); - -// // No funds were transferred -// assert_address_balance(receiver.address(), provider, asset_id, initial_balance).await; - -// predicate -// .encode_data(1078) -// .spend_to_wallet(&receiver, amount, asset_id, None) -// .await?; - -// // The predicate has spent the funds -// assert_address_balance(predicate.address(), provider, asset_id, 0).await; - -// // Funds were transferred -// assert_address_balance( -// receiver.address(), -// provider, -// asset_id, -// initial_balance + amount, -// ) -// .await; - -// Ok(()) -// } - -// #[tokio::test] -// async fn can_call_predicate_with_struct_data_new() -> Result<(), Error> { -// let initial_balance = 16; -// let (sender, receiver, asset_id) = setup_predicate_test2(1, initial_balance).await?; -// let provider = receiver.get_provider()?; -// let amount = 8; - -// predicate_abigen!( -// MyPredicate, -// "packages/fuels/tests/predicates/predicate_struct/out/debug/predicate_struct-abi.json" -// ); - -// let predicate = -// MyPredicate::load_from("tests/predicates/predicate_struct/out/debug/predicate_struct.bin")?; - -// predicate -// .receive_from_wallet(&sender, amount, asset_id, None) -// .await?; - -// // The predicate has received the funds -// assert_address_balance(predicate.address(), provider, asset_id, amount).await; - -// // Run predicate with wrong data -// predicate -// .encode_data(Validation { -// has_account: false, -// total_complete: 10, -// }) -// .spend_to_wallet(&receiver, amount, asset_id, None) -// .await -// .expect_err("Should error"); - -// // No funds were transferred -// assert_address_balance(receiver.address(), provider, asset_id, initial_balance).await; - -// predicate -// .encode_data(Validation { -// has_account: true, -// total_complete: 100, -// }) -// .spend_to_wallet(&receiver, amount, asset_id, None) -// .await?; - -// // The predicate has spent the funds -// assert_address_balance(predicate.address(), provider, asset_id, 0).await; - -// // Funds were transferred -// assert_address_balance( -// receiver.address(), -// provider, -// asset_id, -// initial_balance + amount, -// ) -// .await; - -// Ok(()) -// } +#[tokio::test] +async fn spend_predicate_coins_messages_vectors() -> Result<(), Error> { + predicate_abigen!( + MyPredicate, + "packages/fuels/tests/predicates/predicate_vectors/out/debug/predicate_vectors-abi.json" + ); + + let predicate = MyPredicate::load_from( + "tests/predicates/predicate_vectors/out/debug/predicate_vectors.bin", + )?; + + let num_coins = 4; + let num_messages = 8; + let amount = 16; + let (provider, predicate_balance, receiver, receiver_balance, asset_id) = + setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?; + + let u32_vec = vec![0, 4, 3]; + let vec_in_vec = vec![vec![0, 2, 2], vec![0, 1, 2]]; + let struct_in_vec = vec![SomeStruct { a: 8 }, SomeStruct { a: 1 }]; + let vec_in_struct = SomeStruct { a: vec![0, 16, 2] }; + let array_in_vec = vec![[0u64, 1u64], [32u64, 1u64]]; + let vec_in_array = [vec![0, 63, 3], vec![0, 1, 2]]; + let vec_in_enum = SomeEnum::A(vec![0, 1, 128]); + let enum_in_vec = vec![SomeEnum::A(0), SomeEnum::A(16)]; + let tuple_in_vec = vec![(0, 0), (128, 1)]; + let vec_in_tuple = (vec![0, 64, 2], vec![0, 1, 2]); + let vec_in_a_vec_in_a_struct_in_a_vec = vec![ + SomeStruct { + a: vec![vec![0, 1, 2], vec![3, 4, 5]], + }, + SomeStruct { + a: vec![vec![6, 7, 8], vec![9, 32, 11]], + }, + ]; + + // Run predicate with wrong data + predicate + .encode_data( + u32_vec.clone(), + vec_in_vec.clone(), + struct_in_vec.clone(), + vec_in_struct.clone(), + array_in_vec.clone(), + vec_in_array, + vec_in_enum.clone(), + enum_in_vec.clone(), + tuple_in_vec.clone(), + vec_in_tuple.clone(), + vec_in_a_vec_in_a_struct_in_a_vec.clone(), + ) + .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .await + .expect_err("Should error"); + + // No funds were transferred + assert_address_balance(receiver.address(), &provider, asset_id, receiver_balance).await; + + let vec_in_array = [vec![0, 64, 2], vec![0, 1, 2]]; + + predicate + .encode_data( + u32_vec, + vec_in_vec, + struct_in_vec, + vec_in_struct, + array_in_vec, + vec_in_array, + vec_in_enum, + enum_in_vec, + tuple_in_vec, + vec_in_tuple, + vec_in_a_vec_in_a_struct_in_a_vec, + ) + .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .await?; + + // The predicate has spent the funds + assert_address_balance(predicate.address(), &provider, asset_id, 0).await; + + // Funds were transferred + assert_address_balance( + receiver.address(), + &provider, + asset_id, + receiver_balance + predicate_balance, + ) + .await; + + Ok(()) +} + +#[tokio::test] +async fn spend_predicate_coins_messages_generics() -> Result<(), Error> { + predicate_abigen!( + MyPredicate, + "packages/fuels/tests/predicates/predicate_generics/out/debug/predicate_generics-abi.json" + ); + + let predicate = MyPredicate::load_from( + "tests/predicates/predicate_generics/out/debug/predicate_generics.bin", + )?; + + let num_coins = 4; + let num_messages = 8; + let amount = 16; + let (provider, predicate_balance, receiver, receiver_balance, asset_id) = + setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?; + + let generic_struct = GenericStruct { value: 64 }; + let generic_enum = GenericEnum::AnotherGeneric(64); + // Run predicate with wrong data + predicate + .encode_data(generic_struct, generic_enum) + .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .await + .expect_err("Should error"); + // No funds were transferred + assert_address_balance(receiver.address(), &provider, asset_id, receiver_balance).await; + let generic_struct = GenericStruct { value: 64u8 }; + let generic_struct2 = GenericStruct { value: 64u16 }; + let generic_enum = GenericEnum::Generic(generic_struct2); + + predicate + .encode_data(generic_struct, generic_enum) + .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .await?; + + // The predicate has spent the funds + assert_address_balance(predicate.address(), &provider, asset_id, 0).await; + + // Funds were transferred + assert_address_balance( + receiver.address(), + &provider, + asset_id, + receiver_balance + predicate_balance, + ) + .await; + + Ok(()) +} diff --git a/packages/fuels/tests/predicates/predicate_address/src/main.sw b/packages/fuels/tests/predicates/predicate_address/src/main.sw index 96dde76e4f..c431b853e0 100644 --- a/packages/fuels/tests/predicates/predicate_address/src/main.sw +++ b/packages/fuels/tests/predicates/predicate_address/src/main.sw @@ -1,10 +1,7 @@ predicate; -use std::inputs::input_predicate_data; +fn main(input: Address) -> bool { + let expected_addr = Address::from(0xef86afa9696cf0dc6385e2c407a6e159a1103cefb7e2ae0636fb33d3cb2a9e4a); -fn main() -> bool { - let received: b256 = input_predicate_data(0); - let expected: b256 = 0xef86afa9696cf0dc6385e2c407a6e159a1103cefb7e2ae0636fb33d3cb2a9e4a; - - received == expected + input == expected_addr } diff --git a/packages/fuels/tests/predicates/predicate_false/Forc.toml b/packages/fuels/tests/predicates/predicate_basic/Forc.toml similarity index 81% rename from packages/fuels/tests/predicates/predicate_false/Forc.toml rename to packages/fuels/tests/predicates/predicate_basic/Forc.toml index a47ebd538b..a382d123cf 100644 --- a/packages/fuels/tests/predicates/predicate_false/Forc.toml +++ b/packages/fuels/tests/predicates/predicate_basic/Forc.toml @@ -2,6 +2,6 @@ authors = ["Fuel Labs "] entry = "main.sw" license = "Apache-2.0" -name = "predicate_false" +name = "predicate_basic" [dependencies] diff --git a/packages/fuels/tests/predicates/predicate_basic/src/main.sw b/packages/fuels/tests/predicates/predicate_basic/src/main.sw new file mode 100644 index 0000000000..d48059d7fc --- /dev/null +++ b/packages/fuels/tests/predicates/predicate_basic/src/main.sw @@ -0,0 +1,5 @@ +predicate; + +fn main(a: u32, b: u64) -> bool { + a == b +} diff --git a/packages/fuels/tests/predicates/predicate_data_example/.gitignore b/packages/fuels/tests/predicates/predicate_data_example/.gitignore deleted file mode 100644 index 77d3844f58..0000000000 --- a/packages/fuels/tests/predicates/predicate_data_example/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -out -target diff --git a/packages/fuels/tests/predicates/predicate_data_example/Forc.toml b/packages/fuels/tests/predicates/predicate_data_example/Forc.toml deleted file mode 100644 index c6e958ea99..0000000000 --- a/packages/fuels/tests/predicates/predicate_data_example/Forc.toml +++ /dev/null @@ -1,7 +0,0 @@ -[project] -authors = ["Fuel Labs "] -entry = "main.sw" -license = "Apache-2.0" -name = "predicate_data_example" - -[dependencies] diff --git a/packages/fuels/tests/predicates/predicate_data_example/src/main.sw b/packages/fuels/tests/predicates/predicate_data_example/src/main.sw deleted file mode 100644 index 325f4b4d33..0000000000 --- a/packages/fuels/tests/predicates/predicate_data_example/src/main.sw +++ /dev/null @@ -1,11 +0,0 @@ -predicate; - -use std::inputs::input_predicate_data; - -fn main() -> bool { - let guessed_number: u64 = input_predicate_data(0); - if guessed_number == 42 { - return true; - } - false -} diff --git a/packages/fuels/tests/predicates/predicate_u32/Forc.toml b/packages/fuels/tests/predicates/predicate_enums/Forc.toml similarity index 81% rename from packages/fuels/tests/predicates/predicate_u32/Forc.toml rename to packages/fuels/tests/predicates/predicate_enums/Forc.toml index 4fc7fdf14f..e221a1e923 100644 --- a/packages/fuels/tests/predicates/predicate_u32/Forc.toml +++ b/packages/fuels/tests/predicates/predicate_enums/Forc.toml @@ -2,6 +2,6 @@ authors = ["Fuel Labs "] entry = "main.sw" license = "Apache-2.0" -name = "predicate_u32" +name = "predicate_enums" [dependencies] diff --git a/packages/fuels/tests/predicates/predicate_enums/src/main.sw b/packages/fuels/tests/predicates/predicate_enums/src/main.sw new file mode 100644 index 0000000000..9c1f780226 --- /dev/null +++ b/packages/fuels/tests/predicates/predicate_enums/src/main.sw @@ -0,0 +1,20 @@ +predicate; + +enum TestEnum { + A: u64, +} + +enum AnotherTestEnum { + A: u64, + B: u64, +} + +fn main(test_enum: TestEnum, test_enum2: AnotherTestEnum) -> bool { + if let TestEnum::A(a) = test_enum { + if let AnotherTestEnum::B(b) = test_enum2 { + return a == b; + } + } + + false +} diff --git a/packages/fuels/tests/predicates/predicate_false/src/main.sw b/packages/fuels/tests/predicates/predicate_false/src/main.sw deleted file mode 100644 index d529f4e189..0000000000 --- a/packages/fuels/tests/predicates/predicate_false/src/main.sw +++ /dev/null @@ -1,5 +0,0 @@ -predicate; - -fn main() -> bool { - false -} diff --git a/packages/fuels/tests/predicates/predicate_generics/Forc.toml b/packages/fuels/tests/predicates/predicate_generics/Forc.toml new file mode 100644 index 0000000000..3f72203ca9 --- /dev/null +++ b/packages/fuels/tests/predicates/predicate_generics/Forc.toml @@ -0,0 +1,7 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "predicate_generics" + +[dependencies] diff --git a/packages/fuels/tests/predicates/predicate_generics/src/main.sw b/packages/fuels/tests/predicates/predicate_generics/src/main.sw new file mode 100644 index 0000000000..72c6381180 --- /dev/null +++ b/packages/fuels/tests/predicates/predicate_generics/src/main.sw @@ -0,0 +1,21 @@ +predicate; + +struct GenericStruct { + value: U, +} + +enum GenericEnum { + Generic: GenericStruct, + AnotherGeneric: V, +} + +fn main( + generic_struct: GenericStruct, + generic_enum: GenericEnum, +) -> bool { + if let GenericEnum::Generic(other_struct) = generic_enum { + return generic_struct.value == other_struct.value; + } + + false +} diff --git a/packages/fuels/tests/predicates/predicate_signatures/src/main.sw b/packages/fuels/tests/predicates/predicate_signatures/src/main.sw index c9cf69cf54..87499678b9 100644 --- a/packages/fuels/tests/predicates/predicate_signatures/src/main.sw +++ b/packages/fuels/tests/predicates/predicate_signatures/src/main.sw @@ -12,9 +12,7 @@ fn extract_pulic_key_and_match(signature: B512, expected_public_key: b256) -> u6 0 } -fn main() -> bool { - let signatures: [B512; 3] = input_predicate_data(0); - +fn main(signatures: [B512; 3]) -> bool { let public_keys = [ 0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0, 0x14df7c7e4e662db31fe2763b1734a3d680e7b743516319a49baaa22b2032a857, diff --git a/packages/fuels/tests/predicates/predicate_struct/src/main.sw b/packages/fuels/tests/predicates/predicate_struct/src/main.sw deleted file mode 100644 index c78baa39dc..0000000000 --- a/packages/fuels/tests/predicates/predicate_struct/src/main.sw +++ /dev/null @@ -1,13 +0,0 @@ -predicate; - -struct Validation { - has_account: bool, - total_complete: u64, -} - -fn main(input: Validation) -> bool { - let expected_has_account: bool = true; - let expected_total_complete: u64 = 100; - - input.has_account == expected_has_account && input.total_complete == expected_total_complete -} diff --git a/packages/fuels/tests/predicates/predicate_struct/Forc.toml b/packages/fuels/tests/predicates/predicate_structs/Forc.toml similarity index 80% rename from packages/fuels/tests/predicates/predicate_struct/Forc.toml rename to packages/fuels/tests/predicates/predicate_structs/Forc.toml index 3441b03dcb..30ad28e266 100644 --- a/packages/fuels/tests/predicates/predicate_struct/Forc.toml +++ b/packages/fuels/tests/predicates/predicate_structs/Forc.toml @@ -2,6 +2,6 @@ authors = ["Fuel Labs "] entry = "main.sw" license = "Apache-2.0" -name = "predicate_struct" +name = "predicate_structs" [dependencies] diff --git a/packages/fuels/tests/predicates/predicate_structs/src/main.sw b/packages/fuels/tests/predicates/predicate_structs/src/main.sw new file mode 100644 index 0000000000..d9585b232d --- /dev/null +++ b/packages/fuels/tests/predicates/predicate_structs/src/main.sw @@ -0,0 +1,14 @@ +predicate; + +struct TestStruct { + value: u64, +} + +struct AnotherTestStruct { + value: u64, + number: u64, +} + +fn main(test_struct: TestStruct, test_struct2: AnotherTestStruct) -> bool { + test_struct.value == (test_struct2.value + test_struct2.number) +} diff --git a/packages/fuels/tests/predicates/predicate_true/src/main.sw b/packages/fuels/tests/predicates/predicate_true/src/main.sw deleted file mode 100644 index 084dacab2c..0000000000 --- a/packages/fuels/tests/predicates/predicate_true/src/main.sw +++ /dev/null @@ -1,5 +0,0 @@ -predicate; - -fn main() -> bool { - true -} diff --git a/packages/fuels/tests/predicates/predicate_true/Forc.toml b/packages/fuels/tests/predicates/predicate_tuple/Forc.toml similarity index 81% rename from packages/fuels/tests/predicates/predicate_true/Forc.toml rename to packages/fuels/tests/predicates/predicate_tuple/Forc.toml index b4dcdbf37b..c1d26661ca 100644 --- a/packages/fuels/tests/predicates/predicate_true/Forc.toml +++ b/packages/fuels/tests/predicates/predicate_tuple/Forc.toml @@ -2,6 +2,6 @@ authors = ["Fuel Labs "] entry = "main.sw" license = "Apache-2.0" -name = "predicate_true" +name = "predicate_tuple" [dependencies] diff --git a/packages/fuels/tests/predicates/predicate_tuple/src/main.sw b/packages/fuels/tests/predicates/predicate_tuple/src/main.sw new file mode 100644 index 0000000000..532c7da570 --- /dev/null +++ b/packages/fuels/tests/predicates/predicate_tuple/src/main.sw @@ -0,0 +1,19 @@ +predicate; + +struct TestStruct { + value: u32, +} + +enum TestEnum { + Value: u64, +} + +fn main(input_tuple: (u64, TestStruct, TestEnum), number: u64) -> bool { + let (u64_number, test_struct, test_enum) = input_tuple; + + if let TestEnum::Value(enum_value) = test_enum { + return u64_number == 16 && test_struct.value == 32 && enum_value == 64 && number == 128; + } + + false +} diff --git a/packages/fuels/tests/predicates/predicate_u32/src/main.sw b/packages/fuels/tests/predicates/predicate_u32/src/main.sw deleted file mode 100644 index 10626c0f72..0000000000 --- a/packages/fuels/tests/predicates/predicate_u32/src/main.sw +++ /dev/null @@ -1,7 +0,0 @@ -predicate; - -fn main(input: u32) -> bool { - let expected: u32 = 1078; - - input == expected -} diff --git a/packages/fuels/tests/predicates/predicate_vectors/Forc.toml b/packages/fuels/tests/predicates/predicate_vectors/Forc.toml new file mode 100644 index 0000000000..e540774b19 --- /dev/null +++ b/packages/fuels/tests/predicates/predicate_vectors/Forc.toml @@ -0,0 +1,7 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "predicate_vectors" + +[dependencies] diff --git a/packages/fuels/tests/predicates/predicate_vectors/src/main.sw b/packages/fuels/tests/predicates/predicate_vectors/src/main.sw new file mode 100644 index 0000000000..602cc781e1 --- /dev/null +++ b/packages/fuels/tests/predicates/predicate_vectors/src/main.sw @@ -0,0 +1,60 @@ +predicate; + +pub struct SomeStruct { + a: T, +} + +pub enum SomeEnum { + A: T, +} + +fn main( + u32_vec: Vec, + vec_in_vec: Vec>, + struct_in_vec: Vec>, + vec_in_struct: SomeStruct>, + array_in_vec: Vec<[u64; 2]>, + vec_in_array: [Vec; 2], + vec_in_enum: SomeEnum>, + enum_in_vec: Vec>, + tuple_in_vec: Vec<(u32, u32)>, + vec_in_tuple: (Vec, Vec), + vec_in_a_vec_in_a_struct_in_a_vec: Vec>>>, +) -> bool { + let mut result = true; + + result = result && (u32_vec.get(1).unwrap() == 4u32); + + result = result && (vec_in_vec.get(0).unwrap().get(1).unwrap() == 2u32); + + result = result && (struct_in_vec.get(0).unwrap().a == 8u32); + + result = result && (vec_in_struct.a.get(1).unwrap() == 16u32); + + let array: [u64; 2] = array_in_vec.get(1).unwrap(); + result = result && (array[0] == 32u64); + + result = result && (vec_in_array[0].get(1).unwrap() == 64u32); + + if let SomeEnum::A(some_vec) = vec_in_enum { + result = result && (some_vec.get(2).unwrap() == 128u32); + } else { + result = false; + } + + let enum_a = enum_in_vec.get(1).unwrap(); + if let SomeEnum::A(a) = enum_a { + result = result && (a == 16u32) + } else { + result = false; + } + + result = result && (tuple_in_vec.get(1).unwrap().0 == 128u32); + + let (tuple_a, tuple_b) = vec_in_tuple; + result = result && (tuple_a.get(1).unwrap() == 64u32); + + result = result && (vec_in_a_vec_in_a_struct_in_a_vec.get(1).unwrap().a.get(1).unwrap().get(1).unwrap() == 32u32); + + result +} From c602a035542adfd3f988c3aa249fc7e639c21978 Mon Sep 17 00:00:00 2001 From: hal3e Date: Wed, 14 Dec 2022 01:57:44 +0100 Subject: [PATCH 06/14] minor refactor --- examples/predicates/src/lib.rs | 2 +- packages/fuels-core/src/code_gen/abigen.rs | 35 ++++++++-------------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/examples/predicates/src/lib.rs b/examples/predicates/src/lib.rs index c450e8e60c..1ae767b1b7 100644 --- a/examples/predicates/src/lib.rs +++ b/examples/predicates/src/lib.rs @@ -5,7 +5,7 @@ mod tests { #[tokio::test] async fn predicate_example() -> Result<(), Error> { - use fuels::core::vm::prelude::SecretKey; + use fuels::signers::fuel_crypto::SecretKey; // ANCHOR: predicate_wallets let secret_key1: SecretKey = diff --git a/packages/fuels-core/src/code_gen/abigen.rs b/packages/fuels-core/src/code_gen/abigen.rs index faf16b43ca..e93b9d7139 100644 --- a/packages/fuels-core/src/code_gen/abigen.rs +++ b/packages/fuels-core/src/code_gen/abigen.rs @@ -194,7 +194,7 @@ impl Abigen { let resolved_logs = self.resolve_logs(); let log_id_param_type_pairs = generate_log_id_param_type_pairs(&resolved_logs); - let main_script_function = self.script_function()?; + let main_script_function = self.main_function()?; let code = if self.no_std { quote! {} } else { @@ -249,7 +249,7 @@ impl Abigen { let includes = self.includes(); - let encode_data_function = self.predicate_function()?; + let encode_data_function = self.main_function()?; let code = if self.no_std { quote! {} } else { @@ -409,7 +409,7 @@ impl Abigen { Ok(quote! { #( #tokenized_functions )* }) } - pub fn script_function(&self) -> Result { + pub fn main_function(&self) -> Result { let functions = self .abi .functions @@ -418,30 +418,21 @@ impl Abigen { .collect::>(); if let [main_function] = functions.as_slice() { - let tokenized_function = generate_script_main_function(main_function, &self.types)?; - Ok(quote! { #tokenized_function }) - } else { - Err(Error::CompilationError( - "The script must have one function named `main` to compile!".to_string(), - )) - } - } + let tokenized_function = match self.abigen_type { + AbigenType::Script => generate_script_main_function(main_function, &self.types), - pub fn predicate_function(&self) -> Result { - let functions = self - .abi - .functions - .iter() - .filter(|function| function.name == "main") - .collect::>(); + AbigenType::Predicate => { + generate_predicate_encode_function(main_function, &self.types) + } + AbigenType::Contract => Err(Error::CompilationError( + "Contract does not have a `main` function!".to_string(), + )), + }?; - if let [main_function] = functions.as_slice() { - let tokenized_function = - generate_predicate_encode_function(main_function, &self.types)?; Ok(quote! { #tokenized_function }) } else { Err(Error::CompilationError( - "The script must have one function named `main` to compile!".to_string(), + "Only one function named `main` allowed!".to_string(), )) } } From 66b60e7a8746cb9aa66394a9676fe8da0f2acb65 Mon Sep 17 00:00:00 2001 From: hal3e Date: Wed, 14 Dec 2022 16:36:25 +0100 Subject: [PATCH 07/14] change forc branch to master --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8348caaf44..f023a1b221 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ env: FUEL_CORE_VERSION: 0.15.0 RUST_VERSION: 1.64.0 FORC_VERSION: 0.31.1 - FORC_PATCH_BRANCH: "mohammadfawaz/predicate_data" + FORC_PATCH_BRANCH: "master" FORC_PATCH_REVISION: "" jobs: From 86fa842a0be08c8994c068b0298d14dfae6a0f08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20Beglerovi=C4=87?= Date: Wed, 14 Dec 2022 22:11:30 +0100 Subject: [PATCH 08/14] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rodrigo Araújo --- docs/src/predicates/predicate-data.md | 6 +++--- docs/src/predicates/send-spend-predicate.md | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/src/predicates/predicate-data.md b/docs/src/predicates/predicate-data.md index f7f4041e4b..974dd48d9e 100644 --- a/docs/src/predicates/predicate-data.md +++ b/docs/src/predicates/predicate-data.md @@ -6,13 +6,13 @@ Let's consider the following predicate example: {{#include ../../../packages/fuels/tests/predicates/predicate_basic/src/main.sw}} ``` -Similarly to contracts and scripts, the `predicate_abigen!` is generating an function that will conveniently encode all the arguments of the main function for us. This function is called `encode_data` and it is accessed through the predicate instance as shown below: +Similarly to contracts and scripts, the `predicate_abigen!` generates a function that will conveniently encode all the arguments of the main function for us. This function is called `encode_data`, and it is accessed through the predicate instance as shown below: ```rust,ignore {{#include ../../../examples/predicates/src/lib.rs:encode_predicate_data}} ``` -Next we will have a look on a complete example on how we can use the SDK to send and receive funds from a predicate. +Next, we will look at a complete example of using the SDK to send and receive funds from a predicate. First, we set up the wallets, node, and a predicate instance: @@ -29,7 +29,7 @@ Next, we lock some assets in this predicate using the first wallet: Then, we try to unlock the amount and spend it using the second wallet, effectively sending the previously locked value to itself. -The predicate expects the data sent to it to be two numbers (`u32` and `u64`) whit matching values. +The predicate expects the data sent to it to be two numbers (`u32` and `u64`) with matching values. ```rust,ignore {{#include ../../../examples/predicates/src/lib.rs:predicate_data_unlock}} diff --git a/docs/src/predicates/send-spend-predicate.md b/docs/src/predicates/send-spend-predicate.md index 109449fa93..890734ed75 100644 --- a/docs/src/predicates/send-spend-predicate.md +++ b/docs/src/predicates/send-spend-predicate.md @@ -1,6 +1,6 @@ # Signatures in predicates example -This is a more involved example where the predicate accepts three signatures and matches them to three predefined public keys. The `ec_recover_address` function is used to recover the public key from the signatures. If two of three extracted public keys match the predefined public keys, the funds can be spent. Note that the signature order has to match the order of the predefined public keys. +This is a more involved example where the predicate accepts three signatures and matches them to three predefined public keys. The `ec_recover_address` function is used to recover the public key from the signatures. If two of the three extracted public keys match the predefined public keys, the funds can be spent. Note that the signature order has to match the order of the predefined public keys. ```rust,ignore @@ -8,7 +8,7 @@ This is a more involved example where the predicate accepts three signatures and ``` -Let's use the SDK to interact with the predicate. First, let's create three wallets with specific keys. Their hashed public keys are already hard-coded in the predicate. Then we create the receiver wallet which we will use to spend the predicate funds. +Let's use the SDK to interact with the predicate. First, let's create three wallets with specific keys. Their hashed public keys are already hard-coded in the predicate. Then we create the receiver wallet, which we will use to spend the predicate funds. ```rust,ignore {{#include ../../../examples/predicates/src/lib.rs:predicate_wallets}} @@ -38,7 +38,7 @@ To spend the funds that are now locked in the predicate, we have to provide two {{#include ../../../examples/predicates/src/lib.rs:predicate_signatures}} ``` -After generating the signatures, we can use the predicate's `encode_data` and `spend_to_wallet` functions to spend the funds. If the provided data is correct the `receiver` wallet will get the funds and we verify that the amount is indeed correct. +After generating the signatures, we can use the predicate's `encode_data` and `spend_to_wallet` functions to spend the funds. If the provided data is correct, the `receiver` wallet will get the funds, and we will verify that the amount is indeed correct. ```rust,ignore {{#include ../../../examples/predicates/src/lib.rs:predicate_spend}} From e963afb5ab396bfc1df953e771ba0bf286e5c260 Mon Sep 17 00:00:00 2001 From: Brandon Kite Date: Wed, 14 Dec 2022 09:30:45 -0800 Subject: [PATCH 09/14] Update to fuel-core v0.15.1 (#741) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f023a1b221..5ce824689a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ env: CARGO_TERM_COLOR: always DASEL_VERSION: https://github.com/TomWright/dasel/releases/download/v1.24.3/dasel_linux_amd64 RUSTFLAGS: "-D warnings" - FUEL_CORE_VERSION: 0.15.0 + FUEL_CORE_VERSION: 0.15.1 RUST_VERSION: 1.64.0 FORC_VERSION: 0.31.1 FORC_PATCH_BRANCH: "master" From 6bca973f78a19325465c54a8aa16c930c02eb0ff Mon Sep 17 00:00:00 2001 From: hal3e Date: Wed, 14 Dec 2022 23:24:33 +0100 Subject: [PATCH 10/14] use receive/spend and update docs --- docs/src/predicates/send-spend-predicate.md | 4 +-- examples/predicates/src/lib.rs | 10 +++--- packages/fuels-core/src/code_gen/abigen.rs | 8 ++--- packages/fuels/tests/predicates.rs | 34 ++++++++++----------- 4 files changed, 27 insertions(+), 29 deletions(-) diff --git a/docs/src/predicates/send-spend-predicate.md b/docs/src/predicates/send-spend-predicate.md index 890734ed75..227933c839 100644 --- a/docs/src/predicates/send-spend-predicate.md +++ b/docs/src/predicates/send-spend-predicate.md @@ -26,7 +26,7 @@ Now we can use the predicate abigen, which will create a predicate instance for {{#include ../../../examples/predicates/src/lib.rs:predicate_load}} ``` -After the predicate instance is generated we can use the `receive_from_wallet` function to transfer funds to the predicate. We also make sure that the funds are indeed transferred. +After the predicate instance is generated we can use the `receive` function to transfer funds to the predicate. We also make sure that the funds are indeed transferred. ```rust,ignore {{#include ../../../examples/predicates/src/lib.rs:predicate_receive}} @@ -38,7 +38,7 @@ To spend the funds that are now locked in the predicate, we have to provide two {{#include ../../../examples/predicates/src/lib.rs:predicate_signatures}} ``` -After generating the signatures, we can use the predicate's `encode_data` and `spend_to_wallet` functions to spend the funds. If the provided data is correct, the `receiver` wallet will get the funds, and we will verify that the amount is indeed correct. +After generating the signatures, we can use the predicate's `encode_data` and `spend` functions to spend the funds. If the provided data is correct the `receiver` wallet will get the funds, and we will verify that the amount is indeed correct. ```rust,ignore {{#include ../../../examples/predicates/src/lib.rs:predicate_spend}} diff --git a/examples/predicates/src/lib.rs b/examples/predicates/src/lib.rs index 1ae767b1b7..9470bdee3d 100644 --- a/examples/predicates/src/lib.rs +++ b/examples/predicates/src/lib.rs @@ -63,7 +63,7 @@ mod tests { let amount_to_predicate = 512; predicate - .receive_from_wallet(&wallet, amount_to_predicate, asset_id, None) + .receive(&wallet, amount_to_predicate, asset_id, None) .await?; let predicate_balance = provider @@ -96,7 +96,7 @@ mod tests { // ANCHOR: predicate_spend predicate .encode_data(signatures) - .spend_to_wallet(&receiver, amount_to_predicate, asset_id, None) + .spend(&receiver, amount_to_predicate, asset_id, None) .await?; let receiver_balance_after = provider @@ -146,9 +146,7 @@ mod tests { // ANCHOR: predicate_data_lock_amount // First wallet transfers amount to predicate. - predicate - .receive_from_wallet(first_wallet, 500, asset_id, None) - .await?; + predicate.receive(first_wallet, 500, asset_id, None).await?; // Check predicate balance. let balance = first_wallet @@ -171,7 +169,7 @@ mod tests { predicate .encode_data(4096, 4096) - .spend_to_wallet(second_wallet, amount_to_unlock, asset_id, None) + .spend(second_wallet, amount_to_unlock, asset_id, None) .await?; // Predicate balance is zero. diff --git a/packages/fuels-core/src/code_gen/abigen.rs b/packages/fuels-core/src/code_gen/abigen.rs index e93b9d7139..4b62d6dd31 100644 --- a/packages/fuels-core/src/code_gen/abigen.rs +++ b/packages/fuels-core/src/code_gen/abigen.rs @@ -287,9 +287,9 @@ impl Abigen { self.data.clone() } - pub async fn receive_from_wallet(&self, wallet: &WalletUnlocked, amount:u64, asset_id: AssetId, tx_parameters: Option) -> Result<(String, Vec), SDKError> { + pub async fn receive(&self, from: &WalletUnlocked, amount:u64, asset_id: AssetId, tx_parameters: Option) -> Result<(String, Vec), SDKError> { let tx_parameters = tx_parameters.unwrap_or(TxParameters::default()); - wallet + from .transfer( self.address(), amount, @@ -299,9 +299,9 @@ impl Abigen { .await } - pub async fn spend_to_wallet(&self, wallet: &WalletUnlocked, amount:u64, asset_id: AssetId, tx_parameters: Option) -> Result, SDKError> { + pub async fn spend(&self, to: &WalletUnlocked, amount:u64, asset_id: AssetId, tx_parameters: Option) -> Result, SDKError> { let tx_parameters = tx_parameters.unwrap_or(TxParameters::default()); - wallet + to .receive_from_predicate( self.address(), self.code(), diff --git a/packages/fuels/tests/predicates.rs b/packages/fuels/tests/predicates.rs index d71d030591..47280ba4a6 100644 --- a/packages/fuels/tests/predicates.rs +++ b/packages/fuels/tests/predicates.rs @@ -102,7 +102,7 @@ async fn transfer_coins_and_messages_to_predicate() -> Result<(), Error> { MyPredicate::load_from("tests/predicates/predicate_basic/out/debug/predicate_basic.bin")?; predicate - .receive_from_wallet(&wallet, total_balance, asset_id, None) + .receive(&wallet, total_balance, asset_id, None) .await?; // The predicate has received the funds @@ -130,7 +130,7 @@ async fn spend_predicate_coins_messages_basic() -> Result<(), Error> { // Run predicate with wrong data predicate .encode_data(4096, 4097) - .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .spend(&receiver, predicate_balance, asset_id, None) .await .expect_err("Should error"); @@ -139,7 +139,7 @@ async fn spend_predicate_coins_messages_basic() -> Result<(), Error> { predicate .encode_data(4096, 4096) - .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .spend(&receiver, predicate_balance, asset_id, None) .await?; // The predicate has spent the funds @@ -181,7 +181,7 @@ async fn spend_predicate_coins_messages_address() -> Result<(), Error> { // Run predicate with wrong data predicate .encode_data(wrong_addr) - .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .spend(&receiver, predicate_balance, asset_id, None) .await .expect_err("Should error"); @@ -194,7 +194,7 @@ async fn spend_predicate_coins_messages_address() -> Result<(), Error> { predicate .encode_data(addr) - .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .spend(&receiver, predicate_balance, asset_id, None) .await?; // The predicate has spent the funds @@ -231,7 +231,7 @@ async fn spend_predicate_coins_messages_enums() -> Result<(), Error> { // Run predicate with wrong data predicate .encode_data(TestEnum::A(32), AnotherTestEnum::A(32)) - .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .spend(&receiver, predicate_balance, asset_id, None) .await .expect_err("Should error"); @@ -240,7 +240,7 @@ async fn spend_predicate_coins_messages_enums() -> Result<(), Error> { predicate .encode_data(TestEnum::A(32), AnotherTestEnum::B(32)) - .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .spend(&receiver, predicate_balance, asset_id, None) .await?; // The predicate has spent the funds @@ -284,7 +284,7 @@ async fn spend_predicate_coins_messages_structs() -> Result<(), Error> { number: 127, }, ) - .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .spend(&receiver, predicate_balance, asset_id, None) .await .expect_err("Should error"); @@ -299,7 +299,7 @@ async fn spend_predicate_coins_messages_structs() -> Result<(), Error> { number: 128, }, ) - .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .spend(&receiver, predicate_balance, asset_id, None) .await?; // The predicate has spent the funds @@ -336,7 +336,7 @@ async fn spend_predicate_coins_messages_tuple() -> Result<(), Error> { // Run predicate with wrong data predicate .encode_data((15, TestStruct { value: 31 }, TestEnum::Value(63)), 127) - .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .spend(&receiver, predicate_balance, asset_id, None) .await .expect_err("Should error"); @@ -345,7 +345,7 @@ async fn spend_predicate_coins_messages_tuple() -> Result<(), Error> { predicate .encode_data((16, TestStruct { value: 32 }, TestEnum::Value(64)), 128) - .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .spend(&receiver, predicate_balance, asset_id, None) .await?; // The predicate has spent the funds @@ -382,7 +382,7 @@ async fn spend_predicate_coins_messages_vector() -> Result<(), Error> { // Run predicate with wrong data predicate .encode_data(2, 4, vec![2, 4, 43]) - .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .spend(&receiver, predicate_balance, asset_id, None) .await .expect_err("Should error"); @@ -391,7 +391,7 @@ async fn spend_predicate_coins_messages_vector() -> Result<(), Error> { predicate .encode_data(2, 4, vec![2, 4, 42]) - .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .spend(&receiver, predicate_balance, asset_id, None) .await?; // The predicate has spent the funds @@ -460,7 +460,7 @@ async fn spend_predicate_coins_messages_vectors() -> Result<(), Error> { vec_in_tuple.clone(), vec_in_a_vec_in_a_struct_in_a_vec.clone(), ) - .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .spend(&receiver, predicate_balance, asset_id, None) .await .expect_err("Should error"); @@ -483,7 +483,7 @@ async fn spend_predicate_coins_messages_vectors() -> Result<(), Error> { vec_in_tuple, vec_in_a_vec_in_a_struct_in_a_vec, ) - .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .spend(&receiver, predicate_balance, asset_id, None) .await?; // The predicate has spent the funds @@ -524,7 +524,7 @@ async fn spend_predicate_coins_messages_generics() -> Result<(), Error> { // Run predicate with wrong data predicate .encode_data(generic_struct, generic_enum) - .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .spend(&receiver, predicate_balance, asset_id, None) .await .expect_err("Should error"); @@ -537,7 +537,7 @@ async fn spend_predicate_coins_messages_generics() -> Result<(), Error> { predicate .encode_data(generic_struct, generic_enum) - .spend_to_wallet(&receiver, predicate_balance, asset_id, None) + .spend(&receiver, predicate_balance, asset_id, None) .await?; // The predicate has spent the funds From f0d529a5406bfde01026ceefa85bdec668854dfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20Beglerovi=C4=87?= Date: Thu, 15 Dec 2022 20:27:11 +0100 Subject: [PATCH 11/14] Update packages/fuels-contract/src/execution_script.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rodrigo Araújo --- packages/fuels-contract/src/execution_script.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/fuels-contract/src/execution_script.rs b/packages/fuels-contract/src/execution_script.rs index f57726279a..5325964765 100644 --- a/packages/fuels-contract/src/execution_script.rs +++ b/packages/fuels-contract/src/execution_script.rs @@ -41,7 +41,7 @@ impl ExecutableFuelCall { ) -> Result { let consensus_parameters = wallet.get_provider()?.consensus_parameters().await?; - // Calculate instructions lenght for call instructions + // Calculate instructions length for call instructions // Use placeholder for call param offsets, we only care about the length let calls_instructions_len = get_single_call_instructions(&CallOpcodeParamsOffset::default()).len() * calls.len(); From de8e907184b79786f66db868e85a1ecaae6e8ddf Mon Sep 17 00:00:00 2001 From: hal3e Date: Thu, 15 Dec 2022 20:47:10 +0100 Subject: [PATCH 12/14] update forc, add u64 test --- .github/workflows/ci.yml | 4 +- packages/fuels/tests/predicates.rs | 46 +++++++++++++++++++ .../tests/predicates/predicate_u64/Forc.toml | 7 +++ .../predicates/predicate_u64/src/main.sw | 5 ++ 4 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 packages/fuels/tests/predicates/predicate_u64/Forc.toml create mode 100644 packages/fuels/tests/predicates/predicate_u64/src/main.sw diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ce824689a..58ed2944ec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,8 +18,8 @@ env: RUSTFLAGS: "-D warnings" FUEL_CORE_VERSION: 0.15.1 RUST_VERSION: 1.64.0 - FORC_VERSION: 0.31.1 - FORC_PATCH_BRANCH: "master" + FORC_VERSION: 0.32.1 + FORC_PATCH_BRANCH: "" FORC_PATCH_REVISION: "" jobs: diff --git a/packages/fuels/tests/predicates.rs b/packages/fuels/tests/predicates.rs index 47280ba4a6..a8af5a2747 100644 --- a/packages/fuels/tests/predicates.rs +++ b/packages/fuels/tests/predicates.rs @@ -111,6 +111,52 @@ async fn transfer_coins_and_messages_to_predicate() -> Result<(), Error> { Ok(()) } +#[tokio::test] +async fn spend_predicate_coins_messages_single_u64() -> Result<(), Error> { + predicate_abigen!( + MyPredicate, + "packages/fuels/tests/predicates/predicate_u64/out/debug/predicate_u64-abi.json" + ); + + let predicate = + MyPredicate::load_from("tests/predicates/predicate_u64/out/debug/predicate_u64.bin")?; + + let num_coins = 4; + let num_messages = 8; + let amount = 16; + let (provider, predicate_balance, receiver, receiver_balance, asset_id) = + setup_predicate_test(predicate.address(), num_coins, num_messages, amount).await?; + + // Run predicate with wrong data + predicate + .encode_data(32767) + .spend(&receiver, predicate_balance, asset_id, None) + .await + .expect_err("Should error"); + + // No funds were transferred + assert_address_balance(receiver.address(), &provider, asset_id, receiver_balance).await; + + predicate + .encode_data(32768) + .spend(&receiver, predicate_balance, asset_id, None) + .await?; + + // The predicate has spent the funds + assert_address_balance(predicate.address(), &provider, asset_id, 0).await; + + // Funds were transferred + assert_address_balance( + receiver.address(), + &provider, + asset_id, + receiver_balance + predicate_balance, + ) + .await; + + Ok(()) +} + #[tokio::test] async fn spend_predicate_coins_messages_basic() -> Result<(), Error> { predicate_abigen!( diff --git a/packages/fuels/tests/predicates/predicate_u64/Forc.toml b/packages/fuels/tests/predicates/predicate_u64/Forc.toml new file mode 100644 index 0000000000..3d6f7298fe --- /dev/null +++ b/packages/fuels/tests/predicates/predicate_u64/Forc.toml @@ -0,0 +1,7 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "predicate_u64" + +[dependencies] diff --git a/packages/fuels/tests/predicates/predicate_u64/src/main.sw b/packages/fuels/tests/predicates/predicate_u64/src/main.sw new file mode 100644 index 0000000000..25b48a1fd3 --- /dev/null +++ b/packages/fuels/tests/predicates/predicate_u64/src/main.sw @@ -0,0 +1,5 @@ +predicate; + +fn main(a: u64) -> bool { + a == 32768 +} From 83383398d04b29d69988b9ac00c168c32ba13c5d Mon Sep 17 00:00:00 2001 From: hal3e Date: Fri, 16 Dec 2022 10:17:36 +0100 Subject: [PATCH 13/14] bump forc --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 58ed2944ec..bc68b47ba6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ env: RUSTFLAGS: "-D warnings" FUEL_CORE_VERSION: 0.15.1 RUST_VERSION: 1.64.0 - FORC_VERSION: 0.32.1 + FORC_VERSION: 0.32.2 FORC_PATCH_BRANCH: "" FORC_PATCH_REVISION: "" From c43d3d89a37444021977a712ad17138ad5ff37f3 Mon Sep 17 00:00:00 2001 From: hal3e Date: Sat, 17 Dec 2022 12:05:26 +0100 Subject: [PATCH 14/14] change functions gen to `pub(crate)` --- packages/fuels-core/src/code_gen/functions_gen.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/fuels-core/src/code_gen/functions_gen.rs b/packages/fuels-core/src/code_gen/functions_gen.rs index 470f468767..da420a4616 100644 --- a/packages/fuels-core/src/code_gen/functions_gen.rs +++ b/packages/fuels-core/src/code_gen/functions_gen.rs @@ -25,7 +25,7 @@ use std::collections::HashMap; /// /// [`Contract`]: fuels_contract::contract::Contract // TODO (oleksii/docs): linkify the above `Contract` link properly -pub fn expand_function( +pub(crate) fn expand_function( function: &ABIFunction, types: &HashMap, ) -> Result { @@ -74,7 +74,7 @@ pub fn expand_function( } /// Generate the `main` function of a script -pub fn generate_script_main_function( +pub(crate) fn generate_script_main_function( main_function_abi: &ABIFunction, types: &HashMap, ) -> Result { @@ -116,7 +116,7 @@ pub fn generate_script_main_function( }) } -pub fn generate_predicate_encode_function( +pub(crate) fn generate_predicate_encode_function( main_function_abi: &ABIFunction, types: &HashMap, ) -> Result {