// Copyright 2018 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 .
//! Smart-contract module for runtime; Allows deployment and execution of smart-contracts
//! expressed in WebAssembly.
//!
//! This module provides an ability to create smart-contract accounts and send them messages.
//! A smart-contract is an account with associated code and storage. When such an account receives a message,
//! the code associated with that account gets executed.
//!
//! The code is allowed to alter the storage entries of the associated account,
//! create smart-contracts or send messages to existing smart-contracts.
//!
//! For any actions invoked by the smart-contracts fee must be paid. The fee is paid in gas.
//! Gas is bought upfront up to the, specified in transaction, limit. Any unused gas is refunded
//! after the transaction (regardless of the execution outcome). If all gas is used,
//! then changes made for the specific call or create are reverted (including balance transfers).
//!
//! Failures are typically not cascading. That, for example, means that if contract A calls B and B errors
//! somehow, then A can decide if it should proceed or error.
//!
//! # Interaction with the system
//!
//! ## Finalization
//!
//! This module requires performing some finalization steps at the end of the block. If not performed
//! the module will have incorrect behavior.
//!
//! Thus [`Module::on_finalise`] must be called at the end of the block. The order in relation to
//! the other module doesn't matter.
//!
//! ## Account killing
//!
//! When `staking` module determines that account is dead (e.g. account's balance fell below
//! exsistential deposit) then it reaps the account. That will lead to deletion of the associated
//! code and storage of the account.
//!
//! [`Module::on_finalise`]: struct.Module.html#impl-OnFinalise
#![cfg_attr(not(feature = "std"), no_std)]
#[macro_use]
extern crate parity_codec_derive;
extern crate parity_wasm;
extern crate pwasm_utils;
extern crate parity_codec as codec;
extern crate sr_io as runtime_io;
extern crate sr_sandbox as sandbox;
#[cfg_attr(not(feature = "std"), macro_use)]
extern crate sr_std as rstd;
extern crate srml_balances as balances;
extern crate srml_system as system;
#[macro_use]
extern crate srml_support as runtime_support;
extern crate sr_primitives as runtime_primitives;
#[cfg(test)]
extern crate substrate_primitives;
#[cfg(test)]
#[macro_use]
extern crate assert_matches;
#[cfg(test)]
extern crate wabt;
#[cfg(test)]
#[macro_use]
extern crate hex_literal;
#[macro_use]
mod gas;
mod account_db;
mod exec;
mod wasm;
#[cfg(test)]
mod tests;
use exec::ExecutionContext;
use account_db::AccountDb;
use rstd::prelude::*;
use rstd::marker::PhantomData;
use codec::Codec;
use runtime_primitives::traits::{Hash, As, SimpleArithmetic,Bounded, StaticLookup};
use runtime_support::dispatch::{Result, Dispatchable};
use runtime_support::{Parameter, StorageMap, StorageValue, StorageDoubleMap};
use system::{ensure_signed, RawOrigin};
use runtime_io::{blake2_256, twox_128};
pub type CodeHash = ::Hash;
/// A function that generates an `AccountId` for a contract upon instantiation.
pub trait ContractAddressFor {
fn contract_address_for(code_hash: &CodeHash, data: &[u8], origin: &AccountId) -> AccountId;
}
/// A function that returns the fee for dispatching a `Call`.
pub trait ComputeDispatchFee {
fn compute_dispatch_fee(call: &Call) -> Balance;
}
pub trait Trait: balances::Trait {
/// The outer call dispatch type.
type Call: Parameter + Dispatchable::Origin>;
/// The overarching event type.
type Event: From> + Into<::Event>;
// As is needed for wasm-utils
type Gas: Parameter + Default + Codec + SimpleArithmetic + Bounded + Copy + As + As + As;
/// A function type to get the contract address given the creator.
type DetermineContractAddress: ContractAddressFor, Self::AccountId>;
/// A function type that computes the fee for dispatching the given `Call`.
///
/// It is recommended (though not required) for this function to return a fee that would be taken
/// by executive module for regular dispatch.
type ComputeDispatchFee: ComputeDispatchFee::Balance>;
}
/// Simple contract address determintator.
///
/// Address calculated from the code (of the constructor), input data to the constructor
/// and account id which requested the account creation.
///
/// Formula: `blake2_256(blake2_256(code) + blake2_256(data) + origin)`
pub struct SimpleAddressDeterminator(PhantomData);
impl ContractAddressFor, T::AccountId> for SimpleAddressDeterminator
where
T::AccountId: From + AsRef<[u8]>
{
fn contract_address_for(code_hash: &CodeHash, data: &[u8], origin: &T::AccountId) -> T::AccountId {
let data_hash = T::Hashing::hash(data);
let mut buf = Vec::new();
buf.extend_from_slice(code_hash.as_ref());
buf.extend_from_slice(data_hash.as_ref());
buf.extend_from_slice(origin.as_ref());
T::Hashing::hash(&buf[..]).into()
}
}
/// The default dispatch fee computor computes the fee in the same way that
/// implementation of `MakePayment` for balances module does.
pub struct DefaultDispatchFeeComputor(PhantomData);
impl ComputeDispatchFee for DefaultDispatchFeeComputor {
fn compute_dispatch_fee(call: &T::Call) -> T::Balance {
let encoded_len = codec::Encode::encode(&call).len();
let base_fee = >::transaction_base_fee();
let byte_fee = >::transaction_byte_fee();
base_fee + byte_fee * >::sa(encoded_len as u64)
}
}
decl_module! {
/// Contracts module.
pub struct Module for enum Call where origin: ::Origin {
fn deposit_event() = default;
/// Updates the schedule for metering contracts.
///
/// The schedule must have a greater version than the stored schedule.
fn update_schedule(schedule: Schedule) -> Result {
if >::current_schedule().version >= schedule.version {
return Err("new schedule must have a greater version than current");
}
Self::deposit_event(RawEvent::ScheduleUpdated(schedule.version));
>::put(schedule);
Ok(())
}
/// Stores code in the storage. You can instantiate contracts only with stored code.
fn put_code(
origin,
#[compact] gas_limit: T::Gas,
code: Vec
) -> Result {
let origin = ensure_signed(origin)?;
let schedule = >::current_schedule();
let mut gas_meter = gas::buy_gas::(&origin, gas_limit)?;
let result = wasm::save_code::(code, &mut gas_meter, &schedule);
if let Ok(code_hash) = result {
Self::deposit_event(RawEvent::CodeStored(code_hash));
}
gas::refund_unused_gas::(&origin, gas_meter);
result.map(|_| ())
}
/// Make a call to a specified account, optionally transferring some balance.
fn call(
origin,
dest: ::Source,
#[compact] value: T::Balance,
#[compact] gas_limit: T::Gas,
data: Vec
) -> Result {
let origin = ensure_signed(origin)?;
let dest = T::Lookup::lookup(dest)?;
// Pay for the gas upfront.
//
// NOTE: it is very important to avoid any state changes before
// paying for the gas.
let mut gas_meter = gas::buy_gas::(&origin, gas_limit)?;
let cfg = Config::preload();
let vm = ::wasm::WasmVm::new(&cfg.schedule);
let loader = ::wasm::WasmLoader::new(&cfg.schedule);
let mut ctx = ExecutionContext::top_level(origin.clone(), &cfg, &vm, &loader);
let result = ctx.call(dest, value, &mut gas_meter, &data, exec::EmptyOutputBuf::new());
if let Ok(_) = result {
// Commit all changes that made it thus far into the persistant storage.
account_db::DirectAccountDb.commit(ctx.overlay.into_change_set());
// Then deposit all events produced.
ctx.events.into_iter().for_each(Self::deposit_event);
}
// Refund cost of the unused gas.
//
// NOTE: this should go after the commit to the storage, since the storage changes
// can alter the balance of the caller.
gas::refund_unused_gas::(&origin, gas_meter);
// Dispatch every recorded call with an appropriate origin.
ctx.calls.into_iter().for_each(|(who, call)| {
let result = call.dispatch(RawOrigin::Signed(who.clone()).into());
Self::deposit_event(RawEvent::Dispatched(who, result.is_ok()));
});
result.map(|_| ())
}
/// Create a new contract, optionally transfering some balance to the created account.
///
/// Creation is executed as follows:
///
/// - the destination address is computed based on the sender and hash of the code.
/// - account is created at the computed address.
/// - the `ctor_code` is executed in the context of the newly created account. Buffer returned
/// after the execution is saved as the `code` of the account. That code will be invoked
/// upon any message received by this account.
fn create(
origin,
#[compact] endowment: T::Balance,
#[compact] gas_limit: T::Gas,
code_hash: CodeHash,
data: Vec
) -> Result {
let origin = ensure_signed(origin)?;
// Pay for the gas upfront.
//
// NOTE: it is very important to avoid any state changes before
// paying for the gas.
let mut gas_meter = gas::buy_gas::(&origin, gas_limit)?;
let cfg = Config::preload();
let vm = ::wasm::WasmVm::new(&cfg.schedule);
let loader = ::wasm::WasmLoader::new(&cfg.schedule);
let mut ctx = ExecutionContext::top_level(origin.clone(), &cfg, &vm, &loader);
let result = ctx.instantiate(endowment, &mut gas_meter, &code_hash, &data);
if let Ok(_) = result {
// Commit all changes that made it thus far into the persistant storage.
account_db::DirectAccountDb.commit(ctx.overlay.into_change_set());
// Then deposit all events produced.
ctx.events.into_iter().for_each(Self::deposit_event);
}
// Refund cost of the unused gas.
//
// NOTE: this should go after the commit to the storage, since the storage changes
// can alter the balance of the caller.
gas::refund_unused_gas::(&origin, gas_meter);
// Dispatch every recorded call with an appropriate origin.
ctx.calls.into_iter().for_each(|(who, call)| {
let result = call.dispatch(RawOrigin::Signed(who.clone()).into());
Self::deposit_event(RawEvent::Dispatched(who, result.is_ok()));
});
result.map(|_| ())
}
fn on_finalise() {
>::kill();
}
}
}
decl_event! {
pub enum Event
where
::Balance,
::AccountId,
::Hash
{
/// Transfer happened `from` -> `to` with given `value` as part of a `message-call` or `create`.
Transfer(AccountId, AccountId, Balance),
/// Contract deployed by address at the specified address.
Instantiated(AccountId, AccountId),
/// Code with the specified hash has been stored.
CodeStored(Hash),
/// Triggered when the current schedule is updated.
ScheduleUpdated(u32),
/// A call was dispatched from the given account. The bool signals whether it was
/// successful execution or not.
Dispatched(AccountId, bool),
}
}
decl_storage! {
trait Store for Module as Contract {
/// The fee required to create a contract. At least as big as staking's ReclaimRebate.
ContractFee get(contract_fee) config(): T::Balance = T::Balance::sa(21);
/// The fee charged for a call into a contract.
CallBaseFee get(call_base_fee) config(): T::Gas = T::Gas::sa(135);
/// The fee charged for a create of a contract.
CreateBaseFee get(create_base_fee) config(): T::Gas = T::Gas::sa(175);
/// The price of one unit of gas.
GasPrice get(gas_price) config(): T::Balance = T::Balance::sa(1);
/// The maximum nesting level of a call/create stack.
MaxDepth get(max_depth) config(): u32 = 100;
/// The maximum amount of gas that could be expended per block.
BlockGasLimit get(block_gas_limit) config(): T::Gas = T::Gas::sa(1_000_000);
/// Gas spent so far in this block.
GasSpent get(gas_spent): T::Gas;
/// Current cost schedule for contracts.
CurrentSchedule get(current_schedule) config(): Schedule = Schedule::default();
/// The code associated with a given account.
pub CodeHashOf: map T::AccountId => Option>;
/// A mapping from an original code hash to the original code, untouched by instrumentation.
pub PristineCode: map CodeHash => Option>;
/// A mapping between an original code hash and instrumented wasm code, ready for the execution.
pub CodeStorage: map CodeHash => Option;
}
}
// TODO: consider storing upper-bound for contract's gas limit in fixed-length runtime
// code in contract itself and use that.
/// The storage items associated with an account/key.
///
/// TODO: keys should also be able to take AsRef to ensure Vecs can be passed as &[u8]
pub(crate) struct StorageOf(::rstd::marker::PhantomData);
impl StorageDoubleMap for StorageOf {
const PREFIX: &'static [u8] = b"con:sto:";
type Key1 = T::AccountId;
type Key2 = Vec;
type Value = Vec;
/// Hashed by XX
fn derive_key1(key1_data: Vec) -> Vec {
twox_128(&key1_data).to_vec()
}
/// Blake2 is used for `Key2` is because it will be used as a key for contract's storage and
/// thus will be susceptible for a untrusted input.
fn derive_key2(key2_data: Vec) -> Vec {
blake2_256(&key2_data).to_vec()
}
}
impl balances::OnFreeBalanceZero for Module {
fn on_free_balance_zero(who: &T::AccountId) {
>::remove(who);
>::remove_prefix(who.clone());
}
}
/// In-memory cache of configuration values.
///
/// We assume that these values can't be changed in the
/// course of transaction execution.
pub struct Config {
pub schedule: Schedule,
pub existential_deposit: T::Balance,
pub max_depth: u32,
pub contract_account_instantiate_fee: T::Balance,
pub account_create_fee: T::Balance,
pub transfer_fee: T::Balance,
pub call_base_fee: T::Gas,
pub instantiate_base_fee: T::Gas,
}
impl Config {
fn preload() -> Config {
Config {
schedule: >::current_schedule(),
existential_deposit: >::existential_deposit(),
max_depth: >::max_depth(),
contract_account_instantiate_fee: >::contract_fee(),
account_create_fee: >::creation_fee(),
transfer_fee: >::transfer_fee(),
call_base_fee: >::call_base_fee(),
instantiate_base_fee: >::create_base_fee(),
}
}
}
/// Definition of the cost schedule and other parameterizations for wasm vm.
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
#[derive(Clone, Encode, Decode, PartialEq, Eq)]
pub struct Schedule {
/// Version of the schedule.
pub version: u32,
/// Cost of putting a byte of code into the storage.
pub put_code_per_byte_cost: Gas,
/// Gas cost of a growing memory by single page.
pub grow_mem_cost: Gas,
/// Gas cost of a regular operation.
pub regular_op_cost: Gas,
/// Gas cost per one byte returned.
pub return_data_per_byte_cost: Gas,
/// Gas cost per one byte read from the sandbox memory.
pub sandbox_data_read_cost: Gas,
/// Gas cost per one byte written to the sandbox memory.
pub sandbox_data_write_cost: Gas,
/// How tall the stack is allowed to grow?
///
/// See https://wiki.parity.io/WebAssembly-StackHeight to find out
/// how the stack frame cost is calculated.
pub max_stack_height: u32,
/// What is the maximal memory pages amount is allowed to have for
/// a contract.
pub max_memory_pages: u32,
}
impl> Default for Schedule {
fn default() -> Schedule {
Schedule {
version: 0,
put_code_per_byte_cost: Gas::sa(1),
grow_mem_cost: Gas::sa(1),
regular_op_cost: Gas::sa(1),
return_data_per_byte_cost: Gas::sa(1),
sandbox_data_read_cost: Gas::sa(1),
sandbox_data_write_cost: Gas::sa(1),
max_stack_height: 64 * 1024,
max_memory_pages: 16,
}
}
}