Skip to content

Commit

Permalink
Support external account provider (#1329)
Browse files Browse the repository at this point in the history
* Introduce account provider interface

* Use AccountProvider logic at pallet-evm

* Remove redundant ower type usage

* Fix fmt

* Fix clippy

* Fix clippy

* License at primitives/evm/src/account_provider.rs

* Rename NativeSystemAccountProvider to FrameSystemAccountProvider

* Formatting corrcetions at account_provider.rs

---------

Co-authored-by: MOZGIII <[email protected]>
  • Loading branch information
dmitrylavrenov and MOZGIII committed Sep 11, 2024
1 parent 80c768f commit d21ddc2
Show file tree
Hide file tree
Showing 13 changed files with 142 additions and 45 deletions.
1 change: 1 addition & 0 deletions frame/ethereum/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ parameter_types! {
}

impl pallet_evm::Config for Test {
type AccountProvider = pallet_evm::FrameSystemAccountProvider<Self>;
type FeeCalculator = FixedGasPrice;
type GasWeightMapping = pallet_evm::FixedGasWeightMapping<Self>;
type WeightPerGas = WeightPerGas;
Expand Down
4 changes: 2 additions & 2 deletions frame/evm/precompile/dispatch/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ impl<T, DispatchValidator, DecodeLimit> Precompile for Dispatch<T, DispatchValid
where
T: pallet_evm::Config,
T::RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo + Decode,
<T::RuntimeCall as Dispatchable>::RuntimeOrigin: From<Option<T::AccountId>>,
DispatchValidator: DispatchValidateT<T::AccountId, T::RuntimeCall>,
<T::RuntimeCall as Dispatchable>::RuntimeOrigin: From<Option<pallet_evm::AccountIdOf<T>>>,
DispatchValidator: DispatchValidateT<pallet_evm::AccountIdOf<T>, T::RuntimeCall>,
DecodeLimit: Get<u32>,
{
fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult {
Expand Down
1 change: 1 addition & 0 deletions frame/evm/precompile/dispatch/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ parameter_types! {
pub SuicideQuickClearLimit: u32 = 0;
}
impl pallet_evm::Config for Test {
type AccountProvider = pallet_evm::FrameSystemAccountProvider<Self>;
type FeeCalculator = FixedGasPrice;
type GasWeightMapping = pallet_evm::FixedGasWeightMapping<Self>;
type WeightPerGas = WeightPerGas;
Expand Down
6 changes: 4 additions & 2 deletions frame/evm/precompile/storage-cleaner/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ extern crate alloc;

use alloc::vec::Vec;
use core::marker::PhantomData;
use fp_evm::{PrecompileFailure, ACCOUNT_BASIC_PROOF_SIZE, ACCOUNT_STORAGE_PROOF_SIZE};
use fp_evm::{
AccountProvider, PrecompileFailure, ACCOUNT_BASIC_PROOF_SIZE, ACCOUNT_STORAGE_PROOF_SIZE,
};
use pallet_evm::AddressMapping;
use precompile_utils::{prelude::*, EvmResult};
use sp_core::H160;
Expand Down Expand Up @@ -200,7 +202,7 @@ where
pallet_evm::Suicided::<Runtime>::remove(address);

let account_id = Runtime::AddressMapping::into_account_id(address);
let _ = frame_system::Pallet::<Runtime>::dec_sufficients(&account_id);
Runtime::AccountProvider::remove_account(&account_id);
}
}

Expand Down
1 change: 1 addition & 0 deletions frame/evm/precompile/storage-cleaner/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ parameter_types! {
}

impl pallet_evm::Config for Runtime {
type AccountProvider = pallet_evm::FrameSystemAccountProvider<Self>;
type FeeCalculator = ();
type GasWeightMapping = pallet_evm::FixedGasWeightMapping<Self>;
type WeightPerGas = WeightPerGas;
Expand Down
97 changes: 61 additions & 36 deletions frame/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,10 @@ use sp_runtime::{
use fp_account::AccountId20;
use fp_evm::GenesisAccount;
pub use fp_evm::{
Account, CallInfo, CreateInfo, ExecutionInfoV2 as ExecutionInfo, FeeCalculator,
IsPrecompileResult, LinearCostPrecompile, Log, Precompile, PrecompileFailure, PrecompileHandle,
PrecompileOutput, PrecompileResult, PrecompileSet, TransactionValidationError, Vicinity,
Account, AccountProvider, CallInfo, CreateInfo, ExecutionInfoV2 as ExecutionInfo,
FeeCalculator, IsPrecompileResult, LinearCostPrecompile, Log, Precompile, PrecompileFailure,
PrecompileHandle, PrecompileOutput, PrecompileResult, PrecompileSet,
TransactionValidationError, Vicinity,
};

pub use self::{
Expand All @@ -125,6 +126,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;

Expand All @@ -140,12 +144,12 @@ pub mod pallet {
/// Allow the origin to call on behalf of given address.
type CallOrigin: EnsureAddressOrigin<Self::RuntimeOrigin>;
/// Allow the origin to withdraw on behalf of given address.
type WithdrawOrigin: EnsureAddressOrigin<Self::RuntimeOrigin, Success = Self::AccountId>;
type WithdrawOrigin: EnsureAddressOrigin<Self::RuntimeOrigin, Success = AccountIdOf<Self>>;

/// Mapping from address to account id.
type AddressMapping: AddressMapping<Self::AccountId>;
type AddressMapping: AddressMapping<AccountIdOf<Self>>;
/// Currency type for withdraw and balance storage.
type Currency: Currency<Self::AccountId> + Inspect<Self::AccountId>;
type Currency: Currency<AccountIdOf<Self>> + Inspect<AccountIdOf<Self>>;

/// The overarching event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
Expand Down Expand Up @@ -551,7 +555,7 @@ pub mod pallet {
MAX_ACCOUNT_NONCE,
UniqueSaturatedInto::<usize>::unique_saturated_into(account.nonce),
) {
frame_system::Pallet::<T>::inc_account_nonce(&account_id);
T::AccountProvider::inc_account_nonce(&account_id);
}

let _ = T::Currency::deposit_creating(
Expand Down Expand Up @@ -583,13 +587,14 @@ pub mod pallet {
pub type Suicided<T: Config> = StorageMap<_, Blake2_128Concat, H160, (), OptionQuery>;
}

/// Utility alias for easy access to the [`AccountProvider::AccountId`] type from a given config.
pub type AccountIdOf<T> = <<T as Config>::AccountProvider as AccountProvider>::AccountId;

/// Type alias for currency balance.
pub type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
pub type BalanceOf<T> = <<T as Config>::Currency as Currency<AccountIdOf<T>>>::Balance;

/// Type alias for negative imbalance during fees
type NegativeImbalanceOf<C, T> =
<C as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
type NegativeImbalanceOf<C, T> = <C as Currency<AccountIdOf<T>>>::NegativeImbalance;

#[derive(
Debug,
Expand Down Expand Up @@ -827,7 +832,7 @@ impl<T: Config> Pallet<T> {
// In theory, we can always have pre-EIP161 contracts, so we
// make sure the account nonce is at least one.
let account_id = T::AddressMapping::into_account_id(*address);
frame_system::Pallet::<T>::inc_account_nonce(&account_id);
T::AccountProvider::inc_account_nonce(&account_id);
}

<AccountCodes<T>>::remove(address);
Expand All @@ -842,7 +847,7 @@ impl<T: Config> Pallet<T> {
<Suicided<T>>::remove(address);

let account_id = T::AddressMapping::into_account_id(*address);
let _ = frame_system::Pallet::<T>::dec_sufficients(&account_id);
T::AccountProvider::remove_account(&account_id);
}
KillStorageResult::SomeRemaining(_) => (),
}
Expand All @@ -865,7 +870,7 @@ impl<T: Config> Pallet<T> {

if !<AccountCodes<T>>::contains_key(address) {
let account_id = T::AddressMapping::into_account_id(address);
let _ = frame_system::Pallet::<T>::inc_sufficients(&account_id);
T::AccountProvider::create_account(&account_id);
}

// Update metadata.
Expand Down Expand Up @@ -905,7 +910,7 @@ impl<T: Config> Pallet<T> {
/// Get the account basic in EVM format.
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::<T>::account_nonce(&account_id);
let nonce = T::AccountProvider::account_nonce(&account_id);
let balance =
T::Currency::reducible_balance(&account_id, Preservation::Preserve, Fortitude::Polite);

Expand Down Expand Up @@ -961,17 +966,13 @@ pub struct EVMCurrencyAdapter<C, OU>(core::marker::PhantomData<(C, OU)>);
impl<T, C, OU> OnChargeEVMTransaction<T> for EVMCurrencyAdapter<C, OU>
where
T: Config,
C: Currency<<T as frame_system::Config>::AccountId>,
C::PositiveImbalance: Imbalance<
<C as Currency<<T as frame_system::Config>::AccountId>>::Balance,
Opposite = C::NegativeImbalance,
>,
C::NegativeImbalance: Imbalance<
<C as Currency<<T as frame_system::Config>::AccountId>>::Balance,
Opposite = C::PositiveImbalance,
>,
C: Currency<AccountIdOf<T>>,
C::PositiveImbalance:
Imbalance<<C as Currency<AccountIdOf<T>>>::Balance, Opposite = C::NegativeImbalance>,
C::NegativeImbalance:
Imbalance<<C as Currency<AccountIdOf<T>>>::Balance, Opposite = C::PositiveImbalance>,
OU: OnUnbalanced<NegativeImbalanceOf<C, T>>,
U256: UniqueSaturatedInto<<C as Currency<<T as frame_system::Config>::AccountId>>::Balance>,
U256: UniqueSaturatedInto<<C as Currency<AccountIdOf<T>>>::Balance>,
{
// Kept type as Option to satisfy bound of Default
type LiquidityInfo = Option<NegativeImbalanceOf<C, T>>;
Expand Down Expand Up @@ -1061,12 +1062,12 @@ pub struct EVMFungibleAdapter<F, OU>(core::marker::PhantomData<(F, OU)>);
impl<T, F, OU> OnChargeEVMTransaction<T> for EVMFungibleAdapter<F, OU>
where
T: Config,
F: Balanced<T::AccountId>,
OU: OnUnbalanced<Credit<T::AccountId, F>>,
U256: UniqueSaturatedInto<<F as Inspect<<T as frame_system::Config>::AccountId>>::Balance>,
F: Balanced<AccountIdOf<T>>,
OU: OnUnbalanced<Credit<AccountIdOf<T>, F>>,
U256: UniqueSaturatedInto<<F as Inspect<AccountIdOf<T>>>::Balance>,
{
// Kept type as Option to satisfy bound of Default
type LiquidityInfo = Option<Credit<T::AccountId, F>>;
type LiquidityInfo = Option<Credit<AccountIdOf<T>, F>>;

fn withdraw_fee(who: &H160, fee: U256) -> Result<Self::LiquidityInfo, Error<T>> {
if fee.is_zero() {
Expand Down Expand Up @@ -1099,13 +1100,13 @@ where
.saturating_sub(corrected_fee.unique_saturated_into());
// refund to the account that paid the fees.
let refund_imbalance = F::deposit(&account_id, refund_amount, Precision::BestEffort)
.unwrap_or_else(|_| Debt::<T::AccountId, F>::zero());
.unwrap_or_else(|_| Debt::<AccountIdOf<T>, F>::zero());

// merge the imbalance caused by paying the fees and refunding parts of it again.
let adjusted_paid = paid
.offset(refund_imbalance)
.same()
.unwrap_or_else(|_| Credit::<T::AccountId, F>::zero());
.unwrap_or_else(|_| Credit::<AccountIdOf<T>, F>::zero());

let (base_fee, tip) = adjusted_paid.split(base_fee.unique_saturated_into());
// Handle base fee. Can be either burned, rationed, etc ...
Expand All @@ -1128,13 +1129,11 @@ where
impl<T> OnChargeEVMTransaction<T> for ()
where
T: Config,
T::Currency: Balanced<T::AccountId>,
U256: UniqueSaturatedInto<
<<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance,
>,
T::Currency: Balanced<AccountIdOf<T>>,
U256: UniqueSaturatedInto<<<T as Config>::Currency as Inspect<AccountIdOf<T>>>::Balance>,
{
// Kept type as Option to satisfy bound of Default
type LiquidityInfo = Option<Credit<T::AccountId, T::Currency>>;
type LiquidityInfo = Option<Credit<AccountIdOf<T>, T::Currency>>;

fn withdraw_fee(who: &H160, fee: U256) -> Result<Self::LiquidityInfo, Error<T>> {
EVMFungibleAdapter::<T::Currency, ()>::withdraw_fee(who, fee)
Expand Down Expand Up @@ -1175,3 +1174,29 @@ impl<T> OnCreate<T> for Tuple {
)*)
}
}

/// EVM account provider based on the [`frame_system`] accounts.
///
/// Uses standard Substrate accounts system to hold EVM accounts.
pub struct FrameSystemAccountProvider<T>(core::marker::PhantomData<T>);

impl<T: frame_system::Config> AccountProvider for FrameSystemAccountProvider<T> {
type AccountId = T::AccountId;
type Nonce = T::Nonce;

fn account_nonce(who: &Self::AccountId) -> Self::Nonce {
frame_system::Pallet::<T>::account_nonce(who)
}

fn inc_account_nonce(who: &Self::AccountId) {
frame_system::Pallet::<T>::inc_account_nonce(who)
}

fn create_account(who: &Self::AccountId) {
let _ = frame_system::Pallet::<T>::inc_sufficients(who);
}

fn remove_account(who: &Self::AccountId) {
let _ = frame_system::Pallet::<T>::dec_sufficients(who);
}
}
1 change: 1 addition & 0 deletions frame/evm/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ parameter_types! {
pub SuicideQuickClearLimit: u32 = 0;
}
impl crate::Config for Test {
type AccountProvider = crate::FrameSystemAccountProvider<Self>;
type FeeCalculator = FixedGasPrice;
type GasWeightMapping = crate::FixedGasWeightMapping<Self>;
type WeightPerGas = WeightPerGas;
Expand Down
8 changes: 4 additions & 4 deletions frame/evm/src/runner/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ use fp_evm::{
};

use crate::{
runner::Runner as RunnerT, AccountCodes, AccountCodesMetadata, AccountStorages, AddressMapping,
BalanceOf, BlockHashMapping, Config, Error, Event, FeeCalculator, OnChargeEVMTransaction,
OnCreate, Pallet, RunnerError,
runner::Runner as RunnerT, AccountCodes, AccountCodesMetadata, AccountProvider,
AccountStorages, AddressMapping, BalanceOf, BlockHashMapping, Config, Error, Event,
FeeCalculator, OnChargeEVMTransaction, OnCreate, Pallet, RunnerError,
};

#[cfg(feature = "forbid-evm-reentrancy")]
Expand Down Expand Up @@ -840,7 +840,7 @@ where

fn inc_nonce(&mut self, address: H160) -> Result<(), ExitError> {
let account_id = T::AddressMapping::into_account_id(address);
frame_system::Pallet::<T>::inc_account_nonce(&account_id);
T::AccountProvider::inc_account_nonce(&account_id);
Ok(())
}

Expand Down
2 changes: 1 addition & 1 deletion precompiles/src/precompile_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1085,7 +1085,7 @@ impl<R: pallet_evm::Config, P: PrecompileSetFragment> PrecompileSetBuilder<R, P>
}

/// Return the list of mapped addresses contained in this PrecompileSet.
pub fn used_addresses() -> impl Iterator<Item = R::AccountId> {
pub fn used_addresses() -> impl Iterator<Item = pallet_evm::AccountIdOf<R>> {
Self::used_addresses_h160().map(R::AddressMapping::into_account_id)
}

Expand Down
1 change: 1 addition & 0 deletions precompiles/tests-external/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ parameter_types! {
}

impl pallet_evm::Config for Runtime {
type AccountProvider = pallet_evm::FrameSystemAccountProvider<Self>;
type FeeCalculator = ();
type GasWeightMapping = pallet_evm::FixedGasWeightMapping<Self>;
type WeightPerGas = WeightPerGas;
Expand Down
62 changes: 62 additions & 0 deletions primitives/evm/src/account_provider.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// This file is part of Frontier.

// Copyright (c) Humanode Core.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! 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 nonce type.
///
/// The number that helps to ensure that each transaction in the network is unique
/// for particular account.
type Nonce: 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::Nonce;

/// Increment a particular account's nonce value.
///
/// Incremented with each new transaction submitted by the account.
fn inc_account_nonce(who: &Self::AccountId);
}
2 changes: 2 additions & 0 deletions primitives/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

extern crate alloc;

mod account_provider;
mod precompile;
mod validation;

Expand All @@ -38,6 +39,7 @@ pub use evm::{
};

pub use self::{
account_provider::AccountProvider,
precompile::{
Context, ExitError, ExitRevert, ExitSucceed, IsPrecompileResult, LinearCostPrecompile,
Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult,
Expand Down
1 change: 1 addition & 0 deletions template/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ parameter_types! {
}

impl pallet_evm::Config for Runtime {
type AccountProvider = pallet_evm::FrameSystemAccountProvider<Self>;
type FeeCalculator = BaseFee;
type GasWeightMapping = pallet_evm::FixedGasWeightMapping<Self>;
type WeightPerGas = WeightPerGas;
Expand Down

0 comments on commit d21ddc2

Please sign in to comment.