From 258a0fd8b500635380e5dcc1b9e1b409b980f3e7 Mon Sep 17 00:00:00 2001 From: segfault-magnet Date: Thu, 9 Jan 2025 13:42:49 +0100 Subject: [PATCH 1/7] fetching consensus parameters is not an async operation --- e2e/tests/contracts.rs | 19 ++- e2e/tests/predicates.rs | 23 ++-- e2e/tests/providers.rs | 49 +++++--- e2e/tests/wallets.rs | 20 +++- examples/contracts/src/lib.rs | 13 ++- examples/cookbook/src/lib.rs | 10 +- examples/providers/src/lib.rs | 3 +- packages/fuels-accounts/src/account.rs | 26 +++-- packages/fuels-accounts/src/provider.rs | 108 +++++++++++++----- packages/fuels-core/src/types/dry_runner.rs | 6 +- .../src/types/transaction_builders.rs | 24 ++-- .../src/types/transaction_builders/blob.rs | 12 +- .../script_tx_estimator.rs | 37 +++--- .../fuels-programs/src/calls/call_handler.rs | 8 +- packages/fuels-programs/src/calls/utils.rs | 22 ++-- .../fuels-programs/src/contract/regular.rs | 1 + packages/fuels-test-helpers/src/accounts.rs | 6 +- packages/fuels-test-helpers/src/lib.rs | 4 +- 18 files changed, 266 insertions(+), 125 deletions(-) diff --git a/e2e/tests/contracts.rs b/e2e/tests/contracts.rs index 74b9a4f748..ff7758c48c 100644 --- a/e2e/tests/contracts.rs +++ b/e2e/tests/contracts.rs @@ -1022,7 +1022,7 @@ async fn test_contract_call_with_non_default_max_input() -> Result<()> { let provider = setup_test_provider(coins, vec![], None, Some(chain_config)).await?; wallet.set_provider(provider.clone()); - assert_eq!(consensus_parameters, *provider.consensus_parameters()); + assert_eq!(consensus_parameters, provider.consensus_parameters().await?); setup_program_test!( Abigen(Contract( @@ -1708,8 +1708,9 @@ async fn contract_custom_call_no_signatures_strategy() -> Result<()> { let mut tb = call_handler.transaction_builder().await?; let amount = 10; + let consensus_parameters = provider.consensus_parameters().await?; let new_base_inputs = wallet - .get_asset_inputs_for_amount(*provider.base_asset_id(), amount, None) + .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), amount, None) .await?; tb.inputs_mut().extend(new_base_inputs); @@ -1719,7 +1720,8 @@ async fn contract_custom_call_no_signatures_strategy() -> Result<()> { .build(provider) .await?; // ANCHOR: tx_sign_with - tx.sign_with(&wallet, provider.chain_id()).await?; + tx.sign_with(&wallet, consensus_parameters.chain_id()) + .await?; // ANCHOR_END: tx_sign_with // ANCHOR_END: tb_no_signatures_strategy @@ -1856,7 +1858,14 @@ async fn msg_sender_gas_estimation_issue() { let asset_id = ids[0]; // The fake coin won't be added if we add a base asset, so let's not do that - assert!(asset_id != *provider.base_asset_id()); + assert!( + asset_id + != *provider + .consensus_parameters() + .await + .unwrap() + .base_asset_id() + ); let call_params = CallParameters::default() .with_amount(100) .with_asset_id(asset_id); @@ -2207,7 +2216,7 @@ async fn blob_contract_deployment() -> Result<()> { let provider = wallets[0].provider().unwrap().clone(); - let consensus_parameters = provider.consensus_parameters(); + let consensus_parameters = provider.consensus_parameters().await?; let contract_max_size = consensus_parameters.contract_params().contract_max_size(); assert!( diff --git a/e2e/tests/predicates.rs b/e2e/tests/predicates.rs index 58642149b8..9ffa593eca 100644 --- a/e2e/tests/predicates.rs +++ b/e2e/tests/predicates.rs @@ -240,9 +240,10 @@ async fn pay_with_predicate() -> Result<()> { // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394 let expected_fee = 1; + let consensus_parameters = provider.consensus_parameters().await?; assert_eq!( predicate - .get_asset_balance(provider.base_asset_id()) + .get_asset_balance(consensus_parameters.base_asset_id()) .await?, 192 - expected_fee ); @@ -258,7 +259,7 @@ async fn pay_with_predicate() -> Result<()> { let expected_fee = 2; assert_eq!( predicate - .get_asset_balance(provider.base_asset_id()) + .get_asset_balance(consensus_parameters.base_asset_id()) .await?, 191 - expected_fee ); @@ -309,9 +310,10 @@ async fn pay_with_predicate_vector_data() -> Result<()> { // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394 let expected_fee = 1; + let consensus_parameters = provider.consensus_parameters().await?; assert_eq!( predicate - .get_asset_balance(provider.base_asset_id()) + .get_asset_balance(consensus_parameters.base_asset_id()) .await?, 192 - expected_fee ); @@ -327,7 +329,7 @@ async fn pay_with_predicate_vector_data() -> Result<()> { assert_eq!(42, response.value); assert_eq!( predicate - .get_asset_balance(provider.base_asset_id()) + .get_asset_balance(consensus_parameters.base_asset_id()) .await?, 191 - expected_fee ); @@ -849,9 +851,14 @@ async fn predicate_transfer_non_base_asset() -> Result<()> { let inputs = predicate .get_asset_inputs_for_amount(non_base_asset_id, amount, None) .await?; + let consensus_parameters = provider.consensus_parameters().await?; let outputs = vec![ Output::change(wallet.address().into(), 0, non_base_asset_id), - Output::change(wallet.address().into(), 0, *provider.base_asset_id()), + Output::change( + wallet.address().into(), + 0, + *consensus_parameters.base_asset_id(), + ), ]; let mut tb = ScriptTransactionBuilder::prepare_transfer( @@ -982,14 +989,16 @@ async fn tx_id_not_changed_after_adding_witnesses() -> Result<()> { .build(&provider) .await?; - let tx_id = tx.id(provider.chain_id()); + let consensus_parameters = provider.consensus_parameters().await?; + let chain_id = consensus_parameters.chain_id(); + let tx_id = tx.id(chain_id); let witness = ABIEncoder::default().encode(&[64u64.into_token()])?; // u64 because this is VM memory let witness2 = ABIEncoder::default().encode(&[4096u64.into_token()])?; tx.append_witness(witness.into())?; tx.append_witness(witness2.into())?; - let tx_id_after_witnesses = tx.id(provider.chain_id()); + let tx_id_after_witnesses = tx.id(chain_id); let tx_id_from_provider = provider.send_transaction(tx).await?; diff --git a/e2e/tests/providers.rs b/e2e/tests/providers.rs index a56b6d7666..f8d9cdcdac 100644 --- a/e2e/tests/providers.rs +++ b/e2e/tests/providers.rs @@ -167,7 +167,8 @@ async fn test_input_message_pays_fee() -> Result<()> { ); let provider = setup_test_provider(vec![], vec![messages], None, None).await?; - let base_asset_id = *provider.base_asset_id(); + let consensus_parameters = provider.consensus_parameters().await?; + let base_asset_id = consensus_parameters.base_asset_id(); wallet.set_provider(provider); abigen!(Contract( @@ -192,7 +193,7 @@ async fn test_input_message_pays_fee() -> Result<()> { assert_eq!(42, response.value); - let balance = wallet.get_asset_balance(&base_asset_id).await?; + let balance = wallet.get_asset_balance(base_asset_id).await?; // TODO: https://github.com/FuelLabs/fuels-rs/issues/1394 let expected_fee = 2; assert_eq!(balance, DEFAULT_COIN_AMOUNT - expected_fee); @@ -649,9 +650,14 @@ async fn test_get_spendable_with_exclusion() -> Result<()> { wallet.set_provider(provider.clone()); let requested_amount = coin_amount_1 + coin_amount_2 + message_amount; + let consensus_parameters = provider.consensus_parameters().await?; { let resources = wallet - .get_spendable_resources(*provider.base_asset_id(), requested_amount, None) + .get_spendable_resources( + *consensus_parameters.base_asset_id(), + requested_amount, + None, + ) .await .unwrap(); assert_eq!(resources.len(), 3); @@ -895,8 +901,9 @@ async fn test_cache_invalidation_on_await() -> Result<()> { assert!(matches!(tx_status, TxStatus::Revert { .. })); + let consensus_parameters = provider.consensus_parameters().await?; let coins = wallet - .get_spendable_resources(*provider.base_asset_id(), 1, None) + .get_spendable_resources(*consensus_parameters.base_asset_id(), 1, None) .await?; assert_eq!(coins.len(), 1); @@ -950,11 +957,15 @@ async fn test_build_with_provider() -> Result<()> { let receiver = WalletUnlocked::new_random(Some(provider.clone())); + let consensus_parameters = provider.consensus_parameters().await?; let inputs = wallet - .get_asset_inputs_for_amount(*provider.base_asset_id(), 100, None) + .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), 100, None) .await?; - let outputs = - wallet.get_asset_outputs_for_amount(receiver.address(), *provider.base_asset_id(), 100); + let outputs = wallet.get_asset_outputs_for_amount( + receiver.address(), + *consensus_parameters.base_asset_id(), + 100, + ); let mut tb = ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default()); tb.add_signer(wallet.clone())?; @@ -963,7 +974,9 @@ async fn test_build_with_provider() -> Result<()> { provider.send_transaction_and_await_commit(tx).await?; - let receiver_balance = receiver.get_asset_balance(provider.base_asset_id()).await?; + let receiver_balance = receiver + .get_asset_balance(consensus_parameters.base_asset_id()) + .await?; assert_eq!(receiver_balance, 100); @@ -982,19 +995,20 @@ async fn can_produce_blocks_with_trig_never() -> Result<()> { let wallet = &wallets[0]; let provider = wallet.try_provider()?; + let consensus_parameters = provider.consensus_parameters().await?; let inputs = wallet - .get_asset_inputs_for_amount(*provider.base_asset_id(), 100, None) + .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), 100, None) .await?; let outputs = wallet.get_asset_outputs_for_amount( &Bech32Address::default(), - *provider.base_asset_id(), + *consensus_parameters.base_asset_id(), 100, ); let mut tb = ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default()); tb.add_signer(wallet.clone())?; let tx = tb.build(provider).await?; - let tx_id = tx.id(provider.chain_id()); + let tx_id = tx.id(consensus_parameters.chain_id()); provider.send_transaction(tx).await?; provider.produce_blocks(1, None).await?; @@ -1140,11 +1154,15 @@ async fn tx_with_witness_data() -> Result<()> { let receiver = WalletUnlocked::new_random(Some(provider.clone())); + let consensus_parameters = provider.consensus_parameters().await?; let inputs = wallet - .get_asset_inputs_for_amount(*provider.base_asset_id(), 10000, None) + .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), 10000, None) .await?; - let outputs = - wallet.get_asset_outputs_for_amount(receiver.address(), *provider.base_asset_id(), 1); + let outputs = wallet.get_asset_outputs_for_amount( + receiver.address(), + *consensus_parameters.base_asset_id(), + 1, + ); let mut tb = ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default()); tb.add_signer(wallet.clone())?; @@ -1297,7 +1315,8 @@ async fn is_account_query_test() -> Result<()> { wallet.add_witnesses(&mut tb)?; let tx = tb.build(provider.clone()).await?; - let tx_id = tx.id(provider.chain_id()); + let consensus_parameters = provider.consensus_parameters().await?; + let tx_id = tx.id(consensus_parameters.chain_id()); let is_account = provider.is_user_account(tx_id).await?; assert!(is_account); diff --git a/e2e/tests/wallets.rs b/e2e/tests/wallets.rs index 0a63edc468..e945b4d550 100644 --- a/e2e/tests/wallets.rs +++ b/e2e/tests/wallets.rs @@ -116,7 +116,7 @@ fn compare_inputs(inputs: &[Input], expected_inputs: &mut Vec) -> bool { return false; } - return comparison_results.iter().all(|&r| r); + comparison_results.iter().all(|&r| r) } fn base_asset_wallet_config(num_wallets: u64) -> WalletsConfig { @@ -348,7 +348,10 @@ async fn test_wallet_get_coins() -> Result<()> { let provider = setup_test_provider(coins, vec![], None, None).await?; wallet.set_provider(provider.clone()); - let wallet_initial_coins = wallet.get_coins(*provider.base_asset_id()).await?; + let consensus_parameters = provider.consensus_parameters().await?; + let wallet_initial_coins = wallet + .get_coins(*consensus_parameters.base_asset_id()) + .await?; let total_amount: u64 = wallet_initial_coins.iter().map(|c| c.amount).sum(); assert_eq!(wallet_initial_coins.len(), NUM_COINS as usize); @@ -445,10 +448,15 @@ async fn test_transfer_with_multiple_signatures() -> Result<()> { let amount_to_transfer = 20; let mut inputs = vec![]; + let consensus_parameters = provider.consensus_parameters().await?; for wallet in &wallets { inputs.extend( wallet - .get_asset_inputs_for_amount(*provider.base_asset_id(), amount_to_transfer, None) + .get_asset_inputs_for_amount( + *consensus_parameters.base_asset_id(), + amount_to_transfer, + None, + ) .await?, ); } @@ -458,7 +466,7 @@ async fn test_transfer_with_multiple_signatures() -> Result<()> { // all change goes to the first wallet let outputs = wallets[0].get_asset_outputs_for_amount( receiver.address(), - *provider.base_asset_id(), + *consensus_parameters.base_asset_id(), amount_to_receive, ); @@ -472,7 +480,9 @@ async fn test_transfer_with_multiple_signatures() -> Result<()> { provider.send_transaction_and_await_commit(tx).await?; assert_eq!( - receiver.get_asset_balance(provider.base_asset_id()).await?, + receiver + .get_asset_balance(consensus_parameters.base_asset_id()) + .await?, amount_to_receive, ); diff --git a/examples/contracts/src/lib.rs b/examples/contracts/src/lib.rs index 1698ae3cc8..f0e9a309a6 100644 --- a/examples/contracts/src/lib.rs +++ b/examples/contracts/src/lib.rs @@ -1017,6 +1017,7 @@ mod tests { )?; let max_allowed = provider .consensus_parameters() + .await? .contract_params() .contract_max_size(); @@ -1105,11 +1106,19 @@ mod tests { // ANCHOR_END: use_loader // ANCHOR: show_max_tx_size - provider.consensus_parameters().tx_params().max_size(); + provider + .consensus_parameters() + .await? + .tx_params() + .max_size(); // ANCHOR_END: show_max_tx_size // ANCHOR: show_max_tx_gas - provider.consensus_parameters().tx_params().max_gas_per_tx(); + provider + .consensus_parameters() + .await? + .tx_params() + .max_gas_per_tx(); // ANCHOR_END: show_max_tx_gas let wallet = main_wallet; diff --git a/examples/cookbook/src/lib.rs b/examples/cookbook/src/lib.rs index 7814432c41..e0c0e19b14 100644 --- a/examples/cookbook/src/lib.rs +++ b/examples/cookbook/src/lib.rs @@ -163,6 +163,7 @@ mod tests { // ANCHOR: transfer_multiple_input let balances = wallet_1.get_balances().await?; + let consensus_parameters = provider.consensus_parameters().await?; let mut inputs = vec![]; let mut outputs = vec![]; for (id_string, amount) in balances { @@ -174,7 +175,7 @@ mod tests { inputs.extend(input); // we don't transfer the full base asset so we can cover fees - let output = if id == *provider.base_asset_id() { + let output = if id == *consensus_parameters.base_asset_id() { wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount / 2) } else { wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, amount) @@ -197,7 +198,7 @@ mod tests { assert_eq!(balances.len(), NUM_ASSETS as usize); for (id, balance) in balances { - if id == provider.base_asset_id().to_string() { + if id == *consensus_parameters.base_asset_id().to_string() { assert_eq!(balance, AMOUNT / 2); } else { assert_eq!(balance, AMOUNT); @@ -269,12 +270,13 @@ mod tests { // ANCHOR_END: custom_tx // ANCHOR: custom_tx_io_base + let consensus_parameters = provider.consensus_parameters().await?; let base_inputs = hot_wallet - .get_asset_inputs_for_amount(*provider.base_asset_id(), ask_amount, None) + .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), ask_amount, None) .await?; let base_outputs = hot_wallet.get_asset_outputs_for_amount( &receiver, - *provider.base_asset_id(), + *consensus_parameters.base_asset_id(), ask_amount, ); // ANCHOR_END: custom_tx_io_base diff --git a/examples/providers/src/lib.rs b/examples/providers/src/lib.rs index f93be95141..76fafced39 100644 --- a/examples/providers/src/lib.rs +++ b/examples/providers/src/lib.rs @@ -71,8 +71,9 @@ mod tests { // ANCHOR_END: setup_test_blockchain // ANCHOR: get_coins + let consensus_parameters = provider.consensus_parameters().await?; let coins = provider - .get_coins(wallet.address(), *provider.base_asset_id()) + .get_coins(wallet.address(), *consensus_parameters.base_asset_id()) .await?; assert_eq!(coins.len(), 1); // ANCHOR_END: get_coins diff --git a/packages/fuels-accounts/src/account.rs b/packages/fuels-accounts/src/account.rs index dbc46dab9f..f6a381c799 100644 --- a/packages/fuels-accounts/src/account.rs +++ b/packages/fuels-accounts/src/account.rs @@ -126,15 +126,16 @@ pub trait ViewOnlyAccount: std::fmt::Debug + Send + Sync + Clone { used_base_amount: u64, ) -> Result<()> { let provider = self.try_provider()?; + let consensus_parameters = provider.consensus_parameters().await?; let (base_assets, base_amount) = - available_base_assets_and_amount(tb, provider.base_asset_id()); + available_base_assets_and_amount(tb, consensus_parameters.base_asset_id()); let missing_base_amount = calculate_missing_base_amount(tb, base_amount, used_base_amount, provider).await?; if missing_base_amount > 0 { let new_base_inputs = self .get_asset_inputs_for_amount( - *provider.base_asset_id(), + *consensus_parameters.base_asset_id(), missing_base_amount, Some(base_assets), ) @@ -144,7 +145,7 @@ pub trait ViewOnlyAccount: std::fmt::Debug + Send + Sync + Clone { tb, new_base_inputs, self.address(), - provider.base_asset_id(), + consensus_parameters.base_asset_id(), ); }; @@ -181,7 +182,8 @@ pub trait Account: ViewOnlyAccount { self.add_witnesses(&mut tx_builder)?; - let used_base_amount = if asset_id == *provider.base_asset_id() { + let consensus_parameters = provider.consensus_parameters().await?; + let used_base_amount = if asset_id == *consensus_parameters.base_asset_id() { amount } else { 0 @@ -190,7 +192,7 @@ pub trait Account: ViewOnlyAccount { .await?; let tx = tx_builder.build(provider).await?; - let tx_id = tx.id(provider.chain_id()); + let tx_id = tx.id(consensus_parameters.chain_id()); let tx_status = provider.send_transaction_and_await_commit(tx).await?; @@ -253,7 +255,8 @@ pub trait Account: ViewOnlyAccount { let tx = tb.build(provider).await?; - let tx_id = tx.id(provider.chain_id()); + let consensus_parameters = provider.consensus_parameters().await?; + let tx_id = tx.id(consensus_parameters.chain_id()); let tx_status = provider.send_transaction_and_await_commit(tx).await?; let receipts = tx_status.take_receipts_checked(None)?; @@ -271,9 +274,10 @@ pub trait Account: ViewOnlyAccount { tx_policies: TxPolicies, ) -> Result<(TxId, Nonce, Vec)> { let provider = self.try_provider()?; + let consensus_parameters = provider.consensus_parameters().await?; let inputs = self - .get_asset_inputs_for_amount(*provider.base_asset_id(), amount, None) + .get_asset_inputs_for_amount(*consensus_parameters.base_asset_id(), amount, None) .await?; let mut tb = ScriptTransactionBuilder::prepare_message_to_output( @@ -281,7 +285,7 @@ pub trait Account: ViewOnlyAccount { amount, inputs, tx_policies, - *provider.base_asset_id(), + *consensus_parameters.base_asset_id(), ); self.add_witnesses(&mut tb)?; @@ -289,7 +293,7 @@ pub trait Account: ViewOnlyAccount { let tx = tb.build(provider).await?; - let tx_id = tx.id(provider.chain_id()); + let tx_id = tx.id(consensus_parameters.chain_id()); let tx_status = provider.send_transaction_and_await_commit(tx).await?; let receipts = tx_status.take_receipts_checked(None)?; @@ -361,8 +365,8 @@ mod tests { }) } - fn consensus_parameters(&self) -> &ConsensusParameters { - &self.c_param + async fn consensus_parameters(&self) -> Result { + Ok(self.c_param.clone()) } async fn estimate_gas_price(&self, _block_header: u32) -> Result { diff --git a/packages/fuels-accounts/src/provider.rs b/packages/fuels-accounts/src/provider.rs index eea4cb2ddb..d31d1e8ade 100644 --- a/packages/fuels-accounts/src/provider.rs +++ b/packages/fuels-accounts/src/provider.rs @@ -1,13 +1,10 @@ -use std::{collections::HashMap, fmt::Debug, net::SocketAddr}; +use std::{collections::HashMap, fmt::Debug, net::SocketAddr, sync::Arc, time::Duration}; mod retry_util; mod retryable_client; mod supported_fuel_core_version; mod supported_versions; -#[cfg(feature = "coin-cache")] -use std::sync::Arc; - use chrono::{DateTime, Utc}; use fuel_core_client::client::{ pagination::{PageDirection, PaginatedResult, PaginationRequest}, @@ -21,7 +18,7 @@ use fuel_core_types::services::executor::TransactionExecutionResult; use fuel_tx::{ AssetId, ConsensusParameters, Receipt, Transaction as FuelTransaction, TxId, UtxoId, }; -use fuel_types::{Address, BlockHeight, Bytes32, ChainId, Nonce}; +use fuel_types::{Address, BlockHeight, Bytes32, Nonce}; #[cfg(feature = "coin-cache")] use fuels_core::types::coin_type_id::CoinTypeId; use fuels_core::{ @@ -48,6 +45,7 @@ pub use supported_fuel_core_version::SUPPORTED_FUEL_CORE_VERSION; use tai64::Tai64; #[cfg(feature = "coin-cache")] use tokio::sync::Mutex; +use tokio::sync::RwLock; #[cfg(feature = "coin-cache")] use crate::coin_cache::CoinsCache; @@ -116,9 +114,22 @@ impl ResourceFilter { #[derive(Debug, Clone)] pub struct Provider { client: RetryableClient, - consensus_parameters: ConsensusParameters, #[cfg(feature = "coin-cache")] cache: Arc>, + consensus_parameters: Arc>, +} + +#[derive(Debug, Clone)] +struct CachedConsensusParameters { + value: ConsensusParameters, + cached_at: DateTime, + ttl: Duration, +} + +impl CachedConsensusParameters { + pub fn is_stale(&self) -> bool { + self.cached_at + self.ttl < Utc::now() + } } impl Provider { @@ -134,7 +145,9 @@ impl Provider { /// Connects to an existing node at the given address. pub async fn connect(url: impl AsRef) -> Result { let client = RetryableClient::connect(&url, Default::default()).await?; - let consensus_parameters = client.chain_info().await?.consensus_parameters; + + let consensus_parameters = + Arc::new(RwLock::new(Self::fetch_consensus_params(&client).await?)); Ok(Self { client, @@ -144,6 +157,16 @@ impl Provider { }) } + async fn fetch_consensus_params(client: &RetryableClient) -> Result { + let consensus_parameters = client.chain_info().await?.consensus_parameters; + + Ok(CachedConsensusParameters { + value: consensus_parameters, + cached_at: Utc::now(), + ttl: Duration::from_secs(5), + }) + } + pub fn url(&self) -> &str { self.client.url() } @@ -166,7 +189,13 @@ impl Provider { tx: T, ) -> Result { #[cfg(feature = "coin-cache")] - self.check_inputs_already_in_cache(&tx.used_coins(self.base_asset_id())) + let base_asset_id = { + let consensus_parameters = self.consensus_parameters().await?; + *consensus_parameters.base_asset_id() + }; + + #[cfg(feature = "coin-cache")] + self.check_inputs_already_in_cache(&tx.used_coins(&base_asset_id)) .await?; let tx = self.prepare_transaction_for_sending(tx).await?; @@ -184,14 +213,15 @@ impl Provider { self.cache .lock() .await - .remove_items(tx.used_coins(self.base_asset_id())) + .remove_items(tx.used_coins(&base_asset_id)) } Ok(tx_status) } async fn prepare_transaction_for_sending(&self, mut tx: T) -> Result { - tx.precompute(&self.chain_id())?; + let consensus_parameters = self.consensus_parameters().await?; + tx.precompute(&consensus_parameters.chain_id())?; let chain_info = self.chain_info().await?; let Header { @@ -204,7 +234,7 @@ impl Provider { tx.estimate_predicates(self, Some(latest_chain_executor_version)) .await?; tx.clone() - .validate_predicates(self.consensus_parameters(), latest_block_height)?; + .validate_predicates(&consensus_parameters, latest_block_height)?; } self.validate_transaction(tx.clone()).await?; @@ -283,7 +313,10 @@ impl Provider { #[cfg(feature = "coin-cache")] async fn submit(&self, tx: T) -> Result { - let used_utxos = tx.used_coins(self.base_asset_id()); + let consensus_parameters = self.consensus_parameters().await?; + let base_asset_id = consensus_parameters.base_asset_id(); + + let used_utxos = tx.used_coins(base_asset_id); self.check_inputs_already_in_cache(&used_utxos).await?; let tx_id = self.client.submit(&tx.into()).await?; @@ -300,16 +333,25 @@ impl Provider { Ok(self.client.chain_info().await?.into()) } - pub fn consensus_parameters(&self) -> &ConsensusParameters { - &self.consensus_parameters - } + pub async fn consensus_parameters(&self) -> Result { + { + let read_lock = self.consensus_parameters.read().await; + if !read_lock.is_stale() { + return Ok(read_lock.value.clone()); + } + } - pub fn base_asset_id(&self) -> &AssetId { - self.consensus_parameters.base_asset_id() - } + let mut write_lock = self.consensus_parameters.write().await; + + // because it could have been updated since we last checked + if !write_lock.is_stale() { + return Ok(write_lock.value.clone()); + } - pub fn chain_id(&self) -> ChainId { - self.consensus_parameters.chain_id() + let fresh_parameters = Self::fetch_consensus_params(&self.client).await?; + *write_lock = fresh_parameters; + + Ok(write_lock.value.clone()) } pub async fn node_info(&self) -> Result { @@ -422,11 +464,14 @@ impl Provider { async fn request_coins_to_spend(&self, filter: ResourceFilter) -> Result> { let queries = filter.resource_queries(); + let consensus_parameters = self.consensus_parameters().await?; + let base_asset_id = *consensus_parameters.base_asset_id(); + let res = self .client .coins_to_spend( &filter.owner(), - queries.spend_query(*self.base_asset_id()), + queries.spend_query(base_asset_id), queries.exclusion_query(), ) .await? @@ -455,15 +500,18 @@ impl Provider { &self, mut filter: ResourceFilter, ) -> Result> { - self.extend_filter_with_cached(&mut filter).await; + self.extend_filter_with_cached(&mut filter).await?; self.request_coins_to_spend(filter).await } #[cfg(feature = "coin-cache")] - async fn extend_filter_with_cached(&self, filter: &mut ResourceFilter) { + async fn extend_filter_with_cached(&self, filter: &mut ResourceFilter) -> Result<()> { + let consensus_parameters = self.consensus_parameters().await?; let mut cache = self.cache.lock().await; - let asset_id = filter.asset_id.unwrap_or(*self.base_asset_id()); + let asset_id = filter + .asset_id + .unwrap_or(*consensus_parameters.base_asset_id()); let used_coins = cache.get_active(&(filter.from.clone(), asset_id)); let excluded_utxos = used_coins @@ -488,6 +536,8 @@ impl Provider { filter .excluded_message_nonces .extend(excluded_message_nonces); + + Ok(()) } /// Get the balance of all spendable coins `asset_id` for address `address`. This is different @@ -688,7 +738,7 @@ impl Provider { let transaction_fee = tx .clone() - .fee_checked_from_tx(&self.consensus_parameters, gas_price) + .fee_checked_from_tx(&self.consensus_parameters().await?, gas_price) .expect("Error calculating TransactionFee"); Ok(TransactionCost { @@ -814,10 +864,6 @@ impl DryRunner for Provider { Ok(self.estimate_gas_price(block_horizon).await?.gas_price) } - fn consensus_parameters(&self) -> &ConsensusParameters { - self.consensus_parameters() - } - async fn maybe_estimate_predicates( &self, tx: &FuelTransaction, @@ -827,4 +873,8 @@ impl DryRunner for Provider { // possible due to the need of blob storage Ok(Some(self.client.estimate_predicates(tx).await?)) } + + async fn consensus_parameters(&self) -> Result { + (*self).consensus_parameters().await + } } diff --git a/packages/fuels-core/src/types/dry_runner.rs b/packages/fuels-core/src/types/dry_runner.rs index e9b54b25e3..d3ea5dab8c 100644 --- a/packages/fuels-core/src/types/dry_runner.rs +++ b/packages/fuels-core/src/types/dry_runner.rs @@ -25,7 +25,7 @@ impl DryRun { pub trait DryRunner: Send + Sync { async fn dry_run(&self, tx: FuelTransaction) -> Result; async fn estimate_gas_price(&self, block_horizon: u32) -> Result; - fn consensus_parameters(&self) -> &ConsensusParameters; + async fn consensus_parameters(&self) -> Result; async fn maybe_estimate_predicates( &self, tx: &FuelTransaction, @@ -44,8 +44,8 @@ impl DryRunner for &T { (*self).estimate_gas_price(block_horizon).await } - fn consensus_parameters(&self) -> &ConsensusParameters { - (*self).consensus_parameters() + async fn consensus_parameters(&self) -> Result { + (*self).consensus_parameters().await } async fn maybe_estimate_predicates( diff --git a/packages/fuels-core/src/types/transaction_builders.rs b/packages/fuels-core/src/types/transaction_builders.rs index 6d0fa09bed..e6b4528bef 100644 --- a/packages/fuels-core/src/types/transaction_builders.rs +++ b/packages/fuels-core/src/types/transaction_builders.rs @@ -226,7 +226,7 @@ macro_rules! impl_tx_builder_trait { tx.estimate_predicates(&provider, None).await?; } - let consensus_parameters = provider.consensus_parameters(); + let consensus_parameters = provider.consensus_parameters().await?; let gas_price = provider .estimate_gas_price(self.gas_price_estimation_block_horizon) @@ -236,7 +236,7 @@ macro_rules! impl_tx_builder_trait { tx.tx, self.max_fee_estimation_tolerance, gas_price, - consensus_parameters, + &consensus_parameters, ) } @@ -369,13 +369,13 @@ macro_rules! impl_tx_builder_trait { } let gas_price = provider.estimate_gas_price(block_horizon).await?; - let consensus_parameters = provider.consensus_parameters(); + let consensus_parameters = provider.consensus_parameters().await?; let max_fee = $crate::types::transaction_builders::estimate_max_fee_w_tolerance( wrapper_tx.tx, max_fee_estimation_tolerance, gas_price, - consensus_parameters, + &consensus_parameters, )?; tx.policies_mut().set(PolicyType::MaxFee, Some(max_fee)); @@ -705,14 +705,16 @@ impl ScriptTransactionBuilder { .await?; } - script_tx_estimator.prepare_for_estimation(&mut tx, should_saturate_variable_outputs); + script_tx_estimator + .prepare_for_estimation(&mut tx, should_saturate_variable_outputs) + .await?; Ok(tx) } async fn set_witnesses(self, tx: &mut fuel_tx::Script, provider: impl DryRunner) -> Result<()> { let missing_witnesses = generate_missing_witnesses( - tx.id(&provider.consensus_parameters().chain_id()), + tx.id(&provider.consensus_parameters().await?.chain_id()), &self.unresolved_signers, ) .await?; @@ -946,7 +948,7 @@ impl CreateTransactionBuilder { } async fn resolve_fuel_tx(self, provider: impl DryRunner) -> Result { - let chain_id = provider.consensus_parameters().chain_id(); + let chain_id = provider.consensus_parameters().await?.chain_id(); let num_witnesses = self.num_witnesses()?; let policies = self.generate_fuel_policies()?; let is_using_predicates = self.is_using_predicates(); @@ -1069,7 +1071,7 @@ impl UploadTransactionBuilder { } async fn resolve_fuel_tx(self, provider: impl DryRunner) -> Result { - let chain_id = provider.consensus_parameters().chain_id(); + let chain_id = provider.consensus_parameters().await?.chain_id(); let num_witnesses = self.num_witnesses()?; let policies = self.generate_fuel_policies()?; let is_using_predicates = self.is_using_predicates(); @@ -1202,7 +1204,7 @@ impl UpgradeTransactionBuilder { } async fn resolve_fuel_tx(self, provider: impl DryRunner) -> Result { - let chain_id = provider.consensus_parameters().chain_id(); + let chain_id = provider.consensus_parameters().await?.chain_id(); let num_witnesses = self.num_witnesses()?; let policies = self.generate_fuel_policies()?; let is_using_predicates = self.is_using_predicates(); @@ -1574,8 +1576,8 @@ mod tests { }) } - fn consensus_parameters(&self) -> &ConsensusParameters { - &self.c_param + async fn consensus_parameters(&self) -> Result { + Ok(self.c_param.clone()) } async fn estimate_gas_price(&self, _block_horizon: u32) -> Result { diff --git a/packages/fuels-core/src/types/transaction_builders/blob.rs b/packages/fuels-core/src/types/transaction_builders/blob.rs index cd6c5e8006..af70f5289f 100644 --- a/packages/fuels-core/src/types/transaction_builders/blob.rs +++ b/packages/fuels-core/src/types/transaction_builders/blob.rs @@ -130,8 +130,14 @@ impl BlobTransactionBuilder { .await?; let current_tx_size = tx.size(); - let max_tx_size = usize::try_from(provider.consensus_parameters().tx_params().max_size()) - .unwrap_or(usize::MAX); + let max_tx_size = usize::try_from( + provider + .consensus_parameters() + .await? + .tx_params() + .max_size(), + ) + .unwrap_or(usize::MAX); Ok(max_tx_size.saturating_sub(current_tx_size)) } @@ -170,7 +176,7 @@ impl BlobTransactionBuilder { } async fn resolve_fuel_tx(mut self, provider: &impl DryRunner) -> Result { - let chain_id = provider.consensus_parameters().chain_id(); + let chain_id = provider.consensus_parameters().await?.chain_id(); let free_witness_index = self.num_witnesses()?; let body = self.blob.as_blob_body(free_witness_index); diff --git a/packages/fuels-core/src/types/transaction_builders/script_tx_estimator.rs b/packages/fuels-core/src/types/transaction_builders/script_tx_estimator.rs index 00521b399d..173e8b997c 100644 --- a/packages/fuels-core/src/types/transaction_builders/script_tx_estimator.rs +++ b/packages/fuels-core/src/types/transaction_builders/script_tx_estimator.rs @@ -4,7 +4,7 @@ use fuel_crypto::Signature; use fuel_tx::{ field::{Inputs, Outputs, ScriptGasLimit, WitnessLimit, Witnesses}, input::coin::{CoinPredicate, CoinSigned}, - AssetId, Chargeable, Input as FuelInput, TxPointer, Witness, + AssetId, Chargeable, ConsensusParameters, Input as FuelInput, TxPointer, Witness, }; use itertools::Itertools; @@ -41,22 +41,26 @@ impl ScriptTxEstimator { mut tx: fuel_tx::Script, saturate_variable_outputs: bool, ) -> Result { - self.prepare_for_estimation(&mut tx, saturate_variable_outputs); + self.prepare_for_estimation(&mut tx, saturate_variable_outputs) + .await?; self._run(tx).await } - pub fn prepare_for_estimation( + pub async fn prepare_for_estimation( &mut self, tx: &mut fuel_tx::Script, saturate_variable_outputs: bool, - ) { + ) -> Result<()> { + let consensus_params = self.dry_runner.consensus_parameters().await?; self.add_fake_witnesses(tx); - self.add_fake_coins(tx); + self.add_fake_coins(tx, &consensus_params); if saturate_variable_outputs { - self.saturate_with_variable_outputs(tx); + self.saturate_with_variable_outputs(tx, &consensus_params); } - self.set_script_gas_limit_to_max(tx); + self.set_script_gas_limit_to_max(tx, &consensus_params); + + Ok(()) } pub fn last_dry_run(&self) -> Option { @@ -70,15 +74,20 @@ impl ScriptTxEstimator { Ok(dry_run) } - fn set_script_gas_limit_to_max(&self, tx: &mut fuel_tx::Script) { - let consensus_params = self.dry_runner.consensus_parameters(); + fn set_script_gas_limit_to_max( + &self, + tx: &mut fuel_tx::Script, + consensus_params: &ConsensusParameters, + ) { let max_gas = tx.max_gas(consensus_params.gas_costs(), consensus_params.fee_params()) + 1; *tx.script_gas_limit_mut() = consensus_params.tx_params().max_gas_per_tx() - max_gas; } - fn saturate_with_variable_outputs(&self, tx: &mut fuel_tx::Script) { - let consensus_params = self.dry_runner.consensus_parameters(); - + fn saturate_with_variable_outputs( + &self, + tx: &mut fuel_tx::Script, + consensus_params: &ConsensusParameters, + ) { let max_outputs = usize::from(consensus_params.tx_params().max_outputs()); let used_outputs = tx.outputs().len(); @@ -100,9 +109,7 @@ impl ScriptTxEstimator { *tx.witnesses_mut() = [self.predefined_witnesses.clone(), dry_run_witnesses].concat(); } - fn add_fake_coins(&self, tx: &mut fuel_tx::Script) { - let consensus_params = self.dry_runner.consensus_parameters(); - + fn add_fake_coins(&self, tx: &mut fuel_tx::Script, consensus_params: &ConsensusParameters) { if let Some(fake_input) = Self::needs_fake_base_input(tx.inputs(), consensus_params.base_asset_id()) { diff --git a/packages/fuels-programs/src/calls/call_handler.rs b/packages/fuels-programs/src/calls/call_handler.rs index dcc7a4edb9..21d0302b6f 100644 --- a/packages/fuels-programs/src/calls/call_handler.rs +++ b/packages/fuels-programs/src/calls/call_handler.rs @@ -166,7 +166,9 @@ where let tx = self.build_tx().await?; let provider = self.account.try_provider()?; - self.cached_tx_id = Some(tx.id(provider.chain_id())); + let consensus_parameters = provider.consensus_parameters().await?; + let chain_id = consensus_parameters.chain_id(); + self.cached_tx_id = Some(tx.id(chain_id)); let tx_status = provider.send_transaction_and_await_commit(tx).await?; @@ -425,8 +427,10 @@ where let tx = self.build_tx().await?; let provider = self.account.try_provider()?; + let consensus_parameters = provider.consensus_parameters().await?; + let chain_id = consensus_parameters.chain_id(); - self.cached_tx_id = Some(tx.id(provider.chain_id())); + self.cached_tx_id = Some(tx.id(chain_id)); let tx_status = provider.send_transaction_and_await_commit(tx).await?; diff --git a/packages/fuels-programs/src/calls/utils.rs b/packages/fuels-programs/src/calls/utils.rs index e071506a24..5649207255 100644 --- a/packages/fuels-programs/src/calls/utils.rs +++ b/packages/fuels-programs/src/calls/utils.rs @@ -37,14 +37,18 @@ pub(crate) async fn transaction_builder_from_contract_calls( ) -> Result { let calls_instructions_len = compute_calls_instructions_len(calls); let provider = account.try_provider()?; - let consensus_parameters = provider.consensus_parameters(); - let data_offset = call_script_data_offset(consensus_parameters, calls_instructions_len)?; + let consensus_parameters = provider.consensus_parameters().await?; + 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, *provider.base_asset_id())?; + let (script_data, call_param_offsets) = build_script_data_from_contract_calls( + calls, + data_offset, + *consensus_parameters.base_asset_id(), + )?; let script = get_instructions(call_param_offsets); - let required_asset_amounts = calculate_required_asset_amounts(calls, *provider.base_asset_id()); + let required_asset_amounts = + calculate_required_asset_amounts(calls, *consensus_parameters.base_asset_id()); // Find the spendable resources required for those calls let mut asset_inputs = vec![]; @@ -59,7 +63,7 @@ pub(crate) async fn transaction_builder_from_contract_calls( calls, asset_inputs, account.address(), - *provider.base_asset_id(), + *consensus_parameters.base_asset_id(), ); Ok(ScriptTransactionBuilder::default() @@ -86,13 +90,13 @@ pub(crate) async fn build_tx_from_contract_calls( transaction_builder_from_contract_calls(calls, tx_policies, variable_outputs, account) .await?; - let base_asset_id = *account.try_provider()?.base_asset_id(); + let consensus_parameters = account.try_provider()?.consensus_parameters().await?; + let base_asset_id = *consensus_parameters.base_asset_id(); let required_asset_amounts = calculate_required_asset_amounts(calls, base_asset_id); - let base_asset_id = account.try_provider()?.base_asset_id(); let used_base_amount = required_asset_amounts .iter() - .find_map(|(asset_id, amount)| (asset_id == base_asset_id).then_some(*amount)) + .find_map(|(asset_id, amount)| (*asset_id == base_asset_id).then_some(*amount)) .unwrap_or_default(); account.add_witnesses(&mut tb)?; diff --git a/packages/fuels-programs/src/contract/regular.rs b/packages/fuels-programs/src/contract/regular.rs index 4865e97b8f..8c54a410bd 100644 --- a/packages/fuels-programs/src/contract/regular.rs +++ b/packages/fuels-programs/src/contract/regular.rs @@ -212,6 +212,7 @@ impl Contract { let provider = account.try_provider()?; let max_contract_size = provider .consensus_parameters() + .await? .contract_params() .contract_max_size() as usize; diff --git a/packages/fuels-test-helpers/src/accounts.rs b/packages/fuels-test-helpers/src/accounts.rs index 8da8ea922d..19a744cefd 100644 --- a/packages/fuels-test-helpers/src/accounts.rs +++ b/packages/fuels-test-helpers/src/accounts.rs @@ -105,11 +105,14 @@ mod tests { let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?; let provider = wallets.first().unwrap().try_provider()?; + let consensus_parameters = provider.consensus_parameters().await?; assert_eq!(wallets.len(), num_wallets as usize); for wallet in &wallets { - let coins = wallet.get_coins(*provider.base_asset_id()).await?; + let coins = wallet + .get_coins(*consensus_parameters.base_asset_id()) + .await?; assert_eq!(coins.len(), num_coins as usize); @@ -224,6 +227,7 @@ mod tests { wallet .try_provider()? .consensus_parameters() + .await? .tx_params() .max_gas_per_tx(), max_gas_per_tx diff --git a/packages/fuels-test-helpers/src/lib.rs b/packages/fuels-test-helpers/src/lib.rs index 673ed2c598..0e8951f88a 100644 --- a/packages/fuels-test-helpers/src/lib.rs +++ b/packages/fuels-test-helpers/src/lib.rs @@ -344,9 +344,9 @@ mod tests { }; let provider = setup_test_provider(vec![], vec![], None, Some(chain_config)).await?; - let retrieved_parameters = provider.consensus_parameters(); + let retrieved_parameters = provider.consensus_parameters().await?; - assert_eq!(*retrieved_parameters, consensus_parameters); + assert_eq!(retrieved_parameters, consensus_parameters); Ok(()) } From 42602983820fab736fda55ae7b51d84e602d4519 Mon Sep 17 00:00:00 2001 From: segfault-magnet Date: Thu, 9 Jan 2025 15:19:46 +0100 Subject: [PATCH 2/7] separate out into cache.rs --- Cargo.toml | 1 + packages/fuels-accounts/Cargo.toml | 1 + packages/fuels-accounts/src/provider.rs | 191 +++++++-------- packages/fuels-accounts/src/provider/cache.rs | 225 ++++++++++++++++++ .../src/provider/retryable_client.rs | 15 +- 5 files changed, 336 insertions(+), 97 deletions(-) create mode 100644 packages/fuels-accounts/src/provider/cache.rs diff --git a/Cargo.toml b/Cargo.toml index e85db72461..bdb81343ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,6 +86,7 @@ zeroize = "1.7.0" octocrab = { version = "0.39", default-features = false } dotenv = { version = "0.15", default-features = false } 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 = [ diff --git a/packages/fuels-accounts/Cargo.toml b/packages/fuels-accounts/Cargo.toml index 657db458aa..069b04ac8e 100644 --- a/packages/fuels-accounts/Cargo.toml +++ b/packages/fuels-accounts/Cargo.toml @@ -30,6 +30,7 @@ tokio = { workspace = true, features = ["full"], optional = true } zeroize = { workspace = true, features = ["derive"] } [dev-dependencies] +mockall = { workspace = true, default-features = false } fuel-tx = { workspace = true, features = ["test-helpers", "random"] } tempfile = { workspace = true } tokio = { workspace = true, features = ["test-util"] } diff --git a/packages/fuels-accounts/src/provider.rs b/packages/fuels-accounts/src/provider.rs index d31d1e8ade..a1fd56dd33 100644 --- a/packages/fuels-accounts/src/provider.rs +++ b/packages/fuels-accounts/src/provider.rs @@ -1,10 +1,15 @@ -use std::{collections::HashMap, fmt::Debug, net::SocketAddr, sync::Arc, time::Duration}; +#[cfg(feature = "coin-cache")] +use std::sync::Arc; +use std::{collections::HashMap, fmt::Debug, net::SocketAddr, time::Duration}; +mod cache; mod retry_util; mod retryable_client; mod supported_fuel_core_version; mod supported_versions; +use crate::provider::cache::CacheableRpcs; +use cache::{CachedClient, SystemClock, TtlConfig}; use chrono::{DateTime, Utc}; use fuel_core_client::client::{ pagination::{PageDirection, PaginatedResult, PaginationRequest}, @@ -45,7 +50,6 @@ pub use supported_fuel_core_version::SUPPORTED_FUEL_CORE_VERSION; use tai64::Tai64; #[cfg(feature = "coin-cache")] use tokio::sync::Mutex; -use tokio::sync::RwLock; #[cfg(feature = "coin-cache")] use crate::coin_cache::CoinsCache; @@ -113,23 +117,9 @@ impl ResourceFilter { /// of `FuelClient`, directly, which provides a broader API. #[derive(Debug, Clone)] pub struct Provider { - client: RetryableClient, + cached_client: CachedClient, #[cfg(feature = "coin-cache")] - cache: Arc>, - consensus_parameters: Arc>, -} - -#[derive(Debug, Clone)] -struct CachedConsensusParameters { - value: ConsensusParameters, - cached_at: DateTime, - ttl: Duration, -} - -impl CachedConsensusParameters { - pub fn is_stale(&self) -> bool { - self.cached_at + self.ttl < Utc::now() - } + coins_cache: Arc>, } impl Provider { @@ -138,49 +128,49 @@ impl Provider { Self::connect(format!("http://{addr}")).await } + fn uncached_client(&self) -> &RetryableClient { + self.cached_client.inner() + } + + fn uncached_client_mut(&mut self) -> &mut RetryableClient { + self.cached_client.inner_mut() + } + pub async fn healthy(&self) -> Result { - Ok(self.client.health().await?) + Ok(self.uncached_client().health().await?) } /// Connects to an existing node at the given address. pub async fn connect(url: impl AsRef) -> Result { - let client = RetryableClient::connect(&url, Default::default()).await?; - - let consensus_parameters = - Arc::new(RwLock::new(Self::fetch_consensus_params(&client).await?)); + let client = CachedClient::new( + RetryableClient::connect(&url, Default::default()).await?, + TtlConfig { + consensus_parameters: Duration::from_secs(5), + }, + SystemClock, + ); Ok(Self { - client, - consensus_parameters, + cached_client: client, #[cfg(feature = "coin-cache")] - cache: Default::default(), - }) - } - - async fn fetch_consensus_params(client: &RetryableClient) -> Result { - let consensus_parameters = client.chain_info().await?.consensus_parameters; - - Ok(CachedConsensusParameters { - value: consensus_parameters, - cached_at: Utc::now(), - ttl: Duration::from_secs(5), + coins_cache: Default::default(), }) } pub fn url(&self) -> &str { - self.client.url() + self.uncached_client().url() } pub async fn blob(&self, blob_id: BlobId) -> Result> { Ok(self - .client + .uncached_client() .blob(blob_id.into()) .await? .map(|blob| Blob::new(blob.bytecode))) } pub async fn blob_exists(&self, blob_id: BlobId) -> Result { - Ok(self.client.blob_exists(blob_id.into()).await?) + Ok(self.uncached_client().blob_exists(blob_id.into()).await?) } /// Sends a transaction to the underlying Provider's client. @@ -200,7 +190,7 @@ impl Provider { let tx = self.prepare_transaction_for_sending(tx).await?; let tx_status = self - .client + .uncached_client() .submit_and_await_commit(&tx.clone().into()) .await? .into(); @@ -210,7 +200,7 @@ impl Provider { tx_status, TxStatus::SqueezedOut { .. } | TxStatus::Revert { .. } ) { - self.cache + self.coins_cache .lock() .await .remove_items(tx.used_coins(&base_asset_id)) @@ -248,7 +238,11 @@ impl Provider { } pub async fn await_transaction_commit(&self, id: TxId) -> Result { - Ok(self.client.await_transaction_commit(&id).await?.into()) + Ok(self + .uncached_client() + .await_transaction_commit(&id) + .await? + .into()) } async fn validate_transaction(&self, tx: T) -> Result<()> { @@ -264,7 +258,7 @@ impl Provider { #[cfg(not(feature = "coin-cache"))] async fn submit(&self, tx: T) -> Result { - Ok(self.client.submit(&tx.into()).await?) + Ok(self.uncached_client().submit(&tx.into()).await?) } #[cfg(feature = "coin-cache")] @@ -272,7 +266,7 @@ impl Provider { &self, coin_ids: impl IntoIterator)>, ) -> Option<((Bech32Address, AssetId), CoinTypeId)> { - let mut locked_cache = self.cache.lock().await; + let mut locked_cache = self.coins_cache.lock().await; for (key, ids) in coin_ids { let items = locked_cache.get_active(key); @@ -319,56 +313,46 @@ impl Provider { let used_utxos = tx.used_coins(base_asset_id); self.check_inputs_already_in_cache(&used_utxos).await?; - let tx_id = self.client.submit(&tx.into()).await?; - self.cache.lock().await.insert_multiple(used_utxos); + let tx_id = self.uncached_client().submit(&tx.into()).await?; + self.coins_cache.lock().await.insert_multiple(used_utxos); Ok(tx_id) } pub async fn tx_status(&self, tx_id: &TxId) -> Result { - Ok(self.client.transaction_status(tx_id).await?.into()) + Ok(self + .uncached_client() + .transaction_status(tx_id) + .await? + .into()) } pub async fn chain_info(&self) -> Result { - Ok(self.client.chain_info().await?.into()) + Ok(self.uncached_client().chain_info().await?.into()) } pub async fn consensus_parameters(&self) -> Result { - { - let read_lock = self.consensus_parameters.read().await; - if !read_lock.is_stale() { - return Ok(read_lock.value.clone()); - } - } - - let mut write_lock = self.consensus_parameters.write().await; - - // because it could have been updated since we last checked - if !write_lock.is_stale() { - return Ok(write_lock.value.clone()); - } - - let fresh_parameters = Self::fetch_consensus_params(&self.client).await?; - *write_lock = fresh_parameters; - - Ok(write_lock.value.clone()) + self.cached_client.consensus_parameters().await } pub async fn node_info(&self) -> Result { - Ok(self.client.node_info().await?.into()) + Ok(self.uncached_client().node_info().await?.into()) } pub async fn latest_gas_price(&self) -> Result { - Ok(self.client.latest_gas_price().await?) + Ok(self.uncached_client().latest_gas_price().await?) } pub async fn estimate_gas_price(&self, block_horizon: u32) -> Result { - Ok(self.client.estimate_gas_price(block_horizon).await?) + Ok(self + .uncached_client() + .estimate_gas_price(block_horizon) + .await?) } pub async fn dry_run(&self, tx: impl Transaction) -> Result { let [tx_status] = self - .client + .uncached_client() .dry_run(Transactions::new().insert(tx).as_slice()) .await? .into_iter() @@ -385,7 +369,7 @@ impl Provider { transactions: Transactions, ) -> Result> { Ok(self - .client + .uncached_client() .dry_run(transactions.as_slice()) .await? .into_iter() @@ -400,7 +384,7 @@ impl Provider { gas_price: Option, ) -> Result { let [tx_status] = self - .client + .uncached_client() .dry_run_opt( Transactions::new().insert(tx).as_slice(), Some(utxo_validation), @@ -423,7 +407,7 @@ impl Provider { gas_price: Option, ) -> Result> { Ok(self - .client + .uncached_client() .dry_run_opt(transactions.as_slice(), Some(utxo_validation), gas_price) .await? .into_iter() @@ -439,7 +423,7 @@ impl Provider { loop { let res = self - .client + .uncached_client() .coins( &from.into(), Some(&asset_id), @@ -468,7 +452,7 @@ impl Provider { let base_asset_id = *consensus_parameters.base_asset_id(); let res = self - .client + .uncached_client() .coins_to_spend( &filter.owner(), queries.spend_query(base_asset_id), @@ -508,7 +492,7 @@ impl Provider { #[cfg(feature = "coin-cache")] async fn extend_filter_with_cached(&self, filter: &mut ResourceFilter) -> Result<()> { let consensus_parameters = self.consensus_parameters().await?; - let mut cache = self.cache.lock().await; + let mut cache = self.coins_cache.lock().await; let asset_id = filter .asset_id .unwrap_or(*consensus_parameters.base_asset_id()); @@ -549,7 +533,7 @@ impl Provider { asset_id: AssetId, ) -> Result { Ok(self - .client + .uncached_client() .balance(&address.into(), Some(&asset_id)) .await?) } @@ -561,7 +545,7 @@ impl Provider { asset_id: AssetId, ) -> Result { Ok(self - .client + .uncached_client() .contract_balance(&contract_id.into(), Some(&asset_id)) .await?) } @@ -578,7 +562,7 @@ impl Provider { direction: PageDirection::Forward, }; let balances_vec = self - .client + .uncached_client() .balances(&address.into(), pagination) .await? .results; @@ -609,7 +593,7 @@ impl Provider { let mut balances_vec = vec![]; loop { let mut paginated_result = self - .client + .uncached_client() .contract_balances(&contract_id.into(), pagination.clone()) .await?; @@ -636,14 +620,18 @@ impl Provider { } pub async fn get_transaction_by_id(&self, tx_id: &TxId) -> Result> { - Ok(self.client.transaction(tx_id).await?.map(Into::into)) + Ok(self + .uncached_client() + .transaction(tx_id) + .await? + .map(Into::into)) } pub async fn get_transactions( &self, request: PaginationRequest, ) -> Result> { - let pr = self.client.transactions(request).await?; + let pr = self.uncached_client().transactions(request).await?; Ok(PaginatedResult { cursor: pr.cursor, @@ -660,7 +648,7 @@ impl Provider { request: PaginationRequest, ) -> Result> { let pr = self - .client + .uncached_client() .transactions_by_owner(&owner.into(), request) .await?; @@ -688,18 +676,26 @@ impl Provider { let start_time = start_time.map(|time| Tai64::from_unix(time.timestamp()).0); Ok(self - .client + .uncached_client() .produce_blocks(blocks_to_produce, start_time) .await? .into()) } pub async fn block(&self, block_id: &Bytes32) -> Result> { - Ok(self.client.block(block_id).await?.map(Into::into)) + Ok(self + .uncached_client() + .block(block_id) + .await? + .map(Into::into)) } pub async fn block_by_height(&self, height: BlockHeight) -> Result> { - Ok(self.client.block_by_height(height).await?.map(Into::into)) + Ok(self + .uncached_client() + .block_by_height(height) + .await? + .map(Into::into)) } // - Get block(s) @@ -707,7 +703,7 @@ impl Provider { &self, request: PaginationRequest, ) -> Result> { - let pr = self.client.blocks(request).await?; + let pr = self.uncached_client().blocks(request).await?; Ok(PaginatedResult { cursor: pr.cursor, @@ -781,7 +777,7 @@ impl Provider { }; Ok(self - .client + .uncached_client() .messages(Some(&from.into()), pagination) .await? .results @@ -798,7 +794,7 @@ impl Provider { commit_block_height: Option, ) -> Result> { let proof = self - .client + .uncached_client() .message_proof( tx_id, nonce, @@ -812,17 +808,22 @@ impl Provider { } pub async fn is_user_account(&self, address: impl Into) -> Result { - self.client.is_user_account(*address.into()).await + self.uncached_client() + .is_user_account(*address.into()) + .await } pub fn with_retry_config(mut self, retry_config: RetryConfig) -> Self { - self.client.set_retry_config(retry_config); + self.uncached_client_mut().set_retry_config(retry_config); self } pub async fn contract_exists(&self, contract_id: &Bech32ContractId) -> Result { - Ok(self.client.contract_exists(&contract_id.into()).await?) + Ok(self + .uncached_client() + .contract_exists(&contract_id.into()) + .await?) } } @@ -830,7 +831,7 @@ impl Provider { impl DryRunner for Provider { async fn dry_run(&self, tx: FuelTransaction) -> Result { let [tx_execution_status] = self - .client + .uncached_client() .dry_run_opt(&vec![tx], Some(false), Some(0)) .await? .try_into() @@ -871,10 +872,10 @@ impl DryRunner for Provider { ) -> Result> { // We always delegate the estimation to the client because estimating locally is no longer // possible due to the need of blob storage - Ok(Some(self.client.estimate_predicates(tx).await?)) + Ok(Some(self.uncached_client().estimate_predicates(tx).await?)) } async fn consensus_parameters(&self) -> Result { - (*self).consensus_parameters().await + Provider::consensus_parameters(self).await } } diff --git a/packages/fuels-accounts/src/provider/cache.rs b/packages/fuels-accounts/src/provider/cache.rs new file mode 100644 index 0000000000..76d81c2e58 --- /dev/null +++ b/packages/fuels-accounts/src/provider/cache.rs @@ -0,0 +1,225 @@ +use std::{sync::Arc, time::Duration}; + +use async_trait::async_trait; +use chrono::{DateTime, Utc}; +use fuel_tx::ConsensusParameters; +use fuels_core::types::errors::Result; +use tokio::sync::RwLock; + +#[cfg_attr(test, mockall::automock)] +#[async_trait] +pub trait CacheableRpcs { + async fn consensus_parameters(&self) -> Result; +} + +trait Clock { + fn now(&self) -> DateTime; +} + +#[derive(Debug, Clone)] +pub struct TtlConfig { + pub consensus_parameters: Duration, +} + +#[derive(Debug, Clone)] +struct Dated { + value: T, + date: DateTime, + ttl: Duration, +} + +impl Dated { + fn is_stale(&self, now: DateTime) -> bool { + self.date + self.ttl < now + } +} + +#[derive(Debug, Clone, Copy)] +pub struct SystemClock; +impl Clock for SystemClock { + fn now(&self) -> DateTime { + Utc::now() + } +} + +#[derive(Debug, Clone)] +pub struct CachedClient { + client: Client, + ttl_config: TtlConfig, + cached_consensus_params: Arc>>>, + clock: Clock, +} + +impl CachedClient { + pub fn new(client: Client, ttl: TtlConfig, clock: Clock) -> Self { + Self { + client, + ttl_config: ttl, + cached_consensus_params: Default::default(), + clock, + } + } + + pub fn inner(&self) -> &Client { + &self.client + } + + pub fn inner_mut(&mut self) -> &mut Client { + &mut self.client + } +} + +#[async_trait] +impl CacheableRpcs for CachedClient +where + Clk: Clock + Send + Sync, + Client: CacheableRpcs + Send + Sync, +{ + async fn consensus_parameters(&self) -> Result { + { + let read_lock = self.cached_consensus_params.read().await; + if let Some(entry) = read_lock.as_ref() { + if !entry.is_stale(self.clock.now()) { + return Ok(entry.value.clone()); + } + } + } + + let mut write_lock = self.cached_consensus_params.write().await; + + // because it could have been updated since we last checked + if let Some(entry) = write_lock.as_ref() { + if !entry.is_stale(self.clock.now()) { + return Ok(entry.value.clone()); + } + } + + let fresh_parameters = self.client.consensus_parameters().await?; + *write_lock = Some(Dated { + value: fresh_parameters.clone(), + date: self.clock.now(), + ttl: self.ttl_config.consensus_parameters, + }); + + Ok(fresh_parameters) + } +} + +#[cfg(test)] +mod tests { + use std::sync::Mutex; + + use fuel_types::ChainId; + + use super::*; + + #[derive(Clone, Default)] + struct TestClock { + time: Arc>>, + } + + impl TestClock { + fn update_time(&self, time: DateTime) { + *self.time.lock().unwrap() = time; + } + } + + impl Clock for TestClock { + fn now(&self) -> DateTime { + *self.time.lock().unwrap() + } + } + + #[tokio::test] + async fn initial_call_to_consensus_params_fwd_to_api() { + // given + let mut api = MockCacheableRpcs::new(); + api.expect_consensus_parameters() + .once() + .return_once(|| Ok(ConsensusParameters::default())); + let sut = CachedClient::new( + api, + TtlConfig { + consensus_parameters: Duration::from_secs(10), + }, + TestClock::default(), + ); + + // when + let _consensus_params = sut.consensus_parameters().await.unwrap(); + + // then + // mock validates the call went through + } + + #[tokio::test] + async fn new_call_to_consensus_params_cached() { + // given + let mut api = MockCacheableRpcs::new(); + api.expect_consensus_parameters() + .once() + .return_once(|| Ok(ConsensusParameters::default())); + let sut = CachedClient::new( + api, + TtlConfig { + consensus_parameters: Duration::from_secs(10), + }, + TestClock::default(), + ); + let consensus_parameters = sut.consensus_parameters().await.unwrap(); + + // when + let second_call_consensus_params = sut.consensus_parameters().await.unwrap(); + + // then + // mock validates only one call + assert_eq!(consensus_parameters, second_call_consensus_params); + } + + #[tokio::test] + async fn if_ttl_expired_cache_is_updated() { + // given + let original_consensus_params = ConsensusParameters::default(); + + let changed_consensus_params = { + let mut params = original_consensus_params.clone(); + params.set_chain_id(ChainId::new(99)); + params + }; + + let api = { + let mut api = MockCacheableRpcs::new(); + let original_consensus_params = original_consensus_params.clone(); + let changed_consensus_params = changed_consensus_params.clone(); + api.expect_consensus_parameters() + .once() + .return_once(move || Ok(original_consensus_params)); + + api.expect_consensus_parameters() + .once() + .return_once(move || Ok(changed_consensus_params)); + api + }; + + let clock = TestClock::default(); + let start_time = clock.now(); + + let sut = CachedClient::new( + api, + TtlConfig { + consensus_parameters: Duration::from_secs(10), + }, + clock.clone(), + ); + let consensus_parameters = sut.consensus_parameters().await.unwrap(); + + clock.update_time(start_time + Duration::from_secs(11)); + // when + let second_call_consensus_params = sut.consensus_parameters().await.unwrap(); + + // then + // mock validates two calls made + assert_eq!(consensus_parameters, original_consensus_params); + assert_eq!(second_call_consensus_params, changed_consensus_params); + } +} diff --git a/packages/fuels-accounts/src/provider/retryable_client.rs b/packages/fuels-accounts/src/provider/retryable_client.rs index 06952e5323..d43f344445 100644 --- a/packages/fuels-accounts/src/provider/retryable_client.rs +++ b/packages/fuels-accounts/src/provider/retryable_client.rs @@ -1,5 +1,6 @@ use std::{future::Future, io}; +use async_trait::async_trait; use custom_queries::{ContractExistsQuery, IsUserAccountQuery, IsUserAccountVariables}; use cynic::QueryBuilder; use fuel_core_client::client::{ @@ -14,11 +15,14 @@ use fuel_core_client::client::{ FuelClient, }; use fuel_core_types::services::executor::TransactionExecutionStatus; -use fuel_tx::{BlobId, Transaction, TxId, UtxoId}; +use fuel_tx::{BlobId, ConsensusParameters, Transaction, TxId, UtxoId}; use fuel_types::{Address, AssetId, BlockHeight, ContractId, Nonce}; use fuels_core::types::errors::{error, Error, Result}; -use super::supported_versions::{self, VersionCompatibility}; +use super::{ + cache::CacheableRpcs, + supported_versions::{self, VersionCompatibility}, +}; use crate::provider::{retry_util, RetryConfig}; #[derive(Debug, thiserror::Error)] @@ -43,6 +47,13 @@ pub(crate) struct RetryableClient { prepend_warning: Option, } +#[async_trait] +impl CacheableRpcs for RetryableClient { + async fn consensus_parameters(&self) -> Result { + Ok(self.client.chain_info().await?.consensus_parameters) + } +} + impl RetryableClient { pub(crate) async fn connect(url: impl AsRef, retry_config: RetryConfig) -> Result { let url = url.as_ref().to_string(); From aca0c15457001b48ff4c098e15ff0e4714a5af95 Mon Sep 17 00:00:00 2001 From: segfault-magnet Date: Thu, 9 Jan 2025 15:29:42 +0100 Subject: [PATCH 3/7] add cache clearing --- packages/fuels-accounts/src/provider.rs | 24 ++++++++++++------- packages/fuels-accounts/src/provider/cache.rs | 22 +++++++++++++++++ 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/packages/fuels-accounts/src/provider.rs b/packages/fuels-accounts/src/provider.rs index a1fd56dd33..56b63a9940 100644 --- a/packages/fuels-accounts/src/provider.rs +++ b/packages/fuels-accounts/src/provider.rs @@ -9,7 +9,8 @@ mod supported_fuel_core_version; mod supported_versions; use crate::provider::cache::CacheableRpcs; -use cache::{CachedClient, SystemClock, TtlConfig}; +pub use cache::TtlConfig; +use cache::{CachedClient, SystemClock}; use chrono::{DateTime, Utc}; use fuel_core_client::client::{ pagination::{PageDirection, PaginatedResult, PaginationRequest}, @@ -128,12 +129,13 @@ impl Provider { Self::connect(format!("http://{addr}")).await } - fn uncached_client(&self) -> &RetryableClient { - self.cached_client.inner() + /// To be applied to future requests, call [`clear_cache`] if you need to clear the cache. + pub fn set_cache_ttl(&mut self, ttl: TtlConfig) { + self.cached_client.set_ttl(ttl); } - fn uncached_client_mut(&mut self) -> &mut RetryableClient { - self.cached_client.inner_mut() + pub async fn clear_cache(&self) { + self.cached_client.clear().await; } pub async fn healthy(&self) -> Result { @@ -144,9 +146,7 @@ impl Provider { pub async fn connect(url: impl AsRef) -> Result { let client = CachedClient::new( RetryableClient::connect(&url, Default::default()).await?, - TtlConfig { - consensus_parameters: Duration::from_secs(5), - }, + TtlConfig::default(), SystemClock, ); @@ -825,6 +825,14 @@ impl Provider { .contract_exists(&contract_id.into()) .await?) } + + fn uncached_client(&self) -> &RetryableClient { + self.cached_client.inner() + } + + fn uncached_client_mut(&mut self) -> &mut RetryableClient { + self.cached_client.inner_mut() + } } #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] diff --git a/packages/fuels-accounts/src/provider/cache.rs b/packages/fuels-accounts/src/provider/cache.rs index 76d81c2e58..723ed9499c 100644 --- a/packages/fuels-accounts/src/provider/cache.rs +++ b/packages/fuels-accounts/src/provider/cache.rs @@ -21,6 +21,14 @@ pub struct TtlConfig { pub consensus_parameters: Duration, } +impl Default for TtlConfig { + fn default() -> Self { + TtlConfig { + consensus_parameters: Duration::from_secs(60), + } + } +} + #[derive(Debug, Clone)] struct Dated { value: T, @@ -60,6 +68,11 @@ impl CachedClient { } } + /// To be applied going forward + pub fn set_ttl(&mut self, ttl: TtlConfig) { + self.ttl_config = ttl + } + pub fn inner(&self) -> &Client { &self.client } @@ -69,6 +82,15 @@ impl CachedClient { } } +impl CachedClient +where + Client: CacheableRpcs, +{ + pub async fn clear(&self) { + let _ = self.cached_consensus_params.write().await.take(); + } +} + #[async_trait] impl CacheableRpcs for CachedClient where From 9907d76e23c806ec3398d9c8739b43207333028e Mon Sep 17 00:00:00 2001 From: segfault-magnet Date: Thu, 9 Jan 2025 15:32:54 +0100 Subject: [PATCH 4/7] add test for clearing cache --- packages/fuels-accounts/src/provider/cache.rs | 47 ++++++++++++++++--- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/packages/fuels-accounts/src/provider/cache.rs b/packages/fuels-accounts/src/provider/cache.rs index 723ed9499c..9483fbc0bf 100644 --- a/packages/fuels-accounts/src/provider/cache.rs +++ b/packages/fuels-accounts/src/provider/cache.rs @@ -159,13 +159,7 @@ mod tests { api.expect_consensus_parameters() .once() .return_once(|| Ok(ConsensusParameters::default())); - let sut = CachedClient::new( - api, - TtlConfig { - consensus_parameters: Duration::from_secs(10), - }, - TestClock::default(), - ); + let sut = CachedClient::new(api, TtlConfig::default(), TestClock::default()); // when let _consensus_params = sut.consensus_parameters().await.unwrap(); @@ -244,4 +238,43 @@ mod tests { assert_eq!(consensus_parameters, original_consensus_params); assert_eq!(second_call_consensus_params, changed_consensus_params); } + + #[tokio::test] + async fn clear_cache_clears_consensus_params_cache() { + // given + let first_params = ConsensusParameters::default(); + let second_params = { + let mut params = ConsensusParameters::default(); + params.set_chain_id(ChainId::new(1234)); + params + }; + + let api = { + let mut api = MockCacheableRpcs::new(); + let first_clone = first_params.clone(); + api.expect_consensus_parameters() + .times(1) + .return_once(move || Ok(first_clone)); + + let second_clone = second_params.clone(); + api.expect_consensus_parameters() + .times(1) + .return_once(move || Ok(second_clone)); + api + }; + + let clock = TestClock::default(); + let sut = CachedClient::new(api, TtlConfig::default(), clock.clone()); + + let result1 = sut.consensus_parameters().await.unwrap(); + + // when + sut.clear().await; + + // then + let result2 = sut.consensus_parameters().await.unwrap(); + + assert_eq!(result1, first_params); + assert_eq!(result2, second_params); + } } From dc0c9b1f776f24fb0855246c1c7d8fc56e42c1dd Mon Sep 17 00:00:00 2001 From: segfault-magnet Date: Thu, 9 Jan 2025 15:47:34 +0100 Subject: [PATCH 5/7] apply ttl changes immediately --- e2e/tests/contracts.rs | 5 +++++ packages/fuels-accounts/src/provider/cache.rs | 11 ++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/e2e/tests/contracts.rs b/e2e/tests/contracts.rs index ff7758c48c..68b56e163c 100644 --- a/e2e/tests/contracts.rs +++ b/e2e/tests/contracts.rs @@ -1701,6 +1701,11 @@ async fn contract_custom_call_no_signatures_strategy() -> Result<()> { ), ); let provider = wallet.try_provider()?; + let mut provider = provider.clone(); + provider.set_cache_ttl(TtlConfig { + consensus_parameters: Duration::from_secs(5), + }); + provider.clear_cache().await; let counter = 42; let call_handler = contract_instance.methods().initialize_counter(counter); diff --git a/packages/fuels-accounts/src/provider/cache.rs b/packages/fuels-accounts/src/provider/cache.rs index 9483fbc0bf..493cc11c8b 100644 --- a/packages/fuels-accounts/src/provider/cache.rs +++ b/packages/fuels-accounts/src/provider/cache.rs @@ -33,12 +33,11 @@ impl Default for TtlConfig { struct Dated { value: T, date: DateTime, - ttl: Duration, } impl Dated { - fn is_stale(&self, now: DateTime) -> bool { - self.date + self.ttl < now + fn is_stale(&self, now: DateTime, ttl: Duration) -> bool { + self.date + ttl < now } } @@ -68,7 +67,6 @@ impl CachedClient { } } - /// To be applied going forward pub fn set_ttl(&mut self, ttl: TtlConfig) { self.ttl_config = ttl } @@ -101,7 +99,7 @@ where { let read_lock = self.cached_consensus_params.read().await; if let Some(entry) = read_lock.as_ref() { - if !entry.is_stale(self.clock.now()) { + if !entry.is_stale(self.clock.now(), self.ttl_config.consensus_parameters) { return Ok(entry.value.clone()); } } @@ -111,7 +109,7 @@ where // because it could have been updated since we last checked if let Some(entry) = write_lock.as_ref() { - if !entry.is_stale(self.clock.now()) { + if !entry.is_stale(self.clock.now(), self.ttl_config.consensus_parameters) { return Ok(entry.value.clone()); } } @@ -120,7 +118,6 @@ where *write_lock = Some(Dated { value: fresh_parameters.clone(), date: self.clock.now(), - ttl: self.ttl_config.consensus_parameters, }); Ok(fresh_parameters) From 26bcd5a2dcc7524062c42b7519eb70226d37c833 Mon Sep 17 00:00:00 2001 From: segfault-magnet Date: Thu, 9 Jan 2025 15:48:28 +0100 Subject: [PATCH 6/7] remove comment --- packages/fuels-accounts/src/provider.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/fuels-accounts/src/provider.rs b/packages/fuels-accounts/src/provider.rs index 56b63a9940..0ed0721f9f 100644 --- a/packages/fuels-accounts/src/provider.rs +++ b/packages/fuels-accounts/src/provider.rs @@ -129,7 +129,6 @@ impl Provider { Self::connect(format!("http://{addr}")).await } - /// To be applied to future requests, call [`clear_cache`] if you need to clear the cache. pub fn set_cache_ttl(&mut self, ttl: TtlConfig) { self.cached_client.set_ttl(ttl); } From 8190c4adb8d86a205c7de0c87e3cc43484da5852 Mon Sep 17 00:00:00 2001 From: segfault-magnet Date: Thu, 9 Jan 2025 15:54:19 +0100 Subject: [PATCH 7/7] fix ci --- e2e/tests/contracts.rs | 5 ----- packages/fuels-accounts/src/provider.rs | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/e2e/tests/contracts.rs b/e2e/tests/contracts.rs index 68b56e163c..ff7758c48c 100644 --- a/e2e/tests/contracts.rs +++ b/e2e/tests/contracts.rs @@ -1701,11 +1701,6 @@ async fn contract_custom_call_no_signatures_strategy() -> Result<()> { ), ); let provider = wallet.try_provider()?; - let mut provider = provider.clone(); - provider.set_cache_ttl(TtlConfig { - consensus_parameters: Duration::from_secs(5), - }); - provider.clear_cache().await; let counter = 42; let call_handler = contract_instance.methods().initialize_counter(counter); diff --git a/packages/fuels-accounts/src/provider.rs b/packages/fuels-accounts/src/provider.rs index 0ed0721f9f..4e8ecb6576 100644 --- a/packages/fuels-accounts/src/provider.rs +++ b/packages/fuels-accounts/src/provider.rs @@ -1,6 +1,6 @@ #[cfg(feature = "coin-cache")] use std::sync::Arc; -use std::{collections::HashMap, fmt::Debug, net::SocketAddr, time::Duration}; +use std::{collections::HashMap, fmt::Debug, net::SocketAddr}; mod cache; mod retry_util;