Skip to content

Commit ea840af

Browse files
prestwichEvalir
andauthoredFeb 20, 2025
fix: gas estimation for simple transfers (#78)
* fix: gas estimation for simple transfers * fix: concurrent state link * Update evm.rs Co-authored-by: evalir <e@evalir.xyz> * Update evm.rs Co-authored-by: evalir <e@evalir.xyz> * fix: calu --------- Co-authored-by: evalir <e@evalir.xyz>
1 parent 19d8bca commit ea840af

File tree

4 files changed

+106
-17
lines changed

4 files changed

+106
-17
lines changed
 

‎Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "trevm"
3-
version = "0.19.3"
3+
version = "0.19.5"
44
rust-version = "1.83.0"
55
edition = "2021"
66
authors = ["init4"]
@@ -32,7 +32,7 @@ alloy-sol-types = { version = "0.8.11", default-features = false, features = ["s
3232

3333
alloy = { version = "=0.9.2", default-features = false, features = ["consensus", "rpc-types-mev", "eips", "k256", "std"] }
3434

35-
revm = { version = "19.2.0", default-features = false, features = ["std"] }
35+
revm = { version = "19.5.0", default-features = false, features = ["std"] }
3636

3737
zenith-types = { version = "0.14" }
3838

‎src/db/mod.rs

+36-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod builder;
22

3+
use alloy::rpc::types::BlockOverrides;
34
pub use builder::ConcurrentStateBuilder;
45

56
mod cache_state;
@@ -8,7 +9,7 @@ pub use cache_state::ConcurrentCacheState;
89
mod sync_state;
910
pub use sync_state::{ConcurrentState, ConcurrentStateInfo};
1011

11-
use crate::{EvmNeedsBlock, Trevm};
12+
use crate::{Block, EvmNeedsBlock, EvmNeedsTx, Trevm};
1213
use revm::{
1314
db::{states::bundle_state::BundleRetention, BundleState},
1415
DatabaseRef,
@@ -41,3 +42,37 @@ impl<Ext, Db: DatabaseRef + Sync> EvmNeedsBlock<'_, Ext, ConcurrentState<Db>> {
4142
bundle
4243
}
4344
}
45+
46+
impl<Ext, Db: DatabaseRef + Sync> EvmNeedsTx<'_, Ext, ConcurrentState<Db>> {
47+
/// Apply block overrides to the current block.
48+
///
49+
/// Note that this is NOT reversible. The overrides are applied directly to
50+
/// the underlying state and these changes cannot be removed. If it is
51+
/// important that you have access to the pre-change state, you should wrap
52+
/// the existing DB in a new [`ConcurrentState`] and apply the overrides to
53+
/// that.
54+
pub fn apply_block_overrides(mut self, overrides: &BlockOverrides) -> Self {
55+
overrides.fill_block(&mut self.inner);
56+
57+
if let Some(hashes) = &overrides.block_hash {
58+
self.inner.db_mut().info.block_hashes.write().unwrap().extend(hashes)
59+
}
60+
61+
self
62+
}
63+
64+
/// Apply block overrides to the current block, if they are provided.
65+
///
66+
/// Note that this is NOT reversible. The overrides are applied directly to
67+
/// the underlying state and these changes cannot be removed. If it is
68+
/// important that you have access to the pre-change state, you should wrap
69+
/// the existing DB in a new [`ConcurrentState`] and apply the overrides to
70+
/// that.
71+
pub fn maybe_apply_block_overrides(self, overrides: Option<&BlockOverrides>) -> Self {
72+
if let Some(overrides) = overrides {
73+
self.apply_block_overrides(overrides)
74+
} else {
75+
self
76+
}
77+
}
78+
}

‎src/est.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use crate::MIN_TRANSACTION_GAS;
21
use revm::primitives::{Bytes, ExecutionResult, HaltReason, Output};
32
use std::ops::Range;
43

@@ -132,11 +131,11 @@ impl From<&ExecutionResult> for EstimationResult {
132131

133132
impl EstimationResult {
134133
/// Create a successful estimation result with a gas estimation of 21000.
135-
pub const fn basic_transfer_success() -> Self {
134+
pub const fn basic_transfer_success(estimation: u64) -> Self {
136135
Self::Success {
137-
estimation: MIN_TRANSACTION_GAS,
136+
estimation,
138137
refund: 0,
139-
gas_used: MIN_TRANSACTION_GAS,
138+
gas_used: estimation,
140139
output: Output::Call(Bytes::new()),
141140
}
142141
}

‎src/evm.rs

+65-10
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ use alloy::{
1414
use core::convert::Infallible;
1515
use revm::{
1616
db::{states::bundle_state::BundleRetention, BundleState, State},
17-
interpreter::gas::CALL_STIPEND,
17+
interpreter::gas::{calculate_initial_tx_gas, CALL_STIPEND},
1818
primitives::{
19-
AccountInfo, BlockEnv, Bytecode, EVMError, EvmState, ExecutionResult, InvalidTransaction,
20-
ResultAndState, SpecId, TxEnv, TxKind, KECCAK_EMPTY,
19+
AccountInfo, AuthorizationList, BlockEnv, Bytecode, EVMError, Env, EvmState,
20+
ExecutionResult, InvalidTransaction, ResultAndState, SpecId, TxEnv, TxKind, KECCAK_EMPTY,
2121
},
2222
Database, DatabaseCommit, DatabaseRef, Evm,
2323
};
@@ -71,6 +71,30 @@ impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState> Trevm<'a, Ext, Db, Trev
7171
self.inner
7272
}
7373

74+
/// Get a reference to the inner env. This contains the current
75+
/// [`BlockEnv`], [`TxEnv`], and [`CfgEnv`].
76+
///
77+
/// These values may be meaningless, stale, or otherwise incorrect. Reading
78+
/// them should be done with caution, as it may lead to logic bugs.
79+
///
80+
/// [`CfgEnv`]: revm::primitives::CfgEnv
81+
pub fn env_unchecked(&self) -> &Env {
82+
&self.inner().context.evm.inner.env
83+
}
84+
85+
/// Get a mutable reference to the inner env. This contains the current
86+
/// [`BlockEnv`], [`TxEnv`], and [`CfgEnv`].
87+
///
88+
/// These values may be meaningless, stale, or otherwise incorrect. Reading
89+
/// them should be done with caution, as it may lead to logic bugs.
90+
/// Modifying these values may lead to inconsistent state or invalid
91+
/// execution.
92+
///
93+
/// [`CfgEnv`]: revm::primitives::CfgEnv
94+
pub fn env_mut_unchecked(&mut self) -> &mut Env {
95+
&mut self.inner_mut_unchecked().context.evm.inner.env
96+
}
97+
7498
/// Get the id of the currently running hardfork spec. Convenience function
7599
/// calling [`Evm::spec_id`].
76100
pub fn spec_id(&self) -> SpecId {
@@ -1176,7 +1200,7 @@ impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState: HasTx> Trevm<'a, Ext, D
11761200
}
11771201
}
11781202

1179-
// -- HAS TX with State<Db>
1203+
// -- NEEDS TX with State<Db>
11801204

11811205
impl<Ext, Db: Database> EvmNeedsTx<'_, Ext, State<Db>> {
11821206
/// Apply block overrides to the current block.
@@ -1236,12 +1260,36 @@ impl<'a, Ext, Db: Database + DatabaseCommit> EvmReady<'a, Ext, Db> {
12361260
}
12371261
}
12381262

1263+
/// Calculate the minimum gas required to start EVM execution.
1264+
///
1265+
/// This uses [`calculate_initial_tx_gas`] to calculate the initial gas.
1266+
/// Its output is dependent on
1267+
/// - the EVM spec
1268+
/// - the input data
1269+
/// - whether the transaction is a contract creation or a call
1270+
/// - the EIP-2930 access list
1271+
/// - the number of [EIP-7702] authorizations
1272+
///
1273+
/// [EIP-2930]: https://eips.ethereum.org/EIPS/eip-2930
1274+
/// [EIP-7702]: https://eips.ethereum.org/EIPS/eip-7702
1275+
fn calculate_initial_gas(&self) -> u64 {
1276+
calculate_initial_tx_gas(
1277+
self.spec_id(),
1278+
&[],
1279+
false,
1280+
&self.tx().access_list,
1281+
self.tx().authorization_list.as_ref().map(AuthorizationList::len).unwrap_or_default()
1282+
as u64,
1283+
)
1284+
.initial_gas
1285+
}
1286+
12391287
/// Estimate gas for a simple transfer. This will
12401288
/// - Check that the transaction has no input data.
12411289
/// - Check that the target is not a `create`.
12421290
/// - Check that the target is not a contract.
12431291
/// - Return the minimum gas required for the transfer.
1244-
fn estimate_gas_simple_transfer(&mut self) -> Result<Option<()>, EVMError<Db::Error>> {
1292+
fn estimate_gas_simple_transfer(&mut self) -> Result<Option<u64>, EVMError<Db::Error>> {
12451293
if !self.is_transfer() {
12461294
return Ok(None);
12471295
}
@@ -1254,8 +1302,9 @@ impl<'a, Ext, Db: Database + DatabaseCommit> EvmReady<'a, Ext, Db> {
12541302
return Ok(None);
12551303
}
12561304

1257-
// If the target is not a contract, then the gas is the minimum gas.
1258-
Ok(Some(()))
1305+
// delegate calculation to revm. This ensures that things like bogus
1306+
// 2930 access lists don't mess up our estimates
1307+
Ok(Some(self.calculate_initial_gas()))
12591308
}
12601309

12611310
/// Convenience function to simplify nesting of [`Self::estimate_gas`].
@@ -1330,22 +1379,28 @@ impl<'a, Ext, Db: Database + DatabaseCommit> EvmReady<'a, Ext, Db> {
13301379
///
13311380
/// [here]: https://github.com/paradigmxyz/reth/blob/ad503a08fa242b28ad3c1fea9caa83df2dfcf72d/crates/rpc/rpc-eth-api/src/helpers/estimate.rs#L35-L42
13321381
pub fn estimate_gas(mut self) -> Result<(EstimationResult, Self), EvmErrored<'a, Ext, Db>> {
1333-
if unwrap_or_trevm_err!(self.estimate_gas_simple_transfer(), self).is_some() {
1334-
return Ok((EstimationResult::basic_transfer_success(), self));
1382+
if let Some(est) = unwrap_or_trevm_err!(self.estimate_gas_simple_transfer(), self) {
1383+
return Ok((EstimationResult::basic_transfer_success(est), self));
13351384
}
13361385

13371386
// We shrink the gas limit to 64 bits, as using more than 18 quintillion
1338-
// gas in a block is not likely.
1387+
// gas in a block is unlikely.
13391388
let initial_limit = self.gas_limit();
13401389

1390+
// Start the search range at 21_000 gas.
13411391
let mut search_range = SearchRange::new(MIN_TRANSACTION_GAS, initial_limit);
1392+
1393+
// Block it to the gas cap.
13421394
search_range.maybe_lower_max(self.block_gas_limit().saturating_to::<u64>());
13431395

13441396
// Check that the account has enough ETH to cover the gas, and lower if
13451397
// necessary.
13461398
let allowance = unwrap_or_trevm_err!(self.gas_allowance(), self);
13471399
search_range.maybe_lower_max(allowance);
13481400

1401+
// Raise the floor to the amount of gas required to initialize the EVM.
1402+
search_range.maybe_raise_min(self.calculate_initial_gas());
1403+
13491404
// Run an estimate with the max gas limit.
13501405
// NB: we declare these mut as we re-use the binding throughout the
13511406
// function.

0 commit comments

Comments
 (0)