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