// Copyright 2017-2020 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 . //! 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 sp_std::{vec::Vec, marker::PhantomData}; use frame_support::{ensure, decl_module, decl_storage, decl_event, decl_error}; use frame_support::weights::{Weight, DispatchClass, FunctionOf, Pays}; use frame_support::traits::{Currency, WithdrawReason, ExistenceRequirement, Get}; use frame_system::{self as system, ensure_signed}; use sp_runtime::ModuleId; use sp_core::{U256, H256, H160, Hasher}; use sp_runtime::{ DispatchResult, traits::{UniqueSaturatedInto, AccountIdConversion, SaturatedConversion}, }; use sha3::{Digest, Keccak256}; use evm::{ExitReason, ExitSucceed, ExitError, Config}; use evm::executor::StackExecutor; use evm::backend::ApplyBackend; /// Type alias for currency balance. pub type BalanceOf = <::Currency as Currency<::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() } } /// 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 { /// Given a Substrate address, return the corresponding Ethereum address. fn convert_account_id(account_id: &A) -> H160; } /// Hash and then truncate the account id, taking the last 160-bit as the Ethereum address. pub struct HashTruncateConvertAccountId(PhantomData); impl Default for HashTruncateConvertAccountId { fn default() -> Self { Self(PhantomData) } } impl> ConvertAccountId for HashTruncateConvertAccountId { fn convert_account_id(account_id: &A) -> H160 { let account_id = H::hash(account_id.as_ref()); let account_id_len = account_id.as_ref().len(); let mut value = [0u8; 20]; let value_len = value.len(); if value_len > account_id_len { value[(value_len - account_id_len)..].copy_from_slice(account_id.as_ref()); } else { value.copy_from_slice(&account_id.as_ref()[(account_id_len - value_len)..]); } H160::from(value) } } /// 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 ) -> Option, usize), ExitError>>; } impl Precompiles for () { fn execute( _address: H160, _input: &[u8], _target_gas: Option ) -> Option, usize), ExitError>> { None } } static ISTANBUL_CONFIG: Config = Config::istanbul(); /// EVM module trait pub trait Trait: frame_system::Trait + pallet_timestamp::Trait { /// The EVM's module id type ModuleId: Get; /// Calculator for current gas price. type FeeCalculator: FeeCalculator; /// Convert account ID to H160; type ConvertAccountId: ConvertAccountId; /// Currency type for deposit and withdraw. type Currency: Currency; /// The overarching event type. type Event: From> + Into<::Event>; /// Precompiles associated with this EVM engine. type Precompiles: Precompiles; /// EVM config used in the module. fn config() -> &'static Config { &ISTANBUL_CONFIG } } decl_storage! { trait Store for Module as EVM { Accounts get(fn accounts) config(): map hasher(blake2_128_concat) H160 => Account; AccountCodes: map hasher(blake2_128_concat) H160 => Vec; AccountStorages: double_map hasher(blake2_128_concat) H160, hasher(blake2_128_concat) H256 => H256; } } decl_event! { /// EVM events pub enum Event where ::AccountId, { /// Ethereum events from contracts. Log(Log), /// A contract has been created at given address. Created(H160), /// A deposit has been made at a given address. BalanceDeposit(AccountId, H160, U256), /// A withdrawal has been made from a given address. BalanceWithdraw(AccountId, H160, U256), } } decl_error! { pub enum Error for Module { /// 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, /// Call failed ExitReasonFailed, /// Call reverted ExitReasonRevert, /// Call returned VM fatal error ExitReasonFatal, /// Nonce is invalid InvalidNonce, } } decl_module! { pub struct Module for enum Call where origin: T::Origin { type Error = Error; fn deposit_event() = default; const ModuleId: ModuleId = T::ModuleId::get(); /// Deposit balance from currency/balances module into EVM. #[weight = 0] fn deposit_balance(origin, value: BalanceOf) { 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::::unique_saturated_into(value)); let address = T::ConvertAccountId::convert_account_id(&sender); Accounts::mutate(&address, |account| { account.balance += bvalue; }); Module::::deposit_event(Event::::BalanceDeposit(sender, address, bvalue)); } /// Withdraw balance from EVM into currency/balances module. #[weight = 0] fn withdraw_balance(origin, value: BalanceOf) { let sender = ensure_signed(origin)?; let address = T::ConvertAccountId::convert_account_id(&sender); let bvalue = U256::from(UniqueSaturatedInto::::unique_saturated_into(value)); let mut account = Accounts::get(&address); account.balance = account.balance.checked_sub(bvalue) .ok_or(Error::::BalanceLow)?; let imbalance = T::Currency::withdraw( &Self::account_id(), value, WithdrawReason::Reserve.into(), ExistenceRequirement::AllowDeath )?; Accounts::insert(&address, account); T::Currency::resolve_creating(&sender, imbalance); Module::::deposit_event(Event::::BalanceWithdraw(sender, address, bvalue)); } /// Issue an EVM call operation. This is similar to a message call transaction in Ethereum. #[weight = FunctionOf( |(_, _, _, gas_limit, gas_price, _): (&H160, &Vec, &U256, &u32, &U256, &Option)| (*gas_price).saturated_into::().saturating_mul(*gas_limit as Weight), DispatchClass::Normal, Pays::Yes, )] fn call( origin, target: H160, input: Vec, value: U256, gas_limit: u32, gas_price: U256, nonce: Option, ) -> DispatchResult { let sender = ensure_signed(origin)?; let source = T::ConvertAccountId::convert_account_id(&sender); Self::execute_evm( source, value, gas_limit, gas_price, nonce, |executor| ((), executor.transact_call( source, target, value, input, gas_limit as usize, )), ).map_err(Into::into) } /// Issue an EVM create operation. This is similar to a contract creation transaction in /// Ethereum. #[weight = FunctionOf( |(_, _, gas_limit, gas_price, _): (&Vec, &U256, &u32, &U256, &Option)| (*gas_price).saturated_into::().saturating_mul(*gas_limit as Weight), DispatchClass::Normal, Pays::Yes, )] fn create( origin, init: Vec, value: U256, gas_limit: u32, gas_price: U256, nonce: Option, ) -> DispatchResult { let sender = ensure_signed(origin)?; let source = T::ConvertAccountId::convert_account_id(&sender); let create_address = Self::execute_evm( source, value, gas_limit, gas_price, nonce, |executor| { (executor.create_address( evm::CreateScheme::Legacy { caller: source }, ), executor.transact_create( source, value, init, gas_limit as usize, )) }, )?; Module::::deposit_event(Event::::Created(create_address)); Ok(()) } /// Issue an EVM create2 operation. #[weight = FunctionOf( |(_, _, _, gas_limit, gas_price, _): (&Vec, &H256, &U256, &u32, &U256, &Option)| (*gas_price).saturated_into::().saturating_mul(*gas_limit as Weight), DispatchClass::Normal, Pays::Yes, )] fn create2( origin, init: Vec, salt: H256, value: U256, gas_limit: u32, gas_price: U256, nonce: Option, ) -> DispatchResult { let sender = ensure_signed(origin)?; let source = T::ConvertAccountId::convert_account_id(&sender); let code_hash = H256::from_slice(Keccak256::digest(&init).as_slice()); let create_address = Self::execute_evm( source, value, gas_limit, gas_price, nonce, |executor| { (executor.create_address( evm::CreateScheme::Create2 { caller: source, code_hash, salt }, ), executor.transact_create2( source, value, init, salt, gas_limit as usize, )) }, )?; Module::::deposit_event(Event::::Created(create_address)); Ok(()) } } } impl Module { /// 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 { T::ModuleId::get().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); } /// Execute an EVM operation. fn execute_evm( source: H160, value: U256, gas_limit: u32, gas_price: U256, nonce: Option, f: F, ) -> Result> where F: FnOnce(&mut StackExecutor>) -> (R, ExitReason), { ensure!(gas_price >= T::FeeCalculator::min_gas_price(), Error::::GasPriceTooLow); let vicinity = Vicinity { gas_price, origin: source, }; let mut backend = Backend::::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::::FeeOverflow)?; let total_payment = value.checked_add(total_fee).ok_or(Error::::PaymentOverflow)?; let source_account = Accounts::get(&source); ensure!(source_account.balance >= total_payment, Error::::BalanceLow); executor.withdraw(source, total_fee).map_err(|_| Error::::WithdrawFailed)?; if let Some(nonce) = nonce { ensure!(source_account.nonce == nonce, Error::::InvalidNonce); } let (retv, reason) = f(&mut executor); let ret = match reason { ExitReason::Succeed(_) => Ok(retv), ExitReason::Error(_) => Err(Error::::ExitReasonFailed), ExitReason::Revert(_) => Err(Error::::ExitReasonRevert), ExitReason::Fatal(_) => Err(Error::::ExitReasonFatal), }; 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 } }