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