Skip to content

Commit 6213c9f

Browse files
Evalirprestwich
andauthored
chore(evm): add 7702 estimation test (#125)
* chore(evm): add 7702 estimation test * fix: .accept() * fix: properly set tx type on TransactionRequest filler * chore: push proper bytecode * readd bytecode * chore: address review comments * chore: patch bump * chore: correct runtime bytecode * chore: doctests * fix: bytes * chore: clippy / fmt --------- Co-authored-by: James <[email protected]>
1 parent 129b64f commit 6213c9f

File tree

6 files changed

+170
-7
lines changed

6 files changed

+170
-7
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "trevm"
3-
version = "0.27.8"
3+
version = "0.27.9"
44
rust-version = "1.83.0"
55
edition = "2021"
66
authors = ["init4"]

src/builder.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,19 @@ impl<Db, Insp> TrevmBuilder<Db, Insp> {
4545
}
4646

4747
/// Set the inspector for the EVM.
48+
///
49+
/// Equivalent to [`Self::with_inspector`].
4850
pub fn with_insp<OInsp>(self, insp: OInsp) -> TrevmBuilder<Db, OInsp> {
4951
TrevmBuilder { db: self.db, insp, spec: self.spec, precompiles: self.precompiles }
5052
}
5153

54+
/// Set the inspector for the EVM.
55+
///
56+
/// Equivalent to [`Self::with_insp`].
57+
pub fn with_inspector<OInsp>(self, insp: OInsp) -> TrevmBuilder<Db, OInsp> {
58+
self.with_insp(insp)
59+
}
60+
5261
/// Set the spec id for the EVM.
5362
pub const fn with_spec_id(mut self, spec: SpecId) -> Self {
5463
self.spec = spec;

src/est.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,39 @@ impl EstimationResult {
272272
}
273273
}
274274

275+
#[cfg(test)]
276+
mod tests {
277+
use super::*;
278+
279+
#[test]
280+
fn test_search_range() {
281+
let mut range = SearchRange::new(100, 200);
282+
assert_eq!(range.min(), 100);
283+
assert_eq!(range.max(), 200);
284+
assert_eq!(range.size(), 100);
285+
assert_eq!(range.ratio(), 0.5);
286+
assert_eq!(range.midpoint(), 150);
287+
assert!(range.contains(150));
288+
289+
range.maybe_raise_min(100);
290+
assert_eq!(range.min(), 100);
291+
292+
range.maybe_raise_min(125);
293+
assert_eq!(range.min(), 125);
294+
assert_eq!(range.midpoint(), 162);
295+
296+
range.maybe_lower_max(180);
297+
assert_eq!(range.max(), 180);
298+
assert_eq!(range.midpoint(), 152);
299+
300+
range.maybe_raise_min(100);
301+
assert_eq!(range.min(), 125);
302+
303+
range.maybe_lower_max(200);
304+
assert_eq!(range.max(), 180);
305+
}
306+
}
307+
275308
// Some code above is reproduced from `reth`. It is reused here under the MIT
276309
// license.
277310
//

src/evm.rs

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2033,7 +2033,7 @@ where
20332033
// NB: 64 / 63 is due to Ethereum's gas-forwarding rules. Each call
20342034
// frame can forward only 63/64 of the gas it has when it makes a new
20352035
// frame.
2036-
let mut needle = gas_used + gas_refunded + revm::interpreter::gas::CALL_STIPEND * 64 / 63;
2036+
let mut needle = (gas_used + gas_refunded + revm::interpreter::gas::CALL_STIPEND) * 64 / 63;
20372037

20382038
// If the first search is outside the range, we don't need to try it.
20392039
if search_range.contains(needle) {
@@ -2346,6 +2346,84 @@ where
23462346
}
23472347
}
23482348

2349+
#[cfg(test)]
2350+
mod tests {
2351+
use super::*;
2352+
use crate::{
2353+
test_utils::{test_trevm_with_funds, ALICE, BOB, LOG_DEPLOYED_BYTECODE},
2354+
NoopBlock, NoopCfg, TrevmBuilder,
2355+
};
2356+
use alloy::{
2357+
consensus::constants::ETH_TO_WEI,
2358+
network::{TransactionBuilder, TransactionBuilder7702},
2359+
rpc::types::{Authorization, TransactionRequest},
2360+
signers::SignerSync,
2361+
};
2362+
use revm::{context::transaction::AuthorizationTr, database::InMemoryDB, primitives::bytes};
2363+
2364+
#[test]
2365+
fn test_estimate_gas_simple_transfer() {
2366+
let trevm = test_trevm_with_funds(&[
2367+
(ALICE.address(), U256::from(ETH_TO_WEI)),
2368+
(BOB.address(), U256::from(ETH_TO_WEI)),
2369+
]);
2370+
2371+
let tx = TransactionRequest::default()
2372+
.from(ALICE.address())
2373+
.to(BOB.address())
2374+
.value(U256::from(ETH_TO_WEI / 2));
2375+
2376+
let (estimation, _trevm) =
2377+
trevm.fill_cfg(&NoopCfg).fill_block(&NoopBlock).fill_tx(&tx).estimate_gas().unwrap();
2378+
2379+
assert!(estimation.is_success());
2380+
// The gas used should correspond to a simple transfer.
2381+
assert_eq!(estimation.gas_used(), 21000);
2382+
}
2383+
2384+
#[test]
2385+
fn test_7702_authorization_estimation() {
2386+
// Insert the LogContract code
2387+
let db = InMemoryDB::default();
2388+
let log_address = Address::repeat_byte(0x32);
2389+
2390+
// Set up trevm, and test balances.
2391+
let mut trevm =
2392+
TrevmBuilder::new().with_db(db).with_spec_id(SpecId::PRAGUE).build_trevm().unwrap();
2393+
let _ = trevm.test_set_balance(ALICE.address(), U256::from(ETH_TO_WEI));
2394+
let _ = trevm.set_bytecode_unchecked(log_address, Bytecode::new_raw(LOG_DEPLOYED_BYTECODE));
2395+
2396+
// Bob will sign the authorization.
2397+
let authorization = Authorization {
2398+
chain_id: U256::ZERO,
2399+
address: log_address,
2400+
// We know Bob's nonce is 0.
2401+
nonce: 0,
2402+
};
2403+
let signature = BOB.sign_hash_sync(&authorization.signature_hash()).unwrap();
2404+
let signed_authorization = authorization.into_signed(signature);
2405+
assert_eq!(signed_authorization.authority().unwrap(), BOB.address());
2406+
2407+
let tx = TransactionRequest::default()
2408+
.from(ALICE.address())
2409+
.to(BOB.address())
2410+
.with_authorization_list(vec![signed_authorization])
2411+
.with_input(bytes!("0x7b3ab2d0")); // emitHello()
2412+
2413+
let (estimation, trevm) =
2414+
trevm.fill_cfg(&NoopCfg).fill_block(&NoopBlock).fill_tx(&tx).estimate_gas().unwrap();
2415+
2416+
assert!(estimation.is_success());
2417+
2418+
let tx = tx.with_gas_limit(estimation.limit());
2419+
2420+
let output = trevm.clear_tx().fill_tx(&tx).run().unwrap().accept();
2421+
2422+
assert!(output.0.is_success());
2423+
assert_eq!(output.0.logs().len(), 1);
2424+
}
2425+
}
2426+
23492427
// Some code above and documentation is adapted from the revm crate, and is
23502428
// reproduced here under the terms of the MIT license.
23512429
//

