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