From 3f59bf6ff58ed6136bc0c9d904c7d2d4fe8fe939 Mon Sep 17 00:00:00 2001 From: Ahmed Sagdati <37515857+segfault-magnet@users.noreply.github.com> Date: Thu, 30 Jan 2025 21:17:51 +0300 Subject: [PATCH] bug: script/predicate blob upload now uses the default max fee estimation tolerance (#1590) because the gas price horizon no longer increases the price and this makes the max fee estimation more sensitive to extra cost due to using more coins than what we estimated with. --- e2e/tests/contracts.rs | 5 +- e2e/tests/scripts.rs | 69 ++++++++++++++++++- packages/fuels-accounts/src/provider.rs | 2 +- packages/fuels-core/src/types/dry_runner.rs | 2 +- .../src/types/transaction_builders.rs | 2 +- .../src/calls/traits/transaction_tuner.rs | 13 ++-- packages/fuels-programs/src/calls/utils.rs | 5 +- .../fuels-programs/src/contract/loader.rs | 4 +- .../fuels-programs/src/contract/regular.rs | 4 +- packages/fuels-programs/src/executable.rs | 9 ++- packages/fuels-programs/src/lib.rs | 2 + 11 files changed, 99 insertions(+), 18 deletions(-) diff --git a/e2e/tests/contracts.rs b/e2e/tests/contracts.rs index 9e9e80c47..b41ce21bb 100644 --- a/e2e/tests/contracts.rs +++ b/e2e/tests/contracts.rs @@ -7,6 +7,7 @@ use fuel_tx::{ use fuels::{ core::codec::{calldata, encode_fn_selector, DecoderConfig, EncoderConfig}, prelude::*, + programs::DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE, tx::ContractParameters, types::{errors::transaction::Reason, input::Input, Bits256, Identity}, }; @@ -2114,8 +2115,8 @@ async fn max_fee_estimation_respects_tolerance() -> Result<()> { .unwrap(); assert_eq!( - builder.max_fee_estimation_tolerance, 0.05, - "Expected pre-set tolerance of 0.05" + builder.max_fee_estimation_tolerance, DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE, + "Expected pre-set tolerance" ); builder diff --git a/e2e/tests/scripts.rs b/e2e/tests/scripts.rs index b418dd10b..a36b038de 100644 --- a/e2e/tests/scripts.rs +++ b/e2e/tests/scripts.rs @@ -2,13 +2,14 @@ use std::time::Duration; use fuel_tx::Output; use fuels::{ + client::{PageDirection, PaginationRequest}, core::{ codec::{DecoderConfig, EncoderConfig}, traits::Tokenizable, Configurables, }, prelude::*, - programs::executable::Executable, + programs::{executable::Executable, DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE}, types::{Bits256, Identity}, }; @@ -489,6 +490,72 @@ async fn can_be_run_in_blobs_high_level() -> Result<()> { Ok(()) } +#[tokio::test] +async fn high_level_blob_upload_sets_max_fee_tolerance() -> Result<()> { + let node_config = NodeConfig { + starting_gas_price: 1000000000, + ..Default::default() + }; + let mut wallet = WalletUnlocked::new_random(None); + let coins = setup_single_asset_coins(wallet.address(), AssetId::zeroed(), 1, u64::MAX); + let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?; + wallet.set_provider(provider.clone()); + + setup_program_test!( + Abigen(Script( + project = "e2e/sway/scripts/script_blobs", + name = "MyScript" + )), + LoadScript(name = "my_script", script = "MyScript", wallet = "wallet") + ); + + let loader = Executable::from_bytes(std::fs::read( + "sway/scripts/script_blobs/out/release/script_blobs.bin", + )?) + .convert_to_loader()?; + + let zero_tolerance_fee = { + let mut tb = BlobTransactionBuilder::default() + .with_blob(loader.blob()) + .with_max_fee_estimation_tolerance(0.); + + wallet.adjust_for_fee(&mut tb, 0).await?; + + wallet.add_witnesses(&mut tb)?; + let tx = tb.build(&provider).await?; + tx.max_fee().unwrap() + }; + + let mut my_script = my_script; + my_script.convert_into_loader().await?; + + let max_fee_of_sent_blob_tx = provider + .get_transactions(PaginationRequest { + cursor: None, + results: 100, + direction: PageDirection::Forward, + }) + .await? + .results + .into_iter() + .find_map(|tx| { + if let TransactionType::Blob(blob_transaction) = tx.transaction { + blob_transaction.max_fee() + } else { + None + } + }) + .unwrap(); + + assert_eq!( + max_fee_of_sent_blob_tx, + (zero_tolerance_fee as f32 * (1.0 + DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE)).ceil() as u64, + "the blob upload tx should have had the max fee increased by the default estimation tolerance" + ); + + Ok(()) +} + #[tokio::test] async fn no_data_section_blob_run() -> Result<()> { setup_program_test!( diff --git a/packages/fuels-accounts/src/provider.rs b/packages/fuels-accounts/src/provider.rs index 31f053bba..13668ed80 100644 --- a/packages/fuels-accounts/src/provider.rs +++ b/packages/fuels-accounts/src/provider.rs @@ -758,7 +758,7 @@ impl Provider { let receipts = self.dry_run_opt(tx, false, None).await?.take_receipts(); let gas_used = self.get_script_gas_used(&receipts); - Ok((gas_used as f64 * (1.0 + tolerance)) as u64) + Ok((gas_used as f64 * (1.0 + tolerance)).ceil() as u64) } fn get_script_gas_used(&self, receipts: &[Receipt]) -> u64 { diff --git a/packages/fuels-core/src/types/dry_runner.rs b/packages/fuels-core/src/types/dry_runner.rs index 7f0cbb06e..7fd59ad26 100644 --- a/packages/fuels-core/src/types/dry_runner.rs +++ b/packages/fuels-core/src/types/dry_runner.rs @@ -16,7 +16,7 @@ impl DryRun { pub fn gas_with_tolerance(&self, tolerance: f32) -> u64 { let gas_used = self.script_gas as f64; let adjusted_gas = gas_used * (1.0 + f64::from(tolerance)); - adjusted_gas as u64 + adjusted_gas.ceil() as u64 } } diff --git a/packages/fuels-core/src/types/transaction_builders.rs b/packages/fuels-core/src/types/transaction_builders.rs index d1ce3ca2a..a9992a5c9 100644 --- a/packages/fuels-core/src/types/transaction_builders.rs +++ b/packages/fuels-core/src/types/transaction_builders.rs @@ -456,7 +456,7 @@ pub(crate) fn estimate_max_fee_w_tolerance( let max_fee_w_tolerance = tx_fee.max_fee() as f64 * (1.0 + f64::from(tolerance)); - Ok(max_fee_w_tolerance as u64) + Ok(max_fee_w_tolerance.ceil() as u64) } impl Debug for dyn Signer + Send + Sync { diff --git a/packages/fuels-programs/src/calls/traits/transaction_tuner.rs b/packages/fuels-programs/src/calls/traits/transaction_tuner.rs index 9c699e3c7..92efa07e9 100644 --- a/packages/fuels-programs/src/calls/traits/transaction_tuner.rs +++ b/packages/fuels-programs/src/calls/traits/transaction_tuner.rs @@ -7,9 +7,12 @@ use fuels_core::types::{ }, }; -use crate::calls::{ - utils::{build_tx_from_contract_calls, sealed, transaction_builder_from_contract_calls}, - ContractCall, ScriptCall, +use crate::{ + calls::{ + utils::{build_tx_from_contract_calls, sealed, transaction_builder_from_contract_calls}, + ContractCall, ScriptCall, + }, + DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE, }; #[async_trait::async_trait] @@ -79,8 +82,8 @@ impl TransactionTuner for ScriptCall { .with_script_data(self.compute_script_data()?) .with_inputs(inputs) .with_outputs(outputs) - .with_gas_estimation_tolerance(0.05) - .with_max_fee_estimation_tolerance(0.05)) + .with_gas_estimation_tolerance(DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE) + .with_max_fee_estimation_tolerance(DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE)) } async fn build_tx( diff --git a/packages/fuels-programs/src/calls/utils.rs b/packages/fuels-programs/src/calls/utils.rs index 99cd6242a..552712e0d 100644 --- a/packages/fuels-programs/src/calls/utils.rs +++ b/packages/fuels-programs/src/calls/utils.rs @@ -22,6 +22,7 @@ use itertools::{chain, Itertools}; use crate::{ assembly::contract_call::{CallOpcodeParamsOffset, ContractCallInstructions}, calls::ContractCall, + DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE, }; pub(crate) mod sealed { @@ -73,8 +74,8 @@ pub(crate) async fn transaction_builder_from_contract_calls( .with_script_data(script_data.clone()) .with_inputs(inputs) .with_outputs(outputs) - .with_gas_estimation_tolerance(0.05) - .with_max_fee_estimation_tolerance(0.05)) + .with_gas_estimation_tolerance(DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE) + .with_max_fee_estimation_tolerance(DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE)) } /// Creates a [`ScriptTransaction`] from contract calls. The internal [Transaction] is diff --git a/packages/fuels-programs/src/contract/loader.rs b/packages/fuels-programs/src/contract/loader.rs index fdf01deef..963c81741 100644 --- a/packages/fuels-programs/src/contract/loader.rs +++ b/packages/fuels-programs/src/contract/loader.rs @@ -12,7 +12,7 @@ use fuels_core::{ }, }; -use crate::assembly::contract_call::loader_contract_asm; +use crate::{assembly::contract_call::loader_contract_asm, DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE}; use super::{compute_contract_id_and_state_root, Contract, Regular}; @@ -138,7 +138,7 @@ impl Contract> { let mut tb = BlobTransactionBuilder::default() .with_blob(blob) .with_tx_policies(tx_policies) - .with_max_fee_estimation_tolerance(0.05); + .with_max_fee_estimation_tolerance(DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE); account.adjust_for_fee(&mut tb, 0).await?; account.add_witnesses(&mut tb)?; diff --git a/packages/fuels-programs/src/contract/regular.rs b/packages/fuels-programs/src/contract/regular.rs index 8c54a410b..7ba204578 100644 --- a/packages/fuels-programs/src/contract/regular.rs +++ b/packages/fuels-programs/src/contract/regular.rs @@ -14,6 +14,8 @@ use fuels_core::{ Configurables, }; +use crate::DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE; + use super::{ compute_contract_id_and_state_root, validate_path_and_extension, BlobsNotUploaded, Contract, Loader, StorageConfiguration, @@ -152,7 +154,7 @@ impl Contract { storage_slots.to_vec(), tx_policies, ) - .with_max_fee_estimation_tolerance(0.05); + .with_max_fee_estimation_tolerance(DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE); account.add_witnesses(&mut tb)?; account.adjust_for_fee(&mut tb, 0).await?; diff --git a/packages/fuels-programs/src/executable.rs b/packages/fuels-programs/src/executable.rs index 470399752..28d6d5a77 100644 --- a/packages/fuels-programs/src/executable.rs +++ b/packages/fuels-programs/src/executable.rs @@ -6,7 +6,10 @@ use fuels_core::{ Configurables, }; -use crate::assembly::script_and_predicate_loader::{extract_data_offset, LoaderCode}; +use crate::{ + assembly::script_and_predicate_loader::{extract_data_offset, LoaderCode}, + DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE, +}; /// This struct represents a standard executable with its associated bytecode and configurables. #[derive(Debug, Clone, PartialEq)] @@ -151,7 +154,9 @@ impl Executable { return Ok(()); } - let mut tb = BlobTransactionBuilder::default().with_blob(self.blob()); + let mut tb = BlobTransactionBuilder::default() + .with_blob(self.blob()) + .with_max_fee_estimation_tolerance(DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE); account.adjust_for_fee(&mut tb, 0).await?; diff --git a/packages/fuels-programs/src/lib.rs b/packages/fuels-programs/src/lib.rs index 77ef51275..1bafa66e5 100644 --- a/packages/fuels-programs/src/lib.rs +++ b/packages/fuels-programs/src/lib.rs @@ -7,6 +7,8 @@ pub mod executable; #[cfg(feature = "std")] pub mod responses; +pub const DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE: f32 = 0.50; + pub mod debug; pub(crate) mod assembly;