mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-01-29 17:18:08 -05:00
fix(simulate_v1): fill transactions sequentually (#13532)
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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`].
|
||||
|
||||
Reference in New Issue
Block a user