1use 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};
33use cumulus_primitives_storage_weight_reclaim::get_proof_size;
35use 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;
45use fp_evm::{
47 AccessedStorage, CallInfo, CreateInfo, ExecutionInfoV2, IsPrecompileResult, Log, PrecompileSet,
48 StateOverride, Vicinity, WeightInfo, ACCOUNT_BASIC_PROOF_SIZE, ACCOUNT_CODES_KEY_SIZE,
49 ACCOUNT_CODES_METADATA_PROOF_SIZE, ACCOUNT_STORAGE_PROOF_SIZE, IS_EMPTY_CHECK_PROOF_SIZE,
50 WRITE_PROOF_SIZE,
51};
52
53use super::meter::StorageMeter;
54use crate::{
55 runner::Runner as RunnerT, AccountCodes, AccountCodesMetadata, AccountProvider,
56 AccountStorages, AddressMapping, BalanceOf, BlockHashMapping, Config, EnsureCreateOrigin,
57 Error, Event, FeeCalculator, OnChargeEVMTransaction, OnCreate, Pallet, RunnerError,
58};
59
60#[cfg(feature = "forbid-evm-reentrancy")]
61environmental::environmental!(IN_EVM: bool);
62
63#[derive(Default)]
64pub struct Runner<T: Config> {
65 _marker: PhantomData<T>,
66}
67
68impl<T: Config> Runner<T>
69where
70 BalanceOf<T>: TryFrom<U256> + Into<U256>,
71{
72 #[allow(clippy::let_and_return)]
73 fn execute<'config, 'precompiles, F, R>(
75 source: H160,
76 value: U256,
77 gas_limit: u64,
78 max_fee_per_gas: Option<U256>,
79 max_priority_fee_per_gas: Option<U256>,
80 config: &'config evm::Config,
81 precompiles: &'precompiles T::PrecompilesType,
82 is_transactional: bool,
83 weight_limit: Option<Weight>,
84 proof_size_base_cost: Option<u64>,
85 measured_proof_size_before: u64,
86 state_override: StateOverride,
87 f: F,
88 ) -> Result<ExecutionInfoV2<R>, RunnerError<Error<T>>>
89 where
90 F: FnOnce(
91 &mut StackExecutor<
92 'config,
93 'precompiles,
94 SubstrateStackState<'_, 'config, T>,
95 T::PrecompilesType,
96 >,
97 ) -> (ExitReason, R),
98 R: Default,
99 {
100 let (base_fee, weight) = T::FeeCalculator::min_gas_price();
101
102 #[cfg(not(feature = "forbid-evm-reentrancy"))]
103 let res = Self::execute_inner(
104 source,
105 value,
106 gas_limit,
107 max_fee_per_gas,
108 max_priority_fee_per_gas,
109 config,
110 precompiles,
111 is_transactional,
112 f,
113 base_fee,
114 weight,
115 weight_limit,
116 proof_size_base_cost,
117 measured_proof_size_before,
118 state_override,
119 );
120
121 #[cfg(feature = "forbid-evm-reentrancy")]
122 let res = IN_EVM::using_once(&mut false, || {
123 IN_EVM::with(|in_evm| {
124 if *in_evm {
125 return Err(RunnerError {
126 error: Error::<T>::Reentrancy,
127 weight,
128 });
129 }
130 *in_evm = true;
131 Ok(())
132 })
133 .unwrap_or(Ok(()))?;
135
136 sp_core::defer! {
138 IN_EVM::with(|in_evm| {
139 *in_evm = false;
140 });
141 }
142
143 Self::execute_inner(
144 source,
145 value,
146 gas_limit,
147 max_fee_per_gas,
148 max_priority_fee_per_gas,
149 config,
150 precompiles,
151 is_transactional,
152 f,
153 base_fee,
154 weight,
155 weight_limit,
156 proof_size_base_cost,
157 measured_proof_size_before,
158 state_override,
159 )
160 });
161
162 res
163 }
164
165 fn execute_inner<'config, 'precompiles, F, R>(
167 source: H160,
168 value: U256,
169 mut gas_limit: u64,
170 max_fee_per_gas: Option<U256>,
171 max_priority_fee_per_gas: Option<U256>,
172 config: &'config evm::Config,
173 precompiles: &'precompiles T::PrecompilesType,
174 is_transactional: bool,
175 f: F,
176 base_fee: U256,
177 weight: Weight,
178 weight_limit: Option<Weight>,
179 proof_size_base_cost: Option<u64>,
180 measured_proof_size_before: u64,
181 state_override: StateOverride,
182 ) -> Result<ExecutionInfoV2<R>, RunnerError<Error<T>>>
183 where
184 F: FnOnce(
185 &mut StackExecutor<
186 'config,
187 'precompiles,
188 SubstrateStackState<'_, 'config, T>,
189 T::PrecompilesType,
190 >,
191 ) -> (ExitReason, R),
192 R: Default,
193 {
194 let maybe_weight_info =
196 match WeightInfo::new_from_weight_limit(weight_limit, proof_size_base_cost) {
197 Ok(weight_info) => weight_info,
198 Err(_) => {
199 return Ok(ExecutionInfoV2 {
200 exit_reason: ExitError::OutOfGas.into(),
201 value: Default::default(),
202 used_gas: fp_evm::UsedGas {
203 standard: gas_limit.into(),
204 effective: gas_limit.into(),
205 },
206 weight_info: None,
207 logs: Default::default(),
208 })
209 }
210 };
211 match precompiles.is_precompile(source, gas_limit) {
214 IsPrecompileResult::Answer { extra_cost, .. } => {
215 gas_limit = gas_limit.saturating_sub(extra_cost);
216 }
217 IsPrecompileResult::OutOfGas => {
218 return Ok(ExecutionInfoV2 {
219 exit_reason: ExitError::OutOfGas.into(),
220 value: Default::default(),
221 used_gas: fp_evm::UsedGas {
222 standard: gas_limit.into(),
223 effective: gas_limit.into(),
224 },
225 weight_info: maybe_weight_info,
226 logs: Default::default(),
227 })
228 }
229 };
230
231 if is_transactional {
239 if let Some(metadata) = <AccountCodesMetadata<T>>::get(source) {
241 if metadata.size > 0 {
242 let is_delegation = metadata.size
244 == evm::delegation::EIP_7702_DELEGATION_SIZE as u64
245 && <AccountCodes<T>>::get(source)
246 .starts_with(evm::delegation::EIP_7702_DELEGATION_PREFIX);
247
248 if !is_delegation {
249 return Err(RunnerError {
250 error: Error::<T>::TransactionMustComeFromEOA,
251 weight,
252 });
253 }
254 }
255 }
256 }
257
258 let total_fee_per_gas = if is_transactional {
259 match (max_fee_per_gas, max_priority_fee_per_gas) {
260 (Some(max_fee), _) if max_fee.is_zero() => U256::zero(),
263 (Some(_), None) => base_fee,
265 (Some(max_fee_per_gas), Some(max_priority_fee_per_gas)) => {
268 let actual_priority_fee_per_gas = max_fee_per_gas
269 .saturating_sub(base_fee)
270 .min(max_priority_fee_per_gas);
271
272 base_fee.saturating_add(actual_priority_fee_per_gas)
273 }
274 _ => {
275 return Err(RunnerError {
276 error: Error::<T>::GasPriceTooLow,
277 weight,
278 })
279 }
280 }
281 } else {
282 Default::default()
284 };
285
286 let total_fee =
288 total_fee_per_gas
289 .checked_mul(U256::from(gas_limit))
290 .ok_or(RunnerError {
291 error: Error::<T>::FeeOverflow,
292 weight,
293 })?;
294
295 let fee = T::OnChargeTransaction::withdraw_fee(&source, total_fee)
297 .map_err(|e| RunnerError { error: e, weight })?;
298
299 let vicinity = Vicinity {
300 gas_price: base_fee,
301 origin: source,
302 };
303
304 let storage_growth_ratio = T::GasLimitStorageGrowthRatio::get();
306 let storage_limit = if storage_growth_ratio > 0 {
307 let storage_limit = gas_limit.saturating_div(storage_growth_ratio);
308 Some(storage_limit)
309 } else {
310 None
311 };
312
313 let metadata = StackSubstateMetadata::new(gas_limit, config);
314 let state = SubstrateStackState::new(
315 &vicinity,
316 metadata,
317 maybe_weight_info,
318 storage_limit,
319 state_override,
320 );
321 let mut executor = StackExecutor::new_with_precompiles(state, config, precompiles);
322
323 let (reason, retv, used_gas, effective_gas) = fp_evm::handle_storage_oog::<R, _>(
325 gas_limit,
326 || {
327 let (reason, retv) = f(&mut executor);
328
329 let storage_gas = match &executor.state().storage_meter {
331 Some(storage_meter) => storage_meter.storage_to_gas(storage_growth_ratio),
332 None => 0,
333 };
334
335 let estimated_proof_size = executor
336 .state()
337 .weight_info()
338 .unwrap_or_default()
339 .proof_size_usage
340 .unwrap_or_default();
341
342 let actual_proof_size = if let Some(measured_proof_size_after) = get_proof_size() {
345 let actual_proof_size =
347 proof_size_base_cost.unwrap_or_default().saturating_add(
348 measured_proof_size_after.saturating_sub(measured_proof_size_before),
349 );
350
351 log::trace!(
352 target: "evm",
353 "Proof size computation: (estimated: {estimated_proof_size}, actual: {actual_proof_size})"
354 );
355
356 if actual_proof_size > estimated_proof_size {
362 log::debug!(
363 target: "evm",
364 "Proof size underestimation detected! (estimated: {estimated_proof_size}, actual: {actual_proof_size}, diff: {})",
365 actual_proof_size.saturating_sub(estimated_proof_size)
366 );
367 estimated_proof_size
368 } else {
369 actual_proof_size
370 }
371 } else {
372 estimated_proof_size
373 };
374
375 let pov_gas = actual_proof_size.saturating_mul(T::GasLimitPovSizeRatio::get());
377 let used_gas = executor.used_gas();
378 let effective_gas = core::cmp::max(core::cmp::max(used_gas, pov_gas), storage_gas);
379
380 log::debug!(
381 target: "evm",
382 "Calculating effective gas: max(used: {used_gas}, pov: {pov_gas}, storage: {storage_gas}) = {effective_gas}"
383 );
384
385 (reason, retv, used_gas, U256::from(effective_gas))
386 },
387 );
388
389 let actual_fee = effective_gas.saturating_mul(total_fee_per_gas);
390 let actual_base_fee = effective_gas.saturating_mul(base_fee);
391
392 log::debug!(
393 target: "evm",
394 "Execution {reason:?} [source: {source:?}, value: {value}, gas_limit: {gas_limit}, actual_fee: {actual_fee}, used_gas: {used_gas}, effective_gas: {effective_gas}, base_fee: {base_fee}, total_fee_per_gas: {total_fee_per_gas}, is_transactional: {is_transactional}]"
395 );
396 let actual_priority_fee = T::OnChargeTransaction::correct_and_deposit_fee(
418 &source,
419 actual_fee,
421 actual_base_fee,
423 fee,
425 );
426 T::OnChargeTransaction::pay_priority_fee(actual_priority_fee);
427
428 let state = executor.into_state();
429
430 for address in &state.substate.deletes {
431 log::debug!(
432 target: "evm",
433 "Deleting account at {address:?}"
434 );
435 Pallet::<T>::remove_account(address)
436 }
437
438 for log in &state.substate.logs {
439 log::trace!(
440 target: "evm",
441 "Inserting log for {:?}, topics ({}) {:?}, data ({}): {:?}]",
442 log.address,
443 log.topics.len(),
444 log.topics,
445 log.data.len(),
446 log.data
447 );
448 Pallet::<T>::deposit_event(Event::<T>::Log {
449 log: Log {
450 address: log.address,
451 topics: log.topics.clone(),
452 data: log.data.clone(),
453 },
454 });
455 }
456
457 Ok(ExecutionInfoV2 {
458 value: retv,
459 exit_reason: reason,
460 used_gas: fp_evm::UsedGas {
461 standard: used_gas.into(),
462 effective: effective_gas,
463 },
464 weight_info: state.weight_info(),
465 logs: state.substate.logs,
466 })
467 }
468}
469
470impl<T: Config> RunnerT<T> for Runner<T>
471where
472 BalanceOf<T>: TryFrom<U256> + Into<U256>,
473{
474 type Error = Error<T>;
475
476 fn validate(
477 source: H160,
478 target: Option<H160>,
479 input: Vec<u8>,
480 value: U256,
481 gas_limit: u64,
482 max_fee_per_gas: Option<U256>,
483 max_priority_fee_per_gas: Option<U256>,
484 nonce: Option<U256>,
485 access_list: Vec<(H160, Vec<H256>)>,
486 authorization_list: Vec<(U256, H160, U256, Option<H160>)>,
487 is_transactional: bool,
488 weight_limit: Option<Weight>,
489 proof_size_base_cost: Option<u64>,
490 evm_config: &evm::Config,
491 ) -> Result<(), RunnerError<Self::Error>> {
492 let (base_fee, mut weight) = T::FeeCalculator::min_gas_price();
493 let (source_account, inner_weight) = Pallet::<T>::account_basic(&source);
494 weight = weight.saturating_add(inner_weight);
495
496 let _ = fp_evm::CheckEvmTransaction::<Self::Error>::new(
497 fp_evm::CheckEvmTransactionConfig {
498 evm_config,
499 block_gas_limit: T::BlockGasLimit::get(),
500 transaction_gas_limit: T::TransactionGasLimit::get(),
501 base_fee,
502 chain_id: T::ChainId::get(),
503 is_transactional,
504 allow_unprotected_txs: true,
505 },
506 fp_evm::CheckEvmTransactionInput {
507 chain_id: Some(T::ChainId::get()),
508 to: target,
509 input,
510 nonce: nonce.unwrap_or(source_account.nonce),
511 gas_limit: gas_limit.into(),
512 gas_price: None,
513 max_fee_per_gas,
514 max_priority_fee_per_gas,
515 value,
516 access_list,
517 authorization_list,
518 },
519 weight_limit,
520 proof_size_base_cost,
521 )
522 .validate_in_block_for(&source_account)
523 .and_then(|v| v.with_base_fee())
524 .and_then(|v| v.with_balance_for(&source_account))
525 .map_err(|error| RunnerError { error, weight })?;
526 Ok(())
527 }
528
529 fn call(
530 source: H160,
531 target: H160,
532 input: Vec<u8>,
533 value: U256,
534 gas_limit: u64,
535 max_fee_per_gas: Option<U256>,
536 max_priority_fee_per_gas: Option<U256>,
537 nonce: Option<U256>,
538 access_list: Vec<(H160, Vec<H256>)>,
539 authorization_list: AuthorizationList,
540 is_transactional: bool,
541 validate: bool,
542 weight_limit: Option<Weight>,
543 proof_size_base_cost: Option<u64>,
544 state_override: StateOverride,
545 config: &evm::Config,
546 ) -> Result<CallInfo, RunnerError<Self::Error>> {
547 let measured_proof_size_before = get_proof_size().unwrap_or_default();
548
549 let authorization_list = authorization_list
550 .iter()
551 .map(|d| {
552 (
553 U256::from(d.chain_id),
554 d.address,
555 d.nonce,
556 d.authorizing_address().ok(),
557 )
558 })
559 .collect::<Vec<(U256, sp_core::H160, U256, Option<sp_core::H160>)>>();
560
561 if validate {
562 Self::validate(
563 source,
564 Some(target),
565 input.clone(),
566 value,
567 gas_limit,
568 max_fee_per_gas,
569 max_priority_fee_per_gas,
570 nonce,
571 access_list.clone(),
572 authorization_list.clone(),
573 is_transactional,
574 weight_limit,
575 proof_size_base_cost,
576 config,
577 )?;
578 }
579
580 let precompiles = T::PrecompilesValue::get();
581 Self::execute(
582 source,
583 value,
584 gas_limit,
585 max_fee_per_gas,
586 max_priority_fee_per_gas,
587 config,
588 &precompiles,
589 is_transactional,
590 weight_limit,
591 proof_size_base_cost,
592 measured_proof_size_before,
593 state_override,
594 |executor| {
595 executor.transact_call(
596 source,
597 target,
598 value,
599 input,
600 gas_limit,
601 access_list,
602 authorization_list,
603 )
604 },
605 )
606 }
607
608 fn create(
609 source: H160,
610 init: Vec<u8>,
611 value: U256,
612 gas_limit: u64,
613 max_fee_per_gas: Option<U256>,
614 max_priority_fee_per_gas: Option<U256>,
615 nonce: Option<U256>,
616 access_list: Vec<(H160, Vec<H256>)>,
617 authorization_list: AuthorizationList,
618 is_transactional: bool,
619 validate: bool,
620 weight_limit: Option<Weight>,
621 proof_size_base_cost: Option<u64>,
622 config: &evm::Config,
623 ) -> Result<CreateInfo, RunnerError<Self::Error>> {
624 let measured_proof_size_before = get_proof_size().unwrap_or_default();
625 let (_, weight) = T::FeeCalculator::min_gas_price();
626
627 T::CreateOriginFilter::check_create_origin(&source)
628 .map_err(|error| RunnerError { error, weight })?;
629
630 let authorization_list = authorization_list
631 .iter()
632 .map(|d| {
633 (
634 U256::from(d.chain_id),
635 d.address,
636 d.nonce,
637 d.authorizing_address().ok(),
638 )
639 })
640 .collect::<Vec<(U256, sp_core::H160, U256, Option<sp_core::H160>)>>();
641
642 if validate {
643 Self::validate(
644 source,
645 None,
646 init.clone(),
647 value,
648 gas_limit,
649 max_fee_per_gas,
650 max_priority_fee_per_gas,
651 nonce,
652 access_list.clone(),
653 authorization_list.clone(),
654 is_transactional,
655 weight_limit,
656 proof_size_base_cost,
657 config,
658 )?;
659 }
660
661 let precompiles = T::PrecompilesValue::get();
662 Self::execute(
663 source,
664 value,
665 gas_limit,
666 max_fee_per_gas,
667 max_priority_fee_per_gas,
668 config,
669 &precompiles,
670 is_transactional,
671 weight_limit,
672 proof_size_base_cost,
673 measured_proof_size_before,
674 None,
675 |executor| {
676 let address = executor.create_address(evm::CreateScheme::Legacy { caller: source });
677 T::OnCreate::on_create(source, address);
678 let (reason, _) = executor.transact_create(
679 source,
680 value,
681 init,
682 gas_limit,
683 access_list,
684 authorization_list,
685 );
686 (reason, address)
687 },
688 )
689 }
690
691 fn create2(
692 source: H160,
693 init: Vec<u8>,
694 salt: H256,
695 value: U256,
696 gas_limit: u64,
697 max_fee_per_gas: Option<U256>,
698 max_priority_fee_per_gas: Option<U256>,
699 nonce: Option<U256>,
700 access_list: Vec<(H160, Vec<H256>)>,
701 authorization_list: AuthorizationList,
702 is_transactional: bool,
703 validate: bool,
704 weight_limit: Option<Weight>,
705 proof_size_base_cost: Option<u64>,
706 config: &evm::Config,
707 ) -> Result<CreateInfo, RunnerError<Self::Error>> {
708 let measured_proof_size_before = get_proof_size().unwrap_or_default();
709 let (_, weight) = T::FeeCalculator::min_gas_price();
710
711 T::CreateOriginFilter::check_create_origin(&source)
712 .map_err(|error| RunnerError { error, weight })?;
713
714 let authorization_list = authorization_list
715 .iter()
716 .map(|d| {
717 (
718 U256::from(d.chain_id),
719 d.address,
720 d.nonce,
721 d.authorizing_address().ok(),
722 )
723 })
724 .collect::<Vec<(U256, sp_core::H160, U256, Option<sp_core::H160>)>>();
725
726 if validate {
727 Self::validate(
728 source,
729 None,
730 init.clone(),
731 value,
732 gas_limit,
733 max_fee_per_gas,
734 max_priority_fee_per_gas,
735 nonce,
736 access_list.clone(),
737 authorization_list.clone(),
738 is_transactional,
739 weight_limit,
740 proof_size_base_cost,
741 config,
742 )?;
743 }
744
745 let precompiles = T::PrecompilesValue::get();
746 let code_hash = H256::from(sp_io::hashing::keccak_256(&init));
747 Self::execute(
748 source,
749 value,
750 gas_limit,
751 max_fee_per_gas,
752 max_priority_fee_per_gas,
753 config,
754 &precompiles,
755 is_transactional,
756 weight_limit,
757 proof_size_base_cost,
758 measured_proof_size_before,
759 None,
760 |executor| {
761 let address = executor.create_address(evm::CreateScheme::Create2 {
762 caller: source,
763 code_hash,
764 salt,
765 });
766 T::OnCreate::on_create(source, address);
767 let (reason, _) = executor.transact_create2(
768 source,
769 value,
770 init,
771 salt,
772 gas_limit,
773 access_list,
774 authorization_list,
775 );
776 (reason, address)
777 },
778 )
779 }
780
781 fn create_force_address(
782 source: H160,
783 init: Vec<u8>,
784 value: U256,
785 gas_limit: u64,
786 max_fee_per_gas: Option<U256>,
787 max_priority_fee_per_gas: Option<U256>,
788 nonce: Option<U256>,
789 access_list: Vec<(H160, Vec<H256>)>,
790 authorization_list: AuthorizationList,
791 is_transactional: bool,
792 validate: bool,
793 weight_limit: Option<Weight>,
794 proof_size_base_cost: Option<u64>,
795 config: &evm::Config,
796 contract_address: H160,
797 ) -> Result<CreateInfo, RunnerError<Self::Error>> {
798 let measured_proof_size_before = get_proof_size().unwrap_or_default();
799 let (_, weight) = T::FeeCalculator::min_gas_price();
800
801 T::CreateOriginFilter::check_create_origin(&source)
802 .map_err(|error| RunnerError { error, weight })?;
803
804 let authorization_list = authorization_list
805 .iter()
806 .map(|d| {
807 (
808 U256::from(d.chain_id),
809 d.address,
810 d.nonce,
811 d.authorizing_address().ok(),
812 )
813 })
814 .collect::<Vec<(U256, sp_core::H160, U256, Option<sp_core::H160>)>>();
815
816 if validate {
817 Self::validate(
818 source,
819 None,
820 init.clone(),
821 value,
822 gas_limit,
823 max_fee_per_gas,
824 max_priority_fee_per_gas,
825 nonce,
826 access_list.clone(),
827 authorization_list.clone(),
828 is_transactional,
829 weight_limit,
830 proof_size_base_cost,
831 config,
832 )?;
833 }
834 let precompiles = T::PrecompilesValue::get();
835 Self::execute(
836 source,
837 value,
838 gas_limit,
839 max_fee_per_gas,
840 max_priority_fee_per_gas,
841 config,
842 &precompiles,
843 is_transactional,
844 weight_limit,
845 proof_size_base_cost,
846 measured_proof_size_before,
847 None,
848 |executor| {
849 T::OnCreate::on_create(source, contract_address);
850 let (reason, _) = executor.transact_create_force_address(
851 source,
852 value,
853 init,
854 gas_limit,
855 access_list,
856 authorization_list,
857 contract_address,
858 );
859 (reason, contract_address)
860 },
861 )
862 }
863}
864
865struct SubstrateStackSubstate<'config> {
866 metadata: StackSubstateMetadata<'config>,
867 deletes: BTreeSet<H160>,
868 creates: BTreeSet<H160>,
869 logs: Vec<Log>,
870 parent: Option<Box<SubstrateStackSubstate<'config>>>,
871}
872
873impl<'config> SubstrateStackSubstate<'config> {
874 pub fn metadata(&self) -> &StackSubstateMetadata<'config> {
875 &self.metadata
876 }
877
878 pub fn metadata_mut(&mut self) -> &mut StackSubstateMetadata<'config> {
879 &mut self.metadata
880 }
881
882 pub fn enter(&mut self, gas_limit: u64, is_static: bool) {
883 let mut entering = Self {
884 metadata: self.metadata.spit_child(gas_limit, is_static),
885 parent: None,
886 deletes: BTreeSet::new(),
887 creates: BTreeSet::new(),
888 logs: Vec::new(),
889 };
890 mem::swap(&mut entering, self);
891
892 self.parent = Some(Box::new(entering));
893
894 sp_io::storage::start_transaction();
895 }
896
897 pub fn exit_commit(&mut self) -> Result<(), ExitError> {
898 let mut exited = *self.parent.take().expect("Cannot commit on root substate");
899 mem::swap(&mut exited, self);
900
901 self.metadata.swallow_commit(exited.metadata)?;
902 self.logs.append(&mut exited.logs);
903 self.deletes.append(&mut exited.deletes);
904 self.creates.append(&mut exited.creates);
905 sp_io::storage::commit_transaction();
906 Ok(())
907 }
908
909 pub fn exit_revert(&mut self) -> Result<(), ExitError> {
910 let mut exited = *self.parent.take().expect("Cannot discard on root substate");
911 mem::swap(&mut exited, self);
912 self.metadata.swallow_revert(exited.metadata)?;
913
914 sp_io::storage::rollback_transaction();
915 Ok(())
916 }
917
918 pub fn exit_discard(&mut self) -> Result<(), ExitError> {
919 let mut exited = *self.parent.take().expect("Cannot discard on root substate");
920 mem::swap(&mut exited, self);
921 self.metadata.swallow_discard(exited.metadata)?;
922
923 sp_io::storage::rollback_transaction();
924 Ok(())
925 }
926
927 pub fn deleted(&self, address: H160) -> bool {
928 if self.deletes.contains(&address) {
929 return true;
930 }
931
932 if let Some(parent) = self.parent.as_ref() {
933 return parent.deleted(address);
934 }
935
936 false
937 }
938
939 pub fn created(&self, address: H160) -> bool {
940 if self.creates.contains(&address) {
941 return true;
942 }
943
944 if let Some(parent) = self.parent.as_ref() {
945 return parent.created(address);
946 }
947
948 false
949 }
950
951 pub fn set_deleted(&mut self, address: H160) {
952 self.deletes.insert(address);
953 }
954
955 pub fn set_created(&mut self, address: H160) {
956 self.creates.insert(address);
957 }
958
959 pub fn log(&mut self, address: H160, topics: Vec<H256>, data: Vec<u8>) {
960 self.logs.push(Log {
961 address,
962 topics,
963 data,
964 });
965 }
966
967 fn recursive_is_cold<F: Fn(&Accessed) -> bool>(&self, f: &F) -> bool {
968 let local_is_accessed = self.metadata.accessed().as_ref().map(f).unwrap_or(false);
969 if local_is_accessed {
970 false
971 } else {
972 self.parent
973 .as_ref()
974 .map(|p| p.recursive_is_cold(f))
975 .unwrap_or(true)
976 }
977 }
978}
979
980#[derive(Default, Clone, Eq, PartialEq)]
981pub struct Recorded {
982 account_codes: Vec<H160>,
983 account_storages: BTreeMap<(H160, H256), bool>,
984}
985
986pub struct SubstrateStackState<'vicinity, 'config, T> {
988 vicinity: &'vicinity Vicinity,
989 substate: SubstrateStackSubstate<'config>,
990 original_storage: BTreeMap<(H160, H256), H256>,
991 transient_storage: BTreeMap<(H160, H256), H256>,
992 state_override: BTreeMap<H160, BTreeMap<H256, H256>>,
997 state_override_journal: Vec<Option<BTreeMap<H160, BTreeMap<H256, H256>>>>,
1002 recorded: Recorded,
1003 weight_info: Option<WeightInfo>,
1004 storage_meter: Option<StorageMeter>,
1005 _marker: PhantomData<T>,
1006}
1007
1008impl<'vicinity, 'config, T: Config> SubstrateStackState<'vicinity, 'config, T> {
1009 pub fn new(
1011 vicinity: &'vicinity Vicinity,
1012 metadata: StackSubstateMetadata<'config>,
1013 weight_info: Option<WeightInfo>,
1014 storage_limit: Option<u64>,
1015 state_override: StateOverride,
1016 ) -> Self {
1017 let storage_meter = storage_limit.map(StorageMeter::new);
1018 let state_override = state_override
1019 .map(|per_address| {
1020 per_address
1021 .into_iter()
1022 .map(|(address, slots)| (address, slots.into_iter().collect()))
1023 .collect::<BTreeMap<H160, BTreeMap<H256, H256>>>()
1024 })
1025 .unwrap_or_default();
1026 Self {
1027 vicinity,
1028 substate: SubstrateStackSubstate {
1029 metadata,
1030 deletes: BTreeSet::new(),
1031 creates: BTreeSet::new(),
1032 logs: Vec::new(),
1033 parent: None,
1034 },
1035 _marker: PhantomData,
1036 original_storage: BTreeMap::new(),
1037 transient_storage: BTreeMap::new(),
1038 state_override,
1039 state_override_journal: Vec::new(),
1040 recorded: Default::default(),
1041 weight_info,
1042 storage_meter,
1043 }
1044 }
1045
1046 fn read_effective_storage(&self, address: H160, index: H256) -> H256 {
1052 if let Some(slots) = self.state_override.get(&address) {
1053 return slots.get(&index).copied().unwrap_or_default();
1054 }
1055 <AccountStorages<T>>::get(address, index)
1056 }
1057
1058 pub fn weight_info(&self) -> Option<WeightInfo> {
1059 self.weight_info
1060 }
1061
1062 pub fn recorded(&self) -> &Recorded {
1063 &self.recorded
1064 }
1065
1066 pub fn info_mut(&mut self) -> (&mut Option<WeightInfo>, &mut Recorded) {
1067 (&mut self.weight_info, &mut self.recorded)
1068 }
1069
1070 fn record_address_code_read(
1071 address: H160,
1072 weight_info: &mut WeightInfo,
1073 recorded: &mut Recorded,
1074 create_contract_limit: u64,
1075 ) -> Result<(), ExitError> {
1076 let maybe_record = !recorded.account_codes.contains(&address);
1077 if maybe_record {
1079 weight_info.try_record_proof_size_or_fail(IS_EMPTY_CHECK_PROOF_SIZE)?;
1083 if <AccountCodes<T>>::decode_len(address).unwrap_or(0) == 0 {
1084 return Ok(());
1085 }
1086
1087 weight_info.try_record_proof_size_or_fail(ACCOUNT_CODES_METADATA_PROOF_SIZE)?;
1088 if let Some(meta) = <AccountCodesMetadata<T>>::get(address) {
1089 weight_info.try_record_proof_size_or_fail(meta.size)?;
1090 } else {
1091 weight_info.try_record_proof_size_or_fail(create_contract_limit)?;
1092
1093 let actual_size = Pallet::<T>::account_code_metadata(address).size;
1094 if actual_size > create_contract_limit {
1095 fp_evm::set_storage_oog();
1096 return Err(ExitError::OutOfGas);
1097 }
1098 weight_info.refund_proof_size(create_contract_limit.saturating_sub(actual_size));
1100 }
1101 recorded.account_codes.push(address);
1102 }
1103 Ok(())
1104 }
1105}
1106
1107impl<T: Config> BackendT for SubstrateStackState<'_, '_, T>
1108where
1109 BalanceOf<T>: TryFrom<U256> + Into<U256>,
1110{
1111 fn gas_price(&self) -> U256 {
1112 self.vicinity.gas_price
1113 }
1114 fn origin(&self) -> H160 {
1115 self.vicinity.origin
1116 }
1117
1118 fn block_hash(&self, number: U256) -> H256 {
1119 if number > U256::from(u32::MAX) {
1120 H256::default()
1121 } else {
1122 T::BlockHashMapping::block_hash(number.as_u32())
1123 }
1124 }
1125
1126 fn block_number(&self) -> U256 {
1127 let number: u128 = frame_system::Pallet::<T>::block_number().unique_saturated_into();
1128 U256::from(number)
1129 }
1130
1131 fn block_coinbase(&self) -> H160 {
1132 Pallet::<T>::find_author()
1133 }
1134
1135 fn block_timestamp(&self) -> U256 {
1136 let now: u128 = T::Timestamp::now().unique_saturated_into();
1137 U256::from(now / 1000)
1138 }
1139
1140 fn block_difficulty(&self) -> U256 {
1141 U256::zero()
1142 }
1143
1144 fn block_randomness(&self) -> Option<H256> {
1145 None
1146 }
1147
1148 fn block_gas_limit(&self) -> U256 {
1149 T::BlockGasLimit::get()
1150 }
1151
1152 fn block_base_fee_per_gas(&self) -> U256 {
1153 let (base_fee, _) = T::FeeCalculator::min_gas_price();
1154 base_fee
1155 }
1156
1157 fn chain_id(&self) -> U256 {
1158 U256::from(T::ChainId::get())
1159 }
1160
1161 fn exists(&self, _address: H160) -> bool {
1162 true
1163 }
1164
1165 fn basic(&self, address: H160) -> evm::backend::Basic {
1166 let (account, _) = Pallet::<T>::account_basic(&address);
1167
1168 evm::backend::Basic {
1169 balance: account.balance,
1170 nonce: account.nonce,
1171 }
1172 }
1173
1174 fn code(&self, address: H160) -> Vec<u8> {
1175 <AccountCodes<T>>::get(address)
1176 }
1177
1178 fn storage(&self, address: H160, index: H256) -> H256 {
1179 self.read_effective_storage(address, index)
1180 }
1181
1182 fn transient_storage(&self, address: H160, index: H256) -> H256 {
1183 self.transient_storage
1184 .get(&(address, index))
1185 .copied()
1186 .unwrap_or_default()
1187 }
1188
1189 fn original_storage(&self, address: H160, index: H256) -> Option<H256> {
1190 Some(
1191 self.original_storage
1192 .get(&(address, index))
1193 .cloned()
1194 .unwrap_or_else(|| self.read_effective_storage(address, index)),
1195 )
1196 }
1197}
1198
1199impl<'config, T: Config> StackStateT<'config> for SubstrateStackState<'_, 'config, T>
1200where
1201 BalanceOf<T>: TryFrom<U256> + Into<U256>,
1202{
1203 fn metadata(&self) -> &StackSubstateMetadata<'config> {
1204 self.substate.metadata()
1205 }
1206
1207 fn metadata_mut(&mut self) -> &mut StackSubstateMetadata<'config> {
1208 self.substate.metadata_mut()
1209 }
1210
1211 fn enter(&mut self, gas_limit: u64, is_static: bool) {
1212 let snapshot = (!self.state_override.is_empty()).then(|| self.state_override.clone());
1219 self.state_override_journal.push(snapshot);
1220 self.substate.enter(gas_limit, is_static)
1221 }
1222
1223 fn exit_commit(&mut self) -> Result<(), ExitError> {
1224 let _ = self.state_override_journal.pop();
1226 self.substate.exit_commit()
1227 }
1228
1229 fn exit_revert(&mut self) -> Result<(), ExitError> {
1230 if let Some(snapshot) = self
1231 .state_override_journal
1232 .pop()
1233 .expect("exit_revert paired with enter; snapshot always pushed")
1234 {
1235 self.state_override = snapshot;
1236 }
1237 self.substate.exit_revert()
1238 }
1239
1240 fn exit_discard(&mut self) -> Result<(), ExitError> {
1241 if let Some(snapshot) = self
1242 .state_override_journal
1243 .pop()
1244 .expect("exit_discard paired with enter; snapshot always pushed")
1245 {
1246 self.state_override = snapshot;
1247 }
1248 self.substate.exit_discard()
1249 }
1250
1251 fn is_empty(&self, address: H160) -> bool {
1252 Pallet::<T>::is_account_empty(&address)
1253 }
1254
1255 fn deleted(&self, address: H160) -> bool {
1256 self.substate.deleted(address)
1257 }
1258
1259 fn created(&self, address: H160) -> bool {
1260 self.substate.created(address)
1261 }
1262
1263 fn inc_nonce(&mut self, address: H160) -> Result<(), ExitError> {
1264 let account_id = T::AddressMapping::into_account_id(address);
1265 T::AccountProvider::inc_account_nonce(&account_id);
1266 Ok(())
1267 }
1268
1269 fn set_storage(&mut self, address: H160, index: H256, value: H256) {
1270 use alloc::collections::btree_map::Entry;
1273 let original = self.read_effective_storage(address, index);
1274 if let Entry::Vacant(e) = self.original_storage.entry((address, index)) {
1275 if original != value {
1277 e.insert(original);
1278 }
1279 }
1280
1281 if let Some(slots) = self.state_override.get_mut(&address) {
1286 if value == H256::default() {
1287 slots.remove(&index);
1288 } else {
1289 slots.insert(index, value);
1290 }
1291 return;
1292 }
1293
1294 if value == H256::default() {
1296 log::debug!(
1297 target: "evm",
1298 "Removing storage for {address:?} [index: {index:?}]"
1299 );
1300 <AccountStorages<T>>::remove(address, index);
1301 } else {
1302 log::debug!(
1303 target: "evm",
1304 "Updating storage for {address:?} [index: {index:?}, value: {value:?}]"
1305 );
1306 <AccountStorages<T>>::insert(address, index, value);
1307 }
1308 }
1309
1310 fn set_transient_storage(&mut self, address: H160, key: H256, value: H256) {
1311 self.transient_storage.insert((address, key), value);
1312 }
1313
1314 fn reset_storage(&mut self, address: H160) {
1315 self.state_override.remove(&address);
1319 #[allow(deprecated)]
1320 let _ = <AccountStorages<T>>::remove_prefix(address, None);
1321 }
1322
1323 fn log(&mut self, address: H160, topics: Vec<H256>, data: Vec<u8>) {
1324 self.substate.log(address, topics, data)
1325 }
1326
1327 fn set_deleted(&mut self, address: H160) {
1328 self.substate.set_deleted(address)
1329 }
1330
1331 fn set_created(&mut self, address: H160) {
1332 self.substate.set_created(address);
1333 }
1334
1335 fn set_code(
1336 &mut self,
1337 address: H160,
1338 code: Vec<u8>,
1339 caller: Option<H160>,
1340 ) -> Result<(), ExitError> {
1341 log::debug!(
1342 target: "evm",
1343 "Inserting code ({} bytes) at {:?}",
1344 code.len(),
1345 address
1346 );
1347
1348 Pallet::<T>::create_account(address, code, caller)
1349 }
1350
1351 fn set_delegation(
1352 &mut self,
1353 authority: H160,
1354 delegation: evm::delegation::Delegation,
1355 ) -> Result<(), ExitError> {
1356 log::debug!(
1357 target: "evm",
1358 "Inserting delegation (23 bytes) at {:?}",
1359 delegation.address()
1360 );
1361
1362 let meta = crate::CodeMetadata::from_code(&delegation.to_bytes());
1363 <AccountCodesMetadata<T>>::insert(authority, meta);
1364 <AccountCodes<T>>::insert(authority, delegation.to_bytes());
1365 Ok(())
1366 }
1367
1368 fn reset_delegation(&mut self, address: H160) -> Result<(), ExitError> {
1369 log::debug!(
1370 target: "evm",
1371 "Resetting delegation at {address:?}"
1372 );
1373
1374 Pallet::<T>::remove_account_code(&address);
1375 Ok(())
1376 }
1377
1378 fn transfer(&mut self, transfer: Transfer) -> Result<(), ExitError> {
1379 let source = T::AddressMapping::into_account_id(transfer.source);
1380 let target = T::AddressMapping::into_account_id(transfer.target);
1381 T::Currency::transfer(
1382 &source,
1383 &target,
1384 transfer
1385 .value
1386 .try_into()
1387 .map_err(|_| ExitError::OutOfFund)?,
1388 ExistenceRequirement::AllowDeath,
1389 )
1390 .map_err(|_| ExitError::OutOfFund)
1391 }
1392
1393 fn reset_balance(&mut self, _address: H160) {
1394 }
1400
1401 fn touch(&mut self, _address: H160) {
1402 }
1408
1409 fn is_cold(&self, address: H160) -> bool {
1410 self.substate
1411 .recursive_is_cold(&|a| a.accessed_addresses.contains(&address))
1412 }
1413
1414 fn is_storage_cold(&self, address: H160, key: H256) -> bool {
1415 self.substate
1416 .recursive_is_cold(&|a: &Accessed| a.accessed_storage.contains(&(address, key)))
1417 }
1418
1419 fn code_size(&self, address: H160) -> U256 {
1420 U256::from(<Pallet<T>>::account_code_metadata(address).size)
1423 }
1424
1425 fn code_hash(&self, address: H160) -> H256 {
1426 <Pallet<T>>::account_code_metadata(address).hash
1429 }
1430
1431 fn record_external_operation(&mut self, op: evm::ExternalOperation) -> Result<(), ExitError> {
1432 let size_limit: u64 = self
1433 .metadata()
1434 .gasometer()
1435 .config()
1436 .create_contract_limit
1437 .unwrap_or_default() as u64;
1438 let (weight_info, recorded) = self.info_mut();
1439
1440 if let Some(weight_info) = weight_info {
1441 match op {
1442 ExternalOperation::AccountBasicRead => {
1443 weight_info.try_record_proof_size_or_fail(ACCOUNT_BASIC_PROOF_SIZE)?
1444 }
1445 ExternalOperation::AddressCodeRead(address) => {
1446 Self::record_address_code_read(address, weight_info, recorded, size_limit)?;
1447 }
1448 ExternalOperation::IsEmpty => {
1449 weight_info.try_record_proof_size_or_fail(IS_EMPTY_CHECK_PROOF_SIZE)?
1450 }
1451 ExternalOperation::Write(len) => {
1452 weight_info.try_record_proof_size_or_fail(WRITE_PROOF_SIZE)?;
1453
1454 if let Some(storage_meter) = self.storage_meter.as_mut() {
1455 let storage_growth = ACCOUNT_CODES_KEY_SIZE
1457 .saturating_add(ACCOUNT_CODES_METADATA_PROOF_SIZE)
1458 .saturating_add(len.as_u64());
1459 storage_meter
1460 .record(storage_growth)
1461 .map_err(|_| ExitError::OutOfGas)?;
1462 }
1463 }
1464 ExternalOperation::DelegationResolution(address) => {
1465 Self::record_address_code_read(address, weight_info, recorded, size_limit)?;
1466 }
1467 };
1468 }
1469 Ok(())
1470 }
1471
1472 fn record_external_dynamic_opcode_cost(
1473 &mut self,
1474 opcode: Opcode,
1475 gas_cost: GasCost,
1476 target: evm::gasometer::StorageTarget,
1477 ) -> Result<(), ExitError> {
1478 if let Some(storage_meter) = self.storage_meter.as_mut() {
1479 storage_meter
1480 .record_dynamic_opcode_cost(opcode, gas_cost, target)
1481 .map_err(|_| ExitError::OutOfGas)?;
1482 }
1483
1484 let accessed_storage: Option<AccessedStorage> = match target {
1486 StorageTarget::Address(address) => {
1487 if self.recorded().account_codes.contains(&address) {
1488 return Ok(());
1489 } else {
1490 Some(AccessedStorage::AccountCodes(address))
1491 }
1492 }
1493 StorageTarget::Slot(address, index) => {
1494 if self
1495 .recorded()
1496 .account_storages
1497 .contains_key(&(address, index))
1498 {
1499 return Ok(());
1500 } else {
1501 Some(AccessedStorage::AccountStorages((address, index)))
1502 }
1503 }
1504 _ => None,
1505 };
1506
1507 let size_limit: u64 = self
1508 .metadata()
1509 .gasometer()
1510 .config()
1511 .create_contract_limit
1512 .unwrap_or_default() as u64;
1513 let (weight_info, recorded) = self.info_mut();
1514
1515 if let Some(weight_info) = weight_info {
1516 if weight_info.proof_size_limit.is_none() {
1518 return Ok(());
1519 };
1520
1521 let mut record_account_codes_proof_size =
1522 |address: H160, empty_check: bool| -> Result<(), ExitError> {
1523 let mut base_size = ACCOUNT_CODES_METADATA_PROOF_SIZE;
1524 if empty_check {
1525 base_size = base_size.saturating_add(IS_EMPTY_CHECK_PROOF_SIZE);
1526 }
1527 weight_info.try_record_proof_size_or_fail(base_size)?;
1528
1529 if let Some(meta) = <AccountCodesMetadata<T>>::get(address) {
1530 weight_info.try_record_proof_size_or_fail(meta.size)?;
1531 } else if let Some(remaining_proof_size) = weight_info.remaining_proof_size() {
1532 let pre_size = remaining_proof_size.min(size_limit);
1533 weight_info.try_record_proof_size_or_fail(pre_size)?;
1534
1535 let actual_size = Pallet::<T>::account_code_metadata(address).size;
1536 if actual_size > pre_size {
1537 return Err(ExitError::OutOfGas);
1538 }
1539 weight_info.refund_proof_size(pre_size.saturating_sub(actual_size));
1541 }
1542
1543 Ok(())
1544 };
1545
1546 match opcode {
1555 Opcode::BALANCE => {
1556 weight_info.try_record_proof_size_or_fail(ACCOUNT_BASIC_PROOF_SIZE)?;
1557 }
1558 Opcode::EXTCODESIZE | Opcode::EXTCODECOPY | Opcode::EXTCODEHASH => {
1559 if let Some(AccessedStorage::AccountCodes(address)) = accessed_storage {
1560 record_account_codes_proof_size(address, false)?;
1561 recorded.account_codes.push(address);
1562 }
1563 }
1564 Opcode::CALLCODE | Opcode::CALL | Opcode::DELEGATECALL | Opcode::STATICCALL => {
1565 if let Some(AccessedStorage::AccountCodes(address)) = accessed_storage {
1566 record_account_codes_proof_size(address, true)?;
1567 recorded.account_codes.push(address);
1568 }
1569 }
1570 Opcode::SLOAD => {
1571 if let Some(AccessedStorage::AccountStorages((address, index))) =
1572 accessed_storage
1573 {
1574 weight_info.try_record_proof_size_or_fail(ACCOUNT_STORAGE_PROOF_SIZE)?;
1575 recorded.account_storages.insert((address, index), true);
1576 }
1577 }
1578 Opcode::SSTORE => {
1579 if let Some(AccessedStorage::AccountStorages((address, index))) =
1580 accessed_storage
1581 {
1582 let size = WRITE_PROOF_SIZE.saturating_add(ACCOUNT_STORAGE_PROOF_SIZE);
1583 weight_info.try_record_proof_size_or_fail(size)?;
1584 recorded.account_storages.insert((address, index), true);
1585 }
1586 }
1587 Opcode::CREATE | Opcode::CREATE2 => {
1588 weight_info.try_record_proof_size_or_fail(WRITE_PROOF_SIZE)?;
1589 }
1590 Opcode::SUICIDE => {
1595 weight_info.try_record_proof_size_or_fail(IS_EMPTY_CHECK_PROOF_SIZE)?;
1596 }
1597 _ => return Ok(()),
1599 };
1600 }
1601
1602 Ok(())
1603 }
1604
1605 fn record_external_cost(
1606 &mut self,
1607 ref_time: Option<u64>,
1608 proof_size: Option<u64>,
1609 storage_growth: Option<u64>,
1610 ) -> Result<(), ExitError> {
1611 let weight_info = if let (Some(weight_info), _) = self.info_mut() {
1612 weight_info
1613 } else {
1614 return Ok(());
1615 };
1616
1617 if let Some(amount) = ref_time {
1618 weight_info.try_record_ref_time_or_fail(amount)?;
1619 }
1620 if let Some(amount) = proof_size {
1621 weight_info.try_record_proof_size_or_fail(amount)?;
1622 }
1623 if let Some(storage_meter) = self.storage_meter.as_mut() {
1624 if let Some(amount) = storage_growth {
1625 storage_meter
1626 .record(amount)
1627 .map_err(|_| ExitError::OutOfGas)?;
1628 }
1629 }
1630 Ok(())
1631 }
1632
1633 fn refund_external_cost(&mut self, ref_time: Option<u64>, proof_size: Option<u64>) {
1634 if let Some(weight_info) = self.weight_info.as_mut() {
1635 if let Some(amount) = ref_time {
1636 weight_info.refund_ref_time(amount);
1637 }
1638 if let Some(amount) = proof_size {
1639 weight_info.refund_proof_size(amount);
1640 }
1641 }
1642 }
1643}
1644
1645#[cfg(feature = "forbid-evm-reentrancy")]
1646#[cfg(test)]
1647mod tests {
1648 use super::*;
1649 use crate::mock::{MockPrecompileSet, Test};
1650 use evm::ExitSucceed;
1651 use sp_io::TestExternalities;
1652
1653 macro_rules! assert_matches {
1654 ( $left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )? $(,)? ) => {
1655 match $left {
1656 $( $pattern )|+ $( if $guard )? => {}
1657 ref left_val => panic!("assertion failed: `{:?}` does not match `{}`",
1658 left_val, stringify!($($pattern)|+ $(if $guard)?))
1659 }
1660 };
1661 ( $left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )?, $($arg:tt)+ ) => {
1662 match $left {
1663 $( $pattern )|+ $( if $guard )? => {}
1664 ref left_val => panic!("assertion failed: `{:?}` does not match `{}`",
1665 left_val, stringify!($($pattern)|+ $(if $guard)?))
1666 }
1667 };
1668 }
1669
1670 #[test]
1671 fn test_evm_reentrancy() {
1672 TestExternalities::new_empty().execute_with(|| {
1673 let config = evm::Config::istanbul();
1674
1675 let measured_proof_size_before = get_proof_size().unwrap_or_default();
1676 let res = Runner::<Test>::execute(
1678 H160::default(),
1679 U256::default(),
1680 100_000,
1681 None,
1682 None,
1683 &config,
1684 &MockPrecompileSet,
1685 false,
1686 None,
1687 None,
1688 measured_proof_size_before,
1689 None,
1690 |_| {
1691 let measured_proof_size_before2 = get_proof_size().unwrap_or_default();
1692 let res = Runner::<Test>::execute(
1693 H160::default(),
1694 U256::default(),
1695 100_000,
1696 None,
1697 None,
1698 &config,
1699 &MockPrecompileSet,
1700 false,
1701 None,
1702 None,
1703 measured_proof_size_before2,
1704 None,
1705 |_| (ExitReason::Succeed(ExitSucceed::Stopped), ()),
1706 );
1707 assert_matches!(
1708 res,
1709 Err(RunnerError {
1710 error: Error::<Test>::Reentrancy,
1711 ..
1712 })
1713 );
1714 (ExitReason::Error(ExitError::CallTooDeep), ())
1715 },
1716 );
1717 assert_matches!(
1718 res,
1719 Ok(ExecutionInfoV2 {
1720 exit_reason: ExitReason::Error(ExitError::CallTooDeep),
1721 ..
1722 })
1723 );
1724
1725 let measured_proof_size_before = get_proof_size().unwrap_or_default();
1726 let res = Runner::<Test>::execute(
1728 H160::default(),
1729 U256::default(),
1730 100_000,
1731 None,
1732 None,
1733 &config,
1734 &MockPrecompileSet,
1735 false,
1736 None,
1737 None,
1738 measured_proof_size_before,
1739 None,
1740 |_| (ExitReason::Succeed(ExitSucceed::Stopped), ()),
1741 );
1742 assert!(res.is_ok());
1743 });
1744 }
1745}