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