fix(simulate_v1): fill transactions sequentually (#13532)

This commit is contained in:
Arsenii Kulikov
2024-12-24 02:42:27 +04:00
committed by GitHub
parent 02ad280de6
commit 6822d4f18b
2 changed files with 85 additions and 84 deletions

View File

@@ -136,7 +136,7 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA
block_env.basefee = U256::ZERO;
}
let SimBlock { block_overrides, state_overrides, mut calls } = block;
let SimBlock { block_overrides, state_overrides, calls } = block;
if let Some(block_overrides) = block_overrides {
apply_block_overrides(block_overrides, &mut db, &mut block_env);
@@ -151,26 +151,51 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA
)
}
// Resolve transactions, populate missing fields and enforce calls correctness.
let transactions = simulate::resolve_transactions(
&mut calls,
validation,
block_env.gas_limit.to(),
cfg_env_with_handler_cfg.chain_id,
&mut db,
this.tx_resp_builder(),
)?;
let default_gas_limit = {
let total_specified_gas = calls.iter().filter_map(|tx| tx.gas).sum::<u64>();
let txs_without_gas_limit =
calls.iter().filter(|tx| tx.gas.is_none()).count();
if total_specified_gas > block_env.gas_limit.to() {
return Err(EthApiError::Other(Box::new(
EthSimulateError::BlockGasLimitExceeded,
))
.into())
}
if txs_without_gas_limit > 0 {
(block_env.gas_limit.to::<u64>() - total_specified_gas) /
txs_without_gas_limit as u64
} else {
0
}
};
let mut calls = calls.into_iter().peekable();
let mut senders = Vec::with_capacity(transactions.len());
let mut transactions = Vec::with_capacity(calls.len());
let mut senders = Vec::with_capacity(calls.len());
let mut results = Vec::with_capacity(calls.len());
while let Some(tx) = calls.next() {
let env = this.build_call_evm_env(
while let Some(call) = calls.next() {
let sender = call.from.unwrap_or_default();
// Resolve transaction, populate missing fields and enforce calls
// correctness.
let tx = simulate::resolve_transaction(
call,
validation,
default_gas_limit,
cfg_env_with_handler_cfg.chain_id,
&mut db,
this.tx_resp_builder(),
)?;
let tx_env = this.evm_config().tx_env(&tx, sender);
let env = EnvWithHandlerCfg::new_with_cfg_env(
cfg_env_with_handler_cfg.clone(),
block_env.clone(),
tx,
)?;
tx_env,
);
let (res, env) = {
if trace_transfers {
@@ -190,6 +215,7 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA
db.commit(res.state);
}
transactions.push(tx);
senders.push(env.tx.caller);
results.push(res.result);
}

View File

@@ -50,87 +50,62 @@ impl ToRpcError for EthSimulateError {
///
/// If validation is enabled, the function will return error if any of the transactions can't be
/// built right away.
pub fn resolve_transactions<DB: Database, Tx, T: TransactionCompat<Tx>>(
txs: &mut [TransactionRequest],
pub fn resolve_transaction<DB: Database, Tx, T: TransactionCompat<Tx>>(
mut tx: TransactionRequest,
validation: bool,
block_gas_limit: u64,
default_gas_limit: u64,
chain_id: u64,
db: &mut DB,
tx_resp_builder: &T,
) -> Result<Vec<Tx>, EthApiError>
) -> Result<Tx, EthApiError>
where
EthApiError: From<DB::Error>,
{
let mut transactions = Vec::with_capacity(txs.len());
if tx.buildable_type().is_none() && validation {
return Err(EthApiError::TransactionConversionError);
}
// If we're missing any fields and validation is disabled, we try filling nonce, gas and
// gas price.
let tx_type = tx.preferred_type();
let default_gas_limit = {
let total_specified_gas = txs.iter().filter_map(|tx| tx.gas).sum::<u64>();
let txs_without_gas_limit = txs.iter().filter(|tx| tx.gas.is_none()).count();
if total_specified_gas > block_gas_limit {
return Err(EthApiError::Other(Box::new(EthSimulateError::BlockGasLimitExceeded)))
}
if txs_without_gas_limit > 0 {
(block_gas_limit - total_specified_gas) / txs_without_gas_limit as u64
} else {
0
}
let from = if let Some(from) = tx.from {
from
} else {
tx.from = Some(Address::ZERO);
Address::ZERO
};
for tx in txs {
if tx.buildable_type().is_none() && validation {
return Err(EthApiError::TransactionConversionError);
}
// If we're missing any fields and validation is disabled, we try filling nonce, gas and
// gas price.
let tx_type = tx.preferred_type();
let from = if let Some(from) = tx.from {
from
} else {
tx.from = Some(Address::ZERO);
Address::ZERO
};
if tx.nonce.is_none() {
tx.nonce = Some(db.basic(from)?.map(|acc| acc.nonce).unwrap_or_default());
}
if tx.gas.is_none() {
tx.gas = Some(default_gas_limit);
}
if tx.chain_id.is_none() {
tx.chain_id = Some(chain_id);
}
if tx.to.is_none() {
tx.to = Some(TxKind::Create);
}
match tx_type {
TxType::Legacy | TxType::Eip2930 => {
if tx.gas_price.is_none() {
tx.gas_price = Some(0);
}
}
_ => {
if tx.max_fee_per_gas.is_none() {
tx.max_fee_per_gas = Some(0);
tx.max_priority_fee_per_gas = Some(0);
}
}
}
transactions.push(
tx_resp_builder
.build_simulate_v1_transaction(tx.clone())
.map_err(|e| EthApiError::other(e.into()))?,
);
if tx.nonce.is_none() {
tx.nonce = Some(db.basic(from)?.map(|acc| acc.nonce).unwrap_or_default());
}
Ok(transactions)
if tx.gas.is_none() {
tx.gas = Some(default_gas_limit);
}
if tx.chain_id.is_none() {
tx.chain_id = Some(chain_id);
}
if tx.to.is_none() {
tx.to = Some(TxKind::Create);
}
match tx_type {
TxType::Legacy | TxType::Eip2930 => {
if tx.gas_price.is_none() {
tx.gas_price = Some(0);
}
}
_ => {
if tx.max_fee_per_gas.is_none() {
tx.max_fee_per_gas = Some(0);
tx.max_priority_fee_per_gas = Some(0);
}
}
}
tx_resp_builder.build_simulate_v1_transaction(tx).map_err(|e| EthApiError::other(e.into()))
}
/// Handles outputs of the calls execution and builds a [`SimulatedBlock`].