Rename Palette to FRAME (#4182)

* palette -> frame

* PALETTE, Palette -> FRAME

* Move folder pallete -> frame

* Update docs/Structure.adoc

Co-Authored-By: Benjamin Kampmann <ben.kampmann@googlemail.com>

* Update docs/README.adoc

Co-Authored-By: Benjamin Kampmann <ben.kampmann@googlemail.com>

* Update README.adoc
This commit is contained in:
Shawn Tabrizi
2019-11-22 19:21:25 +01:00
committed by GitHub
parent 68351da29b
commit c9175b59ff
206 changed files with 485 additions and 483 deletions
+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::misc::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,
}));
}
}
}
+299
View File
@@ -0,0 +1,299 @@
// 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::ModuleId;
use support::weights::SimpleDispatchInfo;
use sr_primitives::traits::{UniqueSaturatedInto, AccountIdConversion};
use primitives::{U256, H256, H160};
use evm::{ExitReason, ExitSucceed, ExitError};
use evm::executor::StackExecutor;
use evm::backend::ApplyBackend;
const MODULE_ID: ModuleId = ModuleId(*b"py/ethvm");
/// 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)?;
let imbalance = T::Currency::withdraw(
&sender,
value,
WithdrawReason::Reserve.into(),
ExistenceRequirement::AllowDeath,
)?;
T::Currency::resolve_creating(&Self::account_id(), imbalance);
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));
let mut account = Accounts::get(&address);
account.balance = account.balance.checked_sub(bvalue)
.ok_or("Not enough balance to withdraw")?;
let imbalance = T::Currency::withdraw(
&Self::account_id(),
value,
WithdrawReason::Reserve.into(),
ExistenceRequirement::AllowDeath
)?;
Accounts::insert(&address, account);
T::Currency::resolve_creating(&sender, imbalance);
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> {
/// The account ID of the EVM module.
///
/// This actually does computation. If you need to keep using it, then make sure you cache the
/// value and only call this once.
pub fn account_id() -> T::AccountId {
MODULE_ID.into_account()
}
/// 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);
}
}