-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathvalidation.rs
More file actions
107 lines (95 loc) · 4.08 KB
/
Copy pathvalidation.rs
File metadata and controls
107 lines (95 loc) · 4.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
use alloy_eips::eip7825::MAX_TX_GAS_LIMIT_OSAKA;
use alloy_hardforks::mainnet::MAINNET_OSAKA_TIMESTAMP;
use revm_primitives::hardfork::SpecId;
/// Mainnet chain ID.
pub const MAINNET_CHAIN_ID: u64 = 1;
/// Maximum initcode to permit in a creation transaction and create instructions.
///
/// Limit of maximum initcode size is `2 * MAX_CODE_SIZE`.
pub const MAX_INIT_CODE_BYTE_SIZE: usize = 2 * 0x6000;
#[derive(thiserror::Error, Debug)]
pub enum ValidationError {
/// Thrown when a new transaction is added to the pool, but then immediately discarded to
/// respect the [`MAX_INIT_CODE_BYTE_SIZE`].
#[error("transaction's input size {0} exceeds max_init_code_size {1}")]
ExceedsMaxInitCodeSize(usize, usize),
/// Thrown when the transaction gas limit exceeds the maximum allowed.
#[error("transaction gas limit {0} exceeds the cap {1}")]
ExceedsTxGasLimitCap(u64, u64),
/// Thrown to ensure no one is able to specify a transaction with a tip higher than the total
/// fee cap.
#[error("max priority fee per gas higher than max fee per gas")]
TipAboveFeeCap,
/// The chain ID in the transaction does not match the current network configuration.
#[error("transaction's chain ID does not match")]
ChainIdMismatch,
/// Thrown if the transaction has no items in its authorization list
#[error("no items in authorization list for EIP7702 transaction")]
MissingEip7702AuthorizationList,
/// The transaction is specified to use less gas than required to start the
/// invocation.
#[error("intrinsic gas too low")]
IntrinsicGasTooLow,
/// Thrown if an EIP-4844 transaction without any blobs arrives
#[error("blobless blob transaction")]
NoEip4844Blobs,
}
pub fn validate_transaction(
transaction: &impl alloy_consensus::Transaction,
timestamp: u64,
) -> Result<(), ValidationError> {
// Validate input length.
let input_len = transaction.input().len();
if transaction.is_create() && input_len > MAX_INIT_CODE_BYTE_SIZE {
return Err(ValidationError::ExceedsMaxInitCodeSize(input_len, MAX_INIT_CODE_BYTE_SIZE));
}
// Ensure max_priority_fee_per_gas (if EIP1559) is less than max_fee_per_gas if any.
if transaction.max_priority_fee_per_gas() > Some(transaction.max_fee_per_gas()) {
return Err(ValidationError::TipAboveFeeCap);
}
// Checks for chainid
if let Some(chain_id) = transaction.chain_id() {
if chain_id != MAINNET_CHAIN_ID {
return Err(ValidationError::ChainIdMismatch);
}
}
let spec_id = spec_by_timestamp(timestamp);
let gas_limit = transaction.gas_limit();
if spec_id >= SpecId::OSAKA && gas_limit > MAX_TX_GAS_LIMIT_OSAKA {
return Err(ValidationError::ExceedsTxGasLimitCap(gas_limit, MAX_TX_GAS_LIMIT_OSAKA))
}
let gas = revm_interpreter::gas::calculate_initial_tx_gas(
spec_id,
transaction.input(),
transaction.is_create(),
transaction.access_list().map(|l| l.len()).unwrap_or_default() as u64,
transaction
.access_list()
.map(|l| l.iter().map(|i| i.storage_keys.len()).sum::<usize>())
.unwrap_or_default() as u64,
transaction.authorization_list().map(|l| l.len()).unwrap_or_default() as u64,
);
if gas_limit < gas.initial_gas || gas_limit < gas.floor_gas {
return Err(ValidationError::IntrinsicGasTooLow);
}
if transaction.is_eip4844() {
let blob_count = transaction.blob_versioned_hashes().map(|b| b.len() as u64).unwrap_or(0);
if blob_count == 0 {
// no blobs
return Err(ValidationError::NoEip4844Blobs);
}
}
if transaction.is_eip7702() && transaction.authorization_list().is_none_or(|l| l.is_empty()) {
return Err(ValidationError::MissingEip7702AuthorizationList);
}
Ok(())
}
/// Determine revm's [`SpecId`] based on the current timestamp.
/// Earliest spec supported by the proxy is [`SpecId::PRAGUE`].
pub fn spec_by_timestamp(timestamp: u64) -> SpecId {
if timestamp >= MAINNET_OSAKA_TIMESTAMP {
SpecId::OSAKA
} else {
SpecId::PRAGUE
}
}