diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40e81790a5..ce0d4019bf 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/v2.3.6/dasel_linux_amd64 RUSTFLAGS: "-D warnings" - FUEL_CORE_VERSION: 0.40.0 + FUEL_CORE_VERSION: 0.41.3 FUEL_CORE_PATCH_BRANCH: "" FUEL_CORE_PATCH_REVISION: "" RUST_VERSION: 1.81.0 diff --git a/Cargo.toml b/Cargo.toml index b0e39e5fc6..d494007f41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,7 @@ readme = "README.md" license = "Apache-2.0" repository = "https://github.com/FuelLabs/fuels-rs" rust-version = "1.81.0" -version = "0.66.10" +version = "0.70.0" [workspace.dependencies] Inflector = "0.11.4" @@ -49,7 +49,7 @@ async-trait = { version = "0.1.74", default-features = false } bech32 = "0.9.1" bytes = { version = "1.5.0", default-features = false } chrono = "0.4.31" -cynic = { version = "2.2", default-features = false } +cynic = { version = "3.1.0", default-features = false } elliptic-curve = { version = "0.13.8", default-features = false } test-case = { version = "3.3", default-features = false } eth-keystore = "0.5.0" @@ -90,30 +90,30 @@ toml = { version = "0.8", default-features = false } mockall = { version = "0.13", default-features = false } # Dependencies from the `fuel-core` repository: -fuel-core = { version = "0.40.1", default-features = false, features = [ +fuel-core = { version = "0.41.3", default-features = false, features = [ "wasm-executor", ] } -fuel-core-chain-config = { version = "0.40.1", default-features = false } -fuel-core-client = { version = "0.40.1", default-features = false } -fuel-core-poa = { version = "0.40.1", default-features = false } -fuel-core-services = { version = "0.40.1", default-features = false } -fuel-core-types = { version = "0.40.1", default-features = false } +fuel-core-chain-config = { version = "0.41.3", default-features = false } +fuel-core-client = { version = "0.41.3", default-features = false } +fuel-core-poa = { version = "0.41.3", default-features = false } +fuel-core-services = { version = "0.41.3", default-features = false } +fuel-core-types = { version = "0.41.3", default-features = false } # Dependencies from the `fuel-vm` repository: -fuel-asm = { version = "0.58.2" } -fuel-crypto = { version = "0.58.2" } -fuel-merkle = { version = "0.58.2" } -fuel-storage = { version = "0.58.2" } -fuel-tx = { version = "0.58.2" } -fuel-types = { version = "0.58.2" } -fuel-vm = { version = "0.58.2" } +fuel-asm = { version = "0.59.1" } +fuel-crypto = { version = "0.59.1" } +fuel-merkle = { version = "0.59.1" } +fuel-storage = { version = "0.59.1" } +fuel-tx = { version = "0.59.1" } +fuel-types = { version = "0.59.1" } +fuel-vm = { version = "0.59.1" } # Workspace projects -fuels = { version = "0.66.10", path = "./packages/fuels", default-features = false } -fuels-accounts = { version = "0.66.10", path = "./packages/fuels-accounts", default-features = false } -fuels-code-gen = { version = "0.66.10", path = "./packages/fuels-code-gen", default-features = false } -fuels-core = { version = "0.66.10", path = "./packages/fuels-core", default-features = false } -fuels-macros = { version = "0.66.10", path = "./packages/fuels-macros", default-features = false } -fuels-programs = { version = "0.66.10", path = "./packages/fuels-programs", default-features = false } -fuels-test-helpers = { version = "0.66.10", path = "./packages/fuels-test-helpers", default-features = false } -versions-replacer = { version = "0.66.10", path = "./scripts/versions-replacer", default-features = false } +fuels = { version = "0.70.0", path = "./packages/fuels", default-features = false } +fuels-accounts = { version = "0.70.0", path = "./packages/fuels-accounts", default-features = false } +fuels-code-gen = { version = "0.70.0", path = "./packages/fuels-code-gen", default-features = false } +fuels-core = { version = "0.70.0", path = "./packages/fuels-core", default-features = false } +fuels-macros = { version = "0.70.0", path = "./packages/fuels-macros", default-features = false } +fuels-programs = { version = "0.70.0", path = "./packages/fuels-programs", default-features = false } +fuels-test-helpers = { version = "0.70.0", path = "./packages/fuels-test-helpers", default-features = false } +versions-replacer = { version = "0.70.0", path = "./scripts/versions-replacer", default-features = false } diff --git a/docs/src/connecting/short-lived.md b/docs/src/connecting/short-lived.md index 8d0257ea12..b5ea8e4011 100644 --- a/docs/src/connecting/short-lived.md +++ b/docs/src/connecting/short-lived.md @@ -27,7 +27,7 @@ let wallet = launch_provider_and_get_wallet().await?; The `fuel-core-lib` feature allows us to run a `fuel-core` node without installing the `fuel-core` binary on the local machine. Using the `fuel-core-lib` feature flag entails downloading all the dependencies needed to run the fuel-core node. ```rust,ignore -fuels = { version = "0.66.10", features = ["fuel-core-lib"] } +fuels = { version = "0.70.0", features = ["fuel-core-lib"] } ``` ### RocksDB @@ -35,5 +35,5 @@ fuels = { version = "0.66.10", features = ["fuel-core-lib"] } The `rocksdb` is an additional feature that, when combined with `fuel-core-lib`, provides persistent storage capabilities while using `fuel-core` as a library. ```rust,ignore -fuels = { version = "0.66.10", features = ["rocksdb"] } +fuels = { version = "0.70.0", features = ["rocksdb"] } ``` diff --git a/e2e/tests/contracts.rs b/e2e/tests/contracts.rs index 513a227956..9e9e80c473 100644 --- a/e2e/tests/contracts.rs +++ b/e2e/tests/contracts.rs @@ -1410,11 +1410,11 @@ fn db_rocksdb() { assert_eq!(blocks.len(), 3); assert_eq!( *wallet.get_balances().await?.iter().next().unwrap().1, - DEFAULT_COIN_AMOUNT + DEFAULT_COIN_AMOUNT as u128 ); assert_eq!( *wallet.get_balances().await?.iter().next().unwrap().1, - DEFAULT_COIN_AMOUNT + DEFAULT_COIN_AMOUNT as u128 ); assert_eq!(wallet.get_balances().await?.len(), 2); @@ -2188,11 +2188,8 @@ async fn max_fee_estimation_respects_tolerance() -> Result<()> { ); let normal_base_asset = base_amount_in_inputs(0.0).await; - let more_base_asset_due_to_bigger_tolerance = base_amount_in_inputs(2.00).await; - assert_eq!( - more_base_asset_due_to_bigger_tolerance as f64 / normal_base_asset as f64, - 1.00 + 2.00 - ); + let more_base_asset_due_to_bigger_tolerance = base_amount_in_inputs(5.00).await; + assert!(more_base_asset_due_to_bigger_tolerance > normal_base_asset); Ok(()) } diff --git a/e2e/tests/debug_utils.rs b/e2e/tests/debug_utils.rs index d68d0c6b9b..c959fb888f 100644 --- a/e2e/tests/debug_utils.rs +++ b/e2e/tests/debug_utils.rs @@ -71,7 +71,7 @@ async fn can_debug_single_call_tx() -> Result<()> { assert_eq!( decoder.decode_fn_args( &call_description.decode_fn_selector().unwrap(), - &call_description.encoded_args + call_description.encoded_args.as_slice() )?, vec!["AllStruct { some_struct: SomeStruct { field: 2, field_2: true } }"] ); @@ -115,7 +115,7 @@ async fn can_debug_single_call_tx() -> Result<()> { assert_eq!( decoder.decode_fn_args( &call_description.decode_fn_selector().unwrap(), - &call_description.encoded_args + call_description.encoded_args.as_slice() )?, vec!["AllStruct { some_struct: SomeStruct { field: 2, field_2: true } }"] ); @@ -214,7 +214,7 @@ async fn can_debug_multi_call_tx() -> Result<()> { assert_eq!( decoder.decode_fn_args( &call_description.decode_fn_selector().unwrap(), - &call_description.encoded_args + call_description.encoded_args.as_slice() )?, vec!["AllStruct { some_struct: SomeStruct { field: 2, field_2: true } }"] ); @@ -229,7 +229,7 @@ async fn can_debug_multi_call_tx() -> Result<()> { assert!(call_description.gas_forwarded.is_none()); assert_eq!( - decoder.decode_fn_args(&fn_selector, &call_description.encoded_args)?, + decoder.decode_fn_args(&fn_selector, call_description.encoded_args.as_slice())?, vec!["AllStruct { some_struct: SomeStruct { field: 2, field_2: true } }", "MemoryAddress { contract_id: std::contract_id::ContractId { bits: Bits256([77, 127, 224, 17, 182, 42, 211, 241, 46, 156, 74, 204, 31, 156, 188, 77, 183, 63, 55, 80, 119, 142, 192, 75, 130, 205, 208, 253, 25, 104, 22, 171]) }, function_selector: 123, function_data: 456 }"] ); } @@ -286,7 +286,7 @@ async fn can_debug_multi_call_tx() -> Result<()> { assert_eq!( decoder.decode_fn_args( &call_description.decode_fn_selector().unwrap(), - &call_description.encoded_args + call_description.encoded_args.as_slice() )?, vec!["AllStruct { some_struct: SomeStruct { field: 2, field_2: true } }"] ); @@ -303,7 +303,7 @@ async fn can_debug_multi_call_tx() -> Result<()> { assert_eq!(call_description.gas_forwarded, Some(25)); assert_eq!( - decoder.decode_fn_args(&call_description.decode_fn_selector().unwrap(), &call_description.encoded_args)?, + decoder.decode_fn_args(&call_description.decode_fn_selector().unwrap(), call_description.encoded_args.as_slice())?, vec!["AllStruct { some_struct: SomeStruct { field: 2, field_2: true } }", "MemoryAddress { contract_id: std::contract_id::ContractId { bits: Bits256([77, 127, 224, 17, 182, 42, 211, 241, 46, 156, 74, 204, 31, 156, 188, 77, 183, 63, 55, 80, 119, 142, 192, 75, 130, 205, 208, 253, 25, 104, 22, 171]) }, function_selector: 123, function_data: 456 }"] ); } @@ -345,7 +345,7 @@ async fn can_debug_sway_script() -> Result<()> { }; assert_eq!( - decoder.decode_fn_args("main", &desc.data)?, + decoder.decode_fn_args("main", desc.data.as_slice())?, vec!["MyStruct { number: 10, boolean: false }"] ); @@ -455,7 +455,7 @@ async fn can_detect_a_loader_script_w_data_section() -> Result<()> { )?)?; assert_eq!( - decoder.decode_fn_args("main", &script.data)?, + decoder.decode_fn_args("main", script.data.as_slice())?, vec!["MyStruct { number: 10, boolean: false }"] ); diff --git a/e2e/tests/predicates.rs b/e2e/tests/predicates.rs index 3240d74870..8ca5643e04 100644 --- a/e2e/tests/predicates.rs +++ b/e2e/tests/predicates.rs @@ -86,8 +86,8 @@ async fn setup_predicate_test( coins.extend(setup_single_asset_coins( extra_wallet.address(), AssetId::zeroed(), - 10000, - u64::MAX, + 10_000, + 10_000, )); coins.extend(setup_single_asset_coins( @@ -431,8 +431,7 @@ async fn predicate_transfer_to_base_layer() -> Result<()> { let proof = predicate .try_provider()? .get_message_proof(&tx_id, &msg_nonce, None, Some(2)) - .await? - .expect("failed to retrieve message proof"); + .await?; assert_eq!(proof.amount, amount); assert_eq!(proof.recipient, base_layer_address); diff --git a/e2e/tests/providers.rs b/e2e/tests/providers.rs index 32acfd7c7f..9d82940104 100644 --- a/e2e/tests/providers.rs +++ b/e2e/tests/providers.rs @@ -1130,6 +1130,7 @@ async fn tx_respects_policies() -> Result<()> { } #[tokio::test] +#[ignore] //TODO: https://github.com/FuelLabs/fuels-rs/issues/1581 async fn can_setup_static_gas_price() -> Result<()> { let expected_gas_price = 474; let node_config = NodeConfig { @@ -1140,7 +1141,8 @@ async fn can_setup_static_gas_price() -> Result<()> { let gas_price = provider.estimate_gas_price(0).await?.gas_price; - assert_eq!(gas_price, expected_gas_price); + let da_cost = 1000; + assert_eq!(gas_price, da_cost + expected_gas_price); Ok(()) } diff --git a/e2e/tests/types_predicates.rs b/e2e/tests/types_predicates.rs index 9214dd08c7..3ea61c5c4f 100644 --- a/e2e/tests/types_predicates.rs +++ b/e2e/tests/types_predicates.rs @@ -13,29 +13,27 @@ async fn assert_predicate_spendable(data: Vec, project_path: impl AsRef Result<()> { @@ -33,7 +26,7 @@ async fn test_wallet_balance_api_multi_asset() -> Result<()> { assert!(balances.contains_key(&expected_key)); assert_eq!( *balances.get(&expected_key).unwrap(), - coins_per_asset * amount_per_coin + (coins_per_asset * amount_per_coin) as u128 ); } Ok(()) @@ -65,60 +58,12 @@ async fn test_wallet_balance_api_single_asset() -> Result<()> { assert!(balances.contains_key(&expected_key)); assert_eq!( *balances.get(&expected_key).unwrap(), - number_of_coins * amount_per_coin + (number_of_coins * amount_per_coin) as u128 ); Ok(()) } -fn compare_inputs(inputs: &[Input], expected_inputs: &mut Vec) -> bool { - let zero_utxo_id = UtxoId::new(Bytes32::zeroed(), 0); - - // change UTXO_ids to 0s for comparison, because we can't guess the genesis coin ids - let inputs: Vec = inputs - .iter() - .map(|input| match input { - Input::CoinSigned(CoinSigned { - owner, - amount, - asset_id, - tx_pointer, - witness_index, - .. - }) => Input::coin_signed( - zero_utxo_id, - *owner, - *amount, - *asset_id, - *tx_pointer, - *witness_index, - ), - other => other.clone(), - }) - .collect(); - - let comparison_results: Vec = inputs - .iter() - .map(|input| { - let found_index = expected_inputs - .iter() - .position(|expected| expected == input); - if let Some(index) = found_index { - expected_inputs.remove(index); - true - } else { - false - } - }) - .collect(); - - if !expected_inputs.is_empty() { - return false; - } - - comparison_results.iter().all(|&r| r) -} - fn base_asset_wallet_config(num_wallets: u64) -> WalletsConfig { let asset_configs = vec![AssetConfig { id: AssetId::zeroed(), @@ -130,35 +75,31 @@ fn base_asset_wallet_config(num_wallets: u64) -> WalletsConfig { #[tokio::test] async fn adjust_fee_empty_transaction() -> Result<()> { - let wallet_config = base_asset_wallet_config(1); - let wallet = launch_custom_provider_and_get_wallets(wallet_config, None, None) - .await? - .pop() - .unwrap(); + let wallet = launch_provider_and_get_wallet().await?; let mut tb = ScriptTransactionBuilder::prepare_transfer(vec![], vec![], TxPolicies::default()); - tb.add_signer(wallet.clone())?; + assert!(tb.inputs().is_empty()); + assert!(tb.outputs().is_empty()); + tb.add_signer(wallet.clone())?; wallet.adjust_for_fee(&mut tb, 0).await?; + assert!(!tb.inputs().is_empty(), "inputs should be added"); + assert_eq!(tb.outputs().len(), 1, "output should be added"); let tx = tb.build(wallet.try_provider()?).await?; - let zero_utxo_id = UtxoId::new(Bytes32::zeroed(), 0); - let mut expected_inputs = vec![Input::coin_signed( - zero_utxo_id, - wallet.address().into(), - 20, - AssetId::zeroed(), - TxPointer::default(), - 0, - )]; + let total_amount_inputs: u64 = tx.inputs().iter().map(|i| i.amount().unwrap()).sum(); + assert!( + total_amount_inputs > tx.max_fee().unwrap(), + "amount should cover tx" + ); + let expected_outputs = vec![Output::change( wallet.address().into(), 0, AssetId::zeroed(), )]; - assert!(compare_inputs(tx.inputs(), &mut expected_inputs)); assert_eq!(tx.outputs(), &expected_outputs); Ok(()) @@ -166,11 +107,7 @@ async fn adjust_fee_empty_transaction() -> Result<()> { #[tokio::test] async fn adjust_fee_resources_to_transfer_with_base_asset() -> Result<()> { - let wallet_config = base_asset_wallet_config(1); - let wallet = launch_custom_provider_and_get_wallets(wallet_config, None, None) - .await? - .pop() - .unwrap(); + let wallet = launch_provider_and_get_wallet().await?; let base_amount = 30; let base_asset_id = AssetId::zeroed(); @@ -187,23 +124,14 @@ async fn adjust_fee_resources_to_transfer_with_base_asset() -> Result<()> { let tx = tb.build(wallet.try_provider()?).await?; - let zero_utxo_id = UtxoId::new(Bytes32::zeroed(), 0); - let mut expected_inputs = repeat(Input::coin_signed( - zero_utxo_id, - wallet.address().into(), - 20, - base_asset_id, - TxPointer::default(), - 0, - )) - .take(3) - .collect::>(); + let total_amount_inputs: u64 = tx.inputs().iter().map(|i| i.amount().unwrap()).sum(); + assert!(total_amount_inputs > tx.max_fee().unwrap()); // can cover tx + let expected_outputs = vec![ Output::coin(Address::zeroed(), base_amount, base_asset_id), Output::change(wallet.address().into(), 0, base_asset_id), ]; - assert!(compare_inputs(tx.inputs(), &mut expected_inputs)); assert_eq!(tx.outputs(), &expected_outputs); Ok(()) diff --git a/examples/contracts/src/lib.rs b/examples/contracts/src/lib.rs index 164d944016..7f72eb460d 100644 --- a/examples/contracts/src/lib.rs +++ b/examples/contracts/src/lib.rs @@ -1198,7 +1198,8 @@ mod tests { let call = &calls[0]; let fn_selector = call.decode_fn_selector()?; - let decoded_args = abi_formatter.decode_fn_args(&fn_selector, &call.encoded_args)?; + let decoded_args = + abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?; eprintln!( "The script called: {fn_selector}({})", diff --git a/examples/cookbook/src/lib.rs b/examples/cookbook/src/lib.rs index e0c0e19b14..47e0b2d7b8 100644 --- a/examples/cookbook/src/lib.rs +++ b/examples/cookbook/src/lib.rs @@ -168,6 +168,7 @@ mod tests { let mut outputs = vec![]; for (id_string, amount) in balances { let id = AssetId::from_str(&id_string)?; + let amount = amount as u64; let input = wallet_1 .get_asset_inputs_for_amount(id, amount, None) @@ -199,9 +200,9 @@ mod tests { assert_eq!(balances.len(), NUM_ASSETS as usize); for (id, balance) in balances { if id == *consensus_parameters.base_asset_id().to_string() { - assert_eq!(balance, AMOUNT / 2); + assert_eq!(balance, (AMOUNT / 2) as u128); } else { - assert_eq!(balance, AMOUNT); + assert_eq!(balance, AMOUNT as u128); } } // ANCHOR_END: transfer_multiple_transaction diff --git a/examples/debugging/src/lib.rs b/examples/debugging/src/lib.rs index 1e1b8a19c6..756e21b652 100644 --- a/examples/debugging/src/lib.rs +++ b/examples/debugging/src/lib.rs @@ -67,7 +67,7 @@ mod tests { assert_eq!( format!("{expected_struct:?}"), - decoder.decode_as_debug_str(¶m_type, &[0, 0, 0, 0, 0, 0, 0, 123])? + decoder.decode_as_debug_str(¶m_type, [0, 0, 0, 0, 0, 0, 0, 123].as_slice())? ); } { @@ -83,7 +83,7 @@ mod tests { assert_eq!( format!("{expected_struct:?}"), - decoder.decode_as_debug_str(¶m_type, &[97, 98, 99])? + decoder.decode_as_debug_str(¶m_type, [97, 98, 99].as_slice())? ); } { @@ -97,7 +97,7 @@ mod tests { format!("{expected_enum:?}"), decoder.decode_as_debug_str( ¶m_type, - &[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 10] + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 10].as_slice() )? ); } @@ -117,7 +117,7 @@ mod tests { assert_eq!( format!("{expected_u8}"), - decoder.decode_as_debug_str(¶m_type, &[1])? + decoder.decode_as_debug_str(¶m_type, [1].as_slice())? ); } diff --git a/examples/wallets/src/lib.rs b/examples/wallets/src/lib.rs index dff23836b1..eb4551398f 100644 --- a/examples/wallets/src/lib.rs +++ b/examples/wallets/src/lib.rs @@ -333,14 +333,17 @@ mod tests { let balance: u64 = wallet.get_asset_balance(&asset_id).await?; // ANCHOR_END: get_asset_balance // ANCHOR: get_balances - let balances: HashMap = wallet.get_balances().await?; + let balances: HashMap = wallet.get_balances().await?; // ANCHOR_END: get_balances // ANCHOR: get_balance_hashmap let asset_balance = balances.get(&asset_id.to_string()).unwrap(); // ANCHOR_END: get_balance_hashmap - assert_eq!(*asset_balance, DEFAULT_COIN_AMOUNT * DEFAULT_NUM_COINS); + assert_eq!( + *asset_balance, + (DEFAULT_COIN_AMOUNT * DEFAULT_NUM_COINS) as u128 + ); Ok(()) } @@ -376,8 +379,7 @@ mod tests { let proof = wallet .try_provider()? .get_message_proof(&tx_id, &msg_id, None, Some(2)) - .await? - .expect("failed to retrieve message proof"); + .await?; // Verify the amount and recipient assert_eq!(proof.amount, amount); diff --git a/packages/fuels-accounts/src/account.rs b/packages/fuels-accounts/src/account.rs index 2090364e2b..dded620dc1 100644 --- a/packages/fuels-accounts/src/account.rs +++ b/packages/fuels-accounts/src/account.rs @@ -66,7 +66,7 @@ pub trait ViewOnlyAccount: std::fmt::Debug + Send + Sync + Clone { /// Get all the spendable balances of all assets for the account. This is different from getting /// the coins because we are only returning the sum of UTXOs coins amount and not the UTXOs /// coins themselves. - async fn get_balances(&self) -> Result> { + async fn get_balances(&self) -> Result> { self.try_provider()?.get_balances(self.address()).await } @@ -140,7 +140,9 @@ pub trait ViewOnlyAccount: std::fmt::Debug + Send + Sync + Clone { missing_base_amount, Some(base_assets), ) - .await?; + .await + // if there query fails do nothing + .unwrap_or_default(); tb.inputs_mut().extend(new_base_inputs); }; diff --git a/packages/fuels-accounts/src/accounts_utils.rs b/packages/fuels-accounts/src/accounts_utils.rs index fb1069fa83..2410b67b05 100644 --- a/packages/fuels-accounts/src/accounts_utils.rs +++ b/packages/fuels-accounts/src/accounts_utils.rs @@ -54,11 +54,11 @@ pub fn available_base_assets_and_amount( amount, asset_id, .. }) if asset_id == base_asset_id => { sum += amount; - Some(resource.id()) + resource.id() } CoinType::Message(message) => { sum += message.amount; - Some(resource.id()) + resource.id() } _ => None, }, diff --git a/packages/fuels-accounts/src/impersonated_account.rs b/packages/fuels-accounts/src/impersonated_account.rs index 10f3759dfd..96c48956f5 100644 --- a/packages/fuels-accounts/src/impersonated_account.rs +++ b/packages/fuels-accounts/src/impersonated_account.rs @@ -2,13 +2,15 @@ use std::fmt::Debug; use async_trait::async_trait; use fuel_crypto::{Message, Signature}; -use fuels_core::traits::Signer; -use fuels_core::types::transaction_builders::TransactionBuilder; -use fuels_core::types::{bech32::Bech32Address, errors::Result}; -use fuels_core::types::{coin_type_id::CoinTypeId, input::Input, AssetId}; +use fuels_core::{ + traits::Signer, + types::{ + bech32::Bech32Address, coin_type_id::CoinTypeId, errors::Result, input::Input, + transaction_builders::TransactionBuilder, AssetId, + }, +}; -use crate::accounts_utils::try_provider_error; -use crate::{provider::Provider, Account, ViewOnlyAccount}; +use crate::{accounts_utils::try_provider_error, provider::Provider, Account, ViewOnlyAccount}; /// A `ImpersonatedAccount` simulates ownership of assets held by an account with a given address. /// `ImpersonatedAccount` will only succeed in unlocking assets if the the network is setup with diff --git a/packages/fuels-accounts/src/provider.rs b/packages/fuels-accounts/src/provider.rs index 0e92238ff3..31f053bba6 100644 --- a/packages/fuels-accounts/src/provider.rs +++ b/packages/fuels-accounts/src/provider.rs @@ -56,6 +56,8 @@ use tokio::sync::Mutex; use crate::coin_cache::CoinsCache; use crate::provider::retryable_client::RetryableClient; +const NUM_RESULTS_PER_REQUEST: i32 = 100; + #[derive(Debug, Clone, PartialEq)] // ANCHOR: transaction_cost pub struct TransactionCost { @@ -414,28 +416,28 @@ impl Provider { /// Gets all unspent coins owned by address `from`, with asset ID `asset_id`. pub async fn get_coins(&self, from: &Bech32Address, asset_id: AssetId) -> Result> { let mut coins: Vec = vec![]; - let mut cursor = None; loop { - let res = self + let response = self .uncached_client() .coins( &from.into(), Some(&asset_id), PaginationRequest { cursor: cursor.clone(), - results: 100, + results: NUM_RESULTS_PER_REQUEST, direction: PageDirection::Forward, }, ) .await?; - if res.results.is_empty() { + if response.results.is_empty() { break; } - coins.extend(res.results.into_iter().map(Into::into)); - cursor = res.cursor; + + coins.extend(response.results.into_iter().map(Into::into)); + cursor = response.cursor; } Ok(coins) @@ -457,8 +459,8 @@ impl Provider { .await? .into_iter() .flatten() - .map(CoinType::try_from) - .collect::>>()?; + .map(CoinType::from) + .collect(); Ok(res) } @@ -549,29 +551,37 @@ impl Provider { /// Get all the spendable balances of all assets for address `address`. This is different from /// getting the coins because we are only returning the numbers (the sum of UTXOs coins amount /// for each asset id) and not the UTXOs coins themselves - pub async fn get_balances(&self, address: &Bech32Address) -> Result> { - // We don't paginate results because there are likely at most ~100 different assets in one - // wallet - let pagination = PaginationRequest { - cursor: None, - results: 9999, - direction: PageDirection::Forward, - }; - let balances_vec = self - .uncached_client() - .balances(&address.into(), pagination) - .await? - .results; - let balances = balances_vec - .into_iter() - .map( + pub async fn get_balances(&self, address: &Bech32Address) -> Result> { + let mut balances = HashMap::new(); + let mut cursor = None; + + loop { + let response = self + .uncached_client() + .balances( + &address.into(), + PaginationRequest { + cursor: cursor.clone(), + results: NUM_RESULTS_PER_REQUEST, + direction: PageDirection::Forward, + }, + ) + .await?; + + if response.results.is_empty() { + break; + } + + balances.extend(response.results.into_iter().map( |Balance { owner: _, amount, asset_id, }| (asset_id.to_string(), amount), - ) - .collect(); + )); + cursor = response.cursor; + } + Ok(balances) } @@ -580,39 +590,37 @@ impl Provider { &self, contract_id: &Bech32ContractId, ) -> Result> { - let mut pagination = PaginationRequest { - cursor: None, - results: 512, - direction: PageDirection::Forward, - }; + let mut contract_balances = HashMap::new(); + let mut cursor = None; - let mut balances_vec = vec![]; loop { - let mut paginated_result = self + let response = self .uncached_client() - .contract_balances(&contract_id.into(), pagination.clone()) + .contract_balances( + &contract_id.into(), + PaginationRequest { + cursor: cursor.clone(), + results: NUM_RESULTS_PER_REQUEST, + direction: PageDirection::Forward, + }, + ) .await?; - pagination.cursor = paginated_result.cursor; - balances_vec.append(&mut paginated_result.results); - - if !paginated_result.has_next_page { + if response.results.is_empty() { break; } - } - let balances = balances_vec - .into_iter() - .map( + contract_balances.extend(response.results.into_iter().map( |ContractBalance { contract: _, amount, asset_id, }| (asset_id, amount), - ) - .collect(); + )); + cursor = response.cursor; + } - Ok(balances) + Ok(contract_balances) } pub async fn get_transaction_by_id(&self, tx_id: &TxId) -> Result> { @@ -766,20 +774,31 @@ impl Provider { } pub async fn get_messages(&self, from: &Bech32Address) -> Result> { - let pagination = PaginationRequest { - cursor: None, - results: 100, - direction: PageDirection::Forward, - }; + let mut messages = Vec::new(); + let mut cursor = None; - Ok(self - .uncached_client() - .messages(Some(&from.into()), pagination) - .await? - .results - .into_iter() - .map(Into::into) - .collect()) + loop { + let response = self + .uncached_client() + .messages( + Some(&from.into()), + PaginationRequest { + cursor: cursor.clone(), + results: NUM_RESULTS_PER_REQUEST, + direction: PageDirection::Forward, + }, + ) + .await?; + + if response.results.is_empty() { + break; + } + + messages.extend(response.results.into_iter().map(Into::into)); + cursor = response.cursor; + } + + Ok(messages) } pub async fn get_message_proof( @@ -788,19 +807,17 @@ impl Provider { nonce: &Nonce, commit_block_id: Option<&Bytes32>, commit_block_height: Option, - ) -> Result> { - let proof = self - .uncached_client() + ) -> Result { + self.uncached_client() .message_proof( tx_id, nonce, commit_block_id.map(Into::into), commit_block_height.map(Into::into), ) - .await? - .map(Into::into); - - Ok(proof) + .await + .map(Into::into) + .map_err(Into::into) } pub async fn is_user_account(&self, address: impl Into) -> Result { diff --git a/packages/fuels-accounts/src/provider/retryable_client.rs b/packages/fuels-accounts/src/provider/retryable_client.rs index d43f344445..9ce884927a 100644 --- a/packages/fuels-accounts/src/provider/retryable_client.rs +++ b/packages/fuels-accounts/src/provider/retryable_client.rs @@ -311,7 +311,7 @@ impl RetryableClient { nonce: &Nonce, commit_block_id: Option<&BlockId>, commit_block_height: Option, - ) -> RequestResult> { + ) -> RequestResult { self.wrap(|| { self.client .message_proof(transaction_id, nonce, commit_block_id, commit_block_height) @@ -361,7 +361,7 @@ mod custom_queries { use fuel_core_client::client::schema::blob::BlobIdFragment; use fuel_core_client::client::schema::schema; use fuel_core_client::client::schema::{ - contract::{ContractByIdArgs, ContractIdFragment}, + contract::{ContractByIdArgsFields, ContractIdFragment}, tx::TransactionIdFragment, BlobId, ContractId, TransactionId, }; diff --git a/packages/fuels-accounts/src/provider/supported_fuel_core_version.rs b/packages/fuels-accounts/src/provider/supported_fuel_core_version.rs index 1a8bf5243d..2ded4746f8 100644 --- a/packages/fuels-accounts/src/provider/supported_fuel_core_version.rs +++ b/packages/fuels-accounts/src/provider/supported_fuel_core_version.rs @@ -1 +1 @@ -pub const SUPPORTED_FUEL_CORE_VERSION: semver::Version = semver::Version::new(0, 40, 1); +pub const SUPPORTED_FUEL_CORE_VERSION: semver::Version = semver::Version::new(0, 41, 3); diff --git a/packages/fuels-accounts/src/schema/schema.sdl b/packages/fuels-accounts/src/schema/schema.sdl index b9048362ca..016f7907af 100644 --- a/packages/fuels-accounts/src/schema/schema.sdl +++ b/packages/fuels-accounts/src/schema/schema.sdl @@ -2,9 +2,16 @@ scalar Address scalar AssetId +type AssetInfoDetails { + contractId: ContractId! + subId: SubId! + totalSupply: U128! +} + type Balance { owner: Address! amount: U64! + amountU128: U128! assetId: AssetId! } @@ -442,6 +449,7 @@ type GasCosts { wqmm: U64! xor: U64! xori: U64! + ecop: U64 alocDependentCost: DependentCost! bldd: DependentCost bsiz: DependentCost @@ -466,6 +474,7 @@ type GasCosts { smo: DependentCost! srwq: DependentCost! swwq: DependentCost! + epar: DependentCost contractRoot: DependentCost! stateRoot: DependentCost! vmInitialization: DependentCost! @@ -571,6 +580,21 @@ type HeavyOperation { scalar HexString +type IndexationFlags { + """ + Is balances indexation enabled + """ + balances: Boolean! + """ + Is coins to spend indexation enabled + """ + coinsToSpend: Boolean! + """ + Is asset metadata indexation enabled + """ + assetMetadata: Boolean! +} + union Input = InputCoin | InputContract | InputMessage type InputCoin { @@ -751,8 +775,12 @@ type NodeInfo { utxoValidation: Boolean! vmBacktrace: Boolean! maxTx: U64! + maxGas: U64! + maxSize: U64! maxDepth: U64! nodeVersion: String! + indexation: IndexationFlags! + txPoolStats: TxPoolStats! peers: [PeerInfo!]! } @@ -850,6 +878,12 @@ type ProgramState { } type Query { + assetDetails( + """ + ID of the Asset + """ + id: AssetId! + ): AssetInfoDetails! """ Read register value by index. """ @@ -980,7 +1014,7 @@ type Query { """ owner: Address, first: Int, after: String, last: Int, before: String ): MessageConnection! - messageProof(transactionId: TransactionId!, nonce: Nonce!, commitBlockId: BlockId, commitBlockHeight: U32): MessageProof + messageProof(transactionId: TransactionId!, nonce: Nonce!, commitBlockId: BlockId, commitBlockHeight: U32): MessageProof! messageStatus(nonce: Nonce!): MessageStatus! relayedTransactionStatus( """ @@ -1118,6 +1152,8 @@ type StateTransitionPurpose { } +scalar SubId + type SubmittedStatus { time: Tai64Timestamp! } @@ -1259,6 +1295,23 @@ enum TxParametersVersion { scalar TxPointer +type TxPoolStats { + """ + The number of transactions in the pool + """ + txCount: U64! + """ + The total size of the transactions in the pool + """ + totalSize: U64! + """ + The total gas of the transactions in the pool + """ + totalGas: U64! +} + +scalar U128 + scalar U16 scalar U32 diff --git a/packages/fuels-core/src/codec.rs b/packages/fuels-core/src/codec.rs index af83cbf11a..73d7655803 100644 --- a/packages/fuels-core/src/codec.rs +++ b/packages/fuels-core/src/codec.rs @@ -5,6 +5,8 @@ mod function_selector; mod logs; mod utils; +use std::io::Read; + pub use abi_decoder::*; pub use abi_encoder::*; pub use abi_formatter::*; @@ -17,7 +19,7 @@ use crate::{ }; /// Decodes `bytes` into type `T` following the schema defined by T's `Parameterize` impl -pub fn try_from_bytes(bytes: &[u8], decoder_config: DecoderConfig) -> Result +pub fn try_from_bytes(bytes: impl Read, decoder_config: DecoderConfig) -> Result where T: Parameterize + Tokenizable, { @@ -41,13 +43,16 @@ mod tests { macro_rules! test_decode { ($($for_type: ident),*) => { $(assert_eq!( - try_from_bytes::<$for_type>(&bytes, DecoderConfig::default())?, + try_from_bytes::<$for_type>(bytes.as_slice(), DecoderConfig::default())?, $for_type::MAX );)* }; } - assert!(try_from_bytes::(&bytes, DecoderConfig::default())?); + assert!(try_from_bytes::( + bytes.as_slice(), + DecoderConfig::default() + )?); test_decode!(u8, u16, u32, u64); @@ -58,7 +63,8 @@ mod tests { fn convert_bytes_into_tuple() -> Result<()> { let tuple_in_bytes = [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2]; - let the_tuple: (u64, u32) = try_from_bytes(&tuple_in_bytes, DecoderConfig::default())?; + let the_tuple: (u64, u32) = + try_from_bytes(tuple_in_bytes.as_slice(), DecoderConfig::default())?; assert_eq!(the_tuple, (1, 2)); @@ -72,7 +78,7 @@ mod tests { macro_rules! test_decode { ($($for_type: ident),*) => { $(assert_eq!( - try_from_bytes::<$for_type>(&bytes, DecoderConfig::default())?, + try_from_bytes::<$for_type>(bytes.as_slice(), DecoderConfig::default())?, $for_type::new(bytes.as_slice().try_into()?) );)* }; @@ -109,7 +115,7 @@ mod tests { .unwrap(); // when - let decoded = try_from_bytes::(&encoded, DecoderConfig::default()).unwrap(); + let decoded = try_from_bytes::(encoded.as_slice(), DecoderConfig::default()).unwrap(); // then assert_eq!(decoded, input); diff --git a/packages/fuels-core/src/codec/abi_decoder.rs b/packages/fuels-core/src/codec/abi_decoder.rs index e700a351d2..b8d9c0b31c 100644 --- a/packages/fuels-core/src/codec/abi_decoder.rs +++ b/packages/fuels-core/src/codec/abi_decoder.rs @@ -1,6 +1,8 @@ mod bounded_decoder; mod decode_as_debug_str; +use std::io::Read; + use crate::{ codec::abi_decoder::{ bounded_decoder::BoundedDecoder, decode_as_debug_str::decode_as_debug_str, @@ -55,12 +57,12 @@ impl ABIDecoder { /// /// let decoder = ABIDecoder::default(); /// - /// let token = decoder.decode(&ParamType::U64, &[0, 0, 0, 0, 0, 0, 0, 7]).unwrap(); + /// let token = decoder.decode(&ParamType::U64, [0, 0, 0, 0, 0, 0, 0, 7].as_slice()).unwrap(); /// /// assert_eq!(u64::from_token(token).unwrap(), 7u64); /// ``` - pub fn decode(&self, param_type: &ParamType, bytes: &[u8]) -> Result { - BoundedDecoder::new(self.config).decode(param_type, bytes) + pub fn decode(&self, param_type: &ParamType, mut bytes: impl Read) -> Result { + BoundedDecoder::new(self.config).decode(param_type, &mut bytes) } /// Same as `decode` but decodes multiple `ParamType`s in one go. @@ -71,14 +73,18 @@ impl ABIDecoder { /// use fuels_core::types::Token; /// /// let decoder = ABIDecoder::default(); - /// let data: &[u8] = &[7, 8]; + /// let data = [7, 8]; /// - /// let tokens = decoder.decode_multiple(&[ParamType::U8, ParamType::U8], &data).unwrap(); + /// let tokens = decoder.decode_multiple(&[ParamType::U8, ParamType::U8], data.as_slice()).unwrap(); /// /// assert_eq!(tokens, vec![Token::U8(7), Token::U8(8)]); /// ``` - pub fn decode_multiple(&self, param_types: &[ParamType], bytes: &[u8]) -> Result> { - BoundedDecoder::new(self.config).decode_multiple(param_types, bytes) + pub fn decode_multiple( + &self, + param_types: &[ParamType], + mut bytes: impl Read, + ) -> Result> { + BoundedDecoder::new(self.config).decode_multiple(param_types, &mut bytes) } /// Decodes `bytes` following the schema described in `param_type` into its respective debug @@ -97,22 +103,26 @@ impl ABIDecoder { /// /// let decoder = ABIDecoder::default(); /// - /// let debug_string = decoder.decode_as_debug_str(&ParamType::U64, &[0, 0, 0, 0, 0, 0, 0, 7]).unwrap(); + /// let debug_string = decoder.decode_as_debug_str(&ParamType::U64, [0, 0, 0, 0, 0, 0, 0, 7].as_slice()).unwrap(); /// let expected_value = 7u64; /// /// assert_eq!(debug_string, format!("{expected_value}")); /// ``` - pub fn decode_as_debug_str(&self, param_type: &ParamType, bytes: &[u8]) -> Result { - let token = BoundedDecoder::new(self.config).decode(param_type, bytes)?; + pub fn decode_as_debug_str( + &self, + param_type: &ParamType, + mut bytes: impl Read, + ) -> Result { + let token = BoundedDecoder::new(self.config).decode(param_type, &mut bytes)?; decode_as_debug_str(param_type, &token) } pub fn decode_multiple_as_debug_str( &self, param_types: &[ParamType], - bytes: &[u8], + mut bytes: impl Read, ) -> Result> { - let token = BoundedDecoder::new(self.config).decode_multiple(param_types, bytes)?; + let token = BoundedDecoder::new(self.config).decode_multiple(param_types, &mut bytes)?; token .into_iter() .zip(param_types) @@ -157,7 +167,7 @@ mod tests { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // u256 ]; - let decoded = ABIDecoder::default().decode_multiple(&types, &data)?; + let decoded = ABIDecoder::default().decode_multiple(&types, data.as_slice())?; let expected = vec![ Token::U8(u8::MAX), @@ -177,7 +187,7 @@ mod tests { let types = vec![ParamType::Bool, ParamType::Bool]; let data = [1, 0]; - let decoded = ABIDecoder::default().decode_multiple(&types, &data)?; + let decoded = ABIDecoder::default().decode_multiple(&types, data.as_slice())?; let expected = vec![Token::Bool(true), Token::Bool(false)]; @@ -193,7 +203,7 @@ mod tests { 152, 244, 172, 69, 123, 168, 248, 39, 67, 243, 30, 147, 11, ]; - let decoded = ABIDecoder::default().decode(&ParamType::B256, &data)?; + let decoded = ABIDecoder::default().decode(&ParamType::B256, data.as_slice())?; assert_eq!(decoded, Token::B256(data)); @@ -209,7 +219,7 @@ mod tests { 72, 101, 108, 108, 111, // Hello ]; - let decoded = ABIDecoder::default().decode_multiple(&types, &data)?; + let decoded = ABIDecoder::default().decode_multiple(&types, data.as_slice())?; let expected = vec![ Token::StringArray(StaticStringToken::new( @@ -232,7 +242,7 @@ mod tests { 116, 101, 110, 99, 101, //This is a full sentence ]; - let decoded = ABIDecoder::default().decode(&ParamType::StringSlice, &data)?; + let decoded = ABIDecoder::default().decode(&ParamType::StringSlice, data.as_slice())?; let expected = Token::StringSlice(StaticStringToken::new( "This is a full sentence".into(), @@ -252,7 +262,7 @@ mod tests { 116, 101, 110, 99, 101, //This is a full sentence ]; - let decoded = ABIDecoder::default().decode(&ParamType::String, &data)?; + let decoded = ABIDecoder::default().decode(&ParamType::String, data.as_slice())?; let expected = Token::String("This is a full sentence".to_string()); @@ -269,7 +279,7 @@ mod tests { 1, //bool ]; - let result = ABIDecoder::default().decode(¶m_type, &data)?; + let result = ABIDecoder::default().decode(¶m_type, data.as_slice())?; let expected = Token::Tuple(vec![Token::U32(255), Token::Bool(true)]); @@ -283,7 +293,7 @@ mod tests { let types = vec![ParamType::Array(Box::new(ParamType::U8), 2)]; let data = [255, 42]; - let decoded = ABIDecoder::default().decode_multiple(&types, &data)?; + let decoded = ABIDecoder::default().decode_multiple(&types, data.as_slice())?; let expected = vec![Token::Array(vec![Token::U8(255), Token::U8(42)])]; assert_eq!(decoded, expected); @@ -306,7 +316,7 @@ mod tests { generics: vec![], }; - let decoded = ABIDecoder::default().decode(¶m_type, &data)?; + let decoded = ABIDecoder::default().decode(¶m_type, data.as_slice())?; let expected = Token::Struct(vec![Token::U8(1), Token::Bool(true)]); @@ -319,7 +329,7 @@ mod tests { fn decode_bytes() -> Result<()> { let data = [0, 0, 0, 0, 0, 0, 0, 7, 255, 0, 1, 2, 3, 4, 5]; - let decoded = ABIDecoder::default().decode(&ParamType::Bytes, &data)?; + let decoded = ABIDecoder::default().decode(&ParamType::Bytes, data.as_slice())?; let expected = Token::Bytes([255, 0, 1, 2, 3, 4, 5].to_vec()); @@ -332,7 +342,7 @@ mod tests { fn decode_raw_slice() -> Result<()> { let data = [0, 0, 0, 0, 0, 0, 0, 7, 255, 0, 1, 2, 3, 4, 5]; - let decoded = ABIDecoder::default().decode(&ParamType::RawSlice, &data)?; + let decoded = ABIDecoder::default().decode(&ParamType::RawSlice, data.as_slice())?; let expected = Token::RawSlice([255, 0, 1, 2, 3, 4, 5].to_vec()); @@ -361,7 +371,7 @@ mod tests { 0, 0, 0, 42, // u32 ]; - let decoded = ABIDecoder::default().decode_multiple(&types, &data)?; + let decoded = ABIDecoder::default().decode_multiple(&types, data.as_slice())?; let expected = vec![Token::Enum(Box::new((0, Token::U32(42), inner_enum_types)))]; assert_eq!(decoded, expected); @@ -400,7 +410,7 @@ mod tests { let data = [0, 10, 1, 1, 2]; - let decoded = ABIDecoder::default().decode(&nested_struct, &data)?; + let decoded = ABIDecoder::default().decode(&nested_struct, data.as_slice())?; let my_nested_struct = vec![ Token::U16(10), @@ -461,7 +471,7 @@ mod tests { 152, 244, 172, 69, 123, 168, 248, 39, 67, 243, 30, 147, 11, // b256 ]; - let decoded = ABIDecoder::default().decode_multiple(&types, &bytes)?; + let decoded = ABIDecoder::default().decode_multiple(&types, bytes.as_slice())?; // Expected tokens let foo = Token::Struct(vec![ @@ -497,7 +507,7 @@ mod tests { generics: vec![], }; - let result = ABIDecoder::default().decode(&enum_w_only_units, &data)?; + let result = ABIDecoder::default().decode(&enum_w_only_units, data.as_slice())?; let expected_enum = Token::Enum(Box::new((1, Token::Unit, enum_variants))); assert_eq!(result, expected_enum); @@ -516,7 +526,7 @@ mod tests { generics: vec![], }; - let result = ABIDecoder::default().decode(&enum_type, &data); + let result = ABIDecoder::default().decode(&enum_type, data.as_slice()); let error = result.expect_err("should have resulted in an error"); @@ -529,8 +539,8 @@ mod tests { #[test] pub fn division_by_zero() { let param_type = Vec::<[u16; 0]>::param_type(); - let result = ABIDecoder::default().decode(¶m_type, &[]); - assert!(matches!(result, Err(Error::Codec(_)))); + let result = ABIDecoder::default().decode(¶m_type, [].as_slice()); + assert!(matches!(result, Err(Error::IO(_)))); } #[test] @@ -554,10 +564,10 @@ mod tests { .unwrap(), generics: vec![U16], }, - &[], + [].as_slice(), ); - assert!(matches!(result, Err(Error::Codec(_)))); + assert!(matches!(result, Err(Error::IO(_)))); } #[test] @@ -572,16 +582,16 @@ mod tests { enum_variants: EnumVariants::new(to_named(&[param_type])).unwrap(), generics: vec![U16], }, - &[], + [].as_slice(), ); - assert!(matches!(result, Err(Error::Codec(_)))); + assert!(matches!(result, Err(Error::IO(_)))); } #[test] pub fn capacity_overflow() { let result = ABIDecoder::default().decode( &Array(Box::new(Array(Box::new(Tuple(vec![])), usize::MAX)), 1), - &[], + [].as_slice(), ); assert!(matches!(result, Err(Error::Codec(_)))); } @@ -592,15 +602,15 @@ mod tests { for _ in 0..13500 { param_type = Vector(Box::new(param_type)); } - let result = ABIDecoder::default().decode(¶m_type, &[]); - assert!(matches!(result, Err(Error::Codec(_)))); + let result = ABIDecoder::default().decode(¶m_type, [].as_slice()); + assert!(matches!(result, Err(Error::IO(_)))); } #[test] pub fn capacity_malloc() { let param_type = Array(Box::new(U8), usize::MAX); - let result = ABIDecoder::default().decode(¶m_type, &[]); - assert!(matches!(result, Err(Error::Codec(_)))); + let result = ABIDecoder::default().decode(¶m_type, [].as_slice()); + assert!(matches!(result, Err(Error::IO(_)))); } #[test] @@ -618,7 +628,7 @@ mod tests { .iter() .map(|fun| fun(MAX_DEPTH + 1)) .for_each(|param_type| { - assert_decoding_failed_w_data(config, ¶m_type, &msg, &data); + assert_decoding_failed_w_data(config, ¶m_type, &msg, data.as_slice()); }) } @@ -647,7 +657,9 @@ mod tests { } }) .for_each(|param_type| { - ABIDecoder::new(config).decode(¶m_type, &data).unwrap(); + ABIDecoder::new(config) + .decode(¶m_type, data.as_slice()) + .unwrap(); }) } @@ -700,10 +712,10 @@ mod tests { let param_type = ParamType::Array(Box::new(ParamType::StringArray(0)), 2); let decoder = ABIDecoder::new(config); - decoder.decode(¶m_type, &[]).unwrap(); + decoder.decode(¶m_type, [].as_slice()).unwrap(); // when - let result = decoder.decode(¶m_type, &[]); + let result = decoder.decode(¶m_type, [].as_slice()); // then result.expect("element count to be reset"); diff --git a/packages/fuels-core/src/codec/abi_decoder/bounded_decoder.rs b/packages/fuels-core/src/codec/abi_decoder/bounded_decoder.rs index 9c5f85374c..ac126fafd2 100644 --- a/packages/fuels-core/src/codec/abi_decoder/bounded_decoder.rs +++ b/packages/fuels-core/src/codec/abi_decoder/bounded_decoder.rs @@ -1,11 +1,10 @@ -use std::{iter::repeat, str}; +use std::{io::Read, iter::repeat, str}; use crate::{ codec::{ utils::{CodecDirection, CounterWithLimit}, DecoderConfig, }, - constants::WORD_SIZE, types::{ errors::{error, Result}, param_types::{EnumVariants, NamedParamType, ParamType}, @@ -20,16 +19,6 @@ pub(crate) struct BoundedDecoder { token_tracker: CounterWithLimit, } -const U8_BYTES_SIZE: usize = 1; -const U16_BYTES_SIZE: usize = 2; -const U32_BYTES_SIZE: usize = 4; -const U64_BYTES_SIZE: usize = WORD_SIZE; -const U128_BYTES_SIZE: usize = 2 * WORD_SIZE; -const U256_BYTES_SIZE: usize = 4 * WORD_SIZE; -const B256_BYTES_SIZE: usize = 4 * WORD_SIZE; -const LENGTH_BYTES_SIZE: usize = WORD_SIZE; -const DISCRIMINANT_BYTES_SIZE: usize = WORD_SIZE; - impl BoundedDecoder { pub(crate) fn new(config: DecoderConfig) -> Self { let depth_tracker = @@ -42,24 +31,26 @@ impl BoundedDecoder { } } - pub(crate) fn decode(&mut self, param_type: &ParamType, bytes: &[u8]) -> Result { - self.decode_param(param_type, bytes).map(|x| x.token) + pub(crate) fn decode( + &mut self, + param_type: &ParamType, + bytes: &mut R, + ) -> Result { + self.decode_param(param_type, bytes) } - pub(crate) fn decode_multiple( + pub(crate) fn decode_multiple( &mut self, param_types: &[ParamType], - bytes: &[u8], + bytes: &mut R, ) -> Result> { - let (tokens, _) = self.decode_params(param_types, bytes)?; - - Ok(tokens) + self.decode_params(param_types, bytes) } fn run_w_depth_tracking( &mut self, - decoder: impl FnOnce(&mut Self) -> Result, - ) -> Result { + decoder: impl FnOnce(&mut Self) -> Result, + ) -> Result { self.depth_tracker.increase()?; let res = decoder(self); self.depth_tracker.decrease(); @@ -67,21 +58,21 @@ impl BoundedDecoder { res } - fn decode_param(&mut self, param_type: &ParamType, bytes: &[u8]) -> Result { + fn decode_param(&mut self, param_type: &ParamType, bytes: &mut R) -> Result { self.token_tracker.increase()?; match param_type { - ParamType::Unit => Self::decode_unit(), - ParamType::Bool => Self::decode_bool(bytes), - ParamType::U8 => Self::decode_u8(bytes), - ParamType::U16 => Self::decode_u16(bytes), - ParamType::U32 => Self::decode_u32(bytes), - ParamType::U64 => Self::decode_u64(bytes), - ParamType::U128 => Self::decode_u128(bytes), - ParamType::U256 => Self::decode_u256(bytes), - ParamType::B256 => Self::decode_b256(bytes), - ParamType::Bytes => Self::decode_bytes(bytes), + ParamType::Unit => Ok(Token::Unit), + ParamType::Bool => decode(bytes, |[value]| Token::Bool(value != 0)), + ParamType::U8 => decode(bytes, |[value]| Token::U8(value)), + ParamType::U16 => decode(bytes, |value| Token::U16(u16::from_be_bytes(value))), + ParamType::U32 => decode(bytes, |value| Token::U32(u32::from_be_bytes(value))), + ParamType::U64 => decode(bytes, |value| Token::U64(u64::from_be_bytes(value))), + ParamType::U128 => decode(bytes, |value| Token::U128(u128::from_be_bytes(value))), + ParamType::U256 => decode(bytes, |value| Token::U256(U256::from(value))), + ParamType::B256 => decode(bytes, Token::B256), + ParamType::Bytes => Ok(Token::Bytes(decode_slice(bytes)?)), ParamType::String => Self::decode_std_string(bytes), - ParamType::RawSlice => Self::decode_raw_slice(bytes), + ParamType::RawSlice => Ok(Token::RawSlice(decode_slice(bytes)?)), ParamType::StringArray(length) => Self::decode_string_array(bytes, *length), ParamType::StringSlice => Self::decode_string_slice(bytes), ParamType::Tuple(param_types) => { @@ -98,277 +89,124 @@ impl BoundedDecoder { self.run_w_depth_tracking(|ctx| ctx.decode_struct(fields, bytes)) } ParamType::Enum { enum_variants, .. } => { - self.run_w_depth_tracking(|ctx| ctx.decode_enum(bytes, enum_variants)) + self.run_w_depth_tracking(|ctx| ctx.decode_enum(enum_variants, bytes)) } } } - fn decode_unit() -> Result { - Ok(Decoded { - token: Token::Unit, - bytes_read: 0, - }) + fn decode_std_string(bytes: &mut R) -> Result { + let data = decode_slice(bytes)?; + let string = str::from_utf8(&data)?.to_string(); + Ok(Token::String(string)) } - fn decode_bool(bytes: &[u8]) -> Result { - let value = peek_u8(bytes)? != 0u8; - - Ok(Decoded { - token: Token::Bool(value), - bytes_read: U8_BYTES_SIZE, - }) - } - - fn decode_u8(bytes: &[u8]) -> Result { - Ok(Decoded { - token: Token::U8(peek_u8(bytes)?), - bytes_read: U8_BYTES_SIZE, - }) + fn decode_string_array(bytes: &mut R, length: usize) -> Result { + let data = decode_sized(bytes, length)?; + let decoded = str::from_utf8(&data)?.to_string(); + Ok(Token::StringArray(StaticStringToken::new( + decoded, + Some(length), + ))) } - fn decode_u16(bytes: &[u8]) -> Result { - Ok(Decoded { - token: Token::U16(peek_u16(bytes)?), - bytes_read: U16_BYTES_SIZE, - }) + fn decode_string_slice(bytes: &mut R) -> Result { + let data = decode_slice(bytes)?; + let decoded = str::from_utf8(&data)?.to_string(); + Ok(Token::StringSlice(StaticStringToken::new(decoded, None))) } - fn decode_u32(bytes: &[u8]) -> Result { - Ok(Decoded { - token: Token::U32(peek_u32(bytes)?), - bytes_read: U32_BYTES_SIZE, - }) + fn decode_tuple(&mut self, param_types: &[ParamType], bytes: &mut R) -> Result { + Ok(Token::Tuple(self.decode_params(param_types, bytes)?)) } - fn decode_u64(bytes: &[u8]) -> Result { - Ok(Decoded { - token: Token::U64(peek_u64(bytes)?), - bytes_read: U64_BYTES_SIZE, - }) - } - - fn decode_u128(bytes: &[u8]) -> Result { - Ok(Decoded { - token: Token::U128(peek_u128(bytes)?), - bytes_read: U128_BYTES_SIZE, - }) - } - - fn decode_u256(bytes: &[u8]) -> Result { - Ok(Decoded { - token: Token::U256(peek_u256(bytes)?), - bytes_read: U256_BYTES_SIZE, - }) - } - - fn decode_b256(bytes: &[u8]) -> Result { - Ok(Decoded { - token: Token::B256(*peek_fixed::(bytes)?), - bytes_read: B256_BYTES_SIZE, - }) - } - - fn decode_bytes(bytes: &[u8]) -> Result { - let length = peek_length(bytes)?; - let bytes = peek(skip(bytes, LENGTH_BYTES_SIZE)?, length)?; - - Ok(Decoded { - token: Token::Bytes(bytes.to_vec()), - bytes_read: LENGTH_BYTES_SIZE + bytes.len(), - }) - } - - fn decode_std_string(bytes: &[u8]) -> Result { - let length = peek_length(bytes)?; - let bytes = peek(skip(bytes, LENGTH_BYTES_SIZE)?, length)?; - - Ok(Decoded { - token: Token::String(str::from_utf8(bytes)?.to_string()), - bytes_read: LENGTH_BYTES_SIZE + bytes.len(), - }) - } - - fn decode_raw_slice(bytes: &[u8]) -> Result { - let length = peek_length(bytes)?; - let bytes = peek(skip(bytes, LENGTH_BYTES_SIZE)?, length)?; - - Ok(Decoded { - token: Token::RawSlice(bytes.to_vec()), - bytes_read: LENGTH_BYTES_SIZE + bytes.len(), - }) - } - - fn decode_string_array(bytes: &[u8], length: usize) -> Result { - let bytes = peek(bytes, length)?; - let decoded = str::from_utf8(bytes)?.to_string(); - - Ok(Decoded { - token: Token::StringArray(StaticStringToken::new(decoded, Some(length))), - bytes_read: length, - }) - } - - fn decode_string_slice(bytes: &[u8]) -> Result { - let length = peek_length(bytes)?; - // skipping over the previously read length - let content_bytes = skip(bytes, LENGTH_BYTES_SIZE)?; - let string_bytes = peek(content_bytes, length)?; - let decoded = str::from_utf8(string_bytes)?.to_string(); - - Ok(Decoded { - token: Token::StringSlice(StaticStringToken::new(decoded, None)), - bytes_read: string_bytes.len() + LENGTH_BYTES_SIZE, - }) - } - - fn decode_tuple(&mut self, param_types: &[ParamType], bytes: &[u8]) -> Result { - let (tokens, bytes_read) = self.decode_params(param_types, bytes)?; - - Ok(Decoded { - token: Token::Tuple(tokens), - bytes_read, - }) - } - - fn decode_array( + fn decode_array( &mut self, param_type: &ParamType, - bytes: &[u8], + bytes: &mut R, length: usize, - ) -> Result { - let (tokens, bytes_read) = self.decode_params(repeat(param_type).take(length), bytes)?; - - Ok(Decoded { - token: Token::Array(tokens), - bytes_read, - }) + ) -> Result { + Ok(Token::Array( + self.decode_params(repeat(param_type).take(length), bytes)?, + )) } - fn decode_vector(&mut self, param_type: &ParamType, bytes: &[u8]) -> Result { - let length = peek_length(bytes)?; - let bytes = skip(bytes, LENGTH_BYTES_SIZE)?; - let (tokens, bytes_read) = self.decode_params(repeat(param_type).take(length), bytes)?; - - Ok(Decoded { - token: Token::Vector(tokens), - bytes_read: LENGTH_BYTES_SIZE + bytes_read, - }) + fn decode_vector(&mut self, param_type: &ParamType, bytes: &mut R) -> Result { + let length = decode_len(bytes)?; + Ok(Token::Vector( + self.decode_params(repeat(param_type).take(length), bytes)?, + )) } - fn decode_struct(&mut self, fields: &[NamedParamType], bytes: &[u8]) -> Result { - let (tokens, bytes_read) = self.decode_params(fields.iter().map(|(_, pt)| pt), bytes)?; - - Ok(Decoded { - token: Token::Struct(tokens), - bytes_read, - }) + fn decode_struct( + &mut self, + fields: &[NamedParamType], + bytes: &mut R, + ) -> Result { + Ok(Token::Struct( + self.decode_params(fields.iter().map(|(_, pt)| pt), bytes)?, + )) } - fn decode_enum(&mut self, bytes: &[u8], enum_variants: &EnumVariants) -> Result { - let discriminant = peek_discriminant(bytes)?; - let variant_bytes = skip(bytes, DISCRIMINANT_BYTES_SIZE)?; + fn decode_enum( + &mut self, + enum_variants: &EnumVariants, + bytes: &mut R, + ) -> Result { + let discriminant = decode(bytes, u64::from_be_bytes)?; let (_, selected_variant) = enum_variants.select_variant(discriminant)?; - let decoded = self.decode_param(selected_variant, variant_bytes)?; + let decoded = self.decode_param(selected_variant, bytes)?; - Ok(Decoded { - token: Token::Enum(Box::new(( - discriminant, - decoded.token, - enum_variants.clone(), - ))), - bytes_read: DISCRIMINANT_BYTES_SIZE + decoded.bytes_read, - }) + Ok(Token::Enum(Box::new(( + discriminant, + decoded, + enum_variants.clone(), + )))) } - fn decode_params<'a>( + fn decode_params<'a, R: Read>( &mut self, param_types: impl IntoIterator, - bytes: &[u8], - ) -> Result<(Vec, usize)> { + bytes: &mut R, + ) -> Result> { let mut tokens = vec![]; - let mut bytes_read = 0; - for param_type in param_types { - let decoded = self.decode_param(param_type, skip(bytes, bytes_read)?)?; - tokens.push(decoded.token); - bytes_read += decoded.bytes_read; + tokens.push(self.decode_param(param_type, bytes)?); } - - Ok((tokens, bytes_read)) + Ok(tokens) } } -#[derive(Debug, Clone)] -struct Decoded { - token: Token, - bytes_read: usize, -} - -fn peek_u8(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; - Ok(u8::from_be_bytes(*slice)) -} - -fn peek_u16(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; - Ok(u16::from_be_bytes(*slice)) -} - -fn peek_u32(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; - Ok(u32::from_be_bytes(*slice)) -} - -fn peek_u64(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; - Ok(u64::from_be_bytes(*slice)) -} - -fn peek_u128(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; - Ok(u128::from_be_bytes(*slice)) -} - -fn peek_u256(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; - Ok(U256::from(*slice)) -} - -fn peek_length(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; - - u64::from_be_bytes(*slice) - .try_into() - .map_err(|_| error!(Other, "could not convert `u64` to `usize`")) -} - -fn peek_discriminant(bytes: &[u8]) -> Result { - let slice = peek_fixed::(bytes)?; - Ok(u64::from_be_bytes(*slice)) +/// Decodes a fixed-size array of bytes using a converter function. +fn decode( + bytes: &mut R, + f: impl FnOnce([u8; SIZE]) -> Out, +) -> Result { + let mut buffer = [0u8; SIZE]; + bytes.read_exact(&mut buffer)?; + Ok(f(buffer)) } -fn peek(data: &[u8], len: usize) -> Result<&[u8]> { - (len <= data.len()).then(|| &data[..len]).ok_or(error!( - Codec, - "tried to read `{len}` bytes but only had `{}` remaining!", - data.len() - )) +/// Reads a byte array with known size. +fn decode_sized(bytes: &mut R, len: usize) -> Result> { + let mut data = vec![0; len]; + bytes.read_exact(&mut data)?; + Ok(data) } -fn peek_fixed(data: &[u8]) -> Result<&[u8; LEN]> { - let slice_w_correct_length = peek(data, LEN)?; - Ok(slice_w_correct_length +/// Decodes a length prefix. +fn decode_len(bytes: &mut R) -> Result { + let len_u64 = decode(bytes, u64::from_be_bytes)?; + let len: usize = len_u64 .try_into() - .expect("peek(data, len) must return a slice of length `len` or error out")) + .map_err(|_| error!(Other, "could not convert `u64` to `usize`"))?; + Ok(len) } -fn skip(slice: &[u8], num_bytes: usize) -> Result<&[u8]> { - (num_bytes <= slice.len()) - .then_some(&slice[num_bytes..]) - .ok_or(error!( - Codec, - "tried to consume `{num_bytes}` bytes but only had `{}` remaining!", - slice.len() - )) +/// Decodes a size-prefixed slice. +fn decode_slice(bytes: &mut R) -> Result> { + let len = decode_len(bytes)?; + let mut data = vec![0; len]; + bytes.read_exact(&mut data)?; + Ok(data) } diff --git a/packages/fuels-core/src/codec/abi_decoder/decode_as_debug_str.rs b/packages/fuels-core/src/codec/abi_decoder/decode_as_debug_str.rs index 0c462eb6ca..1f0635f9cd 100644 --- a/packages/fuels-core/src/codec/abi_decoder/decode_as_debug_str.rs +++ b/packages/fuels-core/src/codec/abi_decoder/decode_as_debug_str.rs @@ -110,34 +110,35 @@ mod tests { { assert_eq!( format!("{:?}", true), - decoder.decode_as_debug_str(&bool::param_type(), &[1])? + decoder.decode_as_debug_str(&bool::param_type(), [1].as_slice())? ); assert_eq!( format!("{:?}", 128u8), - decoder.decode_as_debug_str(&u8::param_type(), &[128])? + decoder.decode_as_debug_str(&u8::param_type(), [128].as_slice())? ); assert_eq!( format!("{:?}", 256u16), - decoder.decode_as_debug_str(&u16::param_type(), &[1, 0])? + decoder.decode_as_debug_str(&u16::param_type(), [1, 0].as_slice())? ); assert_eq!( format!("{:?}", 512u32), - decoder.decode_as_debug_str(&u32::param_type(), &[0, 0, 2, 0])? + decoder.decode_as_debug_str(&u32::param_type(), [0, 0, 2, 0].as_slice())? ); assert_eq!( format!("{:?}", 1024u64), - decoder.decode_as_debug_str(&u64::param_type(), &[0, 0, 0, 0, 0, 0, 4, 0])? + decoder + .decode_as_debug_str(&u64::param_type(), [0, 0, 0, 0, 0, 0, 4, 0].as_slice())? ); assert_eq!( format!("{:?}", 1024u128), decoder.decode_as_debug_str( &u128::param_type(), - &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0] + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0].as_slice() )? ); @@ -145,10 +146,11 @@ mod tests { format!("{:?}", U256::from(2048)), decoder.decode_as_debug_str( &U256::param_type(), - &[ + [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0 ] + .as_slice() )? ); } @@ -163,10 +165,11 @@ mod tests { format!("{bits256:?}"), decoder.decode_as_debug_str( &Bits256::param_type(), - &[ + [ 239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74 ] + .as_slice() )? ); @@ -174,11 +177,12 @@ mod tests { format!("{:?}", Bytes(bytes.to_vec())), decoder.decode_as_debug_str( &Bytes::param_type(), - &[ + [ 0, 0, 0, 0, 0, 0, 0, 32, 239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74 ] + .as_slice() )? ); @@ -186,11 +190,12 @@ mod tests { format!("{:?}", RawSlice(bytes.to_vec())), decoder.decode_as_debug_str( &RawSlice::param_type(), - &[ + [ 0, 0, 0, 0, 0, 0, 0, 32, 239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161, 16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74 ] + .as_slice() )? ); @@ -198,10 +203,11 @@ mod tests { format!("{:?}", EvmAddress::from(bits256)), decoder.decode_as_debug_str( &EvmAddress::param_type(), - &[ + [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 166, 225, 89, 161, 16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74 ] + .as_slice() )? ); } @@ -210,7 +216,7 @@ mod tests { format!("{:?}", AsciiString::new("Fuel".to_string())?), decoder.decode_as_debug_str( &AsciiString::param_type(), - &[0, 0, 0, 0, 0, 0, 0, 4, 70, 117, 101, 108] + [0, 0, 0, 0, 0, 0, 0, 4, 70, 117, 101, 108].as_slice() )? ); @@ -218,7 +224,7 @@ mod tests { format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?), decoder.decode_as_debug_str( &SizedAsciiString::<4>::param_type(), - &[70, 117, 101, 108, 0, 0, 0, 0] + [70, 117, 101, 108, 0, 0, 0, 0].as_slice() )? ); @@ -226,21 +232,21 @@ mod tests { format!("{}", "Fuel"), decoder.decode_as_debug_str( &String::param_type(), - &[0, 0, 0, 0, 0, 0, 0, 4, 70, 117, 101, 108] + [0, 0, 0, 0, 0, 0, 0, 4, 70, 117, 101, 108].as_slice() )? ); } { assert_eq!( format!("{:?}", (1, 2)), - decoder.decode_as_debug_str(&<(u8, u8)>::param_type(), &[1, 2])? + decoder.decode_as_debug_str(&<(u8, u8)>::param_type(), [1, 2].as_slice())? ); assert_eq!( format!("{:?}", [3, 4]), decoder.decode_as_debug_str( &<[u64; 2]>::param_type(), - &[0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4] + [0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4].as_slice() )? ); } @@ -249,7 +255,7 @@ mod tests { format!("{:?}", Some(42)), decoder.decode_as_debug_str( &>::param_type(), - &[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 42] + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 42].as_slice() )? ); @@ -257,7 +263,7 @@ mod tests { format!("{:?}", Err::(42u64)), decoder.decode_as_debug_str( &>::param_type(), - &[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 42] + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 42].as_slice() )? ); } diff --git a/packages/fuels-core/src/codec/abi_formatter.rs b/packages/fuels-core/src/codec/abi_formatter.rs index 42a19d3c67..0c3d72651e 100644 --- a/packages/fuels-core/src/codec/abi_formatter.rs +++ b/packages/fuels-core/src/codec/abi_formatter.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{collections::HashMap, io::Read}; use fuel_abi_types::abi::unified_program::UnifiedProgramABI; use itertools::Itertools; @@ -75,7 +75,7 @@ impl ABIFormatter { Self::from_abi(parsed_abi) } - pub fn decode_fn_args(&self, fn_name: &str, data: &[u8]) -> Result> { + pub fn decode_fn_args(&self, fn_name: &str, data: R) -> Result> { let args = self .functions .get(fn_name) @@ -84,7 +84,10 @@ impl ABIFormatter { self.decoder.decode_multiple_as_debug_str(args, data) } - pub fn decode_configurables(&self, configurable_data: &[u8]) -> Result> { + pub fn decode_configurables( + &self, + configurable_data: R, + ) -> Result> { let param_types = self .configurables .iter() @@ -116,7 +119,9 @@ mod tests { let decoder = ABIFormatter::from_abi(UnifiedProgramABI::default()).unwrap(); // when - let err = decoder.decode_fn_args("non_existent_fn", &[]).unwrap_err(); + let err = decoder + .decode_fn_args("non_existent_fn", [].as_slice()) + .unwrap_err(); // then let Error::Codec(err) = err else { diff --git a/packages/fuels-core/src/codec/logs.rs b/packages/fuels-core/src/codec/logs.rs index 9c8ae7b6f1..1561748a67 100644 --- a/packages/fuels-core/src/codec/logs.rs +++ b/packages/fuels-core/src/codec/logs.rs @@ -173,8 +173,8 @@ impl LogDecoder { .extract_log_id_and_data() .filter_map(|(log_id, bytes)| { target_ids.contains(&log_id).then(|| { - let token = - ABIDecoder::new(self.decoder_config).decode(&T::param_type(), &bytes)?; + let token = ABIDecoder::new(self.decoder_config) + .decode(&T::param_type(), bytes.as_slice())?; T::from_token(token) }) diff --git a/packages/fuels-core/src/types/transaction_builders.rs b/packages/fuels-core/src/types/transaction_builders.rs index 0f8ab9658a..d1ce3ca2a1 100644 --- a/packages/fuels-core/src/types/transaction_builders.rs +++ b/packages/fuels-core/src/types/transaction_builders.rs @@ -308,7 +308,7 @@ macro_rules! impl_tx_builder_trait { .inputs() .iter() .filter_map(|input| match input { - Input::ResourceSigned { resource } => Some(resource.owner()), + Input::ResourceSigned { resource } => resource.owner(), _ => None, }) .unique() @@ -361,7 +361,7 @@ macro_rules! impl_tx_builder_trait { .iter() .filter_map(|input| match input { Input::ResourceSigned { resource } | - Input::ResourcePredicate { resource, .. } => Some(resource.asset_id(*base_asset_id)), + Input::ResourcePredicate { resource, .. } => resource.asset_id(*base_asset_id), _ => None, }) .collect::>(); @@ -1380,7 +1380,7 @@ fn resolve_fuel_inputs( resource, code, data, - } => Ok(resolve_predicate_resource(resource, code, data)), + } => resolve_predicate_resource(resource, code, data), Input::Contract { utxo_id, balance_root, @@ -1432,13 +1432,25 @@ fn resolve_signed_resource( create_coin_message_input(message, num_witnesses + *witness_idx_offset as u16) }) } + CoinType::Unknown => Err(error_transaction!( + Builder, + "can not resolve `CoinType::Unknown`" + )), } } -fn resolve_predicate_resource(resource: CoinType, code: Vec, data: Vec) -> FuelInput { +fn resolve_predicate_resource( + resource: CoinType, + code: Vec, + data: Vec, +) -> Result { match resource { - CoinType::Coin(coin) => create_coin_predicate(coin.asset_id, coin, code, data), - CoinType::Message(message) => create_coin_message_predicate(message, code, data), + CoinType::Coin(coin) => Ok(create_coin_predicate(coin.asset_id, coin, code, data)), + CoinType::Message(message) => Ok(create_coin_message_predicate(message, code, data)), + CoinType::Unknown => Err(error_transaction!( + Builder, + "can not resolve `CoinType::Unknown`" + )), } } diff --git a/packages/fuels-core/src/types/wrappers/coin_type.rs b/packages/fuels-core/src/types/wrappers/coin_type.rs index c74be2751f..22e45d9e6c 100644 --- a/packages/fuels-core/src/types/wrappers/coin_type.rs +++ b/packages/fuels-core/src/types/wrappers/coin_type.rs @@ -3,37 +3,32 @@ use fuel_core_client::client::types::CoinType as ClientCoinType; use crate::types::{ - bech32::Bech32Address, - coin::Coin, - coin_type_id::CoinTypeId, - errors::{error, Error}, - message::Message, - AssetId, + bech32::Bech32Address, coin::Coin, coin_type_id::CoinTypeId, message::Message, AssetId, }; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum CoinType { Coin(Coin), Message(Message), + Unknown, } -impl TryFrom for CoinType { - type Error = Error; - - fn try_from(client_resource: ClientCoinType) -> Result { +impl From for CoinType { + fn from(client_resource: ClientCoinType) -> Self { match client_resource { - ClientCoinType::Coin(coin) => Ok(CoinType::Coin(coin.into())), - ClientCoinType::MessageCoin(message) => Ok(CoinType::Message(message.into())), - ClientCoinType::Unknown => Err(error!(Other, "unknown `ClientCoinType`")), + ClientCoinType::Coin(coin) => CoinType::Coin(coin.into()), + ClientCoinType::MessageCoin(message) => CoinType::Message(message.into()), + ClientCoinType::Unknown => CoinType::Unknown, } } } impl CoinType { - pub fn id(&self) -> CoinTypeId { + pub fn id(&self) -> Option { match self { - CoinType::Coin(coin) => CoinTypeId::UtxoId(coin.utxo_id), - CoinType::Message(message) => CoinTypeId::Nonce(message.nonce), + CoinType::Coin(coin) => Some(CoinTypeId::UtxoId(coin.utxo_id)), + CoinType::Message(message) => Some(CoinTypeId::Nonce(message.nonce)), + CoinType::Unknown => None, } } @@ -41,6 +36,7 @@ impl CoinType { match self { CoinType::Coin(coin) => coin.amount, CoinType::Message(message) => message.amount, + CoinType::Unknown => 0, } } @@ -48,20 +44,23 @@ impl CoinType { match self { CoinType::Coin(coin) => Some(coin.asset_id), CoinType::Message(_) => None, + CoinType::Unknown => None, } } - pub fn asset_id(&self, base_asset_id: AssetId) -> AssetId { + pub fn asset_id(&self, base_asset_id: AssetId) -> Option { match self { - CoinType::Coin(coin) => coin.asset_id, - CoinType::Message(_) => base_asset_id, + CoinType::Coin(coin) => Some(coin.asset_id), + CoinType::Message(_) => Some(base_asset_id), + CoinType::Unknown => None, } } - pub fn owner(&self) -> &Bech32Address { + pub fn owner(&self) -> Option<&Bech32Address> { match self { - CoinType::Coin(coin) => &coin.owner, - CoinType::Message(message) => &message.recipient, + CoinType::Coin(coin) => Some(&coin.owner), + CoinType::Message(message) => Some(&message.recipient), + CoinType::Unknown => None, } } } diff --git a/packages/fuels-core/src/types/wrappers/transaction.rs b/packages/fuels-core/src/types/wrappers/transaction.rs index bc414d41f1..992aaa134d 100644 --- a/packages/fuels-core/src/types/wrappers/transaction.rs +++ b/packages/fuels-core/src/types/wrappers/transaction.rs @@ -187,6 +187,7 @@ pub enum TransactionType { Upload(UploadTransaction), Upgrade(UpgradeTransaction), Blob(BlobTransaction), + Unknown, } #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] @@ -284,15 +285,17 @@ pub trait Transaction: ) -> Result; } -impl From for FuelTransaction { - fn from(value: TransactionType) -> Self { +impl TryFrom for FuelTransaction { + type Error = Error; + fn try_from(value: TransactionType) -> Result { match value { - TransactionType::Script(tx) => tx.into(), - TransactionType::Create(tx) => tx.into(), - TransactionType::Mint(tx) => tx.into(), - TransactionType::Upload(tx) => tx.into(), - TransactionType::Upgrade(tx) => tx.into(), - TransactionType::Blob(tx) => tx.into(), + TransactionType::Script(tx) => Ok(tx.into()), + TransactionType::Create(tx) => Ok(tx.into()), + TransactionType::Mint(tx) => Ok(tx.into()), + TransactionType::Upload(tx) => Ok(tx.into()), + TransactionType::Upgrade(tx) => Ok(tx.into()), + TransactionType::Blob(tx) => Ok(tx.into()), + TransactionType::Unknown => Err(error_transaction!(Other, "`Unknown` transaction")), } } } diff --git a/packages/fuels-core/src/types/wrappers/transaction_response.rs b/packages/fuels-core/src/types/wrappers/transaction_response.rs index c0711a4a36..d2051771c0 100644 --- a/packages/fuels-core/src/types/wrappers/transaction_response.rs +++ b/packages/fuels-core/src/types/wrappers/transaction_response.rs @@ -3,14 +3,12 @@ use chrono::{DateTime, Utc}; use fuel_core_client::client::types::{ TransactionResponse as ClientTransactionResponse, TransactionStatus as ClientTransactionStatus, + TransactionType as ClientTxType, }; use fuel_tx::Transaction; use fuel_types::BlockHeight; -use crate::types::{ - transaction::{CreateTransaction, ScriptTransaction, TransactionType}, - tx_status::TxStatus, -}; +use crate::types::{transaction::TransactionType, tx_status::TxStatus}; #[derive(Debug, Clone)] pub struct TransactionResponse { @@ -39,12 +37,13 @@ impl From for TransactionResponse { }; let transaction = match client_response.transaction { - Transaction::Script(tx) => TransactionType::Script(ScriptTransaction::from(tx)), - Transaction::Create(tx) => TransactionType::Create(CreateTransaction::from(tx)), - Transaction::Mint(tx) => TransactionType::Mint(tx.into()), - Transaction::Upgrade(tx) => TransactionType::Upgrade(tx.into()), - Transaction::Upload(tx) => TransactionType::Upload(tx.into()), - Transaction::Blob(tx) => TransactionType::Blob(tx.into()), + ClientTxType::Known(Transaction::Script(tx)) => TransactionType::Script(tx.into()), + ClientTxType::Known(Transaction::Create(tx)) => TransactionType::Create(tx.into()), + ClientTxType::Known(Transaction::Mint(tx)) => TransactionType::Mint(tx.into()), + ClientTxType::Known(Transaction::Upgrade(tx)) => TransactionType::Upgrade(tx.into()), + ClientTxType::Known(Transaction::Upload(tx)) => TransactionType::Upload(tx.into()), + ClientTxType::Known(Transaction::Blob(tx)) => TransactionType::Blob(tx.into()), + ClientTxType::Unknown => TransactionType::Unknown, }; Self { diff --git a/packages/fuels-programs/src/calls/receipt_parser.rs b/packages/fuels-programs/src/calls/receipt_parser.rs index 7027ea9d30..ae165627bc 100644 --- a/packages/fuels-programs/src/calls/receipt_parser.rs +++ b/packages/fuels-programs/src/calls/receipt_parser.rs @@ -41,7 +41,7 @@ impl ReceiptParser { .extract_contract_call_data(contract_id.into()) .ok_or_else(|| Self::missing_receipts_error(output_param))?; - self.decoder.decode(output_param, &data) + self.decoder.decode(output_param, data.as_slice()) } pub fn parse_script(self, output_param: &ParamType) -> Result { @@ -49,7 +49,7 @@ impl ReceiptParser { .extract_script_data() .ok_or_else(|| Self::missing_receipts_error(output_param))?; - self.decoder.decode(output_param, &data) + self.decoder.decode(output_param, data.as_slice()) } fn missing_receipts_error(output_param: &ParamType) -> Error { diff --git a/packages/fuels-programs/src/calls/utils.rs b/packages/fuels-programs/src/calls/utils.rs index 5649207255..99cd6242a9 100644 --- a/packages/fuels-programs/src/calls/utils.rs +++ b/packages/fuels-programs/src/calls/utils.rs @@ -131,45 +131,27 @@ pub(crate) fn calculate_required_asset_amounts( calls: &[ContractCall], base_asset_id: AssetId, ) -> Vec<(AssetId, u64)> { - let call_param_assets = calls - .iter() - .map(|call| { - ( - call.call_parameters.asset_id().unwrap_or(base_asset_id), - call.call_parameters.amount(), - ) - }) - .collect::>(); + let call_param_assets = calls.iter().map(|call| { + ( + call.call_parameters.asset_id().unwrap_or(base_asset_id), + call.call_parameters.amount(), + ) + }); - let custom_assets = calls + let grouped_assets = calls .iter() - .flat_map(|call| call.custom_assets.iter().collect::>()) - .group_by(|custom| custom.0 .0) - .into_iter() - .map(|(asset_id, groups_w_same_asset_id)| { - let total_amount_in_group = groups_w_same_asset_id.map(|(_, amount)| amount).sum(); - (asset_id, total_amount_in_group) - }) - .collect::>(); - - let merged_assets = chain!(call_param_assets, custom_assets).collect::>(); - - sum_up_amounts_for_each_asset_id(merged_assets) -} - -/// Sum up the amounts required in each call for each asset ID, so you can get a total for each -/// asset over all calls. -fn sum_up_amounts_for_each_asset_id( - amounts_per_asset_id: Vec<(AssetId, u64)>, -) -> Vec<(AssetId, u64)> { - amounts_per_asset_id - .into_iter() + .flat_map(|call| call.custom_assets.clone()) + .map(|((asset_id, _), amount)| (asset_id, amount)) + .chain(call_param_assets) .sorted_by_key(|(asset_id, _)| *asset_id) - .group_by(|(asset_id, _)| *asset_id) + .group_by(|(asset_id, _)| *asset_id); + + grouped_assets .into_iter() - .map(|(asset_id, groups_w_same_asset_id)| { + .filter_map(|(asset_id, groups_w_same_asset_id)| { let total_amount_in_group = groups_w_same_asset_id.map(|(_, amount)| amount).sum(); - (asset_id, total_amount_in_group) + + (total_amount_in_group != 0).then_some((asset_id, total_amount_in_group)) }) .collect() } diff --git a/packages/fuels-programs/src/contract/loader.rs b/packages/fuels-programs/src/contract/loader.rs index 4744b21757..fdf01deef0 100644 --- a/packages/fuels-programs/src/contract/loader.rs +++ b/packages/fuels-programs/src/contract/loader.rs @@ -130,6 +130,11 @@ impl Contract> { continue; } + if provider.blob_exists(id).await? { + already_uploaded.insert(id); + continue; + } + let mut tb = BlobTransactionBuilder::default() .with_blob(blob) .with_tx_policies(tx_policies) @@ -141,20 +146,7 @@ impl Contract> { let tx = tb.build(provider).await?; let tx_status_response = provider.send_transaction_and_await_commit(tx).await; - - match tx_status_response { - Ok(tx_status_response) => { - tx_status_response.check(None)?; - } - Err(err) => { - if !err - .to_string() - .contains("Execution error: BlobIdAlreadyUploaded") - { - return Err(err); - } - } - } + tx_status_response.and_then(|response| response.check(None))?; already_uploaded.insert(id); } diff --git a/packages/fuels-test-helpers/src/accounts.rs b/packages/fuels-test-helpers/src/accounts.rs index 19a744cefd..c6010a29af 100644 --- a/packages/fuels-test-helpers/src/accounts.rs +++ b/packages/fuels-test-helpers/src/accounts.rs @@ -171,6 +171,7 @@ mod tests { assert_eq!(&coin.owner, wallet.address()) } CoinType::Message(_) => panic!("resources contained messages"), + CoinType::Unknown => panic!("resources contained unknown coins"), } } } @@ -242,7 +243,7 @@ mod tests { .await? .get("0000000000000000000000000000000000000000000000000000000000000000") .expect("failed to get value"), - num_coins * coin_amount + (num_coins * coin_amount) as u128 ); } diff --git a/packages/fuels-test-helpers/src/node_types.rs b/packages/fuels-test-helpers/src/node_types.rs index 587c94f14d..9c2fab3c58 100644 --- a/packages/fuels-test-helpers/src/node_types.rs +++ b/packages/fuels-test-helpers/src/node_types.rs @@ -6,8 +6,6 @@ use std::{ pub use fuel_core_chain_config::{ChainConfig, StateConfig}; -pub(crate) const MAX_DATABASE_CACHE_SIZE: usize = 10 * 1024 * 1024; - #[derive(Clone, Debug)] pub enum Trigger { Instant, @@ -60,7 +58,7 @@ impl Default for NodeConfig { fn default() -> Self { Self { addr: SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), 0), - max_database_cache_size: Some(MAX_DATABASE_CACHE_SIZE), + max_database_cache_size: None, database_type: DbType::InMemory, utxo_validation: true, debug: true, diff --git a/packages/fuels-test-helpers/src/service.rs b/packages/fuels-test-helpers/src/service.rs index d37621fa4d..d2f63d5289 100644 --- a/packages/fuels-test-helpers/src/service.rs +++ b/packages/fuels-test-helpers/src/service.rs @@ -71,20 +71,26 @@ impl FuelService { }; use fuel_core_chain_config::SnapshotReader; - use crate::{DbType, MAX_DATABASE_CACHE_SIZE}; + #[cfg(feature = "rocksdb")] + use fuel_core::state::rocks_db::{ColumnsPolicy, DatabaseConfig}; + + use crate::DbType; let snapshot_reader = SnapshotReader::new_in_memory(chain_config, state_config); let combined_db_config = CombinedDatabaseConfig { - max_database_cache_size: node_config - .max_database_cache_size - .unwrap_or(MAX_DATABASE_CACHE_SIZE), database_path: match &node_config.database_type { DbType::InMemory => Default::default(), DbType::RocksDb(path) => path.clone().unwrap_or_default(), }, database_type: node_config.database_type.into(), #[cfg(feature = "rocksdb")] + database_config: DatabaseConfig { + cache_capacity: node_config.max_database_cache_size, + max_fds: 512, + columns_policy: ColumnsPolicy::Lazy, + }, + #[cfg(feature = "rocksdb")] state_rewind_policy: Default::default(), }; ServiceConfig { @@ -108,7 +114,7 @@ impl FuelService { utxo_validation: node_config.utxo_validation, debug: node_config.debug, block_production: node_config.block_production.into(), - starting_gas_price: node_config.starting_gas_price, + starting_exec_gas_price: node_config.starting_gas_price, ..ServiceConfig::local_node() } }