src/fill/alloy.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -359,9 +359,22 @@ impl Tx for alloy::rpc::types::TransactionRequest {
359359

360360
*caller = self.from.unwrap_or_default();
361361

362-
// NB: this is set to max if not provided, as users will typically
363-
// intend that to mean "as much as possible"
364-
*tx_type = self.transaction_type.unwrap_or(TxType::Eip1559 as u8);
362+
// Determine the minimal tx type usable.
363+
*tx_type = {
364+
if self.transaction_type.is_some() {
365+
self.transaction_type.unwrap()
366+
} else if self.authorization_list.is_some() {
367+
TxType::Eip7702 as u8
368+
} else if self.has_eip4844_fields() {
369+
TxType::Eip4844 as u8
370+
} else if self.has_eip1559_fields() {
371+
TxType::Eip1559 as u8
372+
} else if self.access_list.is_some() {
373+
TxType::Eip2930 as u8
374+
} else {
375+
TxType::Legacy as u8
376+
}
377+
};
365378
*gas_limit = self.gas.unwrap_or(u64::MAX);
366379
*gas_price =
367380
self.gas_price.unwrap_or_default().max(self.max_fee_per_gas.unwrap_or_default());

src/test_utils.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,47 @@
1+
use std::sync::LazyLock;
2+
13
use crate::{helpers::Ctx, EvmNeedsCfg, Trevm};
2-
use alloy::primitives::{Address, U256};
4+
use alloy::{
5+
primitives::{Address, U256},
6+
signers::{k256::ecdsa::SigningKey, local::PrivateKeySigner},
7+
};
38
use revm::{
49
bytecode::Bytecode,
510
database::{CacheDB, EmptyDB, InMemoryDB, State},
611
inspector::{inspectors::TracerEip3155, NoOpInspector},
712
interpreter::{
813
CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter, InterpreterTypes,
914
},
10-
primitives::{hardfork::SpecId, Log},
15+
primitives::{bytes, hardfork::SpecId, Log},
1116
state::AccountInfo,
1217
Context, Inspector, MainBuilder,
1318
};
1419

20+
/// LogContract bytecode
21+
/// This is the runtime bytecode. This should be set directly with ``set_bytecode_unchecked``
22+
/// ```ignore
23+
/// contract LogContract {
24+
/// event Hello();
25+
/// event World();
26+
///
27+
/// function emitHello() public {
28+
/// emit Hello();
29+
/// }
30+
///
31+
/// function emitWorld() public {
32+
/// emit World();
33+
/// }
34+
/// }
35+
/// ```
36+
pub const LOG_DEPLOYED_BYTECODE: alloy::primitives::Bytes = bytes!("6080604052348015600e575f80fd5b50600436106030575f3560e01c80637b3ab2d01460345780639ee1a44014603c575b5f80fd5b603a6044565b005b60426072565b005b7fbcdfe0d5b27dd186282e187525415c57ea3077c34efb39148111e4d342e7ab0e60405160405180910390a1565b7f2d67bb91f17bca05af6764ab411e86f4ddf757adb89fcec59a7d21c525d4171260405160405180910390a156fea2646970667358221220144b313f421e29c7119666392827595d05f3dc33d0ccb0e75314cc9180e4fb1f64736f6c634300081a0033");
37+
38+
/// Alice testing signer
39+
pub static ALICE: LazyLock<PrivateKeySigner> =
40+
LazyLock::new(|| PrivateKeySigner::from(SigningKey::from_slice(&[0x11; 32]).unwrap()));
41+
/// Bob testing signer
42+
pub static BOB: LazyLock<PrivateKeySigner> =
43+
LazyLock::new(|| PrivateKeySigner::from(SigningKey::from_slice(&[0x22; 32]).unwrap()));
44+
1545
impl<Insp, State> Trevm<InMemoryDB, Insp, State>
1646
where
1747
Insp: Inspector<Ctx<InMemoryDB>>,

0 commit comments

Comments
 (0)