Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sovereign accounts and EVM system layer #94

Merged
merged 4 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"frame/ethereum",
"frame/evm",
"frame/evm-chain-id",
"frame/evm-system",
"frame/hotfix-sufficients",
"frame/evm/precompile/sha3fips",
"frame/evm/precompile/simple",
Expand Down Expand Up @@ -51,6 +52,7 @@ kvdb-rocksdb = "0.17.0"
libsecp256k1 = { version = "0.7.1", default-features = false }
log = { version = "0.4.17", default-features = false }
parity-db = "0.4.6"
mockall = "0.11"
rlp = { version = "0.5", default-features = false }
scale-codec = { package = "parity-scale-codec", version = "3.2.1", default-features = false, features = ["derive"] }
scale-info = { version = "2.3.1", default-features = false, features = ["derive"] }
Expand Down Expand Up @@ -149,6 +151,7 @@ pallet-dynamic-fee = { version = "4.0.0-dev", path = "frame/dynamic-fee", defaul
pallet-ethereum = { version = "4.0.0-dev", path = "frame/ethereum", default-features = false }
pallet-evm = { version = "6.0.0-dev", path = "frame/evm", default-features = false }
pallet-evm-chain-id = { version = "1.0.0-dev", path = "frame/evm-chain-id", default-features = false }
pallet-evm-system = { version = "1.0.0-dev", path = "frame/evm-system", default-features = false }
pallet-evm-precompile-modexp = { version = "2.0.0-dev", path = "frame/evm/precompile/modexp", default-features = false }
pallet-evm-precompile-sha3fips = { version = "2.0.0-dev", path = "frame/evm/precompile/sha3fips", default-features = false }
pallet-evm-precompile-simple = { version = "2.0.0-dev", path = "frame/evm/precompile/simple", default-features = false }
Expand Down
46 changes: 46 additions & 0 deletions frame/evm-system/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
[package]
name = "pallet-evm-system"
version = "1.0.0-dev"
license = "Apache-2.0"
description = "FRAME EVM SYSTEM pallet."
edition = { workspace = true }
repository = { workspace = true }

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
log = { workspace = true, default-features = false }
scale-codec = { package = "parity-scale-codec", workspace = true }
scale-info = { workspace = true }
# Substrate
frame-support = { workspace = true }
frame-system = { workspace = true }
sp-runtime = { workspace = true }
sp-std = { workspace = true }
# Frontier
fp-evm = { workspace = true }

[dev-dependencies]
mockall = { workspace = true }
sp-core = { workspace = true }
sp-io = { workspace = true }

[features]
default = ["std"]
std = [
"log/std",
"scale-codec/std",
"scale-info/std",
# Substrate
"frame-support/std",
"frame-system/std",
"sp-runtime/std",
"sp-std/std",
# Frontier
"fp-evm/std",
]
try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
]
236 changes: 236 additions & 0 deletions frame/evm-system/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
// SPDX-License-Identifier: Apache-2.0

//! # EVM System Pallet.

// Ensure we're `no_std` when compiling for Wasm.
#![cfg_attr(not(feature = "std"), no_std)]

use frame_support::traits::StoredMap;
use sp_runtime::{traits::One, RuntimeDebug, DispatchResult, DispatchError};
use scale_codec::{Encode, Decode, MaxEncodedLen, FullCodec};
use scale_info::TypeInfo;

#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;

pub use pallet::*;

/// Account information.
#[derive(Clone, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]
pub struct AccountInfo<Index, AccountData> {
/// The number of transactions this account has sent.
pub nonce: Index,
/// The additional data that belongs to this account. Used to store the balance(s) in a lot of
/// chains.
pub data: AccountData,
}

#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use sp_runtime::traits::{MaybeDisplay, AtLeast32Bit};
use sp_std::fmt::Debug;

#[pallet::pallet]
#[pallet::without_storage_info]
pub struct Pallet<T>(PhantomData<T>);

#[pallet::config]
pub trait Config: frame_system::Config {
/// The overarching event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;

/// The user account identifier type.
type AccountId: Parameter
+ Member
+ MaybeSerializeDeserialize
+ Debug
+ MaybeDisplay
+ Ord
+ MaxEncodedLen;

/// Account index (aka nonce) type. This stores the number of previous transactions
/// associated with a sender account.
type Index: Parameter
+ Member
+ MaybeSerializeDeserialize
+ Debug
+ Default
+ MaybeDisplay
+ AtLeast32Bit
+ Copy
+ MaxEncodedLen;

/// Data to be associated with an account (other than nonce/transaction counter, which this
/// pallet does regardless).
type AccountData: Member + FullCodec + Clone + Default + TypeInfo + MaxEncodedLen;

/// Handler for when a new account has just been created.
type OnNewAccount: OnNewAccount<<Self as Config>::AccountId>;

/// A function that is invoked when an account has been determined to be dead.
///
/// All resources should be cleaned up associated with the given account.
type OnKilledAccount: OnKilledAccount<<Self as Config>::AccountId>;
}

