1use alloy_primitives::{Address, B256, U256};
4use alloy_rpc_types_eth::{
5 state::{AccountOverride, StateOverride},
6 BlockOverrides,
7};
8use reth_evm::TransactionEnv;
9use revm::{
10 context::BlockEnv,
11 state::{Account, AccountStatus, Bytecode, EvmStorageSlot},
12 Database, DatabaseCommit,
13};
14use revm_database::{CacheDB, State};
15use std::{
16 cmp::min,
17 collections::{BTreeMap, HashMap},
18};
19
20use super::{EthApiError, EthResult, RpcInvalidTransactionError};
21
22pub fn caller_gas_allowance<DB>(db: &mut DB, env: &impl TransactionEnv) -> EthResult<u64>
32where
33 DB: Database,
34 EthApiError: From<<DB as Database>::Error>,
35{
36 let caller = db.basic(env.caller())?;
38 let balance = caller.map(|acc| acc.balance).unwrap_or_default();
40 let value = env.value();
42 let balance = balance
45 .checked_sub(env.value())
46 .ok_or_else(|| RpcInvalidTransactionError::InsufficientFunds { cost: value, balance })?;
47
48 Ok(balance
49 .checked_div(U256::from(env.gas_price()))
51 .unwrap_or_default()
53 .saturating_to())
54}
55
56#[derive(Debug)]
58pub struct CallFees {
59 pub max_priority_fee_per_gas: Option<U256>,
61 pub gas_price: U256,
68 pub max_fee_per_blob_gas: Option<U256>,
70}
71
72impl CallFees {
75 pub fn ensure_fees(
94 call_gas_price: Option<U256>,
95 call_max_fee: Option<U256>,
96 call_priority_fee: Option<U256>,
97 block_base_fee: U256,
98 blob_versioned_hashes: Option<&[B256]>,
99 max_fee_per_blob_gas: Option<U256>,
100 block_blob_fee: Option<U256>,
101 ) -> EthResult<Self> {
102 fn get_effective_gas_price(
105 max_fee_per_gas: Option<U256>,
106 max_priority_fee_per_gas: Option<U256>,
107 block_base_fee: U256,
108 ) -> EthResult<U256> {
109 match max_fee_per_gas {
110 Some(max_fee) => {
111 let max_priority_fee_per_gas = max_priority_fee_per_gas.unwrap_or(U256::ZERO);
112
113 if !(max_fee.is_zero() && max_priority_fee_per_gas.is_zero()) &&
115 max_fee < block_base_fee
116 {
117 return Err(RpcInvalidTransactionError::FeeCapTooLow.into())
119 }
120 if max_fee < max_priority_fee_per_gas {
121 return Err(
122 RpcInvalidTransactionError::TipAboveFeeCap.into(),
124 )
125 }
126 Ok(min(
128 max_fee,
129 block_base_fee.checked_add(max_priority_fee_per_gas).ok_or_else(|| {
130 EthApiError::from(RpcInvalidTransactionError::TipVeryHigh)
131 })?,
132 ))
133 }
134 None => Ok(block_base_fee
135 .checked_add(max_priority_fee_per_gas.unwrap_or(U256::ZERO))
136 .ok_or(EthApiError::from(RpcInvalidTransactionError::TipVeryHigh))?),
137 }
138 }
139
140 let has_blob_hashes =
141 blob_versioned_hashes.as_ref().map(|blobs| !blobs.is_empty()).unwrap_or(false);
142
143 match (call_gas_price, call_max_fee, call_priority_fee, max_fee_per_blob_gas) {
144 (gas_price, None, None, None) => {
145 let gas_price = gas_price.unwrap_or(U256::ZERO);
148 Ok(Self {
149 gas_price,
150 max_priority_fee_per_gas: None,
151 max_fee_per_blob_gas: has_blob_hashes.then_some(block_blob_fee).flatten(),
152 })
153 }
154 (None, max_fee_per_gas, max_priority_fee_per_gas, None) => {
155 let effective_gas_price = get_effective_gas_price(
157 max_fee_per_gas,
158 max_priority_fee_per_gas,
159 block_base_fee,
160 )?;
161 let max_fee_per_blob_gas = has_blob_hashes.then_some(block_blob_fee).flatten();
162
163 Ok(Self {
164 gas_price: effective_gas_price,
165 max_priority_fee_per_gas,
166 max_fee_per_blob_gas,
167 })
168 }
169 (None, max_fee_per_gas, max_priority_fee_per_gas, Some(max_fee_per_blob_gas)) => {
170 let effective_gas_price = get_effective_gas_price(
172 max_fee_per_gas,
173 max_priority_fee_per_gas,
174 block_base_fee,
175 )?;
176 if !has_blob_hashes {
178 return Err(RpcInvalidTransactionError::BlobTransactionMissingBlobHashes.into())
180 }
181
182 Ok(Self {
183 gas_price: effective_gas_price,
184 max_priority_fee_per_gas,
185 max_fee_per_blob_gas: Some(max_fee_per_blob_gas),
186 })
187 }
188 _ => {
189 Err(EthApiError::ConflictingFeeFieldsInRequest)
191 }
192 }
193 }
194}
195
196pub trait OverrideBlockHashes {
200 fn override_block_hashes(&mut self, block_hashes: BTreeMap<u64, B256>);
202}
203
204impl<DB> OverrideBlockHashes for CacheDB<DB> {
205 fn override_block_hashes(&mut self, block_hashes: BTreeMap<u64, B256>) {
206 self.cache
207 .block_hashes
208 .extend(block_hashes.into_iter().map(|(num, hash)| (U256::from(num), hash)))
209 }
210}
211
212impl<DB> OverrideBlockHashes for State<DB> {
213 fn override_block_hashes(&mut self, block_hashes: BTreeMap<u64, B256>) {
214 self.block_hashes.extend(block_hashes);
215 }
216}
217
218pub fn apply_block_overrides(
220 overrides: BlockOverrides,
221 db: &mut impl OverrideBlockHashes,
222 env: &mut BlockEnv,
223) {
224 let BlockOverrides {
225 number,
226 difficulty,
227 time,
228 gas_limit,
229 coinbase,
230 random,
231 base_fee,
232 block_hash,
233 } = overrides;
234
235 if let Some(block_hashes) = block_hash {
236 db.override_block_hashes(block_hashes);
238 }
239
240 if let Some(number) = number {
241 env.number = number.saturating_to();
242 }
243 if let Some(difficulty) = difficulty {
244 env.difficulty = difficulty;
245 }
246 if let Some(time) = time {
247 env.timestamp = time;
248 }
249 if let Some(gas_limit) = gas_limit {
250 env.gas_limit = gas_limit;
251 }
252 if let Some(coinbase) = coinbase {
253 env.beneficiary = coinbase;
254 }
255 if let Some(random) = random {
256 env.prevrandao = Some(random);
257 }
258 if let Some(base_fee) = base_fee {
259 env.basefee = base_fee.saturating_to();
260 }
261}
262
263pub fn apply_state_overrides<DB>(overrides: StateOverride, db: &mut DB) -> EthResult<()>
265where
266 DB: Database + DatabaseCommit,
267 EthApiError: From<DB::Error>,
268{
269 for (account, account_overrides) in overrides {
270 apply_account_override(account, account_overrides, db)?;
271 }
272 Ok(())
273}
274
275fn apply_account_override<DB>(
277 account: Address,
278 account_override: AccountOverride,
279 db: &mut DB,
280) -> EthResult<()>
281where
282 DB: Database + DatabaseCommit,
283 EthApiError: From<DB::Error>,
284{
285 let mut info = db.basic(account)?.unwrap_or_default();
286
287 if let Some(nonce) = account_override.nonce {
288 info.nonce = nonce;
289 }
290 if let Some(code) = account_override.code {
291 info.code = Some(
292 Bytecode::new_raw_checked(code)
293 .map_err(|err| EthApiError::InvalidBytecode(err.to_string()))?,
294 );
295 }
296 if let Some(balance) = account_override.balance {
297 info.balance = balance;
298 }
299
300 let mut acc =
302 revm::state::Account { info, status: AccountStatus::Touched, storage: HashMap::default() };
303
304 let storage_diff = match (account_override.state, account_override.state_diff) {
305 (Some(_), Some(_)) => return Err(EthApiError::BothStateAndStateDiffInOverride(account)),
306 (None, None) => None,
307 (Some(state), None) => {
311 db.commit(HashMap::from_iter([(
313 account,
314 Account {
315 status: AccountStatus::SelfDestructed | AccountStatus::Touched,
316 ..Default::default()
317 },
318 )]));
319 acc.mark_created();
321 Some(state)
322 }
323 (None, Some(state)) => Some(state),
324 };
325
326 if let Some(state) = storage_diff {
327 for (slot, value) in state {
328 acc.storage.insert(
329 slot.into(),
330 EvmStorageSlot {
331 original_value: (!value).into(),
333 present_value: value.into(),
334 is_cold: false,
335 },
336 );
337 }
338 }
339
340 db.commit(HashMap::from_iter([(account, acc)]));
341
342 Ok(())
343}
344
345#[cfg(test)]
346mod tests {
347 use super::*;
348 use alloy_consensus::constants::GWEI_TO_WEI;
349
350 #[test]
351 fn test_ensure_0_fallback() {
352 let CallFees { gas_price, .. } =
353 CallFees::ensure_fees(None, None, None, U256::from(99), None, None, Some(U256::ZERO))
354 .unwrap();
355 assert!(gas_price.is_zero());
356 }
357
358 #[test]
359 fn test_ensure_max_fee_0_exception() {
360 let CallFees { gas_price, .. } =
361 CallFees::ensure_fees(None, Some(U256::ZERO), None, U256::from(99), None, None, None)
362 .unwrap();
363 assert!(gas_price.is_zero());
364 }
365
366 #[test]
367 fn test_blob_fees() {
368 let CallFees { gas_price, max_fee_per_blob_gas, .. } =
369 CallFees::ensure_fees(None, None, None, U256::from(99), None, None, Some(U256::ZERO))
370 .unwrap();
371 assert!(gas_price.is_zero());
372 assert_eq!(max_fee_per_blob_gas, None);
373
374 let CallFees { gas_price, max_fee_per_blob_gas, .. } = CallFees::ensure_fees(
375 None,
376 None,
377 None,
378 U256::from(99),
379 Some(&[B256::from(U256::ZERO)]),
380 None,
381 Some(U256::from(99)),
382 )
383 .unwrap();
384 assert!(gas_price.is_zero());
385 assert_eq!(max_fee_per_blob_gas, Some(U256::from(99)));
386 }
387
388 #[test]
389 fn test_eip_1559_fees() {
390 let CallFees { gas_price, .. } = CallFees::ensure_fees(
391 None,
392 Some(U256::from(25 * GWEI_TO_WEI)),
393 Some(U256::from(15 * GWEI_TO_WEI)),
394 U256::from(15 * GWEI_TO_WEI),
395 None,
396 None,
397 Some(U256::ZERO),
398 )
399 .unwrap();
400 assert_eq!(gas_price, U256::from(25 * GWEI_TO_WEI));
401
402 let CallFees { gas_price, .. } = CallFees::ensure_fees(
403 None,
404 Some(U256::from(25 * GWEI_TO_WEI)),
405 Some(U256::from(5 * GWEI_TO_WEI)),
406 U256::from(15 * GWEI_TO_WEI),
407 None,
408 None,
409 Some(U256::ZERO),
410 )
411 .unwrap();
412 assert_eq!(gas_price, U256::from(20 * GWEI_TO_WEI));
413
414 let CallFees { gas_price, .. } = CallFees::ensure_fees(
415 None,
416 Some(U256::from(30 * GWEI_TO_WEI)),
417 Some(U256::from(30 * GWEI_TO_WEI)),
418 U256::from(15 * GWEI_TO_WEI),
419 None,
420 None,
421 Some(U256::ZERO),
422 )
423 .unwrap();
424 assert_eq!(gas_price, U256::from(30 * GWEI_TO_WEI));
425
426 let call_fees = CallFees::ensure_fees(
427 None,
428 Some(U256::from(30 * GWEI_TO_WEI)),
429 Some(U256::from(31 * GWEI_TO_WEI)),
430 U256::from(15 * GWEI_TO_WEI),
431 None,
432 None,
433 Some(U256::ZERO),
434 );
435 assert!(call_fees.is_err());
436
437 let call_fees = CallFees::ensure_fees(
438 None,
439 Some(U256::from(5 * GWEI_TO_WEI)),
440 Some(U256::from(GWEI_TO_WEI)),
441 U256::from(15 * GWEI_TO_WEI),
442 None,
443 None,
444 Some(U256::ZERO),
445 );
446 assert!(call_fees.is_err());
447
448 let call_fees = CallFees::ensure_fees(
449 None,
450 Some(U256::MAX),
451 Some(U256::MAX),
452 U256::from(5 * GWEI_TO_WEI),
453 None,
454 None,
455 Some(U256::ZERO),
456 );
457 assert!(call_fees.is_err());
458 }
459}