Skip to content

Commit

Permalink
Frame evm system (#62)
Browse files Browse the repository at this point in the history
* Add initial impl of evm-system

* Check account existence

* Improve creation account logic

* Add new line

* Add default implementations

* Add mock

* Use DispatchResult instead of custom enums

* Basic create account tests

* Add simple tests with remove account and nonce update

* Remove default implementations for OnNewAccount and OnKilledAccount

* Add mock objects for OnNewAccount and OnKilledAccount

* Use mock logic in tests

* Some tests improvements

* Add docs to tests

* Check events in tests
  • Loading branch information
dmitrylavrenov committed Oct 19, 2023
1 parent 5d21c6d commit 1bb8d88
Show file tree
Hide file tree
Showing 6 changed files with 504 additions and 0 deletions.
16 changes: 16 additions & 0 deletions Cargo.lock

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

2 changes: 2 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 @@ -170,6 +171,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
39 changes: 39 additions & 0 deletions frame/evm-system/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[package]
name = "pallet-evm-system"
version = "1.0.0-dev"
license = "Apache-2.0"
description = "FRAME EVM SYSTEM pallet."
authors = { workspace = true }
edition = { workspace = true }
repository = { workspace = true }

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

[dependencies]
log = { version = "0.4.17", 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 }

[dev-dependencies]
mockall = "0.11"
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",
]
185 changes: 185 additions & 0 deletions frame/evm-system/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
// SPDX-License-Identifier: Apache-2.0
// This file is part of Frontier.
//
// Copyright (c) 2020-2022 Parity Technologies (UK) Ltd.
//
// 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.

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

use sp_runtime::{traits::One, RuntimeDebug, DispatchResult};
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::generate_store(pub(super) trait Store)]
#[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]
#[pallet::getter(fn full_account)]
pub type FullAccount<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 {
FullAccount::<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 {
FullAccount::<T>::get(who).nonce
}

/// Increment a particular account's nonce by 1.
pub fn inc_account_nonce(who: &<T as Config>::AccountId) {
FullAccount::<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());
}

FullAccount::<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());
}

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

/// Interface to handle account creation.
pub trait OnNewAccount<AccountId> {
/// A new account `who` has been registered.
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);
}
Loading

0 comments on commit 1bb8d88

Please sign in to comment.