Skip to content

Commit 9017c0e

Browse files
authored
Add EIP-7623 support to evm 1.0 (#391)
* feat: ✨ add EIP-7623 floor cost * refactor: ♻️ use struct instead of tuple for transaction costs * refactor: ♻️ rename "real" to "used" * revert: ⏪ keep older name * feat: ✨ add EIP-7623 to Prague config * fix: 🐛 add check for floor requirement * test: 🧪 add pectra tests * refactor: ♻️ add with_floor parameter to effective_gas * refactor: ♻️ remove redundant with_floor parameter * refactor: 🔒 use saturating arithmetic for floor calculation
1 parent 3999dd7 commit 9017c0e

File tree

4 files changed

+94
-26
lines changed

4 files changed

+94
-26
lines changed

jsontests/src/run.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ pub fn run_test(
190190
Fork::London => Config::london(),
191191
Fork::Shanghai => Config::shanghai(),
192192
Fork::Cancun => Config::cancun(),
193+
Fork::Prague => Config::prague(),
193194
_ => return Err(Error::UnsupportedFork),
194195
};
195196
config_change(&mut config);

jsontests/src/types.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ pub enum Fork {
166166
Paris,
167167
Berlin,
168168
Cancun,
169+
Prague,
169170
London,
170171
Merge,
171172
Shanghai,

src/standard/config.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ pub struct Config {
8686
pub eip4844_shard_blob: bool,
8787
/// EIP-7516: Blob base fee per gas.
8888
pub eip7516_blob_base_fee: bool,
89+
/// EIP-7623: Increase calldata cost with floor.
90+
pub eip7623_calldata_floor: bool,
8991
}
9092

9193
impl Config {
@@ -132,6 +134,7 @@ impl Config {
132134
eip2930_access_list: false,
133135
eip4844_shard_blob: false,
134136
eip7516_blob_base_fee: false,
137+
eip7623_calldata_floor: false,
135138
}
136139
}
137140

@@ -237,6 +240,13 @@ impl Config {
237240
config
238241
}
239242

243+
/// Prague
244+
pub const fn prague() -> Config {
245+
let mut config = Self::cancun();
246+
config.eip7623_calldata_floor = true;
247+
config
248+
}
249+
240250
/// Gas paid for extcode.
241251
pub fn gas_ext_code(&self) -> u64 {
242252
if self.eip150_gas_increase { 700 } else { 20 }
@@ -364,6 +374,16 @@ impl Config {
364374
}
365375
}
366376

377+
/// Floor gas paid for zero data in a transaction.
378+
pub fn gas_floor_transaction_zero_data(&self) -> u64 {
379+
if self.eip7623_calldata_floor { 10 } else { 0 }
380+
}
381+
382+
/// Floor gas paid for non-zero data in a transaction.
383+
pub fn gas_floor_transaction_non_zero_data(&self) -> u64 {
384+
if self.eip7623_calldata_floor { 40 } else { 0 }
385+
}
386+
367387
/// Gas paid per address in transaction access list (see EIP-2930).
368388
pub fn gas_access_list_address(&self) -> u64 {
369389
if self.eip2930_access_list { 2400 } else { 0 }

src/standard/gasometer/mod.rs

Lines changed: 72 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ mod costs;
33
mod utils;
44

55
use alloc::vec::Vec;
6-
use core::cmp::{max, min};
6+
use core::cmp::max;
77

88
use evm_interpreter::{
99
Control, ExitError, ExitException, Machine, Opcode, Stack,
@@ -19,6 +19,7 @@ pub struct GasometerState {
1919
gas_limit: u64,
2020
memory_gas: u64,
2121
used_gas: u64,
22+
floor_gas: u64,
2223
refunded_gas: i64,
2324
/// Whether the gasometer is in static context.
2425
pub is_static: bool,
@@ -78,6 +79,13 @@ impl GasometerState {
7879
Ok(())
7980
}
8081

82+
/// Record used and floor costs of a transaction.
83+
pub fn records_transaction_cost(&mut self, cost: TransactionGas) -> Result<(), ExitError> {
84+
self.record_gas64(cost.used)?;
85+
self.floor_gas = cost.floor;
86+
Ok(())
87+
}
88+
8189
/// Set memory gas usage.
8290
pub fn set_memory_gas(&mut self, memory_cost: u64) -> Result<(), ExitError> {
8391
let all_gas_cost = self.used_gas.checked_add(memory_cost);
@@ -99,6 +107,7 @@ impl GasometerState {
99107
gas_limit,
100108
memory_gas: 0,
101109
used_gas: 0,
110+
floor_gas: 0,
102111
refunded_gas: 0,
103112
is_static,
104113
}
@@ -117,10 +126,15 @@ impl GasometerState {
117126
gas_limit.as_u64()
118127
};
119128

120-
let mut s = Self::new(gas_limit, false);
121-
let transaction_cost = TransactionCost::call(data, access_list).cost(config);
129+
let cost = TransactionCost::call(data, access_list).cost(config);
130+
131+
// EIP-7623: Check if gas limit meets the floor requirement
132+
if config.eip7623_calldata_floor && gas_limit < cost.floor {
133+
return Err(ExitException::OutOfGas.into());
134+
}
122135

123-
s.record_gas64(transaction_cost)?;
136+
let mut s = Self::new(gas_limit, false);
137+
s.records_transaction_cost(cost)?;
124138
Ok(s)
125139
}
126140

@@ -137,33 +151,38 @@ impl GasometerState {
137151
gas_limit.as_u64()
138152
};
139153

140-
let mut s = Self::new(gas_limit, false);
141-
let transaction_cost = TransactionCost::create(code, access_list).cost(config);
154+
let cost = TransactionCost::create(code, access_list).cost(config);
155+
156+
// EIP-7623: Check if gas limit meets the floor requirement
157+
if config.eip7623_calldata_floor && gas_limit < cost.floor {
158+
return Err(ExitException::OutOfGas.into());
159+
}
142160

143-
s.record_gas64(transaction_cost)?;
161+
let mut s = Self::new(gas_limit, false);
162+
s.records_transaction_cost(cost)?;
144163
Ok(s)
145164
}
146165

147166
/// The effective used gas at the end of the transaction.
148167
///
149168
/// In case of revert, refunded gas are not taken into account.
150169
pub fn effective_gas(&self, with_refund: bool, config: &Config) -> U256 {
151-
let refunded_gas = if self.refunded_gas >= 0 {
152-
self.refunded_gas as u64
170+
let refunded_gas = self.refunded_gas.max(0) as u64;
171+
172+
let used_gas = if with_refund {
173+
let max_refund = self.total_used_gas() / config.max_refund_quotient();
174+
self.total_used_gas() - refunded_gas.min(max_refund)
153175
} else {
154-
0
176+
self.total_used_gas()
155177
};
156178

157-
U256::from(if with_refund {
158-
self.gas_limit
159-
- (self.total_used_gas()
160-
- min(
161-
self.total_used_gas() / config.max_refund_quotient(),
162-
refunded_gas,
163-
))
179+
let used_gas = if config.eip7623_calldata_floor {
180+
used_gas.max(self.floor_gas)
164181
} else {
165-
self.gas_limit - self.total_used_gas()
166-
})
182+
used_gas
183+
};
184+
185+
U256::from(self.gas_limit - used_gas)
167186
}
168187

169188
/// Create a submeter.
@@ -940,6 +959,11 @@ enum TransactionCost {
940959
},
941960
}
942961

962+
pub struct TransactionGas {
963+
used: u64,
964+
floor: u64,
965+
}
966+
943967
impl TransactionCost {
944968
pub fn call(data: &[u8], access_list: &[(H160, Vec<H256>)]) -> TransactionCost {
945969
let zero_data_len = data.iter().filter(|v| **v == 0).count();
@@ -969,22 +993,32 @@ impl TransactionCost {
969993
}
970994
}
971995

972-
pub fn cost(&self, config: &Config) -> u64 {
996+
pub fn cost(&self, config: &Config) -> TransactionGas {
973997
match self {
974998
TransactionCost::Call {
975999
zero_data_len,
9761000
non_zero_data_len,
9771001
access_list_address_len,
9781002
access_list_storage_len,
9791003
} => {
980-
#[deny(clippy::let_and_return)]
981-
let cost = config.gas_transaction_call()
1004+
let used = config.gas_transaction_call()
9821005
+ *zero_data_len as u64 * config.gas_transaction_zero_data()
9831006
+ *non_zero_data_len as u64 * config.gas_transaction_non_zero_data()
9841007
+ *access_list_address_len as u64 * config.gas_access_list_address()
9851008
+ *access_list_storage_len as u64 * config.gas_access_list_storage_key();
9861009

987-
cost
1010+
let floor = config
1011+
.gas_transaction_call()
1012+
.saturating_add(
1013+
(*zero_data_len as u64)
1014+
.saturating_mul(config.gas_floor_transaction_zero_data()),
1015+
)
1016+
.saturating_add(
1017+
(*non_zero_data_len as u64)
1018+
.saturating_mul(config.gas_floor_transaction_non_zero_data()),
1019+
);
1020+
1021+
TransactionGas { used, floor }
9881022
}
9891023
TransactionCost::Create {
9901024
zero_data_len,
@@ -993,16 +1027,28 @@ impl TransactionCost {
9931027
access_list_storage_len,
9941028
initcode_cost,
9951029
} => {
996-
let mut cost = config.gas_transaction_create()
1030+
let mut used = config.gas_transaction_create()
9971031
+ *zero_data_len as u64 * config.gas_transaction_zero_data()
9981032
+ *non_zero_data_len as u64 * config.gas_transaction_non_zero_data()
9991033
+ *access_list_address_len as u64 * config.gas_access_list_address()
10001034
+ *access_list_storage_len as u64 * config.gas_access_list_storage_key();
1035+
10011036
if config.max_initcode_size().is_some() {
1002-
cost += initcode_cost;
1037+
used += initcode_cost;
10031038
}
10041039

1005-
cost
1040+
let floor = config
1041+
.gas_transaction_call()
1042+
.saturating_add(
1043+
(*zero_data_len as u64)
1044+
.saturating_mul(config.gas_floor_transaction_zero_data()),
1045+
)
1046+
.saturating_add(
1047+
(*non_zero_data_len as u64)
1048+
.saturating_mul(config.gas_floor_transaction_non_zero_data()),
1049+
);
1050+
1051+
TransactionGas { used, floor }
10061052
}
10071053
}
10081054
}

0 commit comments

Comments
 (0)