diff --git a/Cargo.toml b/Cargo.toml index 78436e4..97b9a2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "trevm" -version = "0.27.8" +version = "0.27.9" rust-version = "1.83.0" edition = "2021" authors = ["init4"] diff --git a/src/builder.rs b/src/builder.rs index 4138898..501e6a9 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -45,10 +45,19 @@ impl TrevmBuilder { } /// Set the inspector for the EVM. + /// + /// Equivalent to [`Self::with_inspector`]. pub fn with_insp(self, insp: OInsp) -> TrevmBuilder { TrevmBuilder { db: self.db, insp, spec: self.spec, precompiles: self.precompiles } } + /// Set the inspector for the EVM. + /// + /// Equivalent to [`Self::with_insp`]. + pub fn with_inspector(self, insp: OInsp) -> TrevmBuilder { + self.with_insp(insp) + } + /// Set the spec id for the EVM. pub const fn with_spec_id(mut self, spec: SpecId) -> Self { self.spec = spec; diff --git a/src/est.rs b/src/est.rs index c2a5b4d..ac0ec59 100644 --- a/src/est.rs +++ b/src/est.rs @@ -272,6 +272,39 @@ impl EstimationResult { } } +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_search_range() { + let mut range = SearchRange::new(100, 200); + assert_eq!(range.min(), 100); + assert_eq!(range.max(), 200); + assert_eq!(range.size(), 100); + assert_eq!(range.ratio(), 0.5); + assert_eq!(range.midpoint(), 150); + assert!(range.contains(150)); + + range.maybe_raise_min(100); + assert_eq!(range.min(), 100); + + range.maybe_raise_min(125); + assert_eq!(range.min(), 125); + assert_eq!(range.midpoint(), 162); + + range.maybe_lower_max(180); + assert_eq!(range.max(), 180); + assert_eq!(range.midpoint(), 152); + + range.maybe_raise_min(100); + assert_eq!(range.min(), 125); + + range.maybe_lower_max(200); + assert_eq!(range.max(), 180); + } +} + // Some code above is reproduced from `reth`. It is reused here under the MIT // license. // diff --git a/src/evm.rs b/src/evm.rs index bccf3c8..cfeaf70 100644 --- a/src/evm.rs +++ b/src/evm.rs @@ -2033,7 +2033,7 @@ where // NB: 64 / 63 is due to Ethereum's gas-forwarding rules. Each call // frame can forward only 63/64 of the gas it has when it makes a new // frame. - let mut needle = gas_used + gas_refunded + revm::interpreter::gas::CALL_STIPEND * 64 / 63; + let mut needle = (gas_used + gas_refunded + revm::interpreter::gas::CALL_STIPEND) * 64 / 63; // If the first search is outside the range, we don't need to try it. if search_range.contains(needle) { @@ -2346,6 +2346,84 @@ where } } +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + test_utils::{test_trevm_with_funds, ALICE, BOB, LOG_DEPLOYED_BYTECODE}, + NoopBlock, NoopCfg, TrevmBuilder, + }; + use alloy::{ + consensus::constants::ETH_TO_WEI, + network::{TransactionBuilder, TransactionBuilder7702}, + rpc::types::{Authorization, TransactionRequest}, + signers::SignerSync, + }; + use revm::{context::transaction::AuthorizationTr, database::InMemoryDB, primitives::bytes}; + + #[test] + fn test_estimate_gas_simple_transfer() { + let trevm = test_trevm_with_funds(&[ + (ALICE.address(), U256::from(ETH_TO_WEI)), + (BOB.address(), U256::from(ETH_TO_WEI)), + ]); + + let tx = TransactionRequest::default() + .from(ALICE.address()) + .to(BOB.address()) + .value(U256::from(ETH_TO_WEI / 2)); + + let (estimation, _trevm) = + trevm.fill_cfg(&NoopCfg).fill_block(&NoopBlock).fill_tx(&tx).estimate_gas().unwrap(); + + assert!(estimation.is_success()); + // The gas used should correspond to a simple transfer. + assert_eq!(estimation.gas_used(), 21000); + } + + #[test] + fn test_7702_authorization_estimation() { + // Insert the LogContract code + let db = InMemoryDB::default(); + let log_address = Address::repeat_byte(0x32); + + // Set up trevm, and test balances. + let mut trevm = + TrevmBuilder::new().with_db(db).with_spec_id(SpecId::PRAGUE).build_trevm().unwrap(); + let _ = trevm.test_set_balance(ALICE.address(), U256::from(ETH_TO_WEI)); + let _ = trevm.set_bytecode_unchecked(log_address, Bytecode::new_raw(LOG_DEPLOYED_BYTECODE)); + + // Bob will sign the authorization. + let authorization = Authorization { + chain_id: U256::ZERO, + address: log_address, + // We know Bob's nonce is 0. + nonce: 0, + }; + let signature = BOB.sign_hash_sync(&authorization.signature_hash()).unwrap(); + let signed_authorization = authorization.into_signed(signature); + assert_eq!(signed_authorization.authority().unwrap(), BOB.address()); + + let tx = TransactionRequest::default() + .from(ALICE.address()) + .to(BOB.address()) + .with_authorization_list(vec![signed_authorization]) + .with_input(bytes!("0x7b3ab2d0")); // emitHello() + + let (estimation, trevm) = + trevm.fill_cfg(&NoopCfg).fill_block(&NoopBlock).fill_tx(&tx).estimate_gas().unwrap(); + + assert!(estimation.is_success()); + + let tx = tx.with_gas_limit(estimation.limit()); + + let output = trevm.clear_tx().fill_tx(&tx).run().unwrap().accept(); + + assert!(output.0.is_success()); + assert_eq!(output.0.logs().len(), 1); + } +} + // Some code above and documentation is adapted from the revm crate, and is // reproduced here under the terms of the MIT license. // diff --git a/src/fill/alloy.rs b/src/fill/alloy.rs index d0ff065..9b24d64 100644 --- a/src/fill/alloy.rs +++ b/src/fill/alloy.rs @@ -359,9 +359,22 @@ impl Tx for alloy::rpc::types::TransactionRequest { *caller = self.from.unwrap_or_default(); - // NB: this is set to max if not provided, as users will typically - // intend that to mean "as much as possible" - *tx_type = self.transaction_type.unwrap_or(TxType::Eip1559 as u8); + // Determine the minimal tx type usable. + *tx_type = { + if self.transaction_type.is_some() { + self.transaction_type.unwrap() + } else if self.authorization_list.is_some() { + TxType::Eip7702 as u8 + } else if self.has_eip4844_fields() { + TxType::Eip4844 as u8 + } else if self.has_eip1559_fields() { + TxType::Eip1559 as u8 + } else if self.access_list.is_some() { + TxType::Eip2930 as u8 + } else { + TxType::Legacy as u8 + } + }; *gas_limit = self.gas.unwrap_or(u64::MAX); *gas_price = self.gas_price.unwrap_or_default().max(self.max_fee_per_gas.unwrap_or_default()); diff --git a/src/test_utils.rs b/src/test_utils.rs index c706e45..39b53a7 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -1,5 +1,10 @@ +use std::sync::LazyLock; + use crate::{helpers::Ctx, EvmNeedsCfg, Trevm}; -use alloy::primitives::{Address, U256}; +use alloy::{ + primitives::{Address, U256}, + signers::{k256::ecdsa::SigningKey, local::PrivateKeySigner}, +}; use revm::{ bytecode::Bytecode, database::{CacheDB, EmptyDB, InMemoryDB, State}, @@ -7,11 +12,36 @@ use revm::{ interpreter::{ CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter, InterpreterTypes, }, - primitives::{hardfork::SpecId, Log}, + primitives::{bytes, hardfork::SpecId, Log}, state::AccountInfo, Context, Inspector, MainBuilder, }; +/// LogContract bytecode +/// This is the runtime bytecode. This should be set directly with ``set_bytecode_unchecked`` +/// ```ignore +/// contract LogContract { +/// event Hello(); +/// event World(); +/// +/// function emitHello() public { +/// emit Hello(); +/// } +/// +/// function emitWorld() public { +/// emit World(); +/// } +/// } +/// ``` +pub const LOG_DEPLOYED_BYTECODE: alloy::primitives::Bytes = bytes!("6080604052348015600e575f80fd5b50600436106030575f3560e01c80637b3ab2d01460345780639ee1a44014603c575b5f80fd5b603a6044565b005b60426072565b005b7fbcdfe0d5b27dd186282e187525415c57ea3077c34efb39148111e4d342e7ab0e60405160405180910390a1565b7f2d67bb91f17bca05af6764ab411e86f4ddf757adb89fcec59a7d21c525d4171260405160405180910390a156fea2646970667358221220144b313f421e29c7119666392827595d05f3dc33d0ccb0e75314cc9180e4fb1f64736f6c634300081a0033"); + +/// Alice testing signer +pub static ALICE: LazyLock = + LazyLock::new(|| PrivateKeySigner::from(SigningKey::from_slice(&[0x11; 32]).unwrap())); +/// Bob testing signer +pub static BOB: LazyLock = + LazyLock::new(|| PrivateKeySigner::from(SigningKey::from_slice(&[0x22; 32]).unwrap())); + impl Trevm where Insp: Inspector>,