pallet_evm/runner/
stack.rs

1// This file is part of Frontier.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! EVM stack-based runner.
19
20use alloc::{
21	boxed::Box,
22	collections::{btree_map::BTreeMap, btree_set::BTreeSet},
23	vec::Vec,
24};
25use core::{marker::PhantomData, mem};
26use ethereum::AuthorizationList;
27use evm::{
28	backend::Backend as BackendT,
29	executor::stack::{Accessed, StackExecutor, StackState as StackStateT, StackSubstateMetadata},
30	gasometer::{GasCost, StorageTarget},
31	ExitError, ExitReason, ExternalOperation, Opcode, Transfer,
32};
33// Cumulus
34use cumulus_primitives_storage_weight_reclaim::get_proof_size;
35// Substrate
36use frame_support::{
37	traits::{
38		tokens::{currency::Currency, ExistenceRequirement},
39		Get, Time,
40	},
41	weights::Weight,
42};
43use sp_core::{H160, H256, U256};
44use sp_runtime::traits::UniqueSaturatedInto;
45// Frontier
46use fp_evm::{
47	AccessedStorage, CallInfo, CreateInfo, ExecutionInfoV2, IsPrecompileResult, Log, PrecompileSet,
48	StateOverride, Vicinity, WeightInfo, ACCOUNT_BASIC_PROOF_SIZE, ACCOUNT_CODES_KEY_SIZE,
49	ACCOUNT_CODES_METADATA_PROOF_SIZE, ACCOUNT_STORAGE_PROOF_SIZE, IS_EMPTY_CHECK_PROOF_SIZE,
50	WRITE_PROOF_SIZE,
51};
52
53use super::meter::StorageMeter;
54use crate::{
55	runner::Runner as RunnerT, AccountCodes, AccountCodesMetadata, AccountProvider,
56	AccountStorages, AddressMapping, BalanceOf, BlockHashMapping, Config, EnsureCreateOrigin,
57	Error, Event, FeeCalculator, OnChargeEVMTransaction, OnCreate, Pallet, RunnerError,
58};
59
60#[cfg(feature = "forbid-evm-reentrancy")]
61environmental::environmental!(IN_EVM: bool);
62
63#[derive(Default)]
64pub struct Runner<T: Config> {
65	_marker: PhantomData<T>,
66}
67
68impl<T: Config> Runner<T>
69where
70	BalanceOf<T>: TryFrom<U256> + Into<U256>,
71{
72	#[allow(clippy::let_and_return)]
73	/// Execute an already validated EVM operation.
74	fn execute<'config, 'precompiles, F, R>(
75		source: H160,
76		value: U256,
77		gas_limit: u64,
78		max_fee_per_gas: Option<U256>,
79		max_priority_fee_per_gas: Option<U256>,
80		config: &'config evm::Config,
81		precompiles: &'precompiles T::PrecompilesType,
82		is_transactional: bool,
83		weight_limit: Option<Weight>,
84		proof_size_base_cost: Option<u64>,
85		measured_proof_size_before: u64,
86		state_override: StateOverride,
87		f: F,
88	) -> Result<ExecutionInfoV2<R>, RunnerError<Error<T>>>
89	where
90		F: FnOnce(
91			&mut StackExecutor<
92				'config,
93				'precompiles,
94				SubstrateStackState<'_, 'config, T>,
95				T::PrecompilesType,
96			>,
97		) -> (ExitReason, R),
98		R: Default,
99	{
100		let (base_fee, weight) = T::FeeCalculator::min_gas_price();
101
102		#[cfg(not(feature = "forbid-evm-reentrancy"))]
103		let res = Self::execute_inner(
104			source,
105			value,
106			gas_limit,
107			max_fee_per_gas,
108			max_priority_fee_per_gas,
109			config,
110			precompiles,
111			is_transactional,
112			f,
113			base_fee,
114			weight,
115			weight_limit,
116			proof_size_base_cost,
117			measured_proof_size_before,
118			state_override,
119		);
120
121		#[cfg(feature = "forbid-evm-reentrancy")]
122		let res = IN_EVM::using_once(&mut false, || {
123			IN_EVM::with(|in_evm| {
124				if *in_evm {
125					return Err(RunnerError {
126						error: Error::<T>::Reentrancy,
127						weight,
128					});
129				}
130				*in_evm = true;
131				Ok(())
132			})
133			// This should always return `Some`, but let's play it safe.
134			.unwrap_or(Ok(()))?;
135
136			// Ensure that we always release the lock whenever we finish processing
137			sp_core::defer! {
138				IN_EVM::with(|in_evm| {
139					*in_evm = false;
140				});
141			}
142
143			Self::execute_inner(
144				source,
145				value,
146				gas_limit,
147				max_fee_per_gas,
148				max_priority_fee_per_gas,
149				config,
150				precompiles,
151				is_transactional,
152				f,
153				base_fee,
154				weight,
155				weight_limit,
156				proof_size_base_cost,
157				measured_proof_size_before,
158				state_override,
159			)
160		});
161
162		res
163	}
164
165	// Execute an already validated EVM operation.
166	fn execute_inner<'config, 'precompiles, F, R>(
167		source: H160,
168		value: U256,
169		mut gas_limit: u64,
170		max_fee_per_gas: Option<U256>,
171		max_priority_fee_per_gas: Option<U256>,
172		config: &'config evm::Config,
173		precompiles: &'precompiles T::PrecompilesType,
174		is_transactional: bool,
175		f: F,
176		base_fee: U256,
177		weight: Weight,
178		weight_limit: Option<Weight>,
179		proof_size_base_cost: Option<u64>,
180		measured_proof_size_before: u64,
181		state_override: StateOverride,
182	) -> Result<ExecutionInfoV2<R>, RunnerError<Error<T>>>
183	where
184		F: FnOnce(
185			&mut StackExecutor<
186				'config,
187				'precompiles,
188				SubstrateStackState<'_, 'config, T>,
189				T::PrecompilesType,
190			>,
191		) -> (ExitReason, R),
192		R: Default,
193	{
194		// Used to record the external costs in the evm through the StackState implementation
195		let maybe_weight_info =
196			match WeightInfo::new_from_weight_limit(weight_limit, proof_size_base_cost) {
197				Ok(weight_info) => weight_info,
198				Err(_) => {
199					return Ok(ExecutionInfoV2 {
200						exit_reason: ExitError::OutOfGas.into(),
201						value: Default::default(),
202						used_gas: fp_evm::UsedGas {
203							standard: gas_limit.into(),
204							effective: gas_limit.into(),
205						},
206						weight_info: None,
207						logs: Default::default(),
208					})
209				}
210			};
211		// The precompile check is only used for transactional invocations. However, here we always
212		// execute the check, because the check has side effects.
213		match precompiles.is_precompile(source, gas_limit) {
214			IsPrecompileResult::Answer { extra_cost, .. } => {
215				gas_limit = gas_limit.saturating_sub(extra_cost);
216			}
217			IsPrecompileResult::OutOfGas => {
218				return Ok(ExecutionInfoV2 {
219					exit_reason: ExitError::OutOfGas.into(),
220					value: Default::default(),
221					used_gas: fp_evm::UsedGas {
222						standard: gas_limit.into(),
223						effective: gas_limit.into(),
224					},
225					weight_info: maybe_weight_info,
226					logs: Default::default(),
227				})
228			}
229		};
230
231		// Only check the restrictions of EIP-3607 if the source of the EVM operation is from an external transaction.
232		// If the source of this EVM operation is from an internal call, like from `eth_call` or `eth_estimateGas` RPC,
233		// we will skip the checks for the EIP-3607.
234		//
235		// EIP-3607: https://eips.ethereum.org/EIPS/eip-3607
236		// Do not allow transactions for which `tx.sender` has any code deployed.
237		// Exception: Allow transactions from EOAs whose code is a valid delegation indicator (0xef0100 || address).
238		if is_transactional {
239			// Check if the account has code deployed
240			if let Some(metadata) = <AccountCodesMetadata<T>>::get(source) {
241				if metadata.size > 0 {
242					// Account has code, check if it's a valid delegation
243					let is_delegation = metadata.size
244						== evm::delegation::EIP_7702_DELEGATION_SIZE as u64
245						&& <AccountCodes<T>>::get(source)
246							.starts_with(evm::delegation::EIP_7702_DELEGATION_PREFIX);
247
248					if !is_delegation {
249						return Err(RunnerError {
250							error: Error::<T>::TransactionMustComeFromEOA,
251							weight,
252						});
253					}
254				}
255			}
256		}
257
258		let total_fee_per_gas = if is_transactional {
259			match (max_fee_per_gas, max_priority_fee_per_gas) {
260				// Zero max_fee_per_gas for validated transactional calls exist in XCM -> EVM
261				// because fees are already withdrawn in the xcm-executor.
262				(Some(max_fee), _) if max_fee.is_zero() => U256::zero(),
263				// With no tip, we pay exactly the base_fee
264				(Some(_), None) => base_fee,
265				// With tip, we include as much of the tip on top of base_fee that we can, never
266				// exceeding max_fee_per_gas
267				(Some(max_fee_per_gas), Some(max_priority_fee_per_gas)) => {
268					let actual_priority_fee_per_gas = max_fee_per_gas
269						.saturating_sub(base_fee)
270						.min(max_priority_fee_per_gas);
271
272					base_fee.saturating_add(actual_priority_fee_per_gas)
273				}
274				_ => {
275					return Err(RunnerError {
276						error: Error::<T>::GasPriceTooLow,
277						weight,
278					})
279				}
280			}
281		} else {
282			// Gas price check is skipped for non-transactional calls or creates
283			Default::default()
284		};
285
286		// After eip-1559 we make sure the account can pay both the evm execution and priority fees.
287		let total_fee =
288			total_fee_per_gas
289				.checked_mul(U256::from(gas_limit))
290				.ok_or(RunnerError {
291					error: Error::<T>::FeeOverflow,
292					weight,
293				})?;
294
295		// Deduct fee from the `source` account. Returns `None` if `total_fee` is Zero.
296		let fee = T::OnChargeTransaction::withdraw_fee(&source, total_fee)
297			.map_err(|e| RunnerError { error: e, weight })?;
298
299		let vicinity = Vicinity {
300			gas_price: base_fee,
301			origin: source,
302		};
303
304		// Compute the storage limit based on the gas limit and the storage growth ratio.
305		let storage_growth_ratio = T::GasLimitStorageGrowthRatio::get();
306		let storage_limit = if storage_growth_ratio > 0 {
307			let storage_limit = gas_limit.saturating_div(storage_growth_ratio);
308			Some(storage_limit)
309		} else {
310			None
311		};
312
313		let metadata = StackSubstateMetadata::new(gas_limit, config);
314		let state = SubstrateStackState::new(
315			&vicinity,
316			metadata,
317			maybe_weight_info,
318			storage_limit,
319			state_override,
320		);
321		let mut executor = StackExecutor::new_with_precompiles(state, config, precompiles);
322
323		// Execute the EVM call.
324		let (reason, retv, used_gas, effective_gas) = fp_evm::handle_storage_oog::<R, _>(
325			gas_limit,
326			|| {
327				let (reason, retv) = f(&mut executor);
328
329				// Compute the storage gas cost based on the storage growth.
330				let storage_gas = match &executor.state().storage_meter {
331					Some(storage_meter) => storage_meter.storage_to_gas(storage_growth_ratio),
332					None => 0,
333				};
334
335				let estimated_proof_size = executor
336					.state()
337					.weight_info()
338					.unwrap_or_default()
339					.proof_size_usage
340					.unwrap_or_default();
341
342				// Obtain the actual proof size usage using the ProofSizeExt host-function or fallback
343				// and use the estimated proof size
344				let actual_proof_size = if let Some(measured_proof_size_after) = get_proof_size() {
345					// actual_proof_size = proof_size_base_cost + proof_size measured with ProofSizeExt
346					let actual_proof_size =
347						proof_size_base_cost.unwrap_or_default().saturating_add(
348							measured_proof_size_after.saturating_sub(measured_proof_size_before),
349						);
350
351					log::trace!(
352						target: "evm",
353						"Proof size computation: (estimated: {estimated_proof_size}, actual: {actual_proof_size})"
354					);
355
356					// If the proof_size calculated from the host-function gives an higher cost than
357					// the estimated proof_size, we should use the estimated proof_size to compute
358					// the PoV gas.
359					//
360					// TODO: The estimated proof_size should always be an overestimate
361					if actual_proof_size > estimated_proof_size {
362						log::debug!(
363							target: "evm",
364							"Proof size underestimation detected! (estimated: {estimated_proof_size}, actual: {actual_proof_size}, diff: {})",
365							actual_proof_size.saturating_sub(estimated_proof_size)
366						);
367						estimated_proof_size
368					} else {
369						actual_proof_size
370					}
371				} else {
372					estimated_proof_size
373				};
374
375				// Post execution.
376				let pov_gas = actual_proof_size.saturating_mul(T::GasLimitPovSizeRatio::get());
377				let used_gas = executor.used_gas();
378				let effective_gas = core::cmp::max(core::cmp::max(used_gas, pov_gas), storage_gas);
379
380				log::debug!(
381					target: "evm",
382					"Calculating effective gas: max(used: {used_gas}, pov: {pov_gas}, storage: {storage_gas}) = {effective_gas}"
383				);
384
385				(reason, retv, used_gas, U256::from(effective_gas))
386			},
387		);
388
389		let actual_fee = effective_gas.saturating_mul(total_fee_per_gas);
390		let actual_base_fee = effective_gas.saturating_mul(base_fee);
391
392		log::debug!(
393			target: "evm",
394			"Execution {reason:?} [source: {source:?}, value: {value}, gas_limit: {gas_limit}, actual_fee: {actual_fee}, used_gas: {used_gas}, effective_gas: {effective_gas}, base_fee: {base_fee}, total_fee_per_gas: {total_fee_per_gas}, is_transactional: {is_transactional}]"
395		);
396		// The difference between initially withdrawn and the actual cost is refunded.
397		//
398		// Considered the following request:
399		// +-----------+---------+--------------+
400		// | Gas_limit | Max_Fee | Max_Priority |
401		// +-----------+---------+--------------+
402		// |        20 |      10 |            6 |
403		// +-----------+---------+--------------+
404		//
405		// And execution:
406		// +----------+----------+
407		// | Gas_used | Base_Fee |
408		// +----------+----------+
409		// |        5 |        2 |
410		// +----------+----------+
411		//
412		// Initially withdrawn 10 * 20 = 200.
413		// Actual cost (2 + 6) * 5 = 40.
414		// Refunded 200 - 40 = 160.
415		// Tip 5 * 6 = 30.
416		// Burned 200 - (160 + 30) = 10. Which is equivalent to gas_used * base_fee.
417		let actual_priority_fee = T::OnChargeTransaction::correct_and_deposit_fee(
418			&source,
419			// Actual fee after evm execution, including tip.
420			actual_fee,
421			// Base fee.
422			actual_base_fee,
423			// Fee initially withdrawn.
424			fee,
425		);
426		T::OnChargeTransaction::pay_priority_fee(actual_priority_fee);
427
428		let state = executor.into_state();
429
430		for address in &state.substate.deletes {
431			log::debug!(
432				target: "evm",
433				"Deleting account at {address:?}"
434			);
435			Pallet::<T>::remove_account(address)
436		}
437
438		for log in &state.substate.logs {
439			log::trace!(
440				target: "evm",
441				"Inserting log for {:?}, topics ({}) {:?}, data ({}): {:?}]",
442				log.address,
443				log.topics.len(),
444				log.topics,
445				log.data.len(),
446				log.data
447			);
448			Pallet::<T>::deposit_event(Event::<T>::Log {
449				log: Log {
450					address: log.address,
451					topics: log.topics.clone(),
452					data: log.data.clone(),
453				},
454			});
455		}
456
457		Ok(ExecutionInfoV2 {
458			value: retv,
459			exit_reason: reason,
460			used_gas: fp_evm::UsedGas {
461				standard: used_gas.into(),
462				effective: effective_gas,
463			},
464			weight_info: state.weight_info(),
465			logs: state.substate.logs,
466		})
467	}
468}
469
470impl<T: Config> RunnerT<T> for Runner<T>
471where
472	BalanceOf<T>: TryFrom<U256> + Into<U256>,
473{
474	type Error = Error<T>;
475
476	fn validate(
477		source: H160,
478		target: Option<H160>,
479		input: Vec<u8>,
480		value: U256,
481		gas_limit: u64,
482		max_fee_per_gas: Option<U256>,
483		max_priority_fee_per_gas: Option<U256>,
484		nonce: Option<U256>,
485		access_list: Vec<(H160, Vec<H256>)>,
486		authorization_list: Vec<(U256, H160, U256, Option<H160>)>,
487		is_transactional: bool,
488		weight_limit: Option<Weight>,
489		proof_size_base_cost: Option<u64>,
490		evm_config: &evm::Config,
491	) -> Result<(), RunnerError<Self::Error>> {
492		let (base_fee, mut weight) = T::FeeCalculator::min_gas_price();
493		let (source_account, inner_weight) = Pallet::<T>::account_basic(&source);
494		weight = weight.saturating_add(inner_weight);
495
496		let _ = fp_evm::CheckEvmTransaction::<Self::Error>::new(
497			fp_evm::CheckEvmTransactionConfig {
498				evm_config,
499				block_gas_limit: T::BlockGasLimit::get(),
500				transaction_gas_limit: T::TransactionGasLimit::get(),
501				base_fee,
502				chain_id: T::ChainId::get(),
503				is_transactional,
504				allow_unprotected_txs: true,
505			},
506			fp_evm::CheckEvmTransactionInput {
507				chain_id: Some(T::ChainId::get()),
508				to: target,
509				input,
510				nonce: nonce.unwrap_or(source_account.nonce),
511				gas_limit: gas_limit.into(),
512				gas_price: None,
513				max_fee_per_gas,
514				max_priority_fee_per_gas,
515				value,
516				access_list,
517				authorization_list,
518			},
519			weight_limit,
520			proof_size_base_cost,
521		)
522		.validate_in_block_for(&source_account)
523		.and_then(|v| v.with_base_fee())
524		.and_then(|v| v.with_balance_for(&source_account))
525		.map_err(|error| RunnerError { error, weight })?;
526		Ok(())
527	}
528
529	fn call(
530		source: H160,
531		target: H160,
532		input: Vec<u8>,
533		value: U256,
534		gas_limit: u64,
535		max_fee_per_gas: Option<U256>,
536		max_priority_fee_per_gas: Option<U256>,
537		nonce: Option<U256>,
538		access_list: Vec<(H160, Vec<H256>)>,
539		authorization_list: AuthorizationList,
540		is_transactional: bool,
541		validate: bool,
542		weight_limit: Option<Weight>,
543		proof_size_base_cost: Option<u64>,
544		state_override: StateOverride,
545		config: &evm::Config,
546	) -> Result<CallInfo, RunnerError<Self::Error>> {
547		let measured_proof_size_before = get_proof_size().unwrap_or_default();
548
549		let authorization_list = authorization_list
550			.iter()
551			.map(|d| {
552				(
553					U256::from(d.chain_id),
554					d.address,
555					d.nonce,
556					d.authorizing_address().ok(),
557				)
558			})
559			.collect::<Vec<(U256, sp_core::H160, U256, Option<sp_core::H160>)>>();
560
561		if validate {
562			Self::validate(
563				source,
564				Some(target),
565				input.clone(),
566				value,
567				gas_limit,
568				max_fee_per_gas,
569				max_priority_fee_per_gas,
570				nonce,
571				access_list.clone(),
572				authorization_list.clone(),
573				is_transactional,
574				weight_limit,
575				proof_size_base_cost,
576				config,
577			)?;
578		}
579
580		let precompiles = T::PrecompilesValue::get();
581		Self::execute(
582			source,
583			value,
584			gas_limit,
585			max_fee_per_gas,
586			max_priority_fee_per_gas,
587			config,
588			&precompiles,
589			is_transactional,
590			weight_limit,
591			proof_size_base_cost,
592			measured_proof_size_before,
593			state_override,
594			|executor| {
595				executor.transact_call(
596					source,
597					target,
598					value,
599					input,
600					gas_limit,
601					access_list,
602					authorization_list,
603				)
604			},
605		)
606	}
607
608	fn create(
609		source: H160,
610		init: Vec<u8>,
611		value: U256,
612		gas_limit: u64,
613		max_fee_per_gas: Option<U256>,
614		max_priority_fee_per_gas: Option<U256>,
615		nonce: Option<U256>,
616		access_list: Vec<(H160, Vec<H256>)>,
617		authorization_list: AuthorizationList,
618		is_transactional: bool,
619		validate: bool,
620		weight_limit: Option<Weight>,
621		proof_size_base_cost: Option<u64>,
622		config: &evm::Config,
623	) -> Result<CreateInfo, RunnerError<Self::Error>> {
624		let measured_proof_size_before = get_proof_size().unwrap_or_default();
625		let (_, weight) = T::FeeCalculator::min_gas_price();
626
627		T::CreateOriginFilter::check_create_origin(&source)
628			.map_err(|error| RunnerError { error, weight })?;
629
630		let authorization_list = authorization_list
631			.iter()
632			.map(|d| {
633				(
634					U256::from(d.chain_id),
635					d.address,
636					d.nonce,
637					d.authorizing_address().ok(),
638				)
639			})
640			.collect::<Vec<(U256, sp_core::H160, U256, Option<sp_core::H160>)>>();
641
642		if validate {
643			Self::validate(
644				source,
645				None,
646				init.clone(),
647				value,
648				gas_limit,
649				max_fee_per_gas,
650				max_priority_fee_per_gas,
651				nonce,
652				access_list.clone(),
653				authorization_list.clone(),
654				is_transactional,
655				weight_limit,
656				proof_size_base_cost,
657				config,
658			)?;
659		}
660
661		let precompiles = T::PrecompilesValue::get();
662		Self::execute(
663			source,
664			value,
665			gas_limit,
666			max_fee_per_gas,
667			max_priority_fee_per_gas,
668			config,
669			&precompiles,
670			is_transactional,
671			weight_limit,
672			proof_size_base_cost,
673			measured_proof_size_before,
674			None,
675			|executor| {
676				let address = executor.create_address(evm::CreateScheme::Legacy { caller: source });
677				T::OnCreate::on_create(source, address);
678				let (reason, _) = executor.transact_create(
679					source,
680					value,
681					init,
682					gas_limit,
683					access_list,
684					authorization_list,
685				);
686				(reason, address)
687			},
688		)
689	}
690
691	fn create2(
692		source: H160,
693		init: Vec<u8>,
694		salt: H256,
695		value: U256,
696		gas_limit: u64,
697		max_fee_per_gas: Option<U256>,
698		max_priority_fee_per_gas: Option<U256>,
699		nonce: Option<U256>,
700		access_list: Vec<(H160, Vec<H256>)>,
701		authorization_list: AuthorizationList,
702		is_transactional: bool,
703		validate: bool,
704		weight_limit: Option<Weight>,
705		proof_size_base_cost: Option<u64>,
706		config: &evm::Config,
707	) -> Result<CreateInfo, RunnerError<Self::Error>> {
708		let measured_proof_size_before = get_proof_size().unwrap_or_default();
709		let (_, weight) = T::FeeCalculator::min_gas_price();
710
711		T::CreateOriginFilter::check_create_origin(&source)
712			.map_err(|error| RunnerError { error, weight })?;
713
714		let authorization_list = authorization_list
715			.iter()
716			.map(|d| {
717				(
718					U256::from(d.chain_id),
719					d.address,
720					d.nonce,
721					d.authorizing_address().ok(),
722				)
723			})
724			.collect::<Vec<(U256, sp_core::H160, U256, Option<sp_core::H160>)>>();
725
726		if validate {
727			Self::validate(
728				source,
729				None,
730				init.clone(),
731				value,
732				gas_limit,
733				max_fee_per_gas,
734				max_priority_fee_per_gas,
735				nonce,
736				access_list.clone(),
737				authorization_list.clone(),
738				is_transactional,
739				weight_limit,
740				proof_size_base_cost,
741				config,
742			)?;
743		}
744
745		let precompiles = T::PrecompilesValue::get();
746		let code_hash = H256::from(sp_io::hashing::keccak_256(&init));
747		Self::execute(
748			source,
749			value,
750			gas_limit,
751			max_fee_per_gas,
752			max_priority_fee_per_gas,
753			config,
754			&precompiles,
755			is_transactional,
756			weight_limit,
757			proof_size_base_cost,
758			measured_proof_size_before,
759			None,
760			|executor| {
761				let address = executor.create_address(evm::CreateScheme::Create2 {
762					caller: source,
763					code_hash,
764					salt,
765				});
766				T::OnCreate::on_create(source, address);
767				let (reason, _) = executor.transact_create2(
768					source,
769					value,
770					init,
771					salt,
772					gas_limit,
773					access_list,
774					authorization_list,
775				);
776				(reason, address)
777			},
778		)
779	}
780
781	fn create_force_address(
782		source: H160,
783		init: Vec<u8>,
784		value: U256,
785		gas_limit: u64,
786		max_fee_per_gas: Option<U256>,
787		max_priority_fee_per_gas: Option<U256>,
788		nonce: Option<U256>,
789		access_list: Vec<(H160, Vec<H256>)>,
790		authorization_list: AuthorizationList,
791		is_transactional: bool,
792		validate: bool,
793		weight_limit: Option<Weight>,
794		proof_size_base_cost: Option<u64>,
795		config: &evm::Config,
796		contract_address: H160,
797	) -> Result<CreateInfo, RunnerError<Self::Error>> {
798		let measured_proof_size_before = get_proof_size().unwrap_or_default();
799		let (_, weight) = T::FeeCalculator::min_gas_price();
800
801		T::CreateOriginFilter::check_create_origin(&source)
802			.map_err(|error| RunnerError { error, weight })?;
803
804		let authorization_list = authorization_list
805			.iter()
806			.map(|d| {
807				(
808					U256::from(d.chain_id),
809					d.address,
810					d.nonce,
811					d.authorizing_address().ok(),
812				)
813			})
814			.collect::<Vec<(U256, sp_core::H160, U256, Option<sp_core::H160>)>>();
815
816		if validate {
817			Self::validate(
818				source,
819				None,
820				init.clone(),
821				value,
822				gas_limit,
823				max_fee_per_gas,
824				max_priority_fee_per_gas,
825				nonce,
826				access_list.clone(),
827				authorization_list.clone(),
828				is_transactional,
829				weight_limit,
830				proof_size_base_cost,
831				config,
832			)?;
833		}
834		let precompiles = T::PrecompilesValue::get();
835		Self::execute(
836			source,
837			value,
838			gas_limit,
839			max_fee_per_gas,
840			max_priority_fee_per_gas,
841			config,
842			&precompiles,
843			is_transactional,
844			weight_limit,
845			proof_size_base_cost,
846			measured_proof_size_before,
847			None,
848			|executor| {
849				T::OnCreate::on_create(source, contract_address);
850				let (reason, _) = executor.transact_create_force_address(
851					source,
852					value,
853					init,
854					gas_limit,
855					access_list,
856					authorization_list,
857					contract_address,
858				);
859				(reason, contract_address)
860			},
861		)
862	}
863}
864
865struct SubstrateStackSubstate<'config> {
866	metadata: StackSubstateMetadata<'config>,
867	deletes: BTreeSet<H160>,
868	creates: BTreeSet<H160>,
869	logs: Vec<Log>,
870	parent: Option<Box<SubstrateStackSubstate<'config>>>,
871}
872
873impl<'config> SubstrateStackSubstate<'config> {
874	pub fn metadata(&self) -> &StackSubstateMetadata<'config> {
875		&self.metadata
876	}
877
878	pub fn metadata_mut(&mut self) -> &mut StackSubstateMetadata<'config> {
879		&mut self.metadata
880	}
881
882	pub fn enter(&mut self, gas_limit: u64, is_static: bool) {
883		let mut entering = Self {
884			metadata: self.metadata.spit_child(gas_limit, is_static),
885			parent: None,
886			deletes: BTreeSet::new(),
887			creates: BTreeSet::new(),
888			logs: Vec::new(),
889		};
890		mem::swap(&mut entering, self);
891
892		self.parent = Some(Box::new(entering));
893
894		sp_io::storage::start_transaction();
895	}
896
897	pub fn exit_commit(&mut self) -> Result<(), ExitError> {
898		let mut exited = *self.parent.take().expect("Cannot commit on root substate");
899		mem::swap(&mut exited, self);
900
901		self.metadata.swallow_commit(exited.metadata)?;
902		self.logs.append(&mut exited.logs);
903		self.deletes.append(&mut exited.deletes);
904		self.creates.append(&mut exited.creates);
905		sp_io::storage::commit_transaction();
906		Ok(())
907	}
908
909	pub fn exit_revert(&mut self) -> Result<(), ExitError> {
910		let mut exited = *self.parent.take().expect("Cannot discard on root substate");
911		mem::swap(&mut exited, self);
912		self.metadata.swallow_revert(exited.metadata)?;
913
914		sp_io::storage::rollback_transaction();
915		Ok(())
916	}
917
918	pub fn exit_discard(&mut self) -> Result<(), ExitError> {
919		let mut exited = *self.parent.take().expect("Cannot discard on root substate");
920		mem::swap(&mut exited, self);
921		self.metadata.swallow_discard(exited.metadata)?;
922
923		sp_io::storage::rollback_transaction();
924		Ok(())
925	}
926
927	pub fn deleted(&self, address: H160) -> bool {
928		if self.deletes.contains(&address) {
929			return true;
930		}
931
932		if let Some(parent) = self.parent.as_ref() {
933			return parent.deleted(address);
934		}
935
936		false
937	}
938
939	pub fn created(&self, address: H160) -> bool {
940		if self.creates.contains(&address) {
941			return true;
942		}
943
944		if let Some(parent) = self.parent.as_ref() {
945			return parent.created(address);
946		}
947
948		false
949	}
950
951	pub fn set_deleted(&mut self, address: H160) {
952		self.deletes.insert(address);
953	}
954
955	pub fn set_created(&mut self, address: H160) {
956		self.creates.insert(address);
957	}
958
959	pub fn log(&mut self, address: H160, topics: Vec<H256>, data: Vec<u8>) {
960		self.logs.push(Log {
961			address,
962			topics,
963			data,
964		});
965	}
966
967	fn recursive_is_cold<F: Fn(&Accessed) -> bool>(&self, f: &F) -> bool {
968		let local_is_accessed = self.metadata.accessed().as_ref().map(f).unwrap_or(false);
969		if local_is_accessed {
970			false
971		} else {
972			self.parent
973				.as_ref()
974				.map(|p| p.recursive_is_cold(f))
975				.unwrap_or(true)
976		}
977	}
978}
979
980#[derive(Default, Clone, Eq, PartialEq)]
981pub struct Recorded {
982	account_codes: Vec<H160>,
983	account_storages: BTreeMap<(H160, H256), bool>,
984}
985
986/// Substrate backend for EVM.
987pub struct SubstrateStackState<'vicinity, 'config, T> {
988	vicinity: &'vicinity Vicinity,
989	substate: SubstrateStackSubstate<'config>,
990	original_storage: BTreeMap<(H160, H256), H256>,
991	transient_storage: BTreeMap<(H160, H256), H256>,
992	/// Per-account full storage replacement. When an address is present here, storage
993	/// reads are served exclusively from the per-address map (missing slots return zero)
994	/// instead of the on-chain trie. An entry with an empty inner map represents a full
995	/// wipe (Geth-style `"state": {}`).
996	state_override: BTreeMap<H160, BTreeMap<H256, H256>>,
997	/// Per-frame snapshots of `state_override`, pushed on `enter` and restored
998	/// on `exit_revert`/`exit_discard` (Geth StateDB snapshot parity).
999	/// `None` entries skip the clone when the override map is empty — safe
1000	/// because `state_override` only shrinks during execution.
1001	state_override_journal: Vec<Option<BTreeMap<H160, BTreeMap<H256, H256>>>>,
1002	recorded: Recorded,
1003	weight_info: Option<WeightInfo>,
1004	storage_meter: Option<StorageMeter>,
1005	_marker: PhantomData<T>,
1006}
1007
1008impl<'vicinity, 'config, T: Config> SubstrateStackState<'vicinity, 'config, T> {
1009	/// Create a new backend with given vicinity.
1010	pub fn new(
1011		vicinity: &'vicinity Vicinity,
1012		metadata: StackSubstateMetadata<'config>,
1013		weight_info: Option<WeightInfo>,
1014		storage_limit: Option<u64>,
1015		state_override: StateOverride,
1016	) -> Self {
1017		let storage_meter = storage_limit.map(StorageMeter::new);
1018		let state_override = state_override
1019			.map(|per_address| {
1020				per_address
1021					.into_iter()
1022					.map(|(address, slots)| (address, slots.into_iter().collect()))
1023					.collect::<BTreeMap<H160, BTreeMap<H256, H256>>>()
1024			})
1025			.unwrap_or_default();
1026		Self {
1027			vicinity,
1028			substate: SubstrateStackSubstate {
1029				metadata,
1030				deletes: BTreeSet::new(),
1031				creates: BTreeSet::new(),
1032				logs: Vec::new(),
1033				parent: None,
1034			},
1035			_marker: PhantomData,
1036			original_storage: BTreeMap::new(),
1037			transient_storage: BTreeMap::new(),
1038			state_override,
1039			state_override_journal: Vec::new(),
1040			recorded: Default::default(),
1041			weight_info,
1042			storage_meter,
1043		}
1044	}
1045
1046	/// Resolve a storage slot for `address` as the EVM should observe it.
1047	///
1048	/// If an active state override covers `address`, the slot is served from the
1049	/// in-memory override map (missing slots return zero, matching Geth's
1050	/// "fresh state object" semantics). Otherwise the on-chain trie is read.
1051	fn read_effective_storage(&self, address: H160, index: H256) -> H256 {
1052		if let Some(slots) = self.state_override.get(&address) {
1053			return slots.get(&index).copied().unwrap_or_default();
1054		}
1055		<AccountStorages<T>>::get(address, index)
1056	}
1057
1058	pub fn weight_info(&self) -> Option<WeightInfo> {
1059		self.weight_info
1060	}
1061
1062	pub fn recorded(&self) -> &Recorded {
1063		&self.recorded
1064	}
1065
1066	pub fn info_mut(&mut self) -> (&mut Option<WeightInfo>, &mut Recorded) {
1067		(&mut self.weight_info, &mut self.recorded)
1068	}
1069
1070	fn record_address_code_read(
1071		address: H160,
1072		weight_info: &mut WeightInfo,
1073		recorded: &mut Recorded,
1074		create_contract_limit: u64,
1075	) -> Result<(), ExitError> {
1076		let maybe_record = !recorded.account_codes.contains(&address);
1077		// Skip if the address has been already recorded this block
1078		if maybe_record {
1079			// First we record account emptiness check.
1080			// Transfers to EOAs with standard 21_000 gas limit are able to
1081			// pay for this pov size.
1082			weight_info.try_record_proof_size_or_fail(IS_EMPTY_CHECK_PROOF_SIZE)?;
1083			if <AccountCodes<T>>::decode_len(address).unwrap_or(0) == 0 {
1084				return Ok(());
1085			}
1086
1087			weight_info.try_record_proof_size_or_fail(ACCOUNT_CODES_METADATA_PROOF_SIZE)?;
1088			if let Some(meta) = <AccountCodesMetadata<T>>::get(address) {
1089				weight_info.try_record_proof_size_or_fail(meta.size)?;
1090			} else {
1091				weight_info.try_record_proof_size_or_fail(create_contract_limit)?;
1092
1093				let actual_size = Pallet::<T>::account_code_metadata(address).size;
1094				if actual_size > create_contract_limit {
1095					fp_evm::set_storage_oog();
1096					return Err(ExitError::OutOfGas);
1097				}
1098				// Refund unused proof size
1099				weight_info.refund_proof_size(create_contract_limit.saturating_sub(actual_size));
1100			}
1101			recorded.account_codes.push(address);
1102		}
1103		Ok(())
1104	}
1105}
1106
1107impl<T: Config> BackendT for SubstrateStackState<'_, '_, T>
1108where
1109	BalanceOf<T>: TryFrom<U256> + Into<U256>,
1110{
1111	fn gas_price(&self) -> U256 {
1112		self.vicinity.gas_price
1113	}
1114	fn origin(&self) -> H160 {
1115		self.vicinity.origin
1116	}
1117
1118	fn block_hash(&self, number: U256) -> H256 {
1119		if number > U256::from(u32::MAX) {
1120			H256::default()
1121		} else {
1122			T::BlockHashMapping::block_hash(number.as_u32())
1123		}
1124	}
1125
1126	fn block_number(&self) -> U256 {
1127		let number: u128 = frame_system::Pallet::<T>::block_number().unique_saturated_into();
1128		U256::from(number)
1129	}
1130
1131	fn block_coinbase(&self) -> H160 {
1132		Pallet::<T>::find_author()
1133	}
1134
1135	fn block_timestamp(&self) -> U256 {
1136		let now: u128 = T::Timestamp::now().unique_saturated_into();
1137		U256::from(now / 1000)
1138	}
1139
1140	fn block_difficulty(&self) -> U256 {
1141		U256::zero()
1142	}
1143
1144	fn block_randomness(&self) -> Option<H256> {
1145		None
1146	}
1147
1148	fn block_gas_limit(&self) -> U256 {
1149		T::BlockGasLimit::get()
1150	}
1151
1152	fn block_base_fee_per_gas(&self) -> U256 {
1153		let (base_fee, _) = T::FeeCalculator::min_gas_price();
1154		base_fee
1155	}
1156
1157	fn chain_id(&self) -> U256 {
1158		U256::from(T::ChainId::get())
1159	}
1160
1161	fn exists(&self, _address: H160) -> bool {
1162		true
1163	}
1164
1165	fn basic(&self, address: H160) -> evm::backend::Basic {
1166		let (account, _) = Pallet::<T>::account_basic(&address);
1167
1168		evm::backend::Basic {
1169			balance: account.balance,
1170			nonce: account.nonce,
1171		}
1172	}
1173
1174	fn code(&self, address: H160) -> Vec<u8> {
1175		<AccountCodes<T>>::get(address)
1176	}
1177
1178	fn storage(&self, address: H160, index: H256) -> H256 {
1179		self.read_effective_storage(address, index)
1180	}
1181
1182	fn transient_storage(&self, address: H160, index: H256) -> H256 {
1183		self.transient_storage
1184			.get(&(address, index))
1185			.copied()
1186			.unwrap_or_default()
1187	}
1188
1189	fn original_storage(&self, address: H160, index: H256) -> Option<H256> {
1190		Some(
1191			self.original_storage
1192				.get(&(address, index))
1193				.cloned()
1194				.unwrap_or_else(|| self.read_effective_storage(address, index)),
1195		)
1196	}
1197}
1198
1199impl<'config, T: Config> StackStateT<'config> for SubstrateStackState<'_, 'config, T>
1200where
1201	BalanceOf<T>: TryFrom<U256> + Into<U256>,
1202{
1203	fn metadata(&self) -> &StackSubstateMetadata<'config> {
1204		self.substate.metadata()
1205	}
1206
1207	fn metadata_mut(&mut self) -> &mut StackSubstateMetadata<'config> {
1208		self.substate.metadata_mut()
1209	}
1210
1211	fn enter(&mut self, gas_limit: u64, is_static: bool) {
1212		// Journal the override alongside the Substrate storage transaction that
1213		// `SubstrateStackSubstate::enter` opens, so an inner frame's SSTORE or
1214		// SELFDESTRUCT-driven `reset_storage` against an overridden account can
1215		// be rolled back on revert/discard (Geth StateDB parity).
1216		// `None` when empty: child frames can't mutate an empty map, so we skip
1217		// the clone while still pairing the push/pop with the substate frame.
1218		let snapshot = (!self.state_override.is_empty()).then(|| self.state_override.clone());
1219		self.state_override_journal.push(snapshot);
1220		self.substate.enter(gas_limit, is_static)
1221	}
1222
1223	fn exit_commit(&mut self) -> Result<(), ExitError> {
1224		// Keep the mutated `state_override`; just drop the snapshot.
1225		let _ = self.state_override_journal.pop();
1226		self.substate.exit_commit()
1227	}
1228
1229	fn exit_revert(&mut self) -> Result<(), ExitError> {
1230		if let Some(snapshot) = self
1231			.state_override_journal
1232			.pop()
1233			.expect("exit_revert paired with enter; snapshot always pushed")
1234		{
1235			self.state_override = snapshot;
1236		}
1237		self.substate.exit_revert()
1238	}
1239
1240	fn exit_discard(&mut self) -> Result<(), ExitError> {
1241		if let Some(snapshot) = self
1242			.state_override_journal
1243			.pop()
1244			.expect("exit_discard paired with enter; snapshot always pushed")
1245		{
1246			self.state_override = snapshot;
1247		}
1248		self.substate.exit_discard()
1249	}
1250
1251	fn is_empty(&self, address: H160) -> bool {
1252		Pallet::<T>::is_account_empty(&address)
1253	}
1254
1255	fn deleted(&self, address: H160) -> bool {
1256		self.substate.deleted(address)
1257	}
1258
1259	fn created(&self, address: H160) -> bool {
1260		self.substate.created(address)
1261	}
1262
1263	fn inc_nonce(&mut self, address: H160) -> Result<(), ExitError> {
1264		let account_id = T::AddressMapping::into_account_id(address);
1265		T::AccountProvider::inc_account_nonce(&account_id);
1266		Ok(())
1267	}
1268
1269	fn set_storage(&mut self, address: H160, index: H256, value: H256) {
1270		// We cache the current value if this is the first time we modify it
1271		// in the transaction.
1272		use alloc::collections::btree_map::Entry;
1273		let original = self.read_effective_storage(address, index);
1274		if let Entry::Vacant(e) = self.original_storage.entry((address, index)) {
1275			// No need to cache if same value.
1276			if original != value {
1277				e.insert(original);
1278			}
1279		}
1280
1281		// Geth-parity: if this account has an active state override, writes
1282		// update the override map (the account's "fresh state"), not the
1283		// underlying trie. Subsequent SLOADs will observe the new value via
1284		// `read_effective_storage`, while the on-chain storage stays untouched.
1285		if let Some(slots) = self.state_override.get_mut(&address) {
1286			if value == H256::default() {
1287				slots.remove(&index);
1288			} else {
1289				slots.insert(index, value);
1290			}
1291			return;
1292		}
1293
1294		// Then we insert or remove the entry based on the value.
1295		if value == H256::default() {
1296			log::debug!(
1297				target: "evm",
1298				"Removing storage for {address:?} [index: {index:?}]"
1299			);
1300			<AccountStorages<T>>::remove(address, index);
1301		} else {
1302			log::debug!(
1303				target: "evm",
1304				"Updating storage for {address:?} [index: {index:?}, value: {value:?}]"
1305			);
1306			<AccountStorages<T>>::insert(address, index, value);
1307		}
1308	}
1309
1310	fn set_transient_storage(&mut self, address: H160, key: H256, value: H256) {
1311		self.transient_storage.insert((address, key), value);
1312	}
1313
1314	fn reset_storage(&mut self, address: H160) {
1315		// Geth-parity: once an account is destructed/recreated, the prior state
1316		// override (if any) no longer applies — subsequent SLOADs must fall back
1317		// to the (now-empty) persisted storage, returning zero until written.
1318		self.state_override.remove(&address);
1319		#[allow(deprecated)]
1320		let _ = <AccountStorages<T>>::remove_prefix(address, None);
1321	}
1322
1323	fn log(&mut self, address: H160, topics: Vec<H256>, data: Vec<u8>) {
1324		self.substate.log(address, topics, data)
1325	}
1326
1327	fn set_deleted(&mut self, address: H160) {
1328		self.substate.set_deleted(address)
1329	}
1330
1331	fn set_created(&mut self, address: H160) {
1332		self.substate.set_created(address);
1333	}
1334
1335	fn set_code(
1336		&mut self,
1337		address: H160,
1338		code: Vec<u8>,
1339		caller: Option<H160>,
1340	) -> Result<(), ExitError> {
1341		log::debug!(
1342			target: "evm",
1343			"Inserting code ({} bytes) at {:?}",
1344			code.len(),
1345			address
1346		);
1347
1348		Pallet::<T>::create_account(address, code, caller)
1349	}
1350
1351	fn set_delegation(
1352		&mut self,
1353		authority: H160,
1354		delegation: evm::delegation::Delegation,
1355	) -> Result<(), ExitError> {
1356		log::debug!(
1357			target: "evm",
1358			"Inserting delegation (23 bytes) at {:?}",
1359			delegation.address()
1360		);
1361
1362		let meta = crate::CodeMetadata::from_code(&delegation.to_bytes());
1363		<AccountCodesMetadata<T>>::insert(authority, meta);
1364		<AccountCodes<T>>::insert(authority, delegation.to_bytes());
1365		Ok(())
1366	}
1367
1368	fn reset_delegation(&mut self, address: H160) -> Result<(), ExitError> {
1369		log::debug!(
1370			target: "evm",
1371			"Resetting delegation at {address:?}"
1372		);
1373
1374		Pallet::<T>::remove_account_code(&address);
1375		Ok(())
1376	}
1377
1378	fn transfer(&mut self, transfer: Transfer) -> Result<(), ExitError> {
1379		let source = T::AddressMapping::into_account_id(transfer.source);
1380		let target = T::AddressMapping::into_account_id(transfer.target);
1381		T::Currency::transfer(
1382			&source,
1383			&target,
1384			transfer
1385				.value
1386				.try_into()
1387				.map_err(|_| ExitError::OutOfFund)?,
1388			ExistenceRequirement::AllowDeath,
1389		)
1390		.map_err(|_| ExitError::OutOfFund)
1391	}
1392
1393	fn reset_balance(&mut self, _address: H160) {
1394		// Do nothing on reset balance in Substrate.
1395		//
1396		// This function exists in EVM because a design issue
1397		// (arguably a bug) in SELFDESTRUCT that can cause total
1398		// issuance to be reduced. We do not need to replicate this.
1399	}
1400
1401	fn touch(&mut self, _address: H160) {
1402		// Do nothing on touch in Substrate.
1403		//
1404		// EVM pallet considers all accounts to exist, and distinguish
1405		// only empty and non-empty accounts. This avoids many of the
1406		// subtle issues in EIP-161.
1407	}
1408
1409	fn is_cold(&self, address: H160) -> bool {
1410		self.substate
1411			.recursive_is_cold(&|a| a.accessed_addresses.contains(&address))
1412	}
1413
1414	fn is_storage_cold(&self, address: H160, key: H256) -> bool {
1415		self.substate
1416			.recursive_is_cold(&|a: &Accessed| a.accessed_storage.contains(&(address, key)))
1417	}
1418
1419	fn code_size(&self, address: H160) -> U256 {
1420		// EIP-7702: EXTCODESIZE does NOT follow delegations
1421		// Return the actual code size at the address, including delegation designators
1422		U256::from(<Pallet<T>>::account_code_metadata(address).size)
1423	}
1424
1425	fn code_hash(&self, address: H160) -> H256 {
1426		// EIP-7702: EXTCODEHASH does NOT follow delegations
1427		// Return the hash of the actual code at the address, including delegation designators
1428		<Pallet<T>>::account_code_metadata(address).hash
1429	}
1430
1431	fn record_external_operation(&mut self, op: evm::ExternalOperation) -> Result<(), ExitError> {
1432		let size_limit: u64 = self
1433			.metadata()
1434			.gasometer()
1435			.config()
1436			.create_contract_limit
1437			.unwrap_or_default() as u64;
1438		let (weight_info, recorded) = self.info_mut();
1439
1440		if let Some(weight_info) = weight_info {
1441			match op {
1442				ExternalOperation::AccountBasicRead => {
1443					weight_info.try_record_proof_size_or_fail(ACCOUNT_BASIC_PROOF_SIZE)?
1444				}
1445				ExternalOperation::AddressCodeRead(address) => {
1446					Self::record_address_code_read(address, weight_info, recorded, size_limit)?;
1447				}
1448				ExternalOperation::IsEmpty => {
1449					weight_info.try_record_proof_size_or_fail(IS_EMPTY_CHECK_PROOF_SIZE)?
1450				}
1451				ExternalOperation::Write(len) => {
1452					weight_info.try_record_proof_size_or_fail(WRITE_PROOF_SIZE)?;
1453
1454					if let Some(storage_meter) = self.storage_meter.as_mut() {
1455						// Record the number of bytes written to storage when deploying a contract.
1456						let storage_growth = ACCOUNT_CODES_KEY_SIZE
1457							.saturating_add(ACCOUNT_CODES_METADATA_PROOF_SIZE)
1458							.saturating_add(len.as_u64());
1459						storage_meter
1460							.record(storage_growth)
1461							.map_err(|_| ExitError::OutOfGas)?;
1462					}
1463				}
1464				ExternalOperation::DelegationResolution(address) => {
1465					Self::record_address_code_read(address, weight_info, recorded, size_limit)?;
1466				}
1467			};
1468		}
1469		Ok(())
1470	}
1471
1472	fn record_external_dynamic_opcode_cost(
1473		&mut self,
1474		opcode: Opcode,
1475		gas_cost: GasCost,
1476		target: evm::gasometer::StorageTarget,
1477	) -> Result<(), ExitError> {
1478		if let Some(storage_meter) = self.storage_meter.as_mut() {
1479			storage_meter
1480				.record_dynamic_opcode_cost(opcode, gas_cost, target)
1481				.map_err(|_| ExitError::OutOfGas)?;
1482		}
1483
1484		// If account code or storage slot is in the overlay it is already accounted for and early exit
1485		let accessed_storage: Option<AccessedStorage> = match target {
1486			StorageTarget::Address(address) => {
1487				if self.recorded().account_codes.contains(&address) {
1488					return Ok(());
1489				} else {
1490					Some(AccessedStorage::AccountCodes(address))
1491				}
1492			}
1493			StorageTarget::Slot(address, index) => {
1494				if self
1495					.recorded()
1496					.account_storages
1497					.contains_key(&(address, index))
1498				{
1499					return Ok(());
1500				} else {
1501					Some(AccessedStorage::AccountStorages((address, index)))
1502				}
1503			}
1504			_ => None,
1505		};
1506
1507		let size_limit: u64 = self
1508			.metadata()
1509			.gasometer()
1510			.config()
1511			.create_contract_limit
1512			.unwrap_or_default() as u64;
1513		let (weight_info, recorded) = self.info_mut();
1514
1515		if let Some(weight_info) = weight_info {
1516			// proof_size_limit is None indicates no need to record proof size, return directly.
1517			if weight_info.proof_size_limit.is_none() {
1518				return Ok(());
1519			};
1520
1521			let mut record_account_codes_proof_size =
1522				|address: H160, empty_check: bool| -> Result<(), ExitError> {
1523					let mut base_size = ACCOUNT_CODES_METADATA_PROOF_SIZE;
1524					if empty_check {
1525						base_size = base_size.saturating_add(IS_EMPTY_CHECK_PROOF_SIZE);
1526					}
1527					weight_info.try_record_proof_size_or_fail(base_size)?;
1528
1529					if let Some(meta) = <AccountCodesMetadata<T>>::get(address) {
1530						weight_info.try_record_proof_size_or_fail(meta.size)?;
1531					} else if let Some(remaining_proof_size) = weight_info.remaining_proof_size() {
1532						let pre_size = remaining_proof_size.min(size_limit);
1533						weight_info.try_record_proof_size_or_fail(pre_size)?;
1534
1535						let actual_size = Pallet::<T>::account_code_metadata(address).size;
1536						if actual_size > pre_size {
1537							return Err(ExitError::OutOfGas);
1538						}
1539						// Refund unused proof size
1540						weight_info.refund_proof_size(pre_size.saturating_sub(actual_size));
1541					}
1542
1543					Ok(())
1544				};
1545
1546			// Proof size is fixed length for writes (a 32-byte hash in a merkle trie), and
1547			// the full key/value for reads. For read and writes over the same storage, the full value
1548			// is included.
1549			// For cold reads involving code (call, callcode, staticcall and delegatecall):
1550			//	- We depend on https://github.com/paritytech/frontier/pull/893
1551			//	- Try to get the cached size or compute it on the fly
1552			//	- We record the actual size after caching, refunding the difference between it and the initially deducted
1553			//	contract size limit.
1554			match opcode {
1555				Opcode::BALANCE => {
1556					weight_info.try_record_proof_size_or_fail(ACCOUNT_BASIC_PROOF_SIZE)?;
1557				}
1558				Opcode::EXTCODESIZE | Opcode::EXTCODECOPY | Opcode::EXTCODEHASH => {
1559					if let Some(AccessedStorage::AccountCodes(address)) = accessed_storage {
1560						record_account_codes_proof_size(address, false)?;
1561						recorded.account_codes.push(address);
1562					}
1563				}
1564				Opcode::CALLCODE | Opcode::CALL | Opcode::DELEGATECALL | Opcode::STATICCALL => {
1565					if let Some(AccessedStorage::AccountCodes(address)) = accessed_storage {
1566						record_account_codes_proof_size(address, true)?;
1567						recorded.account_codes.push(address);
1568					}
1569				}
1570				Opcode::SLOAD => {
1571					if let Some(AccessedStorage::AccountStorages((address, index))) =
1572						accessed_storage
1573					{
1574						weight_info.try_record_proof_size_or_fail(ACCOUNT_STORAGE_PROOF_SIZE)?;
1575						recorded.account_storages.insert((address, index), true);
1576					}
1577				}
1578				Opcode::SSTORE => {
1579					if let Some(AccessedStorage::AccountStorages((address, index))) =
1580						accessed_storage
1581					{
1582						let size = WRITE_PROOF_SIZE.saturating_add(ACCOUNT_STORAGE_PROOF_SIZE);
1583						weight_info.try_record_proof_size_or_fail(size)?;
1584						recorded.account_storages.insert((address, index), true);
1585					}
1586				}
1587				Opcode::CREATE | Opcode::CREATE2 => {
1588					weight_info.try_record_proof_size_or_fail(WRITE_PROOF_SIZE)?;
1589				}
1590				// When calling SUICIDE a target account will receive the self destructing
1591				// address's balance. We need to account for both:
1592				//	- Target basic account read
1593				//	- 5 bytes of `decode_len`
1594				Opcode::SUICIDE => {
1595					weight_info.try_record_proof_size_or_fail(IS_EMPTY_CHECK_PROOF_SIZE)?;
1596				}
1597				// Rest of dynamic opcodes that do not involve proof size recording, do nothing
1598				_ => return Ok(()),
1599			};
1600		}
1601
1602		Ok(())
1603	}
1604
1605	fn record_external_cost(
1606		&mut self,
1607		ref_time: Option<u64>,
1608		proof_size: Option<u64>,
1609		storage_growth: Option<u64>,
1610	) -> Result<(), ExitError> {
1611		let weight_info = if let (Some(weight_info), _) = self.info_mut() {
1612			weight_info
1613		} else {
1614			return Ok(());
1615		};
1616
1617		if let Some(amount) = ref_time {
1618			weight_info.try_record_ref_time_or_fail(amount)?;
1619		}
1620		if let Some(amount) = proof_size {
1621			weight_info.try_record_proof_size_or_fail(amount)?;
1622		}
1623		if let Some(storage_meter) = self.storage_meter.as_mut() {
1624			if let Some(amount) = storage_growth {
1625				storage_meter
1626					.record(amount)
1627					.map_err(|_| ExitError::OutOfGas)?;
1628			}
1629		}
1630		Ok(())
1631	}
1632
1633	fn refund_external_cost(&mut self, ref_time: Option<u64>, proof_size: Option<u64>) {
1634		if let Some(weight_info) = self.weight_info.as_mut() {
1635			if let Some(amount) = ref_time {
1636				weight_info.refund_ref_time(amount);
1637			}
1638			if let Some(amount) = proof_size {
1639				weight_info.refund_proof_size(amount);
1640			}
1641		}
1642	}
1643}
1644
1645#[cfg(feature = "forbid-evm-reentrancy")]
1646#[cfg(test)]
1647mod tests {
1648	use super::*;
1649	use crate::mock::{MockPrecompileSet, Test};
1650	use evm::ExitSucceed;
1651	use sp_io::TestExternalities;
1652
1653	macro_rules! assert_matches {
1654		( $left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )? $(,)? ) => {
1655			match $left {
1656				$( $pattern )|+ $( if $guard )? => {}
1657				ref left_val => panic!("assertion failed: `{:?}` does not match `{}`",
1658					left_val, stringify!($($pattern)|+ $(if $guard)?))
1659			}
1660		};
1661		( $left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )?, $($arg:tt)+ ) => {
1662			match $left {
1663				$( $pattern )|+ $( if $guard )? => {}
1664				ref left_val => panic!("assertion failed: `{:?}` does not match `{}`",
1665					left_val, stringify!($($pattern)|+ $(if $guard)?))
1666			}
1667		};
1668	}
1669
1670	#[test]
1671	fn test_evm_reentrancy() {
1672		TestExternalities::new_empty().execute_with(|| {
1673			let config = evm::Config::istanbul();
1674
1675			let measured_proof_size_before = get_proof_size().unwrap_or_default();
1676			// Should fail with the appropriate error if there is reentrancy
1677			let res = Runner::<Test>::execute(
1678				H160::default(),
1679				U256::default(),
1680				100_000,
1681				None,
1682				None,
1683				&config,
1684				&MockPrecompileSet,
1685				false,
1686				None,
1687				None,
1688				measured_proof_size_before,
1689				None,
1690				|_| {
1691					let measured_proof_size_before2 = get_proof_size().unwrap_or_default();
1692					let res = Runner::<Test>::execute(
1693						H160::default(),
1694						U256::default(),
1695						100_000,
1696						None,
1697						None,
1698						&config,
1699						&MockPrecompileSet,
1700						false,
1701						None,
1702						None,
1703						measured_proof_size_before2,
1704						None,
1705						|_| (ExitReason::Succeed(ExitSucceed::Stopped), ()),
1706					);
1707					assert_matches!(
1708						res,
1709						Err(RunnerError {
1710							error: Error::<Test>::Reentrancy,
1711							..
1712						})
1713					);
1714					(ExitReason::Error(ExitError::CallTooDeep), ())
1715				},
1716			);
1717			assert_matches!(
1718				res,
1719				Ok(ExecutionInfoV2 {
1720					exit_reason: ExitReason::Error(ExitError::CallTooDeep),
1721					..
1722				})
1723			);
1724
1725			let measured_proof_size_before = get_proof_size().unwrap_or_default();
1726			// Should succeed if there is no reentrancy
1727			let res = Runner::<Test>::execute(
1728				H160::default(),
1729				U256::default(),
1730				100_000,
1731				None,
1732				None,
1733				&config,
1734				&MockPrecompileSet,
1735				false,
1736				None,
1737				None,
1738				measured_proof_size_before,
1739				None,
1740				|_| (ExitReason::Succeed(ExitSucceed::Stopped), ()),
1741			);
1742			assert!(res.is_ok());
1743		});
1744	}
1745}