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