Skip to content

Commit 1e41a4c

Browse files
fix(rpc): temporary fix for out-of-gas estimation (#2771)
* temporary fix for out-of-gas error during estimation * change revert with zero to initial_gas_limit * address comments * types --------- Signed-off-by: MaksymMalicki <[email protected]>
1 parent 1f92dae commit 1e41a4c

File tree

1 file changed

+115
-54
lines changed

1 file changed

+115
-54
lines changed

vm/rust/src/execution.rs

Lines changed: 115 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use crate::error::ExecutionError;
22
use crate::juno_state_reader::JunoStateReader;
33
use blockifier::execution::contract_class::TrackedResource;
44
use blockifier::state::state_api::{StateReader, StateResult, UpdatableState};
5+
use blockifier::transaction::account_transaction::ExecutionFlags;
6+
use blockifier::transaction::objects::HasRelatedFeeType;
57
use blockifier::transaction::transaction_execution::Transaction;
68
use blockifier::transaction::transactions::ExecutableTransaction;
79
use blockifier::{
@@ -12,8 +14,14 @@ use blockifier::{
1214
use starknet_api::core::ClassHash;
1315
use starknet_api::executable_transaction::AccountTransaction;
1416
use starknet_api::execution_resources::GasAmount;
15-
use starknet_api::transaction::fields::{GasVectorComputationMode, ValidResourceBounds};
16-
use starknet_api::transaction::{DeclareTransaction, DeployAccountTransaction, InvokeTransaction};
17+
use starknet_api::transaction::fields::{
18+
AllResourceBounds, GasVectorComputationMode, ValidResourceBounds,
19+
};
20+
use starknet_api::transaction::{
21+
DeclareTransaction, DeclareTransactionV3, DeployAccountTransaction, DeployAccountTransactionV3,
22+
InvokeTransaction, InvokeTransactionV3,
23+
};
24+
use anyhow::{Error, anyhow};
1725

1826
pub fn process_transaction(
1927
txn: &mut Transaction,
@@ -140,16 +148,12 @@ fn execute_transaction_with_binary_search<S>(
140148
where
141149
S: UpdatableState,
142150
{
143-
let initial_gas_limit = extract_l2_gas_limit(transaction)?;
144-
let mut original_transaction = transaction.clone();
151+
let initial_resource_bounds = extract_resource_bounds(transaction)?;
152+
let initial_gas_limit = initial_resource_bounds.l2_gas.max_amount;
153+
let max_l2_gas_limit = calculate_max_l2_gas_covered(transaction, block_context, state)?;
145154

146155
// Simulate transaction execution with maximum possible gas to get actual gas usage.
147-
set_l2_gas_limit(transaction, GasAmount::MAX)?;
148-
// TODO: Consider getting the upper bound from the balance and not changing the execution flags
149-
if let Transaction::Account(account_transaction) = transaction {
150-
account_transaction.execution_flags.charge_fee = false;
151-
account_transaction.execution_flags.validate = false;
152-
}
156+
set_l2_gas_limit(transaction, max_l2_gas_limit)?;
153157

154158
let simulation_result =
155159
match simulate_execution(transaction, state, block_context, error_on_revert) {
@@ -212,31 +216,35 @@ where
212216
Err(SimulationError::ExecutionError(error)) => return Err(error),
213217
}
214218
};
215-
216-
(current_l2_gas_limit, tx_info, tx_state)
217-
}
218-
Err(SimulationError::ExecutionError(error)) => return Err(error),
219-
};
220-
tx_state.abort();
219+
(current_l2_gas_limit, tx_info, tx_state)
220+
}
221+
Err(SimulationError::ExecutionError(error)) => return Err(error),
222+
};
221223

222224
// If the computed gas limit exceeds the initial limit, revert the transaction.
223225
// The L2 gas limit is set to zero to prevent the transaction execution from succeeding
224226
// in the case where the user defined gas limit is less than the required gas limit
225-
if l2_gas_limit > initial_gas_limit {
226-
set_l2_gas_limit(&mut original_transaction, GasAmount(0))?;
227-
return execute_transaction(&original_transaction, state, block_context, error_on_revert);
227+
if get_execution_flags(transaction).charge_fee && l2_gas_limit > initial_gas_limit {
228+
tx_state.abort();
229+
set_l2_gas_limit(transaction, initial_gas_limit)?;
230+
return execute_transaction(&transaction, state, block_context, error_on_revert);
228231
}
229232

230-
set_l2_gas_limit(&mut original_transaction, initial_gas_limit)?;
231-
let mut exec_info =
232-
execute_transaction(&original_transaction, state, block_context, error_on_revert)?;
233+
let mut exec_info = execute_transaction(&transaction, state, block_context, error_on_revert)?;
233234

234235
// Execute the transaction with the determined gas limit and update the estimate.
235236
exec_info.receipt.gas.l2_gas = l2_gas_limit;
236237

237238
Ok(exec_info)
238239
}
239240

241+
fn get_execution_flags(tx: &Transaction) -> ExecutionFlags {
242+
match tx {
243+
Transaction::Account(account_transaction) => account_transaction.execution_flags.clone(),
244+
Transaction::L1Handler(_) => Default::default(),
245+
}
246+
}
247+
240248
fn calculate_midpoint(a: GasAmount, b: GasAmount) -> GasAmount {
241249
let GasAmount(a) = a;
242250
let GasAmount(b) = b;
@@ -286,7 +294,7 @@ where
286294
fn set_l2_gas_limit(
287295
transaction: &mut Transaction,
288296
gas_limit: GasAmount,
289-
) -> Result<(), anyhow::Error> {
297+
) -> Result<(), Error> {
290298
if let Transaction::Account(ref mut account_transaction) = transaction {
291299
match &mut account_transaction.tx {
292300
AccountTransaction::Declare(ref mut tx) => {
@@ -321,42 +329,95 @@ fn set_l2_gas_limit(
321329
}
322330
}
323331
}
324-
Err(anyhow::anyhow!("Failed to set L2 gas limit"))
332+
Err(anyhow!("Failed to set L2 gas limit"))
325333
}
326334

327-
fn extract_l2_gas_limit(transaction: &Transaction) -> Result<GasAmount, anyhow::Error> {
328-
if let Transaction::Account(account_transaction) = transaction {
329-
match &account_transaction.tx {
330-
AccountTransaction::Declare(tx) => {
331-
if let DeclareTransaction::V3(tx) = &tx.tx {
332-
if let ValidResourceBounds::AllResources(all_resource_bounds) =
333-
&tx.resource_bounds
334-
{
335-
return Ok(all_resource_bounds.l2_gas.max_amount);
336-
}
337-
}
338-
}
339-
AccountTransaction::DeployAccount(tx) => {
340-
if let DeployAccountTransaction::V3(tx) = &tx.tx {
341-
if let ValidResourceBounds::AllResources(all_resource_bounds) =
342-
&tx.resource_bounds
343-
{
344-
return Ok(all_resource_bounds.l2_gas.max_amount);
345-
}
346-
}
347-
}
348-
AccountTransaction::Invoke(tx) => {
349-
if let InvokeTransaction::V3(tx) = &tx.tx {
350-
if let ValidResourceBounds::AllResources(all_resource_bounds) =
351-
&tx.resource_bounds
352-
{
353-
return Ok(all_resource_bounds.l2_gas.max_amount);
354-
}
355-
}
335+
/// Retrieves the resource bounds for a given transaction.
336+
fn extract_resource_bounds(tx: &Transaction) -> Result<AllResourceBounds, Error> {
337+
match tx {
338+
Transaction::Account(account_tx) => match &account_tx.tx {
339+
AccountTransaction::Declare(declare_tx) => match &declare_tx.tx {
340+
DeclareTransaction::V3(DeclareTransactionV3 {
341+
resource_bounds: ValidResourceBounds::AllResources(all_resources),
342+
..
343+
}) => Ok(*all_resources),
344+
_ => Err(anyhow!("Unsupported Declare transaction version")),
345+
},
346+
AccountTransaction::DeployAccount(deploy_tx) => match &deploy_tx.tx {
347+
DeployAccountTransaction::V3(DeployAccountTransactionV3 {
348+
resource_bounds: ValidResourceBounds::AllResources(all_resources),
349+
..
350+
}) => Ok(*all_resources),
351+
_ => Err(anyhow!(
352+
"Unsupported DeployAccount transaction version"
353+
)),
354+
},
355+
AccountTransaction::Invoke(invoke_tx) => match &invoke_tx.tx {
356+
InvokeTransaction::V3(InvokeTransactionV3 {
357+
resource_bounds: ValidResourceBounds::AllResources(all_resources),
358+
..
359+
}) => Ok(*all_resources),
360+
_ => Err(anyhow!("Unsupported Invoke transaction version")),
361+
},
362+
},
363+
_ => Err(anyhow!("Unsupported transaction type")),
364+
}
365+
}
366+
367+
/// Calculates the maximum amount of L2 gas that can be covered by the balance.
368+
fn calculate_max_l2_gas_covered<S>(
369+
tx: &Transaction,
370+
block_context: &blockifier::context::BlockContext,
371+
state: &mut S,
372+
) -> Result<GasAmount, Error>
373+
where
374+
S: UpdatableState,
375+
{
376+
// Extract initial resource bounds from the transaction.
377+
let initial_resource_bounds = extract_resource_bounds(tx)?;
378+
379+
// Create resource bounds without L2 gas.
380+
let resource_bounds_without_l2_gas = AllResourceBounds {
381+
l2_gas: Default::default(),
382+
..initial_resource_bounds
383+
};
384+
385+
// Calculate the maximum possible fee without L2 gas.
386+
let max_fee_without_l2_gas =
387+
ValidResourceBounds::AllResources(resource_bounds_without_l2_gas).max_possible_fee();
388+
389+
match tx {
390+
Transaction::Account(account_transaction) => {
391+
// Retrieve the fee token address.
392+
let fee_token_address = block_context
393+
.chain_info()
394+
.fee_token_address(&account_transaction.fee_type());
395+
396+
// Get the balance of the fee token.
397+
let balance = state
398+
.get_fee_token_balance(account_transaction.sender_address(), fee_token_address)?;
399+
let balance = (balance.1.to_biguint() << 128) + balance.0.to_biguint();
400+
401+
if balance > max_fee_without_l2_gas.0.into() {
402+
// Calculate the maximum amount of L2 gas that can be bought with the balance.
403+
let max_l2_gas = (balance - max_fee_without_l2_gas.0)
404+
/ initial_resource_bounds
405+
.l2_gas
406+
.max_price_per_unit
407+
.0
408+
.max(1u64.into());
409+
Ok(u64::try_from(max_l2_gas).unwrap_or(GasAmount::MAX.0).into())
410+
} else {
411+
// Balance is less than committed L1 gas and L1 data gas, transaction will fail.
412+
// Let it pass through here so that execution returns a detailed error.
413+
Ok(GasAmount::ZERO)
356414
}
357415
}
416+
Transaction::L1Handler(_) => {
417+
// L1 handler transactions don't have L2 gas.
418+
Err(anyhow!("L1 handler transactions don't have L2 gas"))
419+
}
358420
}
359-
Err(anyhow::anyhow!("Failed to extract L2 gas limit"))
360421
}
361422

362423
fn is_out_of_gas(execution_info: &TransactionExecutionInfo) -> bool {

0 commit comments

Comments
 (0)