/// The full account information for a particular account ID.
#[pallet::storage]
pub type Account<T: Config> = StorageMap<
_,
Blake2_128Concat,
<T as Config>::AccountId,
AccountInfo<<T as Config>::Index, <T as Config>::AccountData>,
ValueQuery,
>;

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// A new account was created.
NewAccount { account: <T as Config>::AccountId },
/// An account was reaped.
KilledAccount { account: <T as Config>::AccountId },
}

#[pallet::error]
pub enum Error<T> {
/// The account already exists in case creating it.
AccountAlreadyExist,
/// The account doesn't exist in case removing it.
AccountNotExist,
}
}

impl<T: Config> Pallet<T> {
/// Check the account existence.
pub fn account_exists(who: &<T as Config>::AccountId) -> bool {
Account::<T>::contains_key(who)
}

/// An account is being created.
fn on_created_account(who: <T as Config>::AccountId) {
<T as Config>::OnNewAccount::on_new_account(&who);
Self::deposit_event(Event::NewAccount { account: who });
}

/// Do anything that needs to be done after an account has been killed.
fn on_killed_account(who: <T as Config>::AccountId) {
<T as Config>::OnKilledAccount::on_killed_account(&who);
Self::deposit_event(Event::KilledAccount { account: who });
}

/// Retrieve the account transaction counter from storage.
pub fn account_nonce(who: &<T as Config>::AccountId) -> <T as Config>::Index {
Account::<T>::get(who).nonce
}

/// Increment a particular account's nonce by 1.
pub fn inc_account_nonce(who: &<T as Config>::AccountId) {
Account::<T>::mutate(who, |a| a.nonce += <T as pallet::Config>::Index::one());
}

/// Create an account.
pub fn create_account(who: &<T as Config>::AccountId) -> DispatchResult {
if Self::account_exists(who) {
return Err(Error::<T>::AccountAlreadyExist.into());
}

Account::<T>::insert(who.clone(), AccountInfo::<_, _>::default());
Self::on_created_account(who.clone());
Ok(())
}

/// Remove an account.
pub fn remove_account(who: &<T as Config>::AccountId) -> DispatchResult {
if !Self::account_exists(who) {
return Err(Error::<T>::AccountNotExist.into());
}

Account::<T>::remove(who);
Self::on_killed_account(who.clone());
Ok(())
}
}

impl<T: Config> StoredMap<<T as Config>::AccountId, <T as Config>::AccountData> for Pallet<T> {
fn get(k: &<T as Config>::AccountId) -> <T as Config>::AccountData {
Account::<T>::get(k).data
}

fn try_mutate_exists<R, E: From<DispatchError>>(
k: &<T as Config>::AccountId,
f: impl FnOnce(&mut Option<<T as Config>::AccountData>) -> Result<R, E>,
) -> Result<R, E> {
let (mut maybe_account_data, was_providing) = if Self::account_exists(k) {
(Some(Account::<T>::get(k).data), true)
} else {
(None, false)
};

let result = f(&mut maybe_account_data)?;

match (maybe_account_data, was_providing) {
(Some(data), false) => {
Account::<T>::mutate(k, |a| a.data = data);
Self::on_created_account(k.clone());
}
(Some(data), true) => {
Account::<T>::mutate(k, |a| a.data = data);
}
(None, true) => {
Account::<T>::remove(k);
Self::on_killed_account(k.clone());
}
(None, false) => {
// Do nothing.
}
}

Ok(result)
}
}

impl<T: Config> fp_evm::AccountProvider for Pallet<T> {
type AccountId = <T as Config>::AccountId;
type Index = <T as Config>::Index;

fn create_account(who: &Self::AccountId) {
let _ = Self::create_account(who);
}

fn remove_account(who: &Self::AccountId) {
let _ = Self::remove_account(who);
}

fn account_nonce(who: &Self::AccountId) -> Self::Index {
Self::account_nonce(who)
}

fn inc_account_nonce(who: &Self::AccountId) {
Self::inc_account_nonce(who);
}
}

/// Interface to handle account creation.
pub trait OnNewAccount<AccountId> {
/// A new account `who` has been registered.
fn on_new_account(who: &AccountId);
}

impl<AccountId> OnNewAccount<AccountId> for () {
fn on_new_account(_who: &AccountId) {}
}

/// Interface to handle account killing.
pub trait OnKilledAccount<AccountId> {
/// The account with the given id was reaped.
fn on_killed_account(who: &AccountId);
}

impl<AccountId> OnKilledAccount<AccountId> for () {
fn on_killed_account(_who: &AccountId) {}
}