mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 21:01:05 +00:00
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:
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user