From 58e970d7b677c904bc83bd4653d237ed5ef83e12 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov <39522748+dmitrylavrenov@users.noreply.github.com> Date: Wed, 22 Nov 2023 12:13:45 +0300 Subject: [PATCH] Support external account provider (#83) (#92) * Introduce external account provider into `pallet_evm` (#61) * Introduce AccountProvider interface * Apply account provider logic for tests and template * Fix docs for remove_account method * Fix missing docs * Improve docs for account provider trait * Fix docs for native system account provider * Move account provider logic to separate mod * Add details docs for methods * Move `AccountProvider` interface into `fp-em` (#71) * Move account provider interface into fp-em * Implement AccountProvider interface for EvmSystem * Revert formatter * Move NativeSystemAccountProvider to pallet-evm --- frame/ethereum/src/mock.rs | 1 + frame/evm/precompile/dispatch/src/lib.rs | 4 +- frame/evm/precompile/dispatch/src/mock.rs | 5 +- frame/evm/src/lib.rs | 63 ++++++++++++++++------- frame/evm/src/mock.rs | 5 +- frame/evm/src/runner/stack.rs | 6 +-- primitives/evm/src/account_provider.rs | 41 +++++++++++++++ primitives/evm/src/lib.rs | 2 + template/runtime/src/lib.rs | 1 + 9 files changed, 101 insertions(+), 27 deletions(-) create mode 100644 primitives/evm/src/account_provider.rs diff --git a/frame/ethereum/src/mock.rs b/frame/ethereum/src/mock.rs index 9c22e2e347..25135f2736 100644 --- a/frame/ethereum/src/mock.rs +++ b/frame/ethereum/src/mock.rs @@ -160,6 +160,7 @@ impl AddressMapping for HashedAddressMapping { } impl pallet_evm::Config for Test { + type AccountProvider = pallet_evm::NativeSystemAccountProvider; type FeeCalculator = FixedGasPrice; type GasWeightMapping = pallet_evm::FixedGasWeightMapping; type WeightPerGas = WeightPerGas; diff --git a/frame/evm/precompile/dispatch/src/lib.rs b/frame/evm/precompile/dispatch/src/lib.rs index f73349bf31..b592fe948e 100644 --- a/frame/evm/precompile/dispatch/src/lib.rs +++ b/frame/evm/precompile/dispatch/src/lib.rs @@ -50,8 +50,8 @@ impl Precompile for Dispatch + GetDispatchInfo + Decode, - ::RuntimeOrigin: From>, - DispatchValidator: DispatchValidateT, + ::RuntimeOrigin: From::AccountProvider as pallet_evm::AccountProvider>::AccountId>>, + DispatchValidator: DispatchValidateT<<::AccountProvider as pallet_evm::AccountProvider>::AccountId, T::RuntimeCall>, DecodeLimit: Get, { fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { diff --git a/frame/evm/precompile/dispatch/src/mock.rs b/frame/evm/precompile/dispatch/src/mock.rs index da22a34427..2e33d5a3b9 100644 --- a/frame/evm/precompile/dispatch/src/mock.rs +++ b/frame/evm/precompile/dispatch/src/mock.rs @@ -143,14 +143,15 @@ parameter_types! { pub WeightPerGas: Weight = Weight::from_parts(20_000, 0); } impl pallet_evm::Config for Test { + type AccountProvider = pallet_evm::NativeSystemAccountProvider; type FeeCalculator = FixedGasPrice; type GasWeightMapping = pallet_evm::FixedGasWeightMapping; type WeightPerGas = WeightPerGas; type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping; - type CallOrigin = EnsureAddressRoot; + type CallOrigin = EnsureAddressRoot<::AccountId>; - type WithdrawOrigin = EnsureAddressNever; + type WithdrawOrigin = EnsureAddressNever<::AccountId>; type AddressMapping = IdentityAddressMapping; type Currency = Balances; diff --git a/frame/evm/src/lib.rs b/frame/evm/src/lib.rs index c3ef12460d..adf35ddc38 100644 --- a/frame/evm/src/lib.rs +++ b/frame/evm/src/lib.rs @@ -96,7 +96,7 @@ use fp_account::AccountId20; #[cfg(feature = "std")] use fp_evm::GenesisAccount; pub use fp_evm::{ - Account, CallInfo, CreateInfo, ExecutionInfoV2 as ExecutionInfo, FeeCalculator, + Account, AccountProvider, CallInfo, CreateInfo, ExecutionInfoV2 as ExecutionInfo, FeeCalculator, InvalidEvmTransactionError, IsPrecompileResult, LinearCostPrecompile, Log, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, PrecompileSet, Vicinity, @@ -120,6 +120,9 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { + /// Account info provider. + type AccountProvider: AccountProvider; + /// Calculator for current gas price. type FeeCalculator: FeeCalculator; @@ -135,12 +138,12 @@ pub mod pallet { /// Allow the origin to call on behalf of given address. type CallOrigin: EnsureAddressOrigin; /// Allow the origin to withdraw on behalf of given address. - type WithdrawOrigin: EnsureAddressOrigin; + type WithdrawOrigin: EnsureAddressOrigin::AccountId>; /// Mapping from address to account id. - type AddressMapping: AddressMapping; + type AddressMapping: AddressMapping<::AccountId>; /// Currency type for withdraw and balance storage. - type Currency: Currency + Inspect; + type Currency: Currency<::AccountId> + Inspect<::AccountId>; /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -535,7 +538,7 @@ pub mod pallet { MAX_ACCOUNT_NONCE, UniqueSaturatedInto::::unique_saturated_into(account.nonce), ) { - frame_system::Pallet::::inc_account_nonce(&account_id); + T::AccountProvider::inc_account_nonce(&account_id); } T::Currency::deposit_creating(&account_id, account.balance.unique_saturated_into()); @@ -563,11 +566,11 @@ pub mod pallet { /// Type alias for currency balance. pub type BalanceOf = - <::Currency as Currency<::AccountId>>::Balance; + <::Currency as Currency<<::AccountProvider as AccountProvider>::AccountId>>::Balance; /// Type alias for negative imbalance during fees type NegativeImbalanceOf = - ::AccountId>>::NegativeImbalance; + ::AccountProvider as AccountProvider>::AccountId>>::NegativeImbalance; #[derive( Debug, @@ -792,7 +795,7 @@ impl Pallet { pub fn remove_account(address: &H160) { if >::contains_key(address) { let account_id = T::AddressMapping::into_account_id(*address); - let _ = frame_system::Pallet::::dec_sufficients(&account_id); + T::AccountProvider::remove_account(&account_id); } >::remove(address); @@ -809,7 +812,7 @@ impl Pallet { if !>::contains_key(address) { let account_id = T::AddressMapping::into_account_id(address); - let _ = frame_system::Pallet::::inc_sufficients(&account_id); + T::AccountProvider::create_account(&account_id); } // Update metadata. @@ -850,7 +853,7 @@ impl Pallet { pub fn account_basic(address: &H160) -> (Account, frame_support::weights::Weight) { let account_id = T::AddressMapping::into_account_id(*address); - let nonce = frame_system::Pallet::::account_nonce(&account_id); + let nonce = T::AccountProvider::account_nonce(&account_id); // keepalive `true` takes into account ExistentialDeposit as part of what's considered liquid balance. let balance = T::Currency::reducible_balance(&account_id, Preservation::Preserve, Fortitude::Polite); @@ -907,17 +910,17 @@ pub struct EVMCurrencyAdapter(sp_std::marker::PhantomData<(C, OU)>); impl OnChargeEVMTransaction for EVMCurrencyAdapter where T: Config, - C: Currency<::AccountId>, + C: Currency<<::AccountProvider as AccountProvider>::AccountId>, C::PositiveImbalance: Imbalance< - ::AccountId>>::Balance, + ::AccountProvider as AccountProvider>::AccountId>>::Balance, Opposite = C::NegativeImbalance, >, C::NegativeImbalance: Imbalance< - ::AccountId>>::Balance, + ::AccountProvider as AccountProvider>::AccountId>>::Balance, Opposite = C::PositiveImbalance, >, OU: OnUnbalanced>, - U256: UniqueSaturatedInto<::AccountId>>::Balance>, + U256: UniqueSaturatedInto<::AccountProvider as AccountProvider>::AccountId>>::Balance>, { // Kept type as Option to satisfy bound of Default type LiquidityInfo = Option>; @@ -1001,10 +1004,10 @@ where impl OnChargeEVMTransaction for () where T: Config, - ::AccountId>>::PositiveImbalance: - Imbalance<::AccountId>>::Balance, Opposite = ::AccountId>>::NegativeImbalance>, - ::AccountId>>::NegativeImbalance: -Imbalance<::AccountId>>::Balance, Opposite = ::AccountId>>::PositiveImbalance>, + ::AccountProvider as AccountProvider>::AccountId>>::PositiveImbalance: + Imbalance<::AccountProvider as AccountProvider>::AccountId>>::Balance, Opposite = ::AccountProvider as AccountProvider>::AccountId>>::NegativeImbalance>, + ::AccountProvider as AccountProvider>::AccountId>>::NegativeImbalance: +Imbalance<::AccountProvider as AccountProvider>::AccountId>>::Balance, Opposite = ::AccountProvider as AccountProvider>::AccountId>>::PositiveImbalance>, U256: UniqueSaturatedInto>, { @@ -1048,3 +1051,27 @@ impl OnCreate for Tuple { )*) } } + +/// Native system account provider that `frame_system` provides. +pub struct NativeSystemAccountProvider(sp_std::marker::PhantomData); + +impl AccountProvider for NativeSystemAccountProvider { + type AccountId = T::AccountId; + type Index = T::Index; + + fn account_nonce(who: &Self::AccountId) -> Self::Index { + frame_system::Pallet::::account_nonce(&who) + } + + fn inc_account_nonce(who: &Self::AccountId) { + frame_system::Pallet::::inc_account_nonce(&who) + } + + fn create_account(who: &Self::AccountId) { + let _ = frame_system::Pallet::::inc_sufficients(&who); + } + fn remove_account(who: &Self::AccountId) { + let _ = frame_system::Pallet::::dec_sufficients(&who); + } +} + diff --git a/frame/evm/src/mock.rs b/frame/evm/src/mock.rs index c1e48f1f99..eb4c9485bd 100644 --- a/frame/evm/src/mock.rs +++ b/frame/evm/src/mock.rs @@ -139,14 +139,15 @@ parameter_types! { pub MockPrecompiles: MockPrecompileSet = MockPrecompileSet; } impl crate::Config for Test { + type AccountProvider = crate::NativeSystemAccountProvider; type FeeCalculator = FixedGasPrice; type GasWeightMapping = crate::FixedGasWeightMapping; type WeightPerGas = WeightPerGas; type BlockHashMapping = crate::SubstrateBlockHashMapping; - type CallOrigin = EnsureAddressRoot; + type CallOrigin = EnsureAddressRoot<::AccountId>; - type WithdrawOrigin = EnsureAddressNever; + type WithdrawOrigin = EnsureAddressNever<::AccountId>; type AddressMapping = IdentityAddressMapping; type Currency = Balances; diff --git a/frame/evm/src/runner/stack.rs b/frame/evm/src/runner/stack.rs index 071b569732..280d1cf687 100644 --- a/frame/evm/src/runner/stack.rs +++ b/frame/evm/src/runner/stack.rs @@ -48,8 +48,8 @@ use fp_evm::{ }; use crate::{ - runner::Runner as RunnerT, AccountCodes, AccountCodesMetadata, AccountStorages, AddressMapping, - BalanceOf, BlockHashMapping, Config, Error, Event, FeeCalculator, OnChargeEVMTransaction, + runner::Runner as RunnerT, AccountCodes, AccountCodesMetadata, AccountProvider, AccountStorages, + AddressMapping, BalanceOf, BlockHashMapping, Config, Error, Event, FeeCalculator, OnChargeEVMTransaction, OnCreate, Pallet, RunnerError, }; @@ -838,7 +838,7 @@ where fn inc_nonce(&mut self, address: H160) -> Result<(), ExitError> { let account_id = T::AddressMapping::into_account_id(address); - frame_system::Pallet::::inc_account_nonce(&account_id); + T::AccountProvider::inc_account_nonce(&account_id); Ok(()) } diff --git a/primitives/evm/src/account_provider.rs b/primitives/evm/src/account_provider.rs new file mode 100644 index 0000000000..4fbd07530b --- /dev/null +++ b/primitives/evm/src/account_provider.rs @@ -0,0 +1,41 @@ +//! Custom account provider logic. + +use sp_runtime::traits::AtLeast32Bit; + +/// The account provider interface abstraction layer. +/// +/// Expose account related logic that `pallet_evm` required to control accounts existence +/// in the network and their transactions uniqueness. By default, the pallet operates native +/// system accounts records that `frame_system` provides. +/// +/// The interface allow any custom account provider logic to be used instead of +/// just using `frame_system` account provider. The accounts records should store nonce value +/// for each account at least. +pub trait AccountProvider { + /// The account identifier type. + /// + /// Represent the account itself in accounts records. + type AccountId; + /// Account index (aka nonce) type. + /// + /// The number that helps to ensure that each transaction in the network is unique + /// for particular account. + type Index: AtLeast32Bit; + + /// Creates a new account in accounts records. + /// + /// The account associated with new created address EVM. + fn create_account(who: &Self::AccountId); + /// Removes an account from accounts records. + /// + /// The account associated with removed address from EVM. + fn remove_account(who: &Self::AccountId); + /// Return current account nonce value. + /// + /// Used to represent account basic information in EVM format. + fn account_nonce(who: &Self::AccountId) -> Self::Index; + /// Increment a particular account's nonce value. + /// + /// Incremented with each new transaction submitted by the account. + fn inc_account_nonce(who: &Self::AccountId); +} diff --git a/primitives/evm/src/lib.rs b/primitives/evm/src/lib.rs index 74ca39c5da..1d3a1ad10d 100644 --- a/primitives/evm/src/lib.rs +++ b/primitives/evm/src/lib.rs @@ -18,6 +18,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![deny(unused_crate_dependencies)] +mod account_provider; mod precompile; mod validation; @@ -36,6 +37,7 @@ pub use evm::{ }; pub use self::{ + account_provider::AccountProvider, precompile::{ Context, ExitError, ExitRevert, ExitSucceed, IsPrecompileResult, LinearCostPrecompile, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, diff --git a/template/runtime/src/lib.rs b/template/runtime/src/lib.rs index a658d16b6a..900184c58c 100644 --- a/template/runtime/src/lib.rs +++ b/template/runtime/src/lib.rs @@ -323,6 +323,7 @@ parameter_types! { } impl pallet_evm::Config for Runtime { + type AccountProvider = pallet_evm::NativeSystemAccountProvider; type FeeCalculator = BaseFee; type GasWeightMapping = pallet_evm::FixedGasWeightMapping; type WeightPerGas = WeightPerGas;