Contracts module rejig (#1358)

* Move prepare under code.

* Schedule update

* CodeHash

* create takes code_hash

* pass mem def and use code in vm::execute

* Actually save and load code

* Use T::Hash as CodeHash

* Explicit entrypoint name

* Return code_hash and deposit an Event

* Charge for deployed code with gas.

* ImportSatisfyCheck and FunctionImplProvider

* Progress.

* Use new infrastructure for checking imports

* Rename entrypoint to entrypoint_name

* Use strings instead of a Error enum

* Clean

* WIP

* Fix macro_define_env test.

* Fix vm code tests.

* Remove tests for now.

* Fix borked merge

* Fix build for wasm

* fmt

* Scaffolding for abstracting vm.

* Hook up execution to exec layer.

* Fix vm tests.

* Use schedule directly in WasmLoader

* Implement test language.

* Add input_data test.

* Max depth test

* ext_caller

* Simplify test.

* Add TODO

* Some tests and todos.

* top_level

* Clean.

* Restore a couple of integration tests.

* Add a few comments.

* Add ext_address runtime call.

* Deduplicate caller/self_account

* Add not_exists test.

* Change bool to TransferCause.

* Add address tests.

* Remove output_buf from parameter.

* return from start fn.

* Smart gas meter

* Tracing

* Fix prepare tests.

* Code moving

* Add ExecFeeToken

* Use tokens everywhere.

* Make it compile in no_std.

* Lift all test requirements to TestAuxiliaries

* A minor clean

* First create tests

* Remove unneeded TODO

* Docs.

* Code shuffling

* Rename create → instantiate

* Add test address.

* Code shuffling

* Add base_fee tests.

* rejig the code

* Add some comments

* on_finalise comment

* Move event deposit further

* Update Cargo.lock

* Use crates.io version of pwasm-utils

* Format todo comments

* Fix formatting

* Comments

* EmptyOutputBuf and OutputBuf split.

* Restore code_hash

* Fix node-executor.

* Fix typo

* Fix fmt

* Update srml/contract/src/account_db.rs

Co-Authored-By: pepyakin <s.pepyakin@gmail.com>

* Update srml/contract/src/lib.rs

Co-Authored-By: pepyakin <s.pepyakin@gmail.com>

* Line wraps

* Wrapping macros

* Add _ prefix

* Grumbles

* Doc updates.

* Update srml/contract/src/wasm/mod.rs

Co-Authored-By: pepyakin <s.pepyakin@gmail.com>

* Update srml/contract/src/lib.rs

Co-Authored-By: pepyakin <s.pepyakin@gmail.com>

* Add comment

* Use saturation to signal overflow

* Add prepare_test! macro

* Require deploy function.

* Add entry point tests

* Add comment.

* Rename code → code_cache to better describe

* Get rid of weird match!

* Recompile binaries

* Add comments

* refuse_instantiate_with_value_below_existential_deposit

* Little fix

* Make test more complete

* Clean

* Add integration test for instantiation

* Rebuild runtime.

* Add some tests.

* Attach an issue to a TODO

* Attach another issue

* Apply suggestions from code review

Co-Authored-By: pepyakin <s.pepyakin@gmail.com>

* Update srml/contract/src/exec.rs

Co-Authored-By: pepyakin <s.pepyakin@gmail.com>

* Update srml/contract/src/exec.rs

Co-Authored-By: pepyakin <s.pepyakin@gmail.com>

* Recompile node_runtime
This commit is contained in:
Sergei Pepyakin
2019-01-17 12:01:12 +01:00
committed by GitHub
parent beeacf9cfa
commit c88b44f6db
18 changed files with 2717 additions and 1626 deletions
+99 -49
View File
@@ -39,7 +39,7 @@
//! This module requires performing some finalization steps at the end of the block. If not performed
//! the module will have incorrect behavior.
//!
//! Call [`Module::execute`] at the end of the block. The order in relation to
//! 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
@@ -48,7 +48,7 @@
//! exsistential deposit) then it reaps the account. That will lead to deletion of the associated
//! code and storage of the account.
//!
//! [`Module::execute`]: struct.Module.html#impl-OnFinalise
//! [`Module::on_finalise`]: struct.Module.html#impl-OnFinalise
#![cfg_attr(not(feature = "std"), no_std)]
@@ -83,39 +83,47 @@ 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 vm;
mod gas;
mod wasm;
#[cfg(test)]
mod tests;
use exec::ExecutionContext;
use account_db::{AccountDb, OverlayAccountDb};
use account_db::AccountDb;
use rstd::prelude::*;
use rstd::marker::PhantomData;
use codec::{Codec, HasCompact};
use runtime_primitives::traits::{Hash, As, SimpleArithmetic, StaticLookup};
use runtime_primitives::traits::{Hash, As, SimpleArithmetic,Bounded, StaticLookup};
use runtime_support::dispatch::Result;
use runtime_support::{Parameter, StorageMap, StorageValue, StorageDoubleMap};
use system::ensure_signed;
use runtime_io::{blake2_256, twox_128};
pub type CodeHash<T> = <T as system::Trait>::Hash;
pub trait Trait: balances::Trait {
/// Function type to get the contract address given the creator.
type DetermineContractAddress: ContractAddressFor<Self::AccountId>;
type DetermineContractAddress: ContractAddressFor<CodeHash<Self>, Self::AccountId>;
// As<u32> is needed for wasm-utils
type Gas: Parameter + Default + Codec + SimpleArithmetic + Copy + As<Self::Balance> + As<u64> + As<u32>;
type Gas: Parameter + Default + Codec + SimpleArithmetic + Bounded + Copy + As<Self::Balance> + As<u64> + As<u32>;
/// The overarching event type.
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
}
pub trait ContractAddressFor<AccountId: Sized> {
fn contract_address_for(code: &[u8], data: &[u8], origin: &AccountId) -> AccountId;
pub trait ContractAddressFor<CodeHash, AccountId: Sized> {
fn contract_address_for(code_hash: &CodeHash, data: &[u8], origin: &AccountId) -> AccountId;
}
/// Simple contract address determintator.
@@ -126,12 +134,11 @@ pub trait ContractAddressFor<AccountId: Sized> {
/// Formula: `blake2_256(blake2_256(code) + blake2_256(data) + origin)`
pub struct SimpleAddressDeterminator<T: Trait>(PhantomData<T>);
impl<T: Trait> ContractAddressFor<T::AccountId> for SimpleAddressDeterminator<T>
impl<T: Trait> ContractAddressFor<CodeHash<T>, T::AccountId> for SimpleAddressDeterminator<T>
where
T::AccountId: From<T::Hash> + AsRef<[u8]>
{
fn contract_address_for(code: &[u8], data: &[u8], origin: &T::AccountId) -> T::AccountId {
let code_hash = T::Hashing::hash(code);
fn contract_address_for(code_hash: &CodeHash<T>, data: &[u8], origin: &T::AccountId) -> T::AccountId {
let data_hash = T::Hashing::hash(data);
let mut buf = Vec::new();
@@ -147,8 +154,43 @@ decl_module! {
/// Contracts module.
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
fn deposit_event<T>() = default;
// TODO: Change AccountId to staking::Address
/// Make a call to a specified account, optionally transferring some balance.
/// Updates the schedule for metering contracts.
///
/// The schedule must have a greater version than the stored schedule.
fn update_schedule(schedule: Schedule<T::Gas>) -> Result {
if <Module<T>>::current_schedule().version >= schedule.version {
return Err("new schedule must have a greater version than current");
}
Self::deposit_event(RawEvent::ScheduleUpdated(schedule.version));
<CurrentSchedule<T>>::put(schedule);
Ok(())
}
/// Stores code in the storage. You can instantiate contracts only with stored code.
fn put_code(
origin,
gas_limit: <T::Gas as HasCompact>::Type,
code: Vec<u8>
) -> Result {
let origin = ensure_signed(origin)?;
let gas_limit = gas_limit.into();
let schedule = <Module<T>>::current_schedule();
let mut gas_meter = gas::buy_gas::<T>(&origin, gas_limit)?;
let result = wasm::save_code::<T>(code, &mut gas_meter, &schedule);
if let Ok(code_hash) = result {
Self::deposit_event(RawEvent::CodeStored(code_hash));
}
gas::refund_unused_gas::<T>(&origin, gas_meter);
result.map(|_| ())
}
/// Make a call to a specified account, optionally transferring some balance.
fn call(
origin,
@@ -169,16 +211,11 @@ decl_module! {
let mut gas_meter = gas::buy_gas::<T>(&origin, gas_limit)?;
let cfg = Config::preload();
let mut ctx = ExecutionContext {
self_account: origin.clone(),
depth: 0,
overlay: OverlayAccountDb::<T>::new(&account_db::DirectAccountDb),
events: Vec::new(),
config: &cfg,
};
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 mut output_data = Vec::new();
let result = ctx.call(origin.clone(), dest, value, &mut gas_meter, &data, &mut output_data);
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.
@@ -210,7 +247,7 @@ decl_module! {
origin,
endowment: <T::Balance as HasCompact>::Type,
gas_limit: <T::Gas as HasCompact>::Type,
ctor_code: Vec<u8>,
code_hash: CodeHash<T>,
data: Vec<u8>
) -> Result {
let origin = ensure_signed(origin)?;
@@ -224,23 +261,17 @@ decl_module! {
let mut gas_meter = gas::buy_gas::<T>(&origin, gas_limit)?;
let cfg = Config::preload();
let mut ctx = ExecutionContext {
self_account: origin.clone(),
depth: 0,
overlay: OverlayAccountDb::<T>::new(&account_db::DirectAccountDb),
events: Vec::new(),
config: &cfg,
};
let result = ctx.create(origin.clone(), endowment, &mut gas_meter, &ctor_code, &data);
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(ref r) = result {
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);
Self::deposit_event(RawEvent::Created(origin.clone(), r.address.clone()));
}
// Refund cost of the unused gas.
@@ -262,13 +293,20 @@ decl_event! {
pub enum Event<T>
where
<T as balances::Trait>::Balance,
<T as system::Trait>::AccountId
<T as system::Trait>::AccountId,
<T as system::Trait>::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.
Created(AccountId, AccountId),
Instantiated(AccountId, AccountId),
/// Code with the specified hash has been stored.
CodeStored(Hash),
/// Triggered when the current schedule is updated.
ScheduleUpdated(u32),
}
}
@@ -290,8 +328,12 @@ decl_storage! {
GasSpent get(gas_spent): T::Gas;
/// Current cost schedule for contracts.
CurrentSchedule get(current_schedule) config(): Schedule<T::Gas> = Schedule::default();
/// The code associated with an account.
pub CodeOf: map T::AccountId => Vec<u8>; // TODO Vec<u8> values should be optimised to not do a length prefix.
/// The code associated with a given account.
pub CodeHashOf: map T::AccountId => Option<CodeHash<T>>;
/// A mapping from an original code hash to the original code, untouched by instrumentation.
pub PristineCode: map CodeHash<T> => Option<Vec<u8>>;
/// A mapping between an original code hash and instrumented wasm code, ready for the execution.
pub CodeStorage: map CodeHash<T> => Option<wasm::PrefabWasmModule>;
}
}
@@ -322,7 +364,7 @@ impl<T: Trait> StorageDoubleMap for StorageOf<T> {
impl<T: Trait> balances::OnFreeBalanceZero<T::AccountId> for Module<T> {
fn on_free_balance_zero(who: &T::AccountId) {
<CodeOf<T>>::remove(who);
<CodeHashOf<T>>::remove(who);
<StorageOf<T>>::remove_prefix(who.clone());
}
}
@@ -335,11 +377,11 @@ pub struct Config<T: Trait> {
pub schedule: Schedule<T::Gas>,
pub existential_deposit: T::Balance,
pub max_depth: u32,
pub contract_account_create_fee: T::Balance,
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 create_base_fee: T::Gas,
pub instantiate_base_fee: T::Gas,
}
impl<T: Trait> Config<T> {
@@ -348,19 +390,25 @@ impl<T: Trait> Config<T> {
schedule: <Module<T>>::current_schedule(),
existential_deposit: <balances::Module<T>>::existential_deposit(),
max_depth: <Module<T>>::max_depth(),
contract_account_create_fee: <Module<T>>::contract_fee(),
contract_account_instantiate_fee: <Module<T>>::contract_fee(),
account_create_fee: <balances::Module<T>>::creation_fee(),
transfer_fee: <balances::Module<T>>::transfer_fee(),
call_base_fee: <Module<T>>::call_base_fee(),
create_base_fee: <Module<T>>::create_base_fee(),
instantiate_base_fee: <Module<T>>::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)]
#[derive(Clone, Encode, Decode, PartialEq, Eq)]
pub struct Schedule<Gas> {
/// 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,
@@ -371,10 +419,10 @@ pub struct Schedule<Gas> {
pub return_data_per_byte_cost: Gas,
/// Gas cost per one byte read from the sandbox memory.
sandbox_data_read_cost: Gas,
pub sandbox_data_read_cost: Gas,
/// Gas cost per one byte written to the sandbox memory.
sandbox_data_write_cost: Gas,
pub sandbox_data_write_cost: Gas,
/// How tall the stack is allowed to grow?
///
@@ -382,7 +430,7 @@ pub struct Schedule<Gas> {
/// how the stack frame cost is calculated.
pub max_stack_height: u32,
//// What is the maximal memory pages amount is allowed to have for
/// What is the maximal memory pages amount is allowed to have for
/// a contract.
pub max_memory_pages: u32,
}
@@ -390,6 +438,8 @@ pub struct Schedule<Gas> {
impl<Gas: As<u64>> Default for Schedule<Gas> {
fn default() -> Schedule<Gas> {
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),