Substrate EVM (#3927)

* srml-evm: init the basic structures

* srml-evm: finish executor implementation

* srml-evm: implement balance deposit and withdraw

* srml-evm: implement the actuall call/create

* srml-evm: use crates.io version of evm

* srml-evm: fix no-std compile

* Remove dependency patch

* Update to evm 0.14

* Use double map for account storage

* Add precompiles support

* Add some basic docs

* Use runtime_io::chain_id()

* Update srml/evm/src/lib.rs

Co-Authored-By: Xiliang Chen <xlchen1291@gmail.com>

* Update srml/evm/src/lib.rs

Co-Authored-By: Xiliang Chen <xlchen1291@gmail.com>

* Fix WithdrawReason

* Unique saturate balance to u128

* Unique saturate withdraw to u128

* Remove extern crate alloc

* Move account code to a separate storage and use ref for convert_account_id

* More match cause for error message

* Fix potential interger overflow

* Use decode_len for fetching code length
This commit is contained in:
Wei Tang
2019-11-03 15:57:46 +01:00
committed by GitHub
parent 77ed0dc2bc
commit 2e424f4d9f
7 changed files with 601 additions and 30 deletions
+89 -28
View File
@@ -1016,6 +1016,47 @@ dependencies = [
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "evm"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"evm-core 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"evm-gasometer 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"evm-runtime 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
"primitive-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "evm-core"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"primitive-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "evm-gasometer"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"evm-core 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"evm-runtime 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
"primitive-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "evm-runtime"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"evm-core 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"primitive-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "exit-future"
version = "0.1.4"
@@ -1099,24 +1140,13 @@ dependencies = [
"parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fixed-hash"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fixed-hash"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1639,6 +1669,14 @@ dependencies = [
"parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "impl-rlp"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rlp 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "impl-serde"
version = "0.2.3"
@@ -3274,17 +3312,6 @@ dependencies = [
"output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "primitive-types"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fixed-hash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"impl-codec 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"uint 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "primitive-types"
version = "0.6.0"
@@ -3292,6 +3319,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fixed-hash 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"impl-codec 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"impl-rlp 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"uint 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -3760,6 +3789,14 @@ dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rlp"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rocksdb"
version = "0.11.0"
@@ -4510,6 +4547,26 @@ dependencies = [
"substrate-primitives 2.0.0",
]
[[package]]
name = "srml-evm"
version = "2.0.0"
dependencies = [
"evm 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"primitive-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
"sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-io 2.0.0",
"sr-primitives 2.0.0",
"sr-std 2.0.0",
"srml-balances 2.0.0",
"srml-support 2.0.0",
"srml-system 2.0.0",
"srml-timestamp 2.0.0",
"substrate-primitives 2.0.0",
]
[[package]]
name = "srml-example"
version = "2.0.0"
@@ -5538,7 +5595,7 @@ name = "substrate-externalities"
version = "2.0.0"
dependencies = [
"environmental 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"primitive-types 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"primitive-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-std 2.0.0",
"substrate-primitives-storage 2.0.0",
]
@@ -5776,7 +5833,7 @@ dependencies = [
"parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"primitive-types 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"primitive-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -6683,7 +6740,7 @@ name = "twox-hash"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -7425,6 +7482,10 @@ dependencies = [
"checksum errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2a071601ed01b988f896ab14b95e67335d1eeb50190932a1320f7fe3cadc84e"
"checksum errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067"
"checksum error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ab49e9dcb602294bc42f9a7dfc9bc6e936fca4418ea300dbfb84fe16de0b7d9"
"checksum evm 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1138816a9b7f9a9d1fcabb1b8a7afed2687d035692baf297bd3fea122acdc96f"
"checksum evm-core 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bcde5af3d542874ddeb53de0919302d57586ea04b3f76f54d865f8a6cdc70ae"
"checksum evm-gasometer 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b82bc9f275cb59d2bcc05d85c98736ddfaba003a7ef7b73893fa7c1c1fab29dc"
"checksum evm-runtime 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0dbbc89d29618c3722c17ba78ddf432d40ace8ee27e3f8b28b52a85921112e4b"
"checksum exit-future 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d8013f441e38e31c670e7f34ec8f1d5d3a2bd9d303c1ff83976ca886005e8f48"
"checksum faerie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "875d78b92b2a4d9e1e2c7eeccfa30a327d2ee6434db3beb8fd6fd92f41898bc4"
"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9"
@@ -7434,7 +7495,6 @@ dependencies = [
"checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa"
"checksum file-per-thread-logger 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8505b75b31ef7285168dd237c4a7db3c1f3e0927e7d314e670bc98e854272fe9"
"checksum finality-grandpa 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9681c1f75941ea47584573dd2bc10558b2067d460612945887e00744e43393be"
"checksum fixed-hash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "516877b7b9a1cc2d0293cbce23cd6203f0edbfd4090e6ca4489fecb5aa73050e"
"checksum fixed-hash 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6357b15872f8126e4ea7cf79d579473f132ccd2de239494ad1bf4aa892faea68"
"checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33"
"checksum flate2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ad3c5233c9a940c8719031b423d7e6c16af66e031cb0420b0896f5245bf181d3"
@@ -7492,6 +7552,7 @@ dependencies = [
"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
"checksum impl-codec 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3fa0086251524c50fd53b32e7b05eb6d79e2f97221eaf0c53c0ca9c3096f21d3"
"checksum impl-rlp 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8f7a72f11830b52333f36e3b09a288333888bf54380fd0ac0790a3c31ab0f3c5"
"checksum impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "58e3cae7e99c7ff5a995da2cf78dd0a5383740eda71d98cf7b1910c301ac69b8"
"checksum impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7ef5550a42e3740a0e71f909d4c861056a284060af885ae7aa6242820f920d9d"
"checksum indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a61202fbe46c4a951e9404a720a0180bcf3212c750d735cb5c4ba4dc551299f3"
@@ -7622,7 +7683,6 @@ dependencies = [
"checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b"
"checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427"
"checksum primitive-types 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "83ef7b3b965c0eadcb6838f34f827e1dfb2939bdd5ebd43f9647e009b12b0371"
"checksum primitive-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97b5a08dda18910f056e5c2060c034e77cab18e0bd7d895e44f03207af4c71d5"
"checksum proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e"
"checksum proc-macro-error 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aeccfe4d5d8ea175d5f0e4a2ad0637e0f4121d63bd99d356fb1f39ab2e7c6097"
@@ -7674,6 +7734,7 @@ dependencies = [
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
"checksum rhododendron 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "36542aafc2429a4c010fafa079a20dee953b663cb2427f51d86cf1d436846b4d"
"checksum ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "426bc186e3e95cac1e4a4be125a4aca7e84c2d616ffc02244eef36e2a60a093c"
"checksum rlp 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8376a3f725ebb53f69263bbebb42196361fdfd551212409c8a721239aab4f09f"
"checksum rocksdb 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f1651697fefd273bfb4fd69466cc2a9d20de557a0213b97233b22b5e95924b5e"
"checksum rpassword 4.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f072d931f11a96546efd97642e1e75e807345aced86b947f9239102f262d0fcd"
"checksum rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ca4eaef519b494d1f2848fc602d18816fed808a981aedf4f1f00ceb7c9d32cf"
+1
View File
@@ -93,6 +93,7 @@ members = [
"srml/transaction-payment",
"srml/transaction-payment/rpc",
"srml/utility",
"srml/evm",
"node/cli",
"node/executor",
"node/primitives",
+1 -1
View File
@@ -6,7 +6,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
[dependencies]
primitive-types = { version = "0.5.1", features = ["codec"] }
primitive-types = { version = "0.6", features = ["codec"] }
primitives-storage = { package = "substrate-primitives-storage", path = "../primitives/storage" }
rstd = { package = "sr-std", path = "../sr-std" }
environmental = { version = "1.0.2" }
+1 -1
View File
@@ -12,7 +12,7 @@ log = { version = "0.4.8", default-features = false }
serde = { version = "1.0.101", optional = true, features = ["derive"] }
twox-hash = { version = "1.5.0", optional = true }
byteorder = { version = "1.3.2", default-features = false }
primitive-types = { version = "0.5.1", default-features = false, features = ["codec"] }
primitive-types = { version = "0.6", default-features = false, features = ["codec"] }
impl-serde = { version = "0.2.3", optional = true }
wasmi = { version = "0.5.1", optional = true }
hash-db = { version = "0.15.2", default-features = false }
+40
View File
@@ -0,0 +1,40 @@
[package]
name = "srml-evm"
version = "2.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
[dependencies]
serde = { version = "1.0.101", optional = true, features = ["derive"] }
codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false }
support = { package = "srml-support", path = "../support", default-features = false }
system = { package = "srml-system", path = "../system", default-features = false }
timestamp = { package = "srml-timestamp", path = "../timestamp", default-features = false }
balances = { package = "srml-balances", path = "../balances", default-features = false }
primitives = { package = "substrate-primitives", path = "../../core/primitives", default-features = false }
sr-primitives = { path = "../../core/sr-primitives", default-features = false }
rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false }
runtime-io = { package = "sr-io", path = "../../core/sr-io", default-features = false }
primitive-types = { version = "0.6", default-features = false, features = ["rlp"] }
rlp = { version = "0.4", default-features = false }
evm = { version = "0.14", default-features = false }
sha3 = { version = "0.8", default-features = false }
[features]
default = ["std"]
std = [
"serde",
"codec/std",
"primitives/std",
"sr-primitives/std",
"support/std",
"system/std",
"balances/std",
"runtime-io/std",
"rstd/std",
"sha3/std",
"rlp/std",
"primitive-types/std",
"evm/std",
"timestamp/std",
]
+187
View File
@@ -0,0 +1,187 @@
use rstd::marker::PhantomData;
use rstd::vec::Vec;
#[cfg(feature = "std")]
use serde::{Serialize, Deserialize};
use codec::{Encode, Decode};
use primitives::{U256, H256, H160};
use sr_primitives::traits::UniqueSaturatedInto;
use support::storage::{StorageMap, StorageDoubleMap};
use sha3::{Keccak256, Digest};
use evm::Config;
use evm::backend::{Backend as BackendT, ApplyBackend, Apply};
use crate::{Trait, Accounts, AccountStorages, AccountCodes, Module, Event};
#[derive(Clone, Eq, PartialEq, Encode, Decode, Default)]
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
/// Ethereum account nonce, balance and code. Used by storage.
pub struct Account {
/// Account nonce.
pub nonce: U256,
/// Account balance.
pub balance: U256,
}
#[derive(Clone, Eq, PartialEq, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
/// Ethereum log. Used for `deposit_event`.
pub struct Log {
/// Source address of the log.
pub address: H160,
/// Topics of the log.
pub topics: Vec<H256>,
/// Bytearray data of the log.
pub data: Vec<u8>,
}
#[derive(Clone, Eq, PartialEq, Encode, Decode, Default)]
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
/// External input from the transaction.
pub struct Vicinity {
/// Current transaction gas price.
pub gas_price: U256,
/// Origin of the transaction.
pub origin: H160,
}
/// Gasometer config used for executor. Currently this is hard-coded to
/// Istanbul hard fork.
pub static GASOMETER_CONFIG: Config = Config::istanbul();
/// Substrate backend for EVM.
pub struct Backend<'vicinity, T> {
vicinity: &'vicinity Vicinity,
_marker: PhantomData<T>,
}
impl<'vicinity, T> Backend<'vicinity, T> {
/// Create a new backend with given vicinity.
pub fn new(vicinity: &'vicinity Vicinity) -> Self {
Self { vicinity, _marker: PhantomData }
}
}
impl<'vicinity, T: Trait> BackendT for Backend<'vicinity, T> {
fn gas_price(&self) -> U256 { self.vicinity.gas_price }
fn origin(&self) -> H160 { self.vicinity.origin }
fn block_hash(&self, number: U256) -> H256 {
if number > U256::from(u32::max_value()) {
H256::default()
} else {
let number = T::BlockNumber::from(number.as_u32());
H256::from_slice(system::Module::<T>::block_hash(number).as_ref())
}
}
fn block_number(&self) -> U256 {
let number: u128 = system::Module::<T>::block_number().unique_saturated_into();
U256::from(number)
}
fn block_coinbase(&self) -> H160 {
H160::default()
}
fn block_timestamp(&self) -> U256 {
let now: u128 = timestamp::Module::<T>::get().unique_saturated_into();
U256::from(now)
}
fn block_difficulty(&self) -> U256 {
U256::zero()
}
fn block_gas_limit(&self) -> U256 {
U256::zero()
}
fn chain_id(&self) -> U256 {
U256::from(runtime_io::chain_id())
}
fn exists(&self, _address: H160) -> bool {
true
}
fn basic(&self, address: H160) -> evm::backend::Basic {
let account = Accounts::get(&address);
evm::backend::Basic {
balance: account.balance,
nonce: account.nonce,
}
}
fn code_size(&self, address: H160) -> usize {
AccountCodes::decode_len(&address).unwrap_or(0)
}
fn code_hash(&self, address: H160) -> H256 {
H256::from_slice(Keccak256::digest(&AccountCodes::get(&address)).as_slice())
}
fn code(&self, address: H160) -> Vec<u8> {
AccountCodes::get(&address)
}
fn storage(&self, address: H160, index: H256) -> H256 {
AccountStorages::get(address, index)
}
}
impl<'vicinity, T: Trait> ApplyBackend for Backend<'vicinity, T> {
fn apply<A, I, L>(
&mut self,
values: A,
logs: L,
delete_empty: bool,
) where
A: IntoIterator<Item=Apply<I>>,
I: IntoIterator<Item=(H256, H256)>,
L: IntoIterator<Item=evm::backend::Log>,
{
for apply in values {
match apply {
Apply::Modify {
address, basic, code, storage, reset_storage,
} => {
Accounts::mutate(&address, |account| {
account.balance = basic.balance;
account.nonce = basic.nonce;
});
if let Some(code) = code {
AccountCodes::insert(address, code);
}
if reset_storage {
AccountStorages::remove_prefix(address);
}
for (index, value) in storage {
if value == H256::default() {
AccountStorages::remove(address, index);
} else {
AccountStorages::insert(address, index, value);
}
}
if delete_empty {
Module::<T>::remove_account_if_empty(&address);
}
},
Apply::Delete { address } => {
Module::<T>::remove_account(&address)
},
}
}
for log in logs {
Module::<T>::deposit_event(Event::Log(Log {
address: log.address,
topics: log.topics,
data: log.data,
}));
}
}
}
+282
View File
@@ -0,0 +1,282 @@
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! EVM execution module for Substrate
// Ensure we're `no_std` when compiling for Wasm.
#![cfg_attr(not(feature = "std"), no_std)]
mod backend;
pub use crate::backend::{Account, Log, Vicinity, Backend};
use rstd::vec::Vec;
use support::{dispatch::Result, decl_module, decl_storage, decl_event};
use support::traits::{Currency, WithdrawReason, ExistenceRequirement};
use system::ensure_signed;
use sr_primitives::weights::SimpleDispatchInfo;
use sr_primitives::traits::UniqueSaturatedInto;
use primitives::{U256, H256, H160};
use evm::{ExitReason, ExitSucceed, ExitError};
use evm::executor::StackExecutor;
use evm::backend::ApplyBackend;
/// Type alias for currency balance.
pub type BalanceOf<T> = <<T as Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;
/// Trait that outputs the current transaction gas price.
pub trait FeeCalculator {
/// Return the current gas price.
fn gas_price() -> U256;
}
/// Trait for converting account ids of `balances` module into
/// `H160` for EVM module.
///
/// Accounts and contracts of this module are stored in its own
/// storage, in an Ethereum-compatible format. In order to communicate
/// with the rest of Substrate module, we require an one-to-one
/// mapping of Substrate account to Ethereum address.
pub trait ConvertAccountId<A> {
/// Given a Substrate address, return the corresponding Ethereum address.
fn convert_account_id(account_id: &A) -> H160;
}
/// Custom precompiles to be used by EVM engine.
pub trait Precompiles {
/// Try to execute the code address as precompile. If the code address is not
/// a precompile or the precompile is not yet available, return `None`.
/// Otherwise, calculate the amount of gas needed with given `input` and
/// `target_gas`. Return `Some(Ok(status, output, gas_used))` if the execution
/// is successful. Otherwise return `Some(Err(_))`.
fn execute(
address: H160,
input: &[u8],
target_gas: Option<usize>
) -> Option<core::result::Result<(ExitSucceed, Vec<u8>, usize), ExitError>>;
}
impl Precompiles for () {
fn execute(
_address: H160,
_input: &[u8],
_target_gas: Option<usize>
) -> Option<core::result::Result<(ExitSucceed, Vec<u8>, usize), ExitError>> {
None
}
}
/// EVM module trait
pub trait Trait: system::Trait + timestamp::Trait {
/// Calculator for current gas price.
type FeeCalculator: FeeCalculator;
/// Convert account ID to H160;
type ConvertAccountId: ConvertAccountId<Self::AccountId>;
/// Currency type for deposit and withdraw.
type Currency: Currency<Self::AccountId>;
/// The overarching event type.
type Event: From<Event> + Into<<Self as system::Trait>::Event>;
/// Precompiles associated with this EVM engine.
type Precompiles: Precompiles;
}
decl_storage! {
trait Store for Module<T: Trait> as Example {
Accounts get(fn accounts) config(): map H160 => Account;
AccountCodes: map H160 => Vec<u8>;
AccountStorages: double_map H160, blake2_256(H256) => H256;
}
}
decl_event!(
/// EVM events
pub enum Event {
/// Ethereum events from contracts.
Log(Log),
}
);
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
fn deposit_event() = default;
#[weight = SimpleDispatchInfo::FixedNormal(10_000)]
fn deposit_balance(origin, value: BalanceOf<T>) -> Result {
let sender = ensure_signed(origin)?;
T::Currency::withdraw(
&sender,
value,
WithdrawReason::Reserve.into(),
ExistenceRequirement::KeepAlive,
)?;
let bvalue = U256::from(UniqueSaturatedInto::<u128>::unique_saturated_into(value));
let address = T::ConvertAccountId::convert_account_id(&sender);
Accounts::mutate(&address, |account| {
account.balance += bvalue;
});
Ok(())
}
#[weight = SimpleDispatchInfo::FixedNormal(10_000)]
fn withdraw_balance(origin, value: BalanceOf<T>) -> Result {
let sender = ensure_signed(origin)?;
let address = T::ConvertAccountId::convert_account_id(&sender);
let bvalue = U256::from(UniqueSaturatedInto::<u128>::unique_saturated_into(value));
if Accounts::get(&address).balance < bvalue {
return Err("Not enough balance to withdraw")
}
Accounts::mutate(&address, |account| {
account.balance -= bvalue;
});
T::Currency::deposit_creating(&sender, value);
Ok(())
}
#[weight = SimpleDispatchInfo::FixedNormal(10_000)]
fn call(origin, target: H160, input: Vec<u8>, value: U256, gas_limit: u32) -> Result {
let sender = ensure_signed(origin)?;
let source = T::ConvertAccountId::convert_account_id(&sender);
let gas_price = T::FeeCalculator::gas_price();
let vicinity = Vicinity {
gas_price,
origin: source,
};
let mut backend = Backend::<T>::new(&vicinity);
let mut executor = StackExecutor::new_with_precompile(
&backend,
gas_limit as usize,
&backend::GASOMETER_CONFIG,
T::Precompiles::execute,
);
let total_fee = gas_price.checked_mul(U256::from(gas_limit))
.ok_or("Calculating total fee overflowed")?;
if Accounts::get(&source).balance <
value.checked_add(total_fee).ok_or("Calculating total payment overflowed")?
{
return Err("Not enough balance to pay transaction fee")
}
executor.withdraw(source, total_fee).map_err(|_| "Withdraw fee failed")?;
let reason = executor.transact_call(
source,
target,
value,
input,
gas_limit as usize,
);
let ret = match reason {
ExitReason::Succeed(_) => Ok(()),
ExitReason::Error(_) => Err("Execute message call failed"),
ExitReason::Revert(_) => Err("Execute message call reverted"),
ExitReason::Fatal(_) => Err("Execute message call returned VM fatal error"),
};
let actual_fee = executor.fee(gas_price);
executor.deposit(source, total_fee.saturating_sub(actual_fee));
let (values, logs) = executor.deconstruct();
backend.apply(values, logs, true);
ret
}
#[weight = SimpleDispatchInfo::FixedNormal(10_000)]
fn create(origin, init: Vec<u8>, value: U256, gas_limit: u32) -> Result {
let sender = ensure_signed(origin)?;
let source = T::ConvertAccountId::convert_account_id(&sender);
let gas_price = T::FeeCalculator::gas_price();
let vicinity = Vicinity {
gas_price,
origin: source,
};
let mut backend = Backend::<T>::new(&vicinity);
let mut executor = StackExecutor::new_with_precompile(
&backend,
gas_limit as usize,
&backend::GASOMETER_CONFIG,
T::Precompiles::execute,
);
let total_fee = gas_price.checked_mul(U256::from(gas_limit))
.ok_or("Calculating total fee overflowed")?;
if Accounts::get(&source).balance <
value.checked_add(total_fee).ok_or("Calculating total payment overflowed")?
{
return Err("Not enough balance to pay transaction fee")
}
executor.withdraw(source, total_fee).map_err(|_| "Withdraw fee failed")?;
let reason = executor.transact_create(
source,
value,
init,
gas_limit as usize,
);
let ret = match reason {
ExitReason::Succeed(_) => Ok(()),
ExitReason::Error(_) => Err("Execute contract creation failed"),
ExitReason::Revert(_) => Err("Execute contract creation reverted"),
ExitReason::Fatal(_) => Err("Execute contract creation returned VM fatal error"),
};
let actual_fee = executor.fee(gas_price);
executor.deposit(source, total_fee.saturating_sub(actual_fee));
let (values, logs) = executor.deconstruct();
backend.apply(values, logs, true);
ret
}
}
}
impl<T: Trait> Module<T> {
/// Check whether an account is empty.
pub fn is_account_empty(address: &H160) -> bool {
let account = Accounts::get(address);
let code_len = AccountCodes::decode_len(address).unwrap_or(0);
account.nonce == U256::zero() &&
account.balance == U256::zero() &&
code_len == 0
}
/// Remove an account if its empty.
pub fn remove_account_if_empty(address: &H160) {
if Self::is_account_empty(address) {
Self::remove_account(address)
}
}
/// Remove an account from state.
fn remove_account(address: &H160) {
Accounts::remove(address);
AccountCodes::remove(address);
AccountStorages::remove_prefix(address);
}
}