Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(simulate_v1): fill transactions sequentually #13532

Merged
merged 1 commit into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 41 additions & 15 deletions crates/rpc/rpc-eth-api/src/helpers/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,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);
Expand All @@ -150,26 +150,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 {
Expand All @@ -189,6 +214,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);
}
Expand Down
101 changes: 38 additions & 63 deletions crates/rpc/rpc-eth-types/src/simulate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());

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
}
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
};

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.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.gas.is_none() {
tx.gas = Some(default_gas_limit);
}

if tx.chain_id.is_none() {
tx.chain_id = Some(chain_id);
}
if tx.chain_id.is_none() {
tx.chain_id = Some(chain_id);
}

if tx.to.is_none() {
tx.to = Some(TxKind::Create);
}
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);
}
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);
}
}
_ => {
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()))?,
);
}

Ok(transactions)
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`].
Expand Down
Loading