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 code = delegation.to_bytes();
1363 let code_len = code.len() as u64;
1364
1365 if let Some(weight_info) = self.weight_info.as_mut() {
1366 weight_info.try_record_proof_size_or_fail(WRITE_PROOF_SIZE)?;
1367 }
1368
1369 if let Some(storage_meter) = self.storage_meter.as_mut() {
1370 let storage_growth = ACCOUNT_CODES_KEY_SIZE
1371 .saturating_add(ACCOUNT_CODES_METADATA_PROOF_SIZE)
1372 .saturating_add(code_len);
1373 storage_meter
1374 .record(storage_growth)
1375 .map_err(|_| ExitError::OutOfGas)?;
1376 }
1377
1378 let meta = crate::CodeMetadata::from_code(&code);
1379 <AccountCodesMetadata<T>>::insert(authority, meta);
1380 <AccountCodes<T>>::insert(authority, code);
1381 Ok(())
1382 }
1383
1384 fn reset_delegation(&mut self, address: H160) -> Result<(), ExitError> {
1385 log::debug!(
1386 target: "evm",
1387 "Resetting delegation at {address:?}"
1388 );
1389
1390 Pallet::<T>::remove_account_code(&address);
1391 Ok(())
1392 }
1393
1394 fn transfer(&mut self, transfer: Transfer) -> Result<(), ExitError> {
1395 let source = T::AddressMapping::into_account_id(transfer.source);
1396 let target = T::AddressMapping::into_account_id(transfer.target);
1397 T::Currency::transfer(
1398 &source,
1399 &target,
1400 transfer
1401 .value
1402 .try_into()
1403 .map_err(|_| ExitError::OutOfFund)?,
1404 ExistenceRequirement::AllowDeath,
1405 )
1406 .map_err(|_| ExitError::OutOfFund)
1407 }
1408
1409 fn reset_balance(&mut self, _address: H160) {
1410 }
1416
1417 fn touch(&mut self, _address: H160) {
1418 }
1424
1425 fn is_cold(&self, address: H160) -> bool {
1426 self.substate
1427 .recursive_is_cold(&|a| a.accessed_addresses.contains(&address))
1428 }
1429
1430 fn is_storage_cold(&self, address: H160, key: H256) -> bool {
1431 self.substate
1432 .recursive_is_cold(&|a: &Accessed| a.accessed_storage.contains(&(address, key)))
1433 }
1434
1435 fn code_size(&self, address: H160) -> U256 {
1436 U256::from(<Pallet<T>>::account_code_metadata(address).size)
1439 }
1440
1441 fn code_hash(&self, address: H160) -> H256 {
1442 <Pallet<T>>::account_code_metadata(address).hash
1445 }
1446
1447 fn record_external_operation(&mut self, op: evm::ExternalOperation) -> Result<(), ExitError> {
1448 let size_limit: u64 = self
1449 .metadata()
1450 .gasometer()
1451 .config()
1452 .create_contract_limit
1453 .unwrap_or_default() as u64;
1454 let (weight_info, recorded) = self.info_mut();
1455
1456 if let Some(weight_info) = weight_info {
1457 match op {
1458 ExternalOperation::AccountBasicRead => {
1459 weight_info.try_record_proof_size_or_fail(ACCOUNT_BASIC_PROOF_SIZE)?
1460 }
1461 ExternalOperation::AddressCodeRead(address) => {
1462 Self::record_address_code_read(address, weight_info, recorded, size_limit)?;
1463 }
1464 ExternalOperation::IsEmpty => {
1465 weight_info.try_record_proof_size_or_fail(IS_EMPTY_CHECK_PROOF_SIZE)?
1466 }
1467 ExternalOperation::Write(len) => {
1468 weight_info.try_record_proof_size_or_fail(WRITE_PROOF_SIZE)?;
1469
1470 if let Some(storage_meter) = self.storage_meter.as_mut() {
1471 let storage_growth = ACCOUNT_CODES_KEY_SIZE
1473 .saturating_add(ACCOUNT_CODES_METADATA_PROOF_SIZE)
1474 .saturating_add(len.as_u64());
1475 storage_meter
1476 .record(storage_growth)
1477 .map_err(|_| ExitError::OutOfGas)?;
1478 }
1479 }
1480 ExternalOperation::DelegationResolution(address) => {
1481 Self::record_address_code_read(address, weight_info, recorded, size_limit)?;
1482 }
1483 };
1484 }
1485 Ok(())
1486 }
1487
1488 fn record_external_dynamic_opcode_cost(
1489 &mut self,
1490 opcode: Opcode,
1491 gas_cost: GasCost,
1492 target: evm::gasometer::StorageTarget,
1493 ) -> Result<(), ExitError> {
1494 if let Some(storage_meter) = self.storage_meter.as_mut() {
1495 storage_meter
1496 .record_dynamic_opcode_cost(opcode, gas_cost, target)
1497 .map_err(|_| ExitError::OutOfGas)?;
1498 }
1499
1500 let accessed_storage: Option<AccessedStorage> = match target {
1502 StorageTarget::Address(address) => {
1503 if self.recorded().account_codes.contains(&address) {
1504 return Ok(());
1505 } else {
1506 Some(AccessedStorage::AccountCodes(address))
1507 }
1508 }
1509 StorageTarget::Slot(address, index) => {
1510 if self
1511 .recorded()
1512 .account_storages
1513 .contains_key(&(address, index))
1514 {
1515 return Ok(());
1516 } else {
1517 Some(AccessedStorage::AccountStorages((address, index)))
1518 }
1519 }
1520 _ => None,
1521 };
1522
1523 let size_limit: u64 = self
1524 .metadata()
1525 .gasometer()
1526 .config()
1527 .create_contract_limit
1528 .unwrap_or_default() as u64;
1529 let (weight_info, recorded) = self.info_mut();
1530
1531 if let Some(weight_info) = weight_info {
1532 if weight_info.proof_size_limit.is_none() {
1534 return Ok(());
1535 };
1536
1537 let mut record_account_codes_proof_size =
1538 |address: H160, empty_check: bool| -> Result<(), ExitError> {
1539 let mut base_size = ACCOUNT_CODES_METADATA_PROOF_SIZE;
1540 if empty_check {
1541 base_size = base_size.saturating_add(IS_EMPTY_CHECK_PROOF_SIZE);
1542 }
1543 weight_info.try_record_proof_size_or_fail(base_size)?;
1544
1545 if let Some(meta) = <AccountCodesMetadata<T>>::get(address) {
1546 weight_info.try_record_proof_size_or_fail(meta.size)?;
1547 } else if let Some(remaining_proof_size) = weight_info.remaining_proof_size() {
1548 let pre_size = remaining_proof_size.min(size_limit);
1549 weight_info.try_record_proof_size_or_fail(pre_size)?;
1550
1551 let actual_size = Pallet::<T>::account_code_metadata(address).size;
1552 if actual_size > pre_size {
1553 return Err(ExitError::OutOfGas);
1554 }
1555 weight_info.refund_proof_size(pre_size.saturating_sub(actual_size));
1557 }
1558
1559 Ok(())
1560 };
1561
1562 match opcode {
1571 Opcode::BALANCE => {
1572 weight_info.try_record_proof_size_or_fail(ACCOUNT_BASIC_PROOF_SIZE)?;
1573 }
1574 Opcode::EXTCODESIZE | Opcode::EXTCODECOPY | Opcode::EXTCODEHASH => {
1575 if let Some(AccessedStorage::AccountCodes(address)) = accessed_storage {
1576 record_account_codes_proof_size(address, false)?;
1577 recorded.account_codes.push(address);
1578 }
1579 }
1580 Opcode::CALLCODE | Opcode::CALL | Opcode::DELEGATECALL | Opcode::STATICCALL => {
1581 if let Some(AccessedStorage::AccountCodes(address)) = accessed_storage {
1582 record_account_codes_proof_size(address, true)?;
1583 recorded.account_codes.push(address);
1584 }
1585 }
1586 Opcode::SLOAD => {
1587 if let Some(AccessedStorage::AccountStorages((address, index))) =
1588 accessed_storage
1589 {
1590 weight_info.try_record_proof_size_or_fail(ACCOUNT_STORAGE_PROOF_SIZE)?;
1591 recorded.account_storages.insert((address, index), true);
1592 }
1593 }
1594 Opcode::SSTORE => {
1595 if let Some(AccessedStorage::AccountStorages((address, index))) =
1596 accessed_storage
1597 {
1598 let size = WRITE_PROOF_SIZE.saturating_add(ACCOUNT_STORAGE_PROOF_SIZE);
1599 weight_info.try_record_proof_size_or_fail(size)?;
1600 recorded.account_storages.insert((address, index), true);
1601 }
1602 }
1603 Opcode::CREATE | Opcode::CREATE2 => {
1604 weight_info.try_record_proof_size_or_fail(WRITE_PROOF_SIZE)?;
1605 }
1606 Opcode::SUICIDE => {
1611 weight_info.try_record_proof_size_or_fail(IS_EMPTY_CHECK_PROOF_SIZE)?;
1612 }
1613 _ => return Ok(()),
1615 };
1616 }
1617
1618 Ok(())
1619 }
1620
1621 fn record_external_cost(
1622 &mut self,
1623 ref_time: Option<u64>,
1624 proof_size: Option<u64>,
1625 storage_growth: Option<u64>,
1626 ) -> Result<(), ExitError> {
1627 let weight_info = if let (Some(weight_info), _) = self.info_mut() {
1628 weight_info
1629 } else {
1630 return Ok(());
1631 };
1632
1633 if let Some(amount) = ref_time {
1634 weight_info.try_record_ref_time_or_fail(amount)?;
1635 }
1636 if let Some(amount) = proof_size {
1637 weight_info.try_record_proof_size_or_fail(amount)?;
1638 }
1639 if let Some(storage_meter) = self.storage_meter.as_mut() {
1640 if let Some(amount) = storage_growth {
1641 storage_meter
1642 .record(amount)
1643 .map_err(|_| ExitError::OutOfGas)?;
1644 }
1645 }
1646 Ok(())
1647 }
1648
1649 fn refund_external_cost(&mut self, ref_time: Option<u64>, proof_size: Option<u64>) {
1650 if let Some(weight_info) = self.weight_info.as_mut() {
1651 if let Some(amount) = ref_time {
1652 weight_info.refund_ref_time(amount);
1653 }
1654 if let Some(amount) = proof_size {
1655 weight_info.refund_proof_size(amount);
1656 }
1657 }
1658 }
1659}
1660
1661#[cfg(feature = "forbid-evm-reentrancy")]
1662#[cfg(test)]
1663mod tests {
1664 use super::*;
1665 use crate::mock::{MockPrecompileSet, Test};
1666 use evm::ExitSucceed;
1667 use sp_io::TestExternalities;
1668
1669 macro_rules! assert_matches {
1670 ( $left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )? $(,)? ) => {
1671 match $left {
1672 $( $pattern )|+ $( if $guard )? => {}
1673 ref left_val => panic!("assertion failed: `{:?}` does not match `{}`",
1674 left_val, stringify!($($pattern)|+ $(if $guard)?))
1675 }
1676 };
1677 ( $left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )?, $($arg:tt)+ ) => {
1678 match $left {
1679 $( $pattern )|+ $( if $guard )? => {}
1680 ref left_val => panic!("assertion failed: `{:?}` does not match `{}`",
1681 left_val, stringify!($($pattern)|+ $(if $guard)?))
1682 }
1683 };
1684 }
1685
1686 #[test]
1687 fn test_evm_reentrancy() {
1688 TestExternalities::new_empty().execute_with(|| {
1689 let config = evm::Config::istanbul();
1690
1691 let measured_proof_size_before = get_proof_size().unwrap_or_default();
1692 let res = Runner::<Test>::execute(
1694 H160::default(),
1695 U256::default(),
1696 100_000,
1697 None,
1698 None,
1699 &config,
1700 &MockPrecompileSet,
1701 false,
1702 None,
1703 None,
1704 measured_proof_size_before,
1705 None,
1706 |_| {
1707 let measured_proof_size_before2 = get_proof_size().unwrap_or_default();
1708 let res = Runner::<Test>::execute(
1709 H160::default(),
1710 U256::default(),
1711 100_000,
1712 None,
1713 None,
1714 &config,
1715 &MockPrecompileSet,
1716 false,
1717 None,
1718 None,
1719 measured_proof_size_before2,
1720 None,
1721 |_| (ExitReason::Succeed(ExitSucceed::Stopped), ()),
1722 );
1723 assert_matches!(
1724 res,
1725 Err(RunnerError {
1726 error: Error::<Test>::Reentrancy,
1727 ..
1728 })
1729 );
1730 (ExitReason::Error(ExitError::CallTooDeep), ())
1731 },
1732 );
1733 assert_matches!(
1734 res,
1735 Ok(ExecutionInfoV2 {
1736 exit_reason: ExitReason::Error(ExitError::CallTooDeep),
1737 ..
1738 })
1739 );
1740
1741 let measured_proof_size_before = get_proof_size().unwrap_or_default();
1742 let res = Runner::<Test>::execute(
1744 H160::default(),
1745 U256::default(),
1746 100_000,
1747 None,
1748 None,
1749 &config,
1750 &MockPrecompileSet,
1751 false,
1752 None,
1753 None,
1754 measured_proof_size_before,
1755 None,
1756 |_| (ExitReason::Succeed(ExitSucceed::Stopped), ()),
1757 );
1758 assert!(res.is_ok());
1759 });
1760 }
1761}