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
@@ -0,0 +1,106 @@
// 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 <http://www.gnu.org/licenses/>.
//! A module that implements instrumented code cache.
//!
//! - In order to run contract code we need to instrument it with gas metering.
//! To do that we need to provide the schedule which will supply exact gas costs values.
//! We cache this code in the storage saving the schedule version.
//! - Before running contract code we check if the cached code has the schedule version that is equal to the current saved schedule.
//! If it is equal then run the code, if it isn't reinstrument with the current schedule.
//! - When we update the schedule we want it to have strictly greater version than the current saved one:
//! this guarantees that every instrumented contract code in cache cannot have the version equal to the current one.
//! Thus, before executing a contract it should be reinstrument with new schedule.
use gas::{GasMeter, Token};
use rstd::prelude::*;
use runtime_primitives::traits::{As, CheckedMul, Hash, Bounded};
use runtime_support::StorageMap;
use wasm::{prepare, runtime::Env, PrefabWasmModule};
use {CodeHash, CodeStorage, PristineCode, Schedule, Trait};
/// Gas metering token that used for charging storing code into the code storage.
///
/// Specifies the code length in bytes.
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
#[derive(Copy, Clone)]
pub struct PutCodeToken(u64);
impl<T: Trait> Token<T> for PutCodeToken {
type Metadata = Schedule<T::Gas>;
fn calculate_amount(&self, metadata: &Schedule<T::Gas>) -> T::Gas {
let code_len_in_gas = <T::Gas as As<u64>>::sa(self.0);
metadata
.put_code_per_byte_cost
.checked_mul(&code_len_in_gas)
.unwrap_or_else(|| Bounded::max_value())
}
}
/// Put code in the storage. The hash of code is used as a key and is returned
/// as a result of this function.
///
/// This function instruments the given code and caches it in the storage.
pub fn save<T: Trait>(
original_code: Vec<u8>,
gas_meter: &mut GasMeter<T>,
schedule: &Schedule<T::Gas>,
) -> Result<CodeHash<T>, &'static str> {
// The first time instrumentation is on the user. However, consequent reinstrumentation
// due to the schedule changes is on governance system.
if gas_meter
.charge(schedule, PutCodeToken(original_code.len() as u64))
.is_out_of_gas()
{
return Err("there is not enough gas for storing the code");
}
let prefab_module = prepare::prepare_contract::<T, Env>(&original_code, schedule)?;
let code_hash = T::Hashing::hash(&original_code);
// TODO: #1416 validate the code. If the code is not valid, then don't store it.
<CodeStorage<T>>::insert(code_hash, prefab_module);
<PristineCode<T>>::insert(code_hash, original_code);
Ok(code_hash)
}
/// Load code with the given code hash.
///
/// If the module was instrumented with a lower version of schedule than
/// the current one given as an argument, then this function will perform
/// re-instrumentation and update the cache in the storage.
pub fn load<T: Trait>(
code_hash: &CodeHash<T>,
schedule: &Schedule<T::Gas>,
) -> Result<PrefabWasmModule, &'static str> {
let mut prefab_module =
<CodeStorage<T>>::get(code_hash).ok_or_else(|| "code is not found")?;
if prefab_module.schedule_version < schedule.version {
// The current schedule version is greater than the version of the one cached
// in the storage.
//
// We need to re-instrument the code with the latest schedule here.
let original_code =
<PristineCode<T>>::get(code_hash).ok_or_else(|| "pristine code is not found")?;
prefab_module = prepare::prepare_contract::<T, Env>(&original_code, schedule)?;
<CodeStorage<T>>::insert(code_hash, prefab_module.clone());
}
Ok(prefab_module)
}