|
| 1 | +//! Extensions for unsigned general extrinsic for ethereum pallet |
| 2 | +
|
| 3 | +use crate::pallet::Call as EthereumCall; |
| 4 | +use crate::{Config, Origin, Pallet as Ethereum, RawOrigin}; |
| 5 | +use ethereum_types::H160; |
| 6 | +use fp_evm::TransactionValidationError; |
| 7 | +use frame_support::pallet_prelude::{PhantomData, TypeInfo}; |
| 8 | +use frame_system::pallet_prelude::{OriginFor, RuntimeCallFor}; |
| 9 | +use scale_codec::{Decode, Encode}; |
| 10 | +use scale_info::prelude::fmt; |
| 11 | +use sp_runtime::impl_tx_ext_default; |
| 12 | +use sp_runtime::traits::{ |
| 13 | + AsSystemOriginSigner, DispatchInfoOf, DispatchOriginOf, Dispatchable, Implication, |
| 14 | + TransactionExtension, ValidateResult, |
| 15 | +}; |
| 16 | +use sp_runtime::transaction_validity::{ |
| 17 | + InvalidTransaction, TransactionSource, TransactionValidityError, ValidTransaction, |
| 18 | +}; |
| 19 | + |
| 20 | +/// Trait to be implemented by the Runtime call for Ethereum call conversion and additional validations. |
| 21 | +pub trait EthereumTransactionHook<Runtime> |
| 22 | +where |
| 23 | + Runtime: Config, |
| 24 | + OriginFor<Runtime>: Into<Result<RawOrigin, OriginFor<Runtime>>>, |
| 25 | +{ |
| 26 | + fn maybe_ethereum_call(&self) -> Option<&EthereumCall<Runtime>>; |
| 27 | + |
| 28 | + fn additional_validation( |
| 29 | + &self, |
| 30 | + _signer: H160, |
| 31 | + _source: TransactionSource, |
| 32 | + ) -> Result<(), TransactionValidityError> { |
| 33 | + Ok(()) |
| 34 | + } |
| 35 | +} |
| 36 | + |
| 37 | +/// Extensions for pallet-ethereum unsigned extrinsics. |
| 38 | +#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] |
| 39 | +pub struct EthereumExtension<Runtime>(PhantomData<Runtime>); |
| 40 | + |
| 41 | +impl<Runtime> EthereumExtension<Runtime> { |
| 42 | + pub fn new() -> Self { |
| 43 | + Self(PhantomData) |
| 44 | + } |
| 45 | +} |
| 46 | + |
| 47 | +impl<Runtime> Default for EthereumExtension<Runtime> { |
| 48 | + fn default() -> Self { |
| 49 | + Self::new() |
| 50 | + } |
| 51 | +} |
| 52 | + |
| 53 | +impl<T: Config> fmt::Debug for EthereumExtension<T> { |
| 54 | + #[cfg(feature = "std")] |
| 55 | + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 56 | + write!(f, "EthereumExtension",) |
| 57 | + } |
| 58 | + |
| 59 | + #[cfg(not(feature = "std"))] |
| 60 | + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 61 | + Ok(()) |
| 62 | + } |
| 63 | +} |
| 64 | + |
| 65 | +impl<Runtime> EthereumExtension<Runtime> where |
| 66 | + Runtime: Config + scale_info::TypeInfo + fmt::Debug + Send + Sync |
| 67 | +{ |
| 68 | +} |
| 69 | + |
| 70 | +impl<Runtime> TransactionExtension<RuntimeCallFor<Runtime>> for EthereumExtension<Runtime> |
| 71 | +where |
| 72 | + Runtime: Config + scale_info::TypeInfo + fmt::Debug + Send + Sync, |
| 73 | + <RuntimeCallFor<Runtime> as Dispatchable>::RuntimeOrigin: |
| 74 | + AsSystemOriginSigner<<Runtime as frame_system::Config>::AccountId> + From<Origin> + Clone, |
| 75 | + OriginFor<Runtime>: Into<Result<RawOrigin, OriginFor<Runtime>>>, |
| 76 | + RuntimeCallFor<Runtime>: EthereumTransactionHook<Runtime>, |
| 77 | +{ |
| 78 | + const IDENTIFIER: &'static str = "EthereumExtension"; |
| 79 | + type Implicit = (); |
| 80 | + type Val = (); |
| 81 | + type Pre = (); |
| 82 | + |
| 83 | + fn validate( |
| 84 | + &self, |
| 85 | + origin: DispatchOriginOf<RuntimeCallFor<Runtime>>, |
| 86 | + call: &RuntimeCallFor<Runtime>, |
| 87 | + _info: &DispatchInfoOf<RuntimeCallFor<Runtime>>, |
| 88 | + _len: usize, |
| 89 | + _self_implicit: Self::Implicit, |
| 90 | + _inherited_implication: &impl Implication, |
| 91 | + source: TransactionSource, |
| 92 | + ) -> ValidateResult<Self::Val, RuntimeCallFor<Runtime>> { |
| 93 | + // we only care about unsigned calls |
| 94 | + if origin.as_system_origin_signer().is_some() { |
| 95 | + return Ok((ValidTransaction::default(), (), origin)); |
| 96 | + }; |
| 97 | + |
| 98 | + let transaction = match call.maybe_ethereum_call() { |
| 99 | + Some(EthereumCall::transact { transaction }) => transaction, |
| 100 | + _ => return Ok((ValidTransaction::default(), (), origin)), |
| 101 | + }; |
| 102 | + |
| 103 | + // check signer |
| 104 | + let signer = Ethereum::<Runtime>::recover_signer(transaction).ok_or( |
| 105 | + InvalidTransaction::Custom(TransactionValidationError::InvalidSignature as u8), |
| 106 | + )?; |
| 107 | + |
| 108 | + // validation on transactions based on pre_dispatch or mempool validation. |
| 109 | + let pre_dispatch = source == TransactionSource::InBlock; |
| 110 | + let validity = if pre_dispatch { |
| 111 | + Ethereum::<Runtime>::validate_transaction_in_block(signer, transaction) |
| 112 | + .map(|_| ValidTransaction::default()) |
| 113 | + } else { |
| 114 | + Ethereum::<Runtime>::validate_transaction_in_pool(signer, transaction) |
| 115 | + }?; |
| 116 | + |
| 117 | + // do any additional validations |
| 118 | + call.additional_validation(signer, source)?; |
| 119 | + |
| 120 | + Ok((validity, (), Origin::EthereumTransaction(signer).into())) |
| 121 | + } |
| 122 | + |
| 123 | + impl_tx_ext_default!(RuntimeCallFor<Runtime>; prepare weight); |
| 124 | +} |
0 commit comments