Skip to content

Commit a935d8a

Browse files
Sovereign accounts and EVM system layer (#76)
* Frame evm system (#62) * 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 * Rename Index to Nonce * Remove deprecated account store * Fix mock * Add default implementation for OnNewAccount and OnKilledAccount for empty tuple (#63) * Implement StoredMap for EvmSystem (#64) * Add try-runtime feature into `pallet-evm-system` (#67) Add try-runtime feature at pallet-evm-system * Use `sp_std` library to add FromStr trait for tests at `pallet-evm-system` (#68) Use sp_std library to add FromStr trait for tests at pallet-evm-system * Rename FullAccount into Account at `pallet-evm-system` (#69) Rename FullAccount into Account at pallet-evm-system * Fix `try_mutate_exists` implementation and add tests to check it (#70) * Fix try_mutate_exists logic * Add tests * Fix AccountData type at mock * Remove redundant mock expectations * Add comments for new tests * More explicitly handle (none,false) case * Rename some_data back to maybe_account_data * Add data changes for try_mutate_exists_fails_without_changes test * Add try_mutate_exists_account_not_created test * Add assert_noop to check state chages * Return success for try_mutate_exists_account_not_created test * Use workspace dep
1 parent 5d21c6d commit a935d8a

6 files changed

Lines changed: 717 additions & 0 deletions

File tree

Cargo.lock

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ members = [
55
"frame/ethereum",
66
"frame/evm",
77
"frame/evm-chain-id",
8+
"frame/evm-system",
89
"frame/hotfix-sufficients",
910
"frame/evm/precompile/sha3fips",
1011
"frame/evm/precompile/simple",
@@ -61,6 +62,7 @@ jsonrpsee = "0.16.3"
6162
kvdb-rocksdb = "0.19.0"
6263
libsecp256k1 = { version = "0.7.1", default-features = false }
6364
log = { version = "0.4.20", default-features = false }
65+
mockall = "0.11"
6466
num_enum = { version = "0.7.0", default-features = false }
6567
parity-db = "0.4.10"
6668
parking_lot = "0.12.1"
@@ -170,6 +172,7 @@ pallet-dynamic-fee = { version = "4.0.0-dev", path = "frame/dynamic-fee", defaul
170172
pallet-ethereum = { version = "4.0.0-dev", path = "frame/ethereum", default-features = false }
171173
pallet-evm = { version = "6.0.0-dev", path = "frame/evm", default-features = false }
172174
pallet-evm-chain-id = { version = "1.0.0-dev", path = "frame/evm-chain-id", default-features = false }
175+
pallet-evm-system = { version = "1.0.0-dev", path = "frame/evm-system", default-features = false }
173176
pallet-evm-precompile-modexp = { version = "2.0.0-dev", path = "frame/evm/precompile/modexp", default-features = false }
174177
pallet-evm-precompile-sha3fips = { version = "2.0.0-dev", path = "frame/evm/precompile/sha3fips", default-features = false }
175178
pallet-evm-precompile-simple = { version = "2.0.0-dev", path = "frame/evm/precompile/simple", default-features = false }

frame/evm-system/Cargo.toml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
[package]
2+
name = "pallet-evm-system"
3+
version = "1.0.0-dev"
4+
license = "Apache-2.0"
5+
description = "FRAME EVM SYSTEM pallet."
6+
authors = { workspace = true }
7+
edition = { workspace = true }
8+
repository = { workspace = true }
9+
10+
[package.metadata.docs.rs]
11+
targets = ["x86_64-unknown-linux-gnu"]
12+
13+
[dependencies]
14+
log = { workspace = true, default-features = false }
15+
scale-codec = { package = "parity-scale-codec", workspace = true }
16+
scale-info = { workspace = true }
17+
# Substrate
18+
frame-support = { workspace = true }
19+
frame-system = { workspace = true }
20+
sp-runtime = { workspace = true }
21+
sp-std = { workspace = true }
22+
23+
[dev-dependencies]
24+
mockall = { workspace = true }
25+
sp-core = { workspace = true }
26+
sp-io = { workspace = true }
27+
28+
[features]
29+
default = ["std"]
30+
std = [
31+
"log/std",
32+
"scale-codec/std",
33+
"scale-info/std",
34+
# Substrate
35+
"frame-support/std",
36+
"frame-system/std",
37+
"sp-runtime/std",
38+
"sp-std/std",
39+
]
40+
try-runtime = [
41+
"frame-support/try-runtime",
42+
"frame-system/try-runtime",
43+
]

frame/evm-system/src/lib.rs

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// This file is part of Frontier.
3+
//
4+
// Copyright (c) 2020-2022 Parity Technologies (UK) Ltd.
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
18+
//! # EVM System Pallet.
19+
20+
// Ensure we're `no_std` when compiling for Wasm.
21+
#![cfg_attr(not(feature = "std"), no_std)]
22+
23+
use frame_support::traits::StoredMap;
24+
use sp_runtime::{traits::One, RuntimeDebug, DispatchResult, DispatchError};
25+
use scale_codec::{Encode, Decode, MaxEncodedLen, FullCodec};
26+
use scale_info::TypeInfo;
27+
28+
#[cfg(test)]
29+
mod mock;
30+
#[cfg(test)]
31+
mod tests;
32+
33+
pub use pallet::*;
34+
35+
/// Account information.
36+
#[derive(Clone, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]
37+
pub struct AccountInfo<Nonce, AccountData> {
38+
/// The number of transactions this account has sent.
39+
pub nonce: Nonce,
40+
/// The additional data that belongs to this account. Used to store the balance(s) in a lot of
41+
/// chains.
42+
pub data: AccountData,
43+
}
44+
45+
#[frame_support::pallet]
46+
pub mod pallet {
47+
use super::*;
48+
use frame_support::pallet_prelude::*;
49+
use sp_runtime::traits::{MaybeDisplay, AtLeast32Bit};
50+
use sp_std::fmt::Debug;
51+
52+
#[pallet::pallet]
53+
#[pallet::without_storage_info]
54+
pub struct Pallet<T>(PhantomData<T>);
55+
56+
#[pallet::config]
57+
pub trait Config: frame_system::Config {
58+
/// The overarching event type.
59+
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
60+
61+
/// The user account identifier type.
62+
type AccountId: Parameter
63+
+ Member
64+
+ MaybeSerializeDeserialize
65+
+ Debug
66+
+ MaybeDisplay
67+
+ Ord
68+
+ MaxEncodedLen;
69+
70+
/// Nonce type. This stores the number of previous transactions
71+
/// associated with a sender account.
72+
type Nonce: Parameter
73+
+ Member
74+
+ MaybeSerializeDeserialize
75+
+ Debug
76+
+ Default
77+
+ MaybeDisplay
78+
+ AtLeast32Bit
79+
+ Copy
80+
+ MaxEncodedLen;
81+
82+
/// Data to be associated with an account (other than nonce/transaction counter, which this
83+
/// pallet does regardless).
84+
type AccountData: Member + FullCodec + Clone + Default + TypeInfo + MaxEncodedLen;
85+
86+
/// Handler for when a new account has just been created.
87+
type OnNewAccount: OnNewAccount<<Self as Config>::AccountId>;
88+
89+
/// A function that is invoked when an account has been determined to be dead.
90+
///
91+
/// All resources should be cleaned up associated with the given account.
92+
type OnKilledAccount: OnKilledAccount<<Self as Config>::AccountId>;
93+
}
94+
95+
/// The full account information for a particular account ID.
96+
#[pallet::storage]
97+
#[pallet::getter(fn full_account)]
98+
pub type Account<T: Config> = StorageMap<
99+
_,
100+
Blake2_128Concat,
101+
<T as Config>::AccountId,
102+
AccountInfo<<T as Config>::Nonce, <T as Config>::AccountData>,
103+
ValueQuery,
104+
>;
105+
106+
#[pallet::event]
107+
#[pallet::generate_deposit(pub(super) fn deposit_event)]
108+
pub enum Event<T: Config> {
109+
/// A new account was created.
110+
NewAccount { account: <T as Config>::AccountId },
111+
/// An account was reaped.
112+
KilledAccount { account: <T as Config>::AccountId },
113+
}
114+
115+
#[pallet::error]
116+
pub enum Error<T> {
117+
/// The account already exists in case creating it.
118+
AccountAlreadyExist,
119+
/// The account doesn't exist in case removing it.
120+
AccountNotExist,
121+
}
122+
}
123+
124+
impl<T: Config> Pallet<T> {
125+
/// Check the account existence.
126+
pub fn account_exists(who: &<T as Config>::AccountId) -> bool {
127+
Account::<T>::contains_key(who)
128+
}
129+
130+
/// An account is being created.
131+
fn on_created_account(who: <T as Config>::AccountId) {
132+
<T as Config>::OnNewAccount::on_new_account(&who);
133+
Self::deposit_event(Event::NewAccount { account: who });
134+
}
135+
136+
/// Do anything that needs to be done after an account has been killed.
137+
fn on_killed_account(who: <T as Config>::AccountId) {
138+
<T as Config>::OnKilledAccount::on_killed_account(&who);
139+
Self::deposit_event(Event::KilledAccount { account: who });
140+
}
141+
142+
/// Retrieve the account transaction counter from storage.
143+
pub fn account_nonce(who: &<T as Config>::AccountId) -> <T as Config>::Nonce {
144+
Account::<T>::get(who).nonce
145+
}
146+
147+
/// Increment a particular account's nonce by 1.
148+
pub fn inc_account_nonce(who: &<T as Config>::AccountId) {
149+
Account::<T>::mutate(who, |a| a.nonce += <T as pallet::Config>::Nonce::one());
150+
}
151+
152+
/// Create an account.
153+
pub fn create_account(who: &<T as Config>::AccountId) -> DispatchResult {
154+
if Self::account_exists(who) {
155+
return Err(Error::<T>::AccountAlreadyExist.into());
156+
}
157+
158+
Account::<T>::insert(who.clone(), AccountInfo::<_, _>::default());
159+
Self::on_created_account(who.clone());
160+
Ok(())
161+
}
162+
163+
/// Remove an account.
164+
pub fn remove_account(who: &<T as Config>::AccountId) -> DispatchResult {
165+
if !Self::account_exists(who) {
166+
return Err(Error::<T>::AccountNotExist.into());
167+
}
168+
169+
Account::<T>::remove(who);
170+
Self::on_killed_account(who.clone());
171+
Ok(())
172+
}
173+
}
174+
175+
impl<T: Config> StoredMap<<T as Config>::AccountId, <T as Config>::AccountData> for Pallet<T> {
176+
fn get(k: &<T as Config>::AccountId) -> <T as Config>::AccountData {
177+
Account::<T>::get(k).data
178+
}
179+
180+
fn try_mutate_exists<R, E: From<DispatchError>>(
181+
k: &<T as Config>::AccountId,
182+
f: impl FnOnce(&mut Option<<T as Config>::AccountData>) -> Result<R, E>,
183+
) -> Result<R, E> {
184+
let (mut maybe_account_data, was_providing) = if Self::account_exists(k) {
185+
(Some(Account::<T>::get(k).data), true)
186+
} else {
187+
(None, false)
188+
};
189+
190+
let result = f(&mut maybe_account_data)?;
191+
192+
match (maybe_account_data, was_providing) {
193+
(Some(data), false) => {
194+
Account::<T>::mutate(k, |a| a.data = data);
195+
Self::on_created_account(k.clone());
196+
}
197+
(Some(data), true) => {
198+
Account::<T>::mutate(k, |a| a.data = data);
199+
}
200+
(None, true) => {
201+
Account::<T>::remove(k);
202+
Self::on_killed_account(k.clone());
203+
}
204+
(None, false) => {
205+
// Do nothing.
206+
}
207+
}
208+
209+
Ok(result)
210+
}
211+
}
212+
213+
/// Interface to handle account creation.
214+
pub trait OnNewAccount<AccountId> {
215+
/// A new account `who` has been registered.
216+
fn on_new_account(who: &AccountId);
217+
}
218+
219+
impl<AccountId> OnNewAccount<AccountId> for () {
220+
fn on_new_account(_who: &AccountId) {}
221+
}
222+
223+
/// Interface to handle account killing.
224+
pub trait OnKilledAccount<AccountId> {
225+
/// The account with the given id was reaped.
226+
fn on_killed_account(who: &AccountId);
227+
}
228+
229+
impl<AccountId> OnKilledAccount<AccountId> for () {
230+
fn on_killed_account(_who: &AccountId) {}
231+
}

0 commit comments

Comments
 (0)