Skip to content
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
2 changes: 2 additions & 0 deletions bins/revme/src/cmd/bench/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ pub fn run(criterion: &mut Criterion) {
// drop the journal
let _ = evm.finalize();

evm.modify_cfg(|cfg| cfg.disable_nonce_check = false);

criterion.bench_function("transfer_finalize", |b| {
b.iter(|| {
let _ = evm.replay().unwrap();
Expand Down
5 changes: 5 additions & 0 deletions crates/context/interface/src/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ use core::fmt::Debug;
use core::hash::Hash;
use primitives::{hardfork::SpecId, Address, TxKind, U256};

/// Configuration for the EVM.
#[auto_impl(&, &mut, Box, Arc)]
pub trait Cfg {
type Spec: Into<SpecId> + Clone;

/// Returns the chain ID of the EVM that is compared with the transaction's chain ID.
fn chain_id(&self) -> u64;

/// Returns whether the transaction's chain ID check is enabled.
fn tx_chain_id_check(&self) -> bool;

// Specification id that is set.
fn spec(&self) -> Self::Spec;

Expand Down
3 changes: 3 additions & 0 deletions crates/context/interface/src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,8 @@ pub enum InvalidTransaction {
CreateInitCodeSizeLimit,
/// Transaction chain id does not match the config chain id.
InvalidChainId,
/// Missing chain id.
MissingChainId,
/// Access list is not supported for blocks before the Berlin hardfork.
AccessListNotSupported,
/// `max_fee_per_blob_gas` is not supported for blocks before the Cancun hardfork.
Expand Down Expand Up @@ -445,6 +447,7 @@ impl fmt::Display for InvalidTransaction {
write!(f, "create initcode size limit")
}
Self::InvalidChainId => write!(f, "invalid chain ID"),
Self::MissingChainId => write!(f, "missing chain ID"),
Self::AccessListNotSupported => write!(f, "access list not supported"),
Self::MaxFeePerBlobGasNotSupported => {
write!(f, "max fee per blob gas not supported")
Expand Down
13 changes: 9 additions & 4 deletions crates/context/interface/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,11 @@ pub trait Transaction {
}

/// Returns the effective balance that is going to be spent that depends on base_fee
/// Multiplication for gas are done in u128 type (saturated) and value is added as U256 type.
///
/// # Reason
///
/// This is done for performance reasons and it is known to be safe as there is no more that u128::MAX value of eth in existence.
///
/// This is always strictly less than [`Self::max_balance_spending`].
///
Expand All @@ -195,16 +200,16 @@ pub trait Transaction {
blob_price: u128,
) -> Result<U256, InvalidTransaction> {
// gas_limit * max_fee + value + additional_gas_cost
let mut effective_balance_spending = U256::from(self.gas_limit())
.checked_mul(U256::from(self.effective_gas_price(base_fee)))
.and_then(|gas_cost| gas_cost.checked_add(self.value()))
let mut effective_balance_spending = (self.gas_limit() as u128)
.checked_mul(self.effective_gas_price(base_fee))
.and_then(|gas_cost| U256::from(gas_cost).checked_add(self.value()))
.ok_or(InvalidTransaction::OverflowPaymentInTransaction)?;

// add blob fee
if self.tx_type() == TransactionType::Eip4844 {
let blob_gas = self.total_blob_gas() as u128;
effective_balance_spending = effective_balance_spending
.checked_add(U256::from(blob_price).saturating_mul(U256::from(blob_gas)))
.checked_add(U256::from(blob_price.saturating_mul(blob_gas)))
.ok_or(InvalidTransaction::OverflowPaymentInTransaction)?;
}

Expand Down
12 changes: 12 additions & 0 deletions crates/context/interface/src/transaction/transaction_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ pub enum TransactionType {
Custom = 0xFF,
}

impl TransactionType {
/// Returns true if the transaction type is legacy.
pub fn is_legacy(&self) -> bool {
matches!(self, Self::Legacy)
}

/// Returns true if the transaction type is custom.
pub fn is_custom(&self) -> bool {
matches!(self, Self::Custom)
}
}

impl PartialEq<u8> for TransactionType {
fn eq(&self, other: &u8) -> bool {
(*self as u8) == *other
Expand Down
28 changes: 25 additions & 3 deletions crates/context/src/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ use primitives::{eip170::MAX_CODE_SIZE, hardfork::SpecId};
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub struct CfgEnv<SPEC = SpecId> {
/// Chain ID of the EVM
///
/// `chain_id` will be compared to the transaction's Chain ID.
/// Chain ID of the EVM. Used in CHAINID opcode and transaction's chain ID check.
///
/// Chain ID is introduced EIP-155.
pub chain_id: u64,

/// Whether to check the transaction's chain ID.
///
/// If set to `false`, the transaction's chain ID check will be skipped.
pub tx_chain_id_check: bool,

/// Specification for EVM represent the hardfork
pub spec: SPEC,
/// If some it will effects EIP-170: Contract code size limit.
Expand Down Expand Up @@ -106,6 +110,7 @@ impl<SPEC> CfgEnv<SPEC> {
pub fn new_with_spec(spec: SPEC) -> Self {
Self {
chain_id: 1,
tx_chain_id_check: false,
limit_contract_code_size: None,
spec,
disable_nonce_check: false,
Expand All @@ -130,10 +135,23 @@ impl<SPEC> CfgEnv<SPEC> {
self
}

/// Enables the transaction's chain ID check.
pub fn enable_tx_chain_id_check(mut self) -> Self {
self.tx_chain_id_check = true;
self
}

/// Disables the transaction's chain ID check.
pub fn disable_tx_chain_id_check(mut self) -> Self {
self.tx_chain_id_check = false;
self
}

/// Consumes `self` and returns a new `CfgEnv` with the specified spec.
pub fn with_spec<OSPEC: Into<SpecId>>(self, spec: OSPEC) -> CfgEnv<OSPEC> {
CfgEnv {
chain_id: self.chain_id,
tx_chain_id_check: self.tx_chain_id_check,
limit_contract_code_size: self.limit_contract_code_size,
spec,
disable_nonce_check: self.disable_nonce_check,
Expand Down Expand Up @@ -180,6 +198,10 @@ impl<SPEC: Into<SpecId> + Copy> Cfg for CfgEnv<SPEC> {
self.spec
}

fn tx_chain_id_check(&self) -> bool {
self.tx_chain_id_check
}

#[inline]
fn blob_max_count(&self) -> Option<u64> {
self.blob_max_count
Expand Down
2 changes: 0 additions & 2 deletions crates/context/src/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ pub struct TxEnv {

/// The chain ID of the transaction
///
/// If set to [`None`], no checks are performed.
///
/// Incorporated as part of the Spurious Dragon upgrade via [EIP-155].
///
/// [EIP-155]: https://eips.ethereum.org/EIPS/eip-155
Expand Down
40 changes: 16 additions & 24 deletions crates/handler/src/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,23 @@ pub fn validate_tx_env<CTX: ContextTr, Error>(
Some(context.block().basefee() as u128)
};

match TransactionType::from(tx_type) {
TransactionType::Legacy => {
// Check chain_id only if it is present in the legacy transaction.
// EIP-155: Simple replay attack protection
if let Some(chain_id) = tx.chain_id() {
if chain_id != context.cfg().chain_id() {
return Err(InvalidTransaction::InvalidChainId);
}
let tx_type = TransactionType::from(tx_type);

// Check chain_id if config is enabled.
// EIP-155: Simple replay attack protection
if context.cfg().tx_chain_id_check() {
if let Some(chain_id) = tx.chain_id() {
if chain_id != context.cfg().chain_id() {
return Err(InvalidTransaction::InvalidChainId);
}
} else if !tx_type.is_legacy() && !tx_type.is_custom() {
// Legacy transaction are the only one that can omit chain_id.
return Err(InvalidTransaction::MissingChainId);
}
}

match tx_type {
TransactionType::Legacy => {
// Gas price must be at least the basefee.
if let Some(base_fee) = base_fee {
if tx.gas_price() < base_fee {
Expand All @@ -118,10 +126,6 @@ pub fn validate_tx_env<CTX: ContextTr, Error>(
return Err(InvalidTransaction::Eip2930NotSupported);
}

if Some(context.cfg().chain_id()) != tx.chain_id() {
return Err(InvalidTransaction::InvalidChainId);
}

// Gas price must be at least the basefee.
if let Some(base_fee) = base_fee {
if tx.gas_price() < base_fee {
Expand All @@ -134,10 +138,6 @@ pub fn validate_tx_env<CTX: ContextTr, Error>(
return Err(InvalidTransaction::Eip1559NotSupported);
}

if Some(context.cfg().chain_id()) != tx.chain_id() {
return Err(InvalidTransaction::InvalidChainId);
}

validate_priority_fee_tx(
tx.max_fee_per_gas(),
tx.max_priority_fee_per_gas().unwrap_or_default(),
Expand All @@ -149,10 +149,6 @@ pub fn validate_tx_env<CTX: ContextTr, Error>(
return Err(InvalidTransaction::Eip4844NotSupported);
}

if Some(context.cfg().chain_id()) != tx.chain_id() {
return Err(InvalidTransaction::InvalidChainId);
}

validate_priority_fee_tx(
tx.max_fee_per_gas(),
tx.max_priority_fee_per_gas().unwrap_or_default(),
Expand All @@ -172,10 +168,6 @@ pub fn validate_tx_env<CTX: ContextTr, Error>(
return Err(InvalidTransaction::Eip7702NotSupported);
}

if Some(context.cfg().chain_id()) != tx.chain_id() {
return Err(InvalidTransaction::InvalidChainId);
}

validate_priority_fee_tx(
tx.max_fee_per_gas(),
tx.max_priority_fee_per_gas().unwrap_or_default(),
Expand Down
Loading