mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
intrinsic gas check (#4867)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -6145,6 +6145,7 @@ name = "reth-revm-primitives"
|
||||
version = "0.1.0-alpha.10"
|
||||
dependencies = [
|
||||
"reth-primitives",
|
||||
"revm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6402,7 +6403,9 @@ dependencies = [
|
||||
"reth-metrics",
|
||||
"reth-primitives",
|
||||
"reth-provider",
|
||||
"reth-revm-primitives",
|
||||
"reth-tasks",
|
||||
"revm",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
|
||||
@ -41,6 +41,8 @@ mod receipt;
|
||||
pub mod serde_helper;
|
||||
pub mod stage;
|
||||
mod storage;
|
||||
|
||||
/// Helpers for working with transactions
|
||||
mod transaction;
|
||||
pub mod trie;
|
||||
mod withdrawal;
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
use std::mem;
|
||||
|
||||
use crate::{Address, B256};
|
||||
use alloy_primitives::U256;
|
||||
use alloy_rlp::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper};
|
||||
use reth_codecs::{main_codec, Compact};
|
||||
use revm_primitives::U256;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::mem;
|
||||
|
||||
/// A list of addresses and storage keys that the transaction plans to access.
|
||||
/// Accesses outside the list are possible, but become more expensive.
|
||||
@ -47,12 +46,17 @@ pub struct AccessList(
|
||||
|
||||
impl AccessList {
|
||||
/// Converts the list into a vec, expected by revm
|
||||
pub fn flattened(self) -> Vec<(Address, Vec<U256>)> {
|
||||
pub fn flattened(&self) -> Vec<(Address, Vec<U256>)> {
|
||||
self.flatten().collect()
|
||||
}
|
||||
|
||||
/// Returns an iterator over the list's addresses and storage keys.
|
||||
pub fn flatten(self) -> impl Iterator<Item = (Address, Vec<U256>)> {
|
||||
/// Consumes the type and converts the list into a vec, expected by revm
|
||||
pub fn into_flattened(self) -> Vec<(Address, Vec<U256>)> {
|
||||
self.into_flatten().collect()
|
||||
}
|
||||
|
||||
/// Consumes the type and returns an iterator over the list's addresses and storage keys.
|
||||
pub fn into_flatten(self) -> impl Iterator<Item = (Address, Vec<U256>)> {
|
||||
self.0.into_iter().map(|item| {
|
||||
(
|
||||
item.address,
|
||||
@ -61,6 +65,16 @@ impl AccessList {
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns an iterator over the list's addresses and storage keys.
|
||||
pub fn flatten(&self) -> impl Iterator<Item = (Address, Vec<U256>)> + '_ {
|
||||
self.0.iter().map(|item| {
|
||||
(
|
||||
item.address,
|
||||
item.storage_keys.iter().map(|slot| U256::from_be_bytes(slot.0)).collect(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Calculates a heuristic for the in-memory size of the [AccessList].
|
||||
#[inline]
|
||||
pub fn size(&self) -> usize {
|
||||
|
||||
@ -178,6 +178,18 @@ impl Transaction {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [AccessList] of the transaction.
|
||||
///
|
||||
/// Returns `None` for legacy transactions.
|
||||
pub fn access_list(&self) -> Option<&AccessList> {
|
||||
match self {
|
||||
Transaction::Legacy(_) => None,
|
||||
Transaction::Eip2930(tx) => Some(&tx.access_list),
|
||||
Transaction::Eip1559(tx) => Some(&tx.access_list),
|
||||
Transaction::Eip4844(tx) => Some(&tx.access_list),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the gas limit of the transaction.
|
||||
pub fn gas_limit(&self) -> u64 {
|
||||
match self {
|
||||
@ -565,6 +577,18 @@ impl TransactionKind {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the transaction is a contract creation.
|
||||
#[inline]
|
||||
pub fn is_create(self) -> bool {
|
||||
matches!(self, TransactionKind::Create)
|
||||
}
|
||||
|
||||
/// Returns true if the transaction is a contract call.
|
||||
#[inline]
|
||||
pub fn is_call(self) -> bool {
|
||||
matches!(self, TransactionKind::Call(_))
|
||||
}
|
||||
|
||||
/// Calculates a heuristic for the in-memory size of the [TransactionKind].
|
||||
#[inline]
|
||||
fn size(self) -> usize {
|
||||
|
||||
@ -11,3 +11,4 @@ description = "core reth specific revm utilities"
|
||||
[dependencies]
|
||||
# reth
|
||||
reth-primitives.workspace = true
|
||||
revm.workspace = true
|
||||
@ -1,6 +1,10 @@
|
||||
use reth_primitives::{
|
||||
revm_primitives::{AccountInfo, Log},
|
||||
Account, Log as RethLog, KECCAK_EMPTY,
|
||||
Account, Address, Log as RethLog, TransactionKind, KECCAK_EMPTY, U256,
|
||||
};
|
||||
use revm::{
|
||||
interpreter::gas::initial_tx_gas,
|
||||
primitives::{MergeSpec, ShanghaiSpec},
|
||||
};
|
||||
|
||||
/// Check equality between Revm and Reth `Log`s.
|
||||
@ -38,3 +42,20 @@ pub fn into_revm_acc(reth_acc: Account) -> AccountInfo {
|
||||
code: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the Intrinsic Gas usage for a Transaction
|
||||
///
|
||||
/// Caution: This only checks past the Merge hardfork.
|
||||
#[inline]
|
||||
pub fn calculate_intrinsic_gas_after_merge(
|
||||
input: &[u8],
|
||||
kind: &TransactionKind,
|
||||
access_list: &[(Address, Vec<U256>)],
|
||||
is_shanghai: bool,
|
||||
) -> u64 {
|
||||
if is_shanghai {
|
||||
initial_tx_gas::<ShanghaiSpec>(input, kind.is_create(), access_list)
|
||||
} else {
|
||||
initial_tx_gas::<MergeSpec>(input, kind.is_create(), access_list)
|
||||
}
|
||||
}
|
||||
|
||||
@ -576,6 +576,9 @@ impl From<InvalidPoolTransactionError> for RpcPoolError {
|
||||
InvalidPoolTransactionError::ExceedsMaxInitCodeSize(_, _) => {
|
||||
RpcPoolError::ExceedsMaxInitCodeSize
|
||||
}
|
||||
InvalidPoolTransactionError::IntrinsicGasTooLow => {
|
||||
RpcPoolError::Invalid(RpcInvalidTransactionError::GasTooLow)
|
||||
}
|
||||
InvalidPoolTransactionError::OversizedData(_, _) => RpcPoolError::OversizedData,
|
||||
InvalidPoolTransactionError::Underpriced => RpcPoolError::Underpriced,
|
||||
InvalidPoolTransactionError::Other(err) => RpcPoolError::PoolTransactionError(err),
|
||||
|
||||
@ -309,7 +309,7 @@ pub(crate) fn create_txn_env(block_env: &BlockEnv, request: CallRequest) -> EthR
|
||||
value: value.unwrap_or_default(),
|
||||
data: input.try_into_unique_input()?.unwrap_or_default(),
|
||||
chain_id: chain_id.map(|c| c.to()),
|
||||
access_list: access_list.map(AccessList::flattened).unwrap_or_default(),
|
||||
access_list: access_list.map(AccessList::into_flattened).unwrap_or_default(),
|
||||
// EIP-4844 fields
|
||||
blob_hashes: blob_versioned_hashes.unwrap_or_default(),
|
||||
max_fee_per_blob_gas,
|
||||
|
||||
@ -22,7 +22,8 @@ reth-primitives.workspace = true
|
||||
reth-provider.workspace = true
|
||||
reth-interfaces.workspace = true
|
||||
reth-tasks.workspace = true
|
||||
|
||||
revm.workspace = true
|
||||
reth-revm-primitives = { path = "../revm/revm-primitives" }
|
||||
alloy-rlp.workspace = true
|
||||
|
||||
# async/futures
|
||||
|
||||
@ -187,6 +187,10 @@ pub enum InvalidPoolTransactionError {
|
||||
/// Any other error that occurred while inserting/validating that is transaction specific
|
||||
#[error("{0:?}")]
|
||||
Other(Box<dyn PoolTransactionError>),
|
||||
/// The transaction is specified to use less gas than required to start the
|
||||
/// invocation.
|
||||
#[error("intrinsic gas too low")]
|
||||
IntrinsicGasTooLow,
|
||||
}
|
||||
|
||||
// === impl InvalidPoolTransactionError ===
|
||||
@ -240,6 +244,7 @@ impl InvalidPoolTransactionError {
|
||||
// local setting
|
||||
false
|
||||
}
|
||||
InvalidPoolTransactionError::IntrinsicGasTooLow => true,
|
||||
InvalidPoolTransactionError::Overdraft => false,
|
||||
InvalidPoolTransactionError::Other(err) => err.is_bad_transaction(),
|
||||
InvalidPoolTransactionError::Eip4844(eip4844_err) => {
|
||||
|
||||
@ -13,7 +13,7 @@ use rand::{
|
||||
};
|
||||
use reth_primitives::{
|
||||
constants::{eip4844::DATA_GAS_PER_BLOB, MIN_PROTOCOL_BASE_FEE},
|
||||
hex, Address, FromRecoveredPooledTransaction, FromRecoveredTransaction,
|
||||
hex, AccessList, Address, FromRecoveredPooledTransaction, FromRecoveredTransaction,
|
||||
IntoRecoveredTransaction, PooledTransactionsElementEcRecovered, Signature, Transaction,
|
||||
TransactionKind, TransactionSigned, TransactionSignedEcRecovered, TxEip1559, TxEip2930,
|
||||
TxEip4844, TxHash, TxLegacy, TxType, TxValue, B256, EIP1559_TX_TYPE_ID, EIP4844_TX_TYPE_ID,
|
||||
@ -420,6 +420,10 @@ impl PoolTransaction for MockTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
fn access_list(&self) -> Option<&AccessList> {
|
||||
None
|
||||
}
|
||||
|
||||
fn max_priority_fee_per_gas(&self) -> Option<u128> {
|
||||
match self {
|
||||
MockTransaction::Legacy { .. } => None,
|
||||
@ -470,6 +474,10 @@ impl PoolTransaction for MockTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
fn input(&self) -> &[u8] {
|
||||
&[]
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ use crate::{
|
||||
use alloy_rlp::Encodable;
|
||||
use futures_util::{ready, Stream};
|
||||
use reth_primitives::{
|
||||
Address, BlobTransactionSidecar, BlobTransactionValidationError,
|
||||
AccessList, Address, BlobTransactionSidecar, BlobTransactionValidationError,
|
||||
FromRecoveredPooledTransaction, FromRecoveredTransaction, IntoRecoveredTransaction, PeerId,
|
||||
PooledTransactionsElement, PooledTransactionsElementEcRecovered, SealedBlock, Transaction,
|
||||
TransactionKind, TransactionSignedEcRecovered, TxEip4844, TxHash, B256, EIP1559_TX_TYPE_ID,
|
||||
@ -696,6 +696,10 @@ pub trait PoolTransaction:
|
||||
/// This is also commonly referred to as the "Gas Fee Cap" (`GasFeeCap`).
|
||||
fn max_fee_per_gas(&self) -> u128;
|
||||
|
||||
/// Returns the access_list for the particular transaction type.
|
||||
/// For Legacy transactions, returns default.
|
||||
fn access_list(&self) -> Option<&AccessList>;
|
||||
|
||||
/// Returns the EIP-1559 Priority fee the caller is paying to the block author.
|
||||
///
|
||||
/// This will return `None` for non-EIP1559 transactions
|
||||
@ -720,6 +724,9 @@ pub trait PoolTransaction:
|
||||
/// [`TransactionKind::Create`] if the transaction is a contract creation.
|
||||
fn kind(&self) -> &TransactionKind;
|
||||
|
||||
/// Returns the input data of this transaction.
|
||||
fn input(&self) -> &[u8];
|
||||
|
||||
/// Returns a measurement of the heap usage of this type and all its internals.
|
||||
fn size(&self) -> usize;
|
||||
|
||||
@ -910,6 +917,10 @@ impl PoolTransaction for EthPooledTransaction {
|
||||
self.transaction.max_fee_per_blob_gas()
|
||||
}
|
||||
|
||||
fn access_list(&self) -> Option<&AccessList> {
|
||||
self.transaction.access_list()
|
||||
}
|
||||
|
||||
/// Returns the effective tip for this transaction.
|
||||
///
|
||||
/// For EIP-1559 transactions: `min(max_fee_per_gas - base_fee, max_priority_fee_per_gas)`.
|
||||
@ -930,6 +941,10 @@ impl PoolTransaction for EthPooledTransaction {
|
||||
self.transaction.kind()
|
||||
}
|
||||
|
||||
fn input(&self) -> &[u8] {
|
||||
self.transaction.input().as_ref()
|
||||
}
|
||||
|
||||
/// Returns a measurement of the heap usage of this type and all its internals.
|
||||
fn size(&self) -> usize {
|
||||
self.transaction.transaction.input().len()
|
||||
|
||||
@ -18,6 +18,7 @@ use reth_primitives::{
|
||||
EIP4844_TX_TYPE_ID, LEGACY_TX_TYPE_ID,
|
||||
};
|
||||
use reth_provider::{AccountReader, StateProviderFactory};
|
||||
use reth_revm_primitives::calculate_intrinsic_gas_after_merge;
|
||||
use reth_tasks::TaskSpawner;
|
||||
use std::{
|
||||
marker::PhantomData,
|
||||
@ -200,6 +201,25 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// intrinsic gas checks
|
||||
let access_list =
|
||||
transaction.access_list().map(|list| list.flattened()).unwrap_or_default();
|
||||
let is_shanghai = self.fork_tracker.is_shanghai_activated();
|
||||
|
||||
if transaction.gas_limit() <
|
||||
calculate_intrinsic_gas_after_merge(
|
||||
transaction.input(),
|
||||
transaction.kind(),
|
||||
&access_list,
|
||||
is_shanghai,
|
||||
)
|
||||
{
|
||||
return TransactionValidationOutcome::Invalid(
|
||||
transaction,
|
||||
InvalidPoolTransactionError::IntrinsicGasTooLow,
|
||||
)
|
||||
}
|
||||
|
||||
let mut maybe_blob_sidecar = None;
|
||||
|
||||
// blob tx checks
|
||||
|
||||
Reference in New Issue
Block a user