mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 15:51:12 +00:00
pallet-evm: move to Frontier (Part IV) (#7573)
This commit is contained in:
Generated
+2
-136
@@ -1439,23 +1439,6 @@ dependencies = [
|
||||
"tiny-keccak",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ethereum"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df706418ff7d3874b9506424b04ea0bef569a2b39412b43a27ea86e679be108e"
|
||||
dependencies = [
|
||||
"ethereum-types",
|
||||
"hash-db",
|
||||
"hash256-std-hasher",
|
||||
"parity-scale-codec",
|
||||
"rlp",
|
||||
"rlp-derive",
|
||||
"serde",
|
||||
"sha3 0.9.1",
|
||||
"triehash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ethereum-types"
|
||||
version = "0.9.2"
|
||||
@@ -1476,56 +1459,6 @@ version = "2.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59"
|
||||
|
||||
[[package]]
|
||||
name = "evm"
|
||||
version = "0.17.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16c8deca0ec3efa361b03d9cae6fe94321a1d2d0a523437edd720b3d140e3c08"
|
||||
dependencies = [
|
||||
"ethereum",
|
||||
"evm-core",
|
||||
"evm-gasometer",
|
||||
"evm-runtime",
|
||||
"log",
|
||||
"parity-scale-codec",
|
||||
"primitive-types",
|
||||
"rlp",
|
||||
"serde",
|
||||
"sha3 0.8.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "evm-core"
|
||||
version = "0.17.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf2d732b3c36df36833761cf67df8f65866be1d368d20508bc3e13e6f256c8c5"
|
||||
dependencies = [
|
||||
"log",
|
||||
"primitive-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "evm-gasometer"
|
||||
version = "0.17.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46de1b91ccd744627484183729f1b5af484b3bf15505007fc28cc54264cb9ea1"
|
||||
dependencies = [
|
||||
"evm-core",
|
||||
"evm-runtime",
|
||||
"primitive-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "evm-runtime"
|
||||
version = "0.17.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2c1d1ffe96f833788512c890d702457d790dba4917ac6f64f8f60fbd9bc40b8"
|
||||
dependencies = [
|
||||
"evm-core",
|
||||
"primitive-types",
|
||||
"sha3 0.8.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exit-future"
|
||||
version = "0.2.0"
|
||||
@@ -3248,7 +3181,7 @@ dependencies = [
|
||||
"pin-project 0.4.27",
|
||||
"rand 0.7.3",
|
||||
"salsa20",
|
||||
"sha3 0.9.1",
|
||||
"sha3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3740,7 +3673,7 @@ dependencies = [
|
||||
"digest 0.9.0",
|
||||
"sha-1 0.9.2",
|
||||
"sha2 0.9.2",
|
||||
"sha3 0.9.1",
|
||||
"sha3",
|
||||
"unsigned-varint 0.5.1",
|
||||
]
|
||||
|
||||
@@ -4686,28 +4619,6 @@ dependencies = [
|
||||
"substrate-test-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-evm"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"evm",
|
||||
"frame-support",
|
||||
"frame-system",
|
||||
"impl-trait-for-tuples",
|
||||
"pallet-balances",
|
||||
"pallet-timestamp",
|
||||
"parity-scale-codec",
|
||||
"primitive-types",
|
||||
"ripemd160",
|
||||
"rlp",
|
||||
"serde",
|
||||
"sha3 0.8.2",
|
||||
"sp-core",
|
||||
"sp-io",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-example"
|
||||
version = "2.0.0"
|
||||
@@ -6344,17 +6255,6 @@ dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ripemd160"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251"
|
||||
dependencies = [
|
||||
"block-buffer 0.9.0",
|
||||
"digest 0.9.0",
|
||||
"opaque-debug 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rlp"
|
||||
version = "0.4.6"
|
||||
@@ -6364,17 +6264,6 @@ dependencies = [
|
||||
"rustc-hex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rlp-derive"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rocksdb"
|
||||
version = "0.15.0"
|
||||
@@ -7923,19 +7812,6 @@ dependencies = [
|
||||
"opaque-debug 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha3"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf"
|
||||
dependencies = [
|
||||
"block-buffer 0.7.3",
|
||||
"byte-tools",
|
||||
"digest 0.8.1",
|
||||
"keccak",
|
||||
"opaque-debug 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha3"
|
||||
version = "0.9.1"
|
||||
@@ -9843,16 +9719,6 @@ dependencies = [
|
||||
"keccak-hasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "triehash"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f490aa7aa4e4d07edeba442c007e42e3e7f43aafb5112c5b047fff0b1aa5449c"
|
||||
dependencies = [
|
||||
"hash-db",
|
||||
"rlp",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.3"
|
||||
|
||||
@@ -75,7 +75,6 @@ members = [
|
||||
"frame/democracy",
|
||||
"frame/elections-phragmen",
|
||||
"frame/elections",
|
||||
"frame/evm",
|
||||
"frame/example",
|
||||
"frame/example-offchain-worker",
|
||||
"frame/example-parallel",
|
||||
|
||||
@@ -45,9 +45,6 @@
|
||||
# Contracts
|
||||
/frame/contracts/ @athei
|
||||
|
||||
# EVM
|
||||
/frame/evm/ @sorpaas
|
||||
|
||||
# NPoS and election
|
||||
/frame/staking/ @kianenigma
|
||||
/frame/elections/ @kianenigma
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
[package]
|
||||
name = "pallet-evm"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://substrate.dev"
|
||||
repository = "https://github.com/paritytech/substrate/"
|
||||
description = "FRAME EVM contracts pallet"
|
||||
readme = "README.md"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.101", optional = true, features = ["derive"] }
|
||||
codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false }
|
||||
frame-support = { version = "2.0.0", default-features = false, path = "../support" }
|
||||
frame-system = { version = "2.0.0", default-features = false, path = "../system" }
|
||||
pallet-timestamp = { version = "2.0.0", default-features = false, path = "../timestamp" }
|
||||
pallet-balances = { version = "2.0.0", default-features = false, path = "../balances" }
|
||||
sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" }
|
||||
sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" }
|
||||
sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" }
|
||||
sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" }
|
||||
primitive-types = { version = "0.7.0", default-features = false, features = ["rlp", "byteorder"] }
|
||||
rlp = { version = "0.4", default-features = false }
|
||||
evm = { version = "0.17", default-features = false }
|
||||
sha3 = { version = "0.8", default-features = false }
|
||||
impl-trait-for-tuples = "0.1"
|
||||
ripemd160 = { version = "0.9", default-features = false }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"serde",
|
||||
"codec/std",
|
||||
"sp-core/std",
|
||||
"sp-runtime/std",
|
||||
"frame-support/std",
|
||||
"frame-system/std",
|
||||
"pallet-balances/std",
|
||||
"sp-io/std",
|
||||
"sp-std/std",
|
||||
"sha3/std",
|
||||
"rlp/std",
|
||||
"primitive-types/std",
|
||||
"evm/std",
|
||||
"pallet-timestamp/std",
|
||||
"ripemd160/std",
|
||||
]
|
||||
@@ -1,29 +0,0 @@
|
||||
# EVM Module
|
||||
|
||||
The EVM module allows unmodified EVM code to be executed in a Substrate-based blockchain.
|
||||
- [`evm::Trait`](https://docs.rs/pallet-evm/2.0.0/pallet_evm/trait.Trait.html)
|
||||
|
||||
## EVM Engine
|
||||
|
||||
The EVM module uses [`SputnikVM`](https://github.com/rust-blockchain/evm) as the underlying EVM engine. The engine is overhauled so that it's [`modular`](https://github.com/corepaper/evm).
|
||||
|
||||
## Execution Lifecycle
|
||||
|
||||
There are a separate set of accounts managed by the EVM module. Substrate based accounts can call the EVM Module to deposit or withdraw balance from the Substrate base-currency into a different balance managed and used by the EVM module. Once a user has populated their balance, they can create and call smart contracts using this module.
|
||||
|
||||
There's one-to-one mapping from Substrate accounts and EVM external accounts that is defined by a conversion function.
|
||||
|
||||
## EVM Module vs Ethereum Network
|
||||
|
||||
The EVM module should be able to produce nearly identical results compared to the Ethereum mainnet, including gas cost and balance changes.
|
||||
|
||||
Observable differences include:
|
||||
|
||||
- The available length of block hashes may not be 256 depending on the configuration of the System module in the Substrate runtime.
|
||||
- Difficulty and coinbase, which do not make sense in this module and is currently hard coded to zero.
|
||||
|
||||
We currently do not aim to make unobservable behaviors, such as state root, to be the same. We also don't aim to follow the exact same transaction / receipt format. However, given one Ethereum transaction and one Substrate account's private key, one should be able to convert any Ethereum transaction into a transaction compatible with this module.
|
||||
|
||||
The gas configurations are configurable. Right now, a pre-defined Istanbul hard fork configuration option is provided.
|
||||
|
||||
License: Apache-2.0
|
||||
@@ -1,216 +0,0 @@
|
||||
use sp_std::marker::PhantomData;
|
||||
use sp_std::vec::Vec;
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
use codec::{Encode, Decode};
|
||||
use sp_core::{U256, H256, H160};
|
||||
use sp_runtime::traits::UniqueSaturatedInto;
|
||||
use frame_support::traits::Get;
|
||||
use frame_support::{debug, storage::{StorageMap, StorageDoubleMap}};
|
||||
use sha3::{Keccak256, Digest};
|
||||
use evm::backend::{Backend as BackendT, ApplyBackend, Apply};
|
||||
use crate::{Trait, 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>,
|
||||
/// Byte array 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,
|
||||
}
|
||||
|
||||
/// 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(frame_system::Module::<T>::block_hash(number).as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
fn block_number(&self) -> U256 {
|
||||
let number: u128 = frame_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 = pallet_timestamp::Module::<T>::get().unique_saturated_into();
|
||||
U256::from(now / 1000)
|
||||
}
|
||||
|
||||
fn block_difficulty(&self) -> U256 {
|
||||
U256::zero()
|
||||
}
|
||||
|
||||
fn block_gas_limit(&self) -> U256 {
|
||||
U256::zero()
|
||||
}
|
||||
|
||||
fn chain_id(&self) -> U256 {
|
||||
U256::from(T::ChainId::get())
|
||||
}
|
||||
|
||||
fn exists(&self, _address: H160) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn basic(&self, address: H160) -> evm::backend::Basic {
|
||||
let account = Module::<T>::account_basic(&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,
|
||||
} => {
|
||||
Module::<T>::mutate_account_basic(&address, Account {
|
||||
nonce: basic.nonce,
|
||||
balance: basic.balance,
|
||||
});
|
||||
|
||||
if let Some(code) = code {
|
||||
debug::debug!(
|
||||
target: "evm",
|
||||
"Inserting code ({} bytes) at {:?}",
|
||||
code.len(),
|
||||
address
|
||||
);
|
||||
AccountCodes::insert(address, code);
|
||||
}
|
||||
|
||||
if reset_storage {
|
||||
AccountStorages::remove_prefix(address);
|
||||
}
|
||||
|
||||
for (index, value) in storage {
|
||||
if value == H256::default() {
|
||||
debug::debug!(
|
||||
target: "evm",
|
||||
"Removing storage for {:?} [index: {:?}]",
|
||||
address,
|
||||
index
|
||||
);
|
||||
AccountStorages::remove(address, index);
|
||||
} else {
|
||||
debug::debug!(
|
||||
target: "evm",
|
||||
"Updating storage for {:?} [index: {:?}, value: {:?}]",
|
||||
address,
|
||||
index,
|
||||
value
|
||||
);
|
||||
AccountStorages::insert(address, index, value);
|
||||
}
|
||||
}
|
||||
|
||||
if delete_empty {
|
||||
Module::<T>::remove_account_if_empty(&address);
|
||||
}
|
||||
},
|
||||
Apply::Delete { address } => {
|
||||
debug::debug!(
|
||||
target: "evm",
|
||||
"Deleting account at {:?}",
|
||||
address
|
||||
);
|
||||
Module::<T>::remove_account(&address)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
for log in logs {
|
||||
debug::trace!(
|
||||
target: "evm",
|
||||
"Inserting log for {:?}, topics ({}) {:?}, data ({}): {:?}]",
|
||||
log.address,
|
||||
log.topics.len(),
|
||||
log.topics,
|
||||
log.data.len(),
|
||||
log.data
|
||||
);
|
||||
Module::<T>::deposit_event(Event::<T>::Log(Log {
|
||||
address: log.address,
|
||||
topics: log.topics,
|
||||
data: log.data,
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,678 +0,0 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd.
|
||||
// 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.
|
||||
|
||||
//! # EVM Module
|
||||
//!
|
||||
//! The EVM module allows unmodified EVM code to be executed in a Substrate-based blockchain.
|
||||
//! - [`evm::Trait`]
|
||||
//!
|
||||
//! ## EVM Engine
|
||||
//!
|
||||
//! The EVM module uses [`SputnikVM`](https://github.com/rust-blockchain/evm) as the underlying EVM engine.
|
||||
//! The engine is overhauled so that it's [`modular`](https://github.com/corepaper/evm).
|
||||
//!
|
||||
//! ## Execution Lifecycle
|
||||
//!
|
||||
//! There are a separate set of accounts managed by the EVM module. Substrate based accounts can call the EVM Module
|
||||
//! to deposit or withdraw balance from the Substrate base-currency into a different balance managed and used by
|
||||
//! the EVM module. Once a user has populated their balance, they can create and call smart contracts using this module.
|
||||
//!
|
||||
//! There's one-to-one mapping from Substrate accounts and EVM external accounts that is defined by a conversion function.
|
||||
//!
|
||||
//! ## EVM Module vs Ethereum Network
|
||||
//!
|
||||
//! The EVM module should be able to produce nearly identical results compared to the Ethereum mainnet,
|
||||
//! including gas cost and balance changes.
|
||||
//!
|
||||
//! Observable differences include:
|
||||
//!
|
||||
//! - The available length of block hashes may not be 256 depending on the configuration of the System module
|
||||
//! in the Substrate runtime.
|
||||
//! - Difficulty and coinbase, which do not make sense in this module and is currently hard coded to zero.
|
||||
//!
|
||||
//! We currently do not aim to make unobservable behaviors, such as state root, to be the same. We also don't aim to follow
|
||||
//! the exact same transaction / receipt format. However, given one Ethereum transaction and one Substrate account's
|
||||
//! private key, one should be able to convert any Ethereum transaction into a transaction compatible with this module.
|
||||
//!
|
||||
//! The gas configurations are configurable. Right now, a pre-defined Istanbul hard fork configuration option is provided.
|
||||
|
||||
// Ensure we're `no_std` when compiling for Wasm.
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
mod backend;
|
||||
mod tests;
|
||||
pub mod precompiles;
|
||||
|
||||
pub use crate::precompiles::{Precompile, Precompiles};
|
||||
pub use crate::backend::{Account, Log, Vicinity, Backend};
|
||||
|
||||
use sp_std::vec::Vec;
|
||||
#[cfg(feature = "std")]
|
||||
use codec::{Encode, Decode};
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
use frame_support::{debug, ensure, decl_module, decl_storage, decl_event, decl_error};
|
||||
use frame_support::weights::{Weight, Pays};
|
||||
use frame_support::traits::{Currency, ExistenceRequirement, Get};
|
||||
use frame_support::dispatch::DispatchResultWithPostInfo;
|
||||
use frame_system::RawOrigin;
|
||||
use sp_core::{U256, H256, H160, Hasher};
|
||||
use sp_runtime::{AccountId32, traits::{UniqueSaturatedInto, SaturatedConversion, BadOrigin}};
|
||||
use sha3::{Digest, Keccak256};
|
||||
pub use evm::{ExitReason, ExitSucceed, ExitError, ExitRevert, ExitFatal};
|
||||
use evm::Config;
|
||||
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 frame_system::Trait>::AccountId>>::Balance;
|
||||
|
||||
/// Trait that outputs the current transaction gas price.
|
||||
pub trait FeeCalculator {
|
||||
/// Return the minimal required gas price.
|
||||
fn min_gas_price() -> U256;
|
||||
}
|
||||
|
||||
impl FeeCalculator for () {
|
||||
fn min_gas_price() -> U256 { U256::zero() }
|
||||
}
|
||||
|
||||
pub trait EnsureAddressOrigin<OuterOrigin> {
|
||||
/// Success return type.
|
||||
type Success;
|
||||
|
||||
/// Perform the origin check.
|
||||
fn ensure_address_origin(
|
||||
address: &H160,
|
||||
origin: OuterOrigin,
|
||||
) -> Result<Self::Success, BadOrigin> {
|
||||
Self::try_address_origin(address, origin).map_err(|_| BadOrigin)
|
||||
}
|
||||
|
||||
/// Try with origin.
|
||||
fn try_address_origin(
|
||||
address: &H160,
|
||||
origin: OuterOrigin,
|
||||
) -> Result<Self::Success, OuterOrigin>;
|
||||
}
|
||||
|
||||
/// Ensure that the EVM address is the same as the Substrate address. This only works if the account
|
||||
/// ID is `H160`.
|
||||
pub struct EnsureAddressSame;
|
||||
|
||||
impl<OuterOrigin> EnsureAddressOrigin<OuterOrigin> for EnsureAddressSame where
|
||||
OuterOrigin: Into<Result<RawOrigin<H160>, OuterOrigin>> + From<RawOrigin<H160>>,
|
||||
{
|
||||
type Success = H160;
|
||||
|
||||
fn try_address_origin(
|
||||
address: &H160,
|
||||
origin: OuterOrigin,
|
||||
) -> Result<H160, OuterOrigin> {
|
||||
origin.into().and_then(|o| match o {
|
||||
RawOrigin::Signed(who) if &who == address => Ok(who),
|
||||
r => Err(OuterOrigin::from(r))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure that the origin is root.
|
||||
pub struct EnsureAddressRoot<AccountId>(sp_std::marker::PhantomData<AccountId>);
|
||||
|
||||
impl<OuterOrigin, AccountId> EnsureAddressOrigin<OuterOrigin> for EnsureAddressRoot<AccountId> where
|
||||
OuterOrigin: Into<Result<RawOrigin<AccountId>, OuterOrigin>> + From<RawOrigin<AccountId>>,
|
||||
{
|
||||
type Success = ();
|
||||
|
||||
fn try_address_origin(
|
||||
_address: &H160,
|
||||
origin: OuterOrigin,
|
||||
) -> Result<(), OuterOrigin> {
|
||||
origin.into().and_then(|o| match o {
|
||||
RawOrigin::Root => Ok(()),
|
||||
r => Err(OuterOrigin::from(r)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure that the origin never happens.
|
||||
pub struct EnsureAddressNever<AccountId>(sp_std::marker::PhantomData<AccountId>);
|
||||
|
||||
impl<OuterOrigin, AccountId> EnsureAddressOrigin<OuterOrigin> for EnsureAddressNever<AccountId> {
|
||||
type Success = AccountId;
|
||||
|
||||
fn try_address_origin(
|
||||
_address: &H160,
|
||||
origin: OuterOrigin,
|
||||
) -> Result<AccountId, OuterOrigin> {
|
||||
Err(origin)
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure that the address is truncated hash of the origin. Only works if the account id is
|
||||
/// `AccountId32`.
|
||||
pub struct EnsureAddressTruncated;
|
||||
|
||||
impl<OuterOrigin> EnsureAddressOrigin<OuterOrigin> for EnsureAddressTruncated where
|
||||
OuterOrigin: Into<Result<RawOrigin<AccountId32>, OuterOrigin>> + From<RawOrigin<AccountId32>>,
|
||||
{
|
||||
type Success = AccountId32;
|
||||
|
||||
fn try_address_origin(
|
||||
address: &H160,
|
||||
origin: OuterOrigin,
|
||||
) -> Result<AccountId32, OuterOrigin> {
|
||||
origin.into().and_then(|o| match o {
|
||||
RawOrigin::Signed(who)
|
||||
if AsRef::<[u8; 32]>::as_ref(&who)[0..20] == address[0..20] => Ok(who),
|
||||
r => Err(OuterOrigin::from(r))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AddressMapping<A> {
|
||||
fn into_account_id(address: H160) -> A;
|
||||
}
|
||||
|
||||
/// Identity address mapping.
|
||||
pub struct IdentityAddressMapping;
|
||||
|
||||
impl AddressMapping<H160> for IdentityAddressMapping {
|
||||
fn into_account_id(address: H160) -> H160 { address }
|
||||
}
|
||||
|
||||
/// Hashed address mapping.
|
||||
pub struct HashedAddressMapping<H>(sp_std::marker::PhantomData<H>);
|
||||
|
||||
impl<H: Hasher<Out=H256>> AddressMapping<AccountId32> for HashedAddressMapping<H> {
|
||||
fn into_account_id(address: H160) -> AccountId32 {
|
||||
let mut data = [0u8; 24];
|
||||
data[0..4].copy_from_slice(b"evm:");
|
||||
data[4..24].copy_from_slice(&address[..]);
|
||||
let hash = H::hash(&data);
|
||||
|
||||
AccountId32::from(Into::<[u8; 32]>::into(hash))
|
||||
}
|
||||
}
|
||||
|
||||
/// Substrate system chain ID.
|
||||
pub struct SystemChainId;
|
||||
|
||||
impl Get<u64> for SystemChainId {
|
||||
fn get() -> u64 {
|
||||
sp_io::misc::chain_id()
|
||||
}
|
||||
}
|
||||
|
||||
static ISTANBUL_CONFIG: Config = Config::istanbul();
|
||||
|
||||
/// EVM module trait
|
||||
pub trait Trait: frame_system::Trait + pallet_timestamp::Trait {
|
||||
/// Calculator for current gas price.
|
||||
type FeeCalculator: FeeCalculator;
|
||||
|
||||
/// Allow the origin to call on behalf of given address.
|
||||
type CallOrigin: EnsureAddressOrigin<Self::Origin>;
|
||||
/// Allow the origin to withdraw on behalf of given address.
|
||||
type WithdrawOrigin: EnsureAddressOrigin<Self::Origin, Success=Self::AccountId>;
|
||||
|
||||
/// Mapping from address to account id.
|
||||
type AddressMapping: AddressMapping<Self::AccountId>;
|
||||
/// Currency type for withdraw and balance storage.
|
||||
type Currency: Currency<Self::AccountId>;
|
||||
|
||||
/// The overarching event type.
|
||||
type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
|
||||
/// Precompiles associated with this EVM engine.
|
||||
type Precompiles: Precompiles;
|
||||
/// Chain ID of EVM.
|
||||
type ChainId: Get<u64>;
|
||||
|
||||
/// EVM config used in the module.
|
||||
fn config() -> &'static Config {
|
||||
&ISTANBUL_CONFIG
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, Serialize, Deserialize)]
|
||||
/// Account definition used for genesis block construction.
|
||||
pub struct GenesisAccount {
|
||||
/// Account nonce.
|
||||
pub nonce: U256,
|
||||
/// Account balance.
|
||||
pub balance: U256,
|
||||
/// Full account storage.
|
||||
pub storage: std::collections::BTreeMap<H256, H256>,
|
||||
/// Account code.
|
||||
pub code: Vec<u8>,
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as EVM {
|
||||
AccountCodes get(fn account_codes): map hasher(blake2_128_concat) H160 => Vec<u8>;
|
||||
AccountStorages get(fn account_storages):
|
||||
double_map hasher(blake2_128_concat) H160, hasher(blake2_128_concat) H256 => H256;
|
||||
}
|
||||
|
||||
add_extra_genesis {
|
||||
config(accounts): std::collections::BTreeMap<H160, GenesisAccount>;
|
||||
build(|config: &GenesisConfig| {
|
||||
for (address, account) in &config.accounts {
|
||||
Module::<T>::mutate_account_basic(&address, Account {
|
||||
balance: account.balance,
|
||||
nonce: account.nonce,
|
||||
});
|
||||
AccountCodes::insert(address, &account.code);
|
||||
|
||||
for (index, value) in &account.storage {
|
||||
AccountStorages::insert(address, index, value);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
decl_event! {
|
||||
/// EVM events
|
||||
pub enum Event<T> where
|
||||
<T as frame_system::Trait>::AccountId,
|
||||
{
|
||||
/// Ethereum events from contracts.
|
||||
Log(Log),
|
||||
/// A contract has been created at given \[address\].
|
||||
Created(H160),
|
||||
/// A \[contract\] was attempted to be created, but the execution failed.
|
||||
CreatedFailed(H160),
|
||||
/// A \[contract\] has been executed successfully with states applied.
|
||||
Executed(H160),
|
||||
/// A \[contract\] has been executed with errors. States are reverted with only gas fees applied.
|
||||
ExecutedFailed(H160),
|
||||
/// A deposit has been made at a given address. \[sender, address, value\]
|
||||
BalanceDeposit(AccountId, H160, U256),
|
||||
/// A withdrawal has been made from a given address. \[sender, address, value\]
|
||||
BalanceWithdraw(AccountId, H160, U256),
|
||||
}
|
||||
}
|
||||
|
||||
decl_error! {
|
||||
pub enum Error for Module<T: Trait> {
|
||||
/// Not enough balance to perform action
|
||||
BalanceLow,
|
||||
/// Calculating total fee overflowed
|
||||
FeeOverflow,
|
||||
/// Calculating total payment overflowed
|
||||
PaymentOverflow,
|
||||
/// Withdraw fee failed
|
||||
WithdrawFailed,
|
||||
/// Gas price is too low.
|
||||
GasPriceTooLow,
|
||||
/// Nonce is invalid
|
||||
InvalidNonce,
|
||||
}
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
type Error = Error<T>;
|
||||
|
||||
fn deposit_event() = default;
|
||||
|
||||
/// Withdraw balance from EVM into currency/balances module.
|
||||
#[weight = 0]
|
||||
fn withdraw(origin, address: H160, value: BalanceOf<T>) {
|
||||
let destination = T::WithdrawOrigin::ensure_address_origin(&address, origin)?;
|
||||
let address_account_id = T::AddressMapping::into_account_id(address);
|
||||
|
||||
T::Currency::transfer(
|
||||
&address_account_id,
|
||||
&destination,
|
||||
value,
|
||||
ExistenceRequirement::AllowDeath
|
||||
)?;
|
||||
}
|
||||
|
||||
/// Issue an EVM call operation. This is similar to a message call transaction in Ethereum.
|
||||
#[weight = (*gas_price).saturated_into::<Weight>().saturating_mul(*gas_limit as Weight)]
|
||||
fn call(
|
||||
origin,
|
||||
source: H160,
|
||||
target: H160,
|
||||
input: Vec<u8>,
|
||||
value: U256,
|
||||
gas_limit: u32,
|
||||
gas_price: U256,
|
||||
nonce: Option<U256>,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
T::CallOrigin::ensure_address_origin(&source, origin)?;
|
||||
|
||||
match Self::execute_call(
|
||||
source,
|
||||
target,
|
||||
input,
|
||||
value,
|
||||
gas_limit,
|
||||
gas_price,
|
||||
nonce,
|
||||
true,
|
||||
)? {
|
||||
(ExitReason::Succeed(_), _, _, _) => {
|
||||
Module::<T>::deposit_event(Event::<T>::Executed(target));
|
||||
},
|
||||
(_, _, _, _) => {
|
||||
Module::<T>::deposit_event(Event::<T>::ExecutedFailed(target));
|
||||
},
|
||||
}
|
||||
|
||||
Ok(Pays::No.into())
|
||||
}
|
||||
|
||||
/// Issue an EVM create operation. This is similar to a contract creation transaction in
|
||||
/// Ethereum.
|
||||
#[weight = (*gas_price).saturated_into::<Weight>().saturating_mul(*gas_limit as Weight)]
|
||||
fn create(
|
||||
origin,
|
||||
source: H160,
|
||||
init: Vec<u8>,
|
||||
value: U256,
|
||||
gas_limit: u32,
|
||||
gas_price: U256,
|
||||
nonce: Option<U256>,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
T::CallOrigin::ensure_address_origin(&source, origin)?;
|
||||
|
||||
match Self::execute_create(
|
||||
source,
|
||||
init,
|
||||
value,
|
||||
gas_limit,
|
||||
gas_price,
|
||||
nonce,
|
||||
true,
|
||||
)? {
|
||||
(ExitReason::Succeed(_), create_address, _, _) => {
|
||||
Module::<T>::deposit_event(Event::<T>::Created(create_address));
|
||||
},
|
||||
(_, create_address, _, _) => {
|
||||
Module::<T>::deposit_event(Event::<T>::CreatedFailed(create_address));
|
||||
},
|
||||
}
|
||||
|
||||
Ok(Pays::No.into())
|
||||
}
|
||||
|
||||
/// Issue an EVM create2 operation.
|
||||
#[weight = (*gas_price).saturated_into::<Weight>().saturating_mul(*gas_limit as Weight)]
|
||||
fn create2(
|
||||
origin,
|
||||
source: H160,
|
||||
init: Vec<u8>,
|
||||
salt: H256,
|
||||
value: U256,
|
||||
gas_limit: u32,
|
||||
gas_price: U256,
|
||||
nonce: Option<U256>,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
T::CallOrigin::ensure_address_origin(&source, origin)?;
|
||||
|
||||
match Self::execute_create2(
|
||||
source,
|
||||
init,
|
||||
salt,
|
||||
value,
|
||||
gas_limit,
|
||||
gas_price,
|
||||
nonce,
|
||||
true,
|
||||
)? {
|
||||
(ExitReason::Succeed(_), create_address, _, _) => {
|
||||
Module::<T>::deposit_event(Event::<T>::Created(create_address));
|
||||
},
|
||||
(_, create_address, _, _) => {
|
||||
Module::<T>::deposit_event(Event::<T>::CreatedFailed(create_address));
|
||||
},
|
||||
}
|
||||
|
||||
Ok(Pays::No.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
fn remove_account(address: &H160) {
|
||||
AccountCodes::remove(address);
|
||||
AccountStorages::remove_prefix(address);
|
||||
}
|
||||
|
||||
fn mutate_account_basic(address: &H160, new: Account) {
|
||||
let account_id = T::AddressMapping::into_account_id(*address);
|
||||
let current = Self::account_basic(address);
|
||||
|
||||
if current.nonce < new.nonce {
|
||||
// ASSUME: in one single EVM transaction, the nonce will not increase more than
|
||||
// `u128::max_value()`.
|
||||
for _ in 0..(new.nonce - current.nonce).low_u128() {
|
||||
frame_system::Module::<T>::inc_account_nonce(&account_id);
|
||||
}
|
||||
}
|
||||
|
||||
if current.balance > new.balance {
|
||||
let diff = current.balance - new.balance;
|
||||
T::Currency::slash(&account_id, diff.low_u128().unique_saturated_into());
|
||||
} else if current.balance < new.balance {
|
||||
let diff = new.balance - current.balance;
|
||||
T::Currency::deposit_creating(&account_id, diff.low_u128().unique_saturated_into());
|
||||
}
|
||||
}
|
||||
|
||||
/// Check whether an account is empty.
|
||||
pub fn is_account_empty(address: &H160) -> bool {
|
||||
let account = Self::account_basic(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);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the account basic in EVM format.
|
||||
pub fn account_basic(address: &H160) -> Account {
|
||||
let account_id = T::AddressMapping::into_account_id(*address);
|
||||
|
||||
let nonce = frame_system::Module::<T>::account_nonce(&account_id);
|
||||
let balance = T::Currency::free_balance(&account_id);
|
||||
|
||||
Account {
|
||||
nonce: U256::from(UniqueSaturatedInto::<u128>::unique_saturated_into(nonce)),
|
||||
balance: U256::from(UniqueSaturatedInto::<u128>::unique_saturated_into(balance)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute a create transaction on behalf of given sender.
|
||||
pub fn execute_create(
|
||||
source: H160,
|
||||
init: Vec<u8>,
|
||||
value: U256,
|
||||
gas_limit: u32,
|
||||
gas_price: U256,
|
||||
nonce: Option<U256>,
|
||||
apply_state: bool,
|
||||
) -> Result<(ExitReason, H160, U256, Vec<Log>), Error<T>> {
|
||||
Self::execute_evm(
|
||||
source,
|
||||
value,
|
||||
gas_limit,
|
||||
gas_price,
|
||||
nonce,
|
||||
apply_state,
|
||||
|executor| {
|
||||
let address = executor.create_address(
|
||||
evm::CreateScheme::Legacy { caller: source },
|
||||
);
|
||||
(executor.transact_create(
|
||||
source,
|
||||
value,
|
||||
init,
|
||||
gas_limit as usize,
|
||||
), address)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Execute a create2 transaction on behalf of a given sender.
|
||||
pub fn execute_create2(
|
||||
source: H160,
|
||||
init: Vec<u8>,
|
||||
salt: H256,
|
||||
value: U256,
|
||||
gas_limit: u32,
|
||||
gas_price: U256,
|
||||
nonce: Option<U256>,
|
||||
apply_state: bool,
|
||||
) -> Result<(ExitReason, H160, U256, Vec<Log>), Error<T>> {
|
||||
let code_hash = H256::from_slice(Keccak256::digest(&init).as_slice());
|
||||
Self::execute_evm(
|
||||
source,
|
||||
value,
|
||||
gas_limit,
|
||||
gas_price,
|
||||
nonce,
|
||||
apply_state,
|
||||
|executor| {
|
||||
let address = executor.create_address(
|
||||
evm::CreateScheme::Create2 { caller: source, code_hash, salt },
|
||||
);
|
||||
(executor.transact_create2(
|
||||
source,
|
||||
value,
|
||||
init,
|
||||
salt,
|
||||
gas_limit as usize,
|
||||
), address)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Execute a call transaction on behalf of a given sender.
|
||||
pub fn execute_call(
|
||||
source: H160,
|
||||
target: H160,
|
||||
input: Vec<u8>,
|
||||
value: U256,
|
||||
gas_limit: u32,
|
||||
gas_price: U256,
|
||||
nonce: Option<U256>,
|
||||
apply_state: bool,
|
||||
) -> Result<(ExitReason, Vec<u8>, U256, Vec<Log>), Error<T>> {
|
||||
Self::execute_evm(
|
||||
source,
|
||||
value,
|
||||
gas_limit,
|
||||
gas_price,
|
||||
nonce,
|
||||
apply_state,
|
||||
|executor| executor.transact_call(
|
||||
source,
|
||||
target,
|
||||
value,
|
||||
input,
|
||||
gas_limit as usize,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/// Execute an EVM operation.
|
||||
fn execute_evm<F, R>(
|
||||
source: H160,
|
||||
value: U256,
|
||||
gas_limit: u32,
|
||||
gas_price: U256,
|
||||
nonce: Option<U256>,
|
||||
apply_state: bool,
|
||||
f: F,
|
||||
) -> Result<(ExitReason, R, U256, Vec<Log>), Error<T>> where
|
||||
F: FnOnce(&mut StackExecutor<Backend<T>>) -> (ExitReason, R),
|
||||
{
|
||||
|
||||
// Gas price check is skipped when performing a gas estimation.
|
||||
if apply_state {
|
||||
ensure!(gas_price >= T::FeeCalculator::min_gas_price(), Error::<T>::GasPriceTooLow);
|
||||
}
|
||||
|
||||
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,
|
||||
T::config(),
|
||||
T::Precompiles::execute,
|
||||
);
|
||||
|
||||
let total_fee = gas_price.checked_mul(U256::from(gas_limit))
|
||||
.ok_or(Error::<T>::FeeOverflow)?;
|
||||
let total_payment = value.checked_add(total_fee).ok_or(Error::<T>::PaymentOverflow)?;
|
||||
let source_account = Self::account_basic(&source);
|
||||
ensure!(source_account.balance >= total_payment, Error::<T>::BalanceLow);
|
||||
executor.withdraw(source, total_fee).map_err(|_| Error::<T>::WithdrawFailed)?;
|
||||
|
||||
if let Some(nonce) = nonce {
|
||||
ensure!(source_account.nonce == nonce, Error::<T>::InvalidNonce);
|
||||
}
|
||||
|
||||
let (retv, reason) = f(&mut executor);
|
||||
|
||||
let used_gas = U256::from(executor.used_gas());
|
||||
let actual_fee = executor.fee(gas_price);
|
||||
debug::debug!(
|
||||
target: "evm",
|
||||
"Execution {:?} [source: {:?}, value: {}, gas_limit: {}, used_gas: {}, actual_fee: {}]",
|
||||
retv,
|
||||
source,
|
||||
value,
|
||||
gas_limit,
|
||||
used_gas,
|
||||
actual_fee
|
||||
);
|
||||
executor.deposit(source, total_fee.saturating_sub(actual_fee));
|
||||
|
||||
let (values, logs) = executor.deconstruct();
|
||||
let logs_data = logs.into_iter().map(|x| x ).collect::<Vec<_>>();
|
||||
let logs_result = logs_data.clone().into_iter().map(|it| {
|
||||
Log {
|
||||
address: it.address,
|
||||
topics: it.topics,
|
||||
data: it.data
|
||||
}
|
||||
}).collect();
|
||||
if apply_state {
|
||||
backend.apply(values, logs_data, true);
|
||||
}
|
||||
|
||||
Ok((retv, reason, used_gas, logs_result))
|
||||
}
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd.
|
||||
// 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.
|
||||
|
||||
//! Builtin precompiles.
|
||||
|
||||
use sp_std::{cmp::min, vec::Vec};
|
||||
use sp_core::H160;
|
||||
use evm::{ExitError, ExitSucceed};
|
||||
use ripemd160::Digest;
|
||||
use impl_trait_for_tuples::impl_for_tuples;
|
||||
|
||||
/// 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>>;
|
||||
}
|
||||
|
||||
/// One single precompile used by EVM engine.
|
||||
pub trait Precompile {
|
||||
/// Try to execute the precompile. Calculate the amount of gas needed with given `input` and
|
||||
/// `target_gas`. Return `Ok(status, output, gas_used)` if the execution is
|
||||
/// successful. Otherwise return `Err(_)`.
|
||||
fn execute(
|
||||
input: &[u8],
|
||||
target_gas: Option<usize>,
|
||||
) -> core::result::Result<(ExitSucceed, Vec<u8>, usize), ExitError>;
|
||||
}
|
||||
|
||||
#[impl_for_tuples(16)]
|
||||
#[tuple_types_no_default_trait_bound]
|
||||
impl Precompiles for Tuple {
|
||||
for_tuples!( where #( Tuple: Precompile )* );
|
||||
|
||||
fn execute(
|
||||
address: H160,
|
||||
input: &[u8],
|
||||
target_gas: Option<usize>,
|
||||
) -> Option<core::result::Result<(ExitSucceed, Vec<u8>, usize), ExitError>> {
|
||||
let mut index = 0;
|
||||
|
||||
for_tuples!( #(
|
||||
index += 1;
|
||||
if address == H160::from_low_u64_be(index) {
|
||||
return Some(Tuple::execute(input, target_gas))
|
||||
}
|
||||
)* );
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Linear gas cost
|
||||
fn ensure_linear_cost(
|
||||
target_gas: Option<usize>,
|
||||
len: usize,
|
||||
base: usize,
|
||||
word: usize
|
||||
) -> Result<usize, ExitError> {
|
||||
let cost = base.checked_add(
|
||||
word.checked_mul(len.saturating_add(31) / 32).ok_or(ExitError::OutOfGas)?
|
||||
).ok_or(ExitError::OutOfGas)?;
|
||||
|
||||
if let Some(target_gas) = target_gas {
|
||||
if cost > target_gas {
|
||||
return Err(ExitError::OutOfGas)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(cost)
|
||||
}
|
||||
|
||||
/// The identity precompile.
|
||||
pub struct Identity;
|
||||
|
||||
impl Precompile for Identity {
|
||||
fn execute(
|
||||
input: &[u8],
|
||||
target_gas: Option<usize>,
|
||||
) -> core::result::Result<(ExitSucceed, Vec<u8>, usize), ExitError> {
|
||||
let cost = ensure_linear_cost(target_gas, input.len(), 15, 3)?;
|
||||
|
||||
Ok((ExitSucceed::Returned, input.to_vec(), cost))
|
||||
}
|
||||
}
|
||||
|
||||
/// The ecrecover precompile.
|
||||
pub struct ECRecover;
|
||||
|
||||
impl Precompile for ECRecover {
|
||||
fn execute(
|
||||
i: &[u8],
|
||||
target_gas: Option<usize>,
|
||||
) -> core::result::Result<(ExitSucceed, Vec<u8>, usize), ExitError> {
|
||||
let cost = ensure_linear_cost(target_gas, i.len(), 3000, 0)?;
|
||||
|
||||
let mut input = [0u8; 128];
|
||||
input[..min(i.len(), 128)].copy_from_slice(&i[..min(i.len(), 128)]);
|
||||
|
||||
let mut msg = [0u8; 32];
|
||||
let mut sig = [0u8; 65];
|
||||
|
||||
msg[0..32].copy_from_slice(&input[0..32]);
|
||||
sig[0..32].copy_from_slice(&input[64..96]);
|
||||
sig[32..64].copy_from_slice(&input[96..128]);
|
||||
sig[64] = input[63];
|
||||
|
||||
let pubkey = sp_io::crypto::secp256k1_ecdsa_recover(&sig, &msg)
|
||||
.map_err(|_| ExitError::Other("Public key recover failed"))?;
|
||||
let mut address = sp_io::hashing::keccak_256(&pubkey);
|
||||
address[0..12].copy_from_slice(&[0u8; 12]);
|
||||
|
||||
Ok((ExitSucceed::Returned, address.to_vec(), cost))
|
||||
}
|
||||
}
|
||||
|
||||
/// The ripemd precompile.
|
||||
pub struct Ripemd160;
|
||||
|
||||
impl Precompile for Ripemd160 {
|
||||
fn execute(
|
||||
input: &[u8],
|
||||
target_gas: Option<usize>,
|
||||
) -> core::result::Result<(ExitSucceed, Vec<u8>, usize), ExitError> {
|
||||
let cost = ensure_linear_cost(target_gas, input.len(), 600, 120)?;
|
||||
|
||||
let mut ret = [0u8; 32];
|
||||
ret[12..32].copy_from_slice(&ripemd160::Ripemd160::digest(input));
|
||||
Ok((ExitSucceed::Returned, ret.to_vec(), cost))
|
||||
}
|
||||
}
|
||||
|
||||
/// The sha256 precompile.
|
||||
pub struct Sha256;
|
||||
|
||||
impl Precompile for Sha256 {
|
||||
fn execute(
|
||||
input: &[u8],
|
||||
target_gas: Option<usize>,
|
||||
) -> core::result::Result<(ExitSucceed, Vec<u8>, usize), ExitError> {
|
||||
let cost = ensure_linear_cost(target_gas, input.len(), 60, 12)?;
|
||||
|
||||
let ret = sp_io::hashing::sha2_256(input);
|
||||
Ok((ExitSucceed::Returned, ret.to_vec(), cost))
|
||||
}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
#![cfg(test)]
|
||||
|
||||
use super::*;
|
||||
|
||||
use std::{str::FromStr, collections::BTreeMap};
|
||||
use frame_support::{
|
||||
assert_ok, impl_outer_origin, parameter_types, impl_outer_dispatch,
|
||||
};
|
||||
use sp_core::{Blake2Hasher, H256};
|
||||
use sp_runtime::{
|
||||
Perbill,
|
||||
testing::Header,
|
||||
traits::{BlakeTwo256, IdentityLookup},
|
||||
};
|
||||
|
||||
impl_outer_origin! {
|
||||
pub enum Origin for Test where system = frame_system {}
|
||||
}
|
||||
|
||||
impl_outer_dispatch! {
|
||||
pub enum OuterCall for Test where origin: Origin {
|
||||
self::EVM,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct Test;
|
||||
parameter_types! {
|
||||
pub const BlockHashCount: u64 = 250;
|
||||
pub const MaximumBlockWeight: Weight = 1024;
|
||||
pub const MaximumBlockLength: u32 = 2 * 1024;
|
||||
pub const AvailableBlockRatio: Perbill = Perbill::one();
|
||||
}
|
||||
impl frame_system::Trait for Test {
|
||||
type BaseCallFilter = ();
|
||||
type Origin = Origin;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
type Call = OuterCall;
|
||||
type Hashing = BlakeTwo256;
|
||||
type AccountId = AccountId32;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
type DbWeight = ();
|
||||
type BlockExecutionWeight = ();
|
||||
type ExtrinsicBaseWeight = ();
|
||||
type MaximumExtrinsicWeight = MaximumBlockWeight;
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type PalletInfo = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
type SystemWeightInfo = ();
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const ExistentialDeposit: u64 = 1;
|
||||
}
|
||||
impl pallet_balances::Trait for Test {
|
||||
type MaxLocks = ();
|
||||
type Balance = u64;
|
||||
type DustRemoval = ();
|
||||
type Event = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type AccountStore = System;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const MinimumPeriod: u64 = 1000;
|
||||
}
|
||||
impl pallet_timestamp::Trait for Test {
|
||||
type Moment = u64;
|
||||
type OnTimestampSet = ();
|
||||
type MinimumPeriod = MinimumPeriod;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
/// Fixed gas price of `0`.
|
||||
pub struct FixedGasPrice;
|
||||
impl FeeCalculator for FixedGasPrice {
|
||||
fn min_gas_price() -> U256 {
|
||||
// Gas price is always one token per gas.
|
||||
0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Trait for Test {
|
||||
type FeeCalculator = FixedGasPrice;
|
||||
|
||||
type CallOrigin = EnsureAddressRoot<Self::AccountId>;
|
||||
type WithdrawOrigin = EnsureAddressNever<Self::AccountId>;
|
||||
|
||||
type AddressMapping = HashedAddressMapping<Blake2Hasher>;
|
||||
type Currency = Balances;
|
||||
|
||||
type Event = Event<Test>;
|
||||
type Precompiles = ();
|
||||
type ChainId = SystemChainId;
|
||||
}
|
||||
|
||||
type System = frame_system::Module<Test>;
|
||||
type Balances = pallet_balances::Module<Test>;
|
||||
type EVM = Module<Test>;
|
||||
|
||||
pub fn new_test_ext() -> sp_io::TestExternalities {
|
||||
let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
|
||||
|
||||
let mut accounts = BTreeMap::new();
|
||||
accounts.insert(
|
||||
H160::from_str("1000000000000000000000000000000000000001").unwrap(),
|
||||
GenesisAccount {
|
||||
nonce: U256::from(1),
|
||||
balance: U256::from(1000000),
|
||||
storage: Default::default(),
|
||||
code: vec![
|
||||
0x00, // STOP
|
||||
],
|
||||
}
|
||||
);
|
||||
accounts.insert(
|
||||
H160::from_str("1000000000000000000000000000000000000002").unwrap(),
|
||||
GenesisAccount {
|
||||
nonce: U256::from(1),
|
||||
balance: U256::from(1000000),
|
||||
storage: Default::default(),
|
||||
code: vec![
|
||||
0xff, // INVALID
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
pallet_balances::GenesisConfig::<Test>::default().assimilate_storage(&mut t).unwrap();
|
||||
GenesisConfig { accounts }.assimilate_storage::<Test>(&mut t).unwrap();
|
||||
t.into()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fail_call_return_ok() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(EVM::call(
|
||||
Origin::root(),
|
||||
H160::default(),
|
||||
H160::from_str("1000000000000000000000000000000000000001").unwrap(),
|
||||
Vec::new(),
|
||||
U256::default(),
|
||||
1000000,
|
||||
U256::default(),
|
||||
None,
|
||||
));
|
||||
|
||||
assert_ok!(EVM::call(
|
||||
Origin::root(),
|
||||
H160::default(),
|
||||
H160::from_str("1000000000000000000000000000000000000002").unwrap(),
|
||||
Vec::new(),
|
||||
U256::default(),
|
||||
1000000,
|
||||
U256::default(),
|
||||
None,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mutate_account_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
EVM::mutate_account_basic(
|
||||
&H160::from_str("1000000000000000000000000000000000000001").unwrap(),
|
||||
Account {
|
||||
nonce: U256::from(10),
|
||||
balance: U256::from(1000),
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(EVM::account_basic(
|
||||
&H160::from_str("1000000000000000000000000000000000000001").unwrap()
|
||||
), Account {
|
||||
nonce: U256::from(10),
|
||||
balance: U256::from(1000),
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user