srml-contract: Extract schedule (#1044)

* Rename Config → Schedule.

* Fetch and pass config.

* Integrate config everywhere.

* <<<E as Ext>::T as Trait> → <<E::T as Trait>

* Update roots

* Cache existential_deposit

* Update COMPLEXITY.md

* Update roots.
This commit is contained in:
Sergey Pepyakin
2018-11-12 20:44:05 +01:00
committed by Gav Wood
parent 367c99b2bb
commit 1f0f3c8f6b
12 changed files with 164 additions and 133 deletions
@@ -219,7 +219,7 @@ mod tests {
#[test]
fn macro_define_func() {
define_func!( <E: Ext> ext_gas (_ctx, amount: u32) => {
let amount = <<<E as Ext>::T as Trait>::Gas as As<u32>>::sa(amount);
let amount = <<E::T as Trait>::Gas as As<u32>>::sa(amount);
if !amount.is_zero() {
Ok(())
} else {
@@ -269,7 +269,7 @@ mod tests {
fn macro_define_env() {
define_env!(init_env, <E: Ext>,
ext_gas( _ctx, amount: u32 ) => {
let amount = <<<E as Ext>::T as Trait>::Gas as As<u32>>::sa(amount);
let amount = <<E::T as Trait>::Gas as As<u32>>::sa(amount);
if !amount.is_zero() {
Ok(())
} else {
+9 -53
View File
@@ -20,8 +20,7 @@
use exec::CreateReceipt;
use gas::GasMeter;
use rstd::prelude::*;
use runtime_primitives::traits::As;
use Trait;
use {Trait, Schedule};
use {balances, sandbox, system};
type BalanceOf<T> = <T as balances::Trait>::Balance;
@@ -118,7 +117,7 @@ pub fn execute<'a, E: Ext>(
input_data: &[u8],
output_data: &mut Vec<u8>,
ext: &'a mut E,
config: &Config<E::T>,
schedule: &Schedule<<E::T as Trait>::Gas>,
gas_meter: &mut GasMeter<E::T>,
) -> Result<(), Error> {
let env = runtime::init_env();
@@ -126,7 +125,7 @@ pub fn execute<'a, E: Ext>(
let PreparedContract {
instrumented_code,
memory,
} = prepare_contract(code, &config, &env)?;
} = prepare_contract(code, &schedule, &env)?;
let mut imports = sandbox::EnvironmentDefinitionBuilder::new();
for (func_name, ext_func) in &env.funcs {
@@ -134,7 +133,7 @@ pub fn execute<'a, E: Ext>(
}
imports.add_memory("env", "memory", memory.clone());
let mut runtime = Runtime::new(ext, input_data, output_data, &config, memory, gas_meter);
let mut runtime = Runtime::new(ext, input_data, output_data, &schedule, memory, gas_meter);
// Instantiate the instance from the instrumented module code.
match sandbox::Instance::new(&instrumented_code, &imports, &mut runtime) {
@@ -152,49 +151,6 @@ pub fn execute<'a, E: Ext>(
}
}
// TODO: Extract it to the root of the crate
#[derive(Clone)]
pub struct Config<T: Trait> {
/// Gas cost of a growing memory by single page.
grow_mem_cost: T::Gas,
/// Gas cost of a regular operation.
regular_op_cost: T::Gas,
/// Gas cost per one byte returned.
return_data_per_byte_cost: T::Gas,
/// Gas cost per one byte read from the sandbox memory.
sandbox_data_read_cost: T::Gas,
/// Gas cost per one byte written to the sandbox memory.
sandbox_data_write_cost: T::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.
max_stack_height: u32,
//// What is the maximal memory pages amount is allowed to have for
/// a contract.
max_memory_pages: u32,
}
impl<T: Trait> Default for Config<T> {
fn default() -> Config<T> {
Config {
grow_mem_cost: T::Gas::sa(1),
regular_op_cost: T::Gas::sa(1),
return_data_per_byte_cost: T::Gas::sa(1),
sandbox_data_read_cost: T::Gas::sa(1),
sandbox_data_write_cost: T::Gas::sa(1),
max_stack_height: 64 * 1024,
max_memory_pages: 16,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -318,7 +274,7 @@ mod tests {
&[],
&mut Vec::new(),
&mut mock_ext,
&::vm::Config::default(),
&Schedule::<u64>::default(),
&mut GasMeter::with_limit(50_000, 1),
).unwrap();
@@ -381,7 +337,7 @@ mod tests {
&[],
&mut Vec::new(),
&mut mock_ext,
&::vm::Config::default(),
&Schedule::default(),
&mut GasMeter::with_limit(50_000, 1),
).unwrap();
@@ -421,7 +377,7 @@ mod tests {
&[],
&mut Vec::new(),
&mut mock_ext,
&::vm::Config::default(),
&Schedule::default(),
&mut GasMeter::with_limit(100_000, 1)
),
Err(_)
@@ -475,7 +431,7 @@ mod tests {
&[],
&mut Vec::new(),
&mut mock_ext,
&::vm::Config::default(),
&Schedule::default(),
&mut GasMeter::with_limit(50_000, 1),
).unwrap();
@@ -568,7 +524,7 @@ mod tests {
&[],
&mut return_buf,
&mut mock_ext,
&Config::default(),
&Schedule::default(),
&mut GasMeter::with_limit(50_000, 1),
).unwrap();
+17 -18
View File
@@ -18,30 +18,30 @@
//! wasm module before execution.
use super::env_def::HostFunctionSet;
use super::{Config, Error, Ext};
use super::{Error, Ext};
use rstd::prelude::*;
use parity_wasm::elements::{self, External, MemoryType, Type};
use pwasm_utils;
use pwasm_utils::rules;
use runtime_primitives::traits::As;
use sandbox;
use Trait;
use {Trait, Schedule};
struct ContractModule<'a, T: Trait + 'a> {
struct ContractModule<'a, Gas: 'a> {
// An `Option` is used here for loaning (`take()`-ing) the module.
// Invariant: Can't be `None` (i.e. on enter and on exit from the function
// the value *must* be `Some`).
module: Option<elements::Module>,
config: &'a Config<T>,
schedule: &'a Schedule<Gas>,
}
impl<'a, T: Trait> ContractModule<'a, T> {
fn new(original_code: &[u8], config: &'a Config<T>) -> Result<ContractModule<'a, T>, Error> {
impl<'a, Gas: 'a + As<u32> + Clone> ContractModule<'a, Gas> {
fn new(original_code: &[u8], schedule: &'a Schedule<Gas>) -> Result<ContractModule<'a, Gas>, Error> {
let module =
elements::deserialize_buffer(original_code).map_err(|_| Error::Deserialization)?;
Ok(ContractModule {
module: Some(module),
config,
schedule,
})
}
@@ -65,8 +65,8 @@ impl<'a, T: Trait> ContractModule<'a, T> {
}
fn inject_gas_metering(&mut self) -> Result<(), Error> {
let gas_rules = rules::Set::new(self.config.regular_op_cost.as_(), Default::default())
.with_grow_cost(self.config.grow_mem_cost.as_())
let gas_rules = rules::Set::new(self.schedule.regular_op_cost.clone().as_(), Default::default())
.with_grow_cost(self.schedule.grow_mem_cost.clone().as_())
.with_forbidden_floats();
let module = self
@@ -88,7 +88,7 @@ impl<'a, T: Trait> ContractModule<'a, T> {
.expect("On entry to the function `module` can't be `None`; qed");
let contract_module =
pwasm_utils::stack_height::inject_limiter(module, self.config.max_stack_height)
pwasm_utils::stack_height::inject_limiter(module, self.schedule.max_stack_height)
.map_err(|_| Error::StackHeightInstrumentation)?;
self.module = Some(contract_module);
@@ -167,16 +167,16 @@ pub(super) struct PreparedContract {
/// The checks are:
///
/// - module doesn't define an internal memory instance,
/// - imported memory (if any) doesn't reserve more memory than permitted by the `config`,
/// - imported memory (if any) doesn't reserve more memory than permitted by the `schedule`,
/// - all imported functions from the external environment matches defined by `env` module,
///
/// The preprocessing includes injecting code for gas metering and metering the height of stack.
pub(super) fn prepare_contract<E: Ext>(
original_code: &[u8],
config: &Config<E::T>,
schedule: &Schedule<<E::T as Trait>::Gas>,
env: &HostFunctionSet<E>,
) -> Result<PreparedContract, Error> {
let mut contract_module = ContractModule::new(original_code, config)?;
let mut contract_module = ContractModule::new(original_code, schedule)?;
contract_module.ensure_no_internal_memory()?;
contract_module.inject_gas_metering()?;
contract_module.inject_stack_height_metering()?;
@@ -189,7 +189,7 @@ pub(super) fn prepare_contract<E: Ext>(
// Requested initial number of pages should not exceed the requested maximum.
return Err(Error::Memory);
}
(_, Some(maximum)) if maximum > config.max_memory_pages => {
(_, Some(maximum)) if maximum > schedule.max_memory_pages => {
// Maximum number of pages should not exceed the configured maximum.
return Err(Error::Memory);
}
@@ -218,7 +218,6 @@ pub(super) fn prepare_contract<E: Ext>(
mod tests {
use super::*;
use std::fmt;
use tests::Test;
use vm::tests::MockExt;
use wabt;
@@ -230,9 +229,9 @@ mod tests {
fn parse_and_prepare_wat(wat: &str) -> Result<PreparedContract, Error> {
let wasm = wabt::Wat2Wasm::new().validate(false).convert(wat).unwrap();
let config = Config::<Test>::default();
let schedule = Schedule::<u64>::default();
let env = ::vm::runtime::init_env();
prepare_contract::<MockExt>(wasm.as_ref(), &config, &env)
prepare_contract::<MockExt>(wasm.as_ref(), &schedule, &env)
}
#[test]
@@ -244,7 +243,7 @@ mod tests {
#[test]
fn memory() {
// This test assumes that maximum page number is configured to a certain number.
assert_eq!(Config::<Test>::default().max_memory_pages, 16);
assert_eq!(Schedule::<u64>::default().max_memory_pages, 16);
let r = parse_and_prepare_wat(r#"(module (import "env" "memory" (memory 1 1)))"#);
assert_matches!(r, Ok(_));
+12 -12
View File
@@ -16,7 +16,7 @@
//! Environment definition of the wasm smart-contract runtime.
use super::{BalanceOf, Config, CreateReceipt, Error, Ext};
use super::{BalanceOf, Schedule, CreateReceipt, Error, Ext};
use rstd::prelude::*;
use codec::{Decode, Encode};
use gas::{GasMeter, GasMeterResult};
@@ -41,7 +41,7 @@ pub(crate) struct Runtime<'a, 'data, E: Ext + 'a> {
input_data: &'data [u8],
output_data: &'data mut Vec<u8>,
scratch_buf: Vec<u8>,
config: &'a Config<E::T>,
schedule: &'a Schedule<<E::T as Trait>::Gas>,
memory: sandbox::Memory,
gas_meter: &'a mut GasMeter<E::T>,
special_trap: Option<SpecialTrap>,
@@ -51,7 +51,7 @@ impl<'a, 'data, E: Ext + 'a> Runtime<'a, 'data, E> {
ext: &'a mut E,
input_data: &'data [u8],
output_data: &'data mut Vec<u8>,
config: &'a Config<E::T>,
schedule: &'a Schedule<<E::T as Trait>::Gas>,
memory: sandbox::Memory,
gas_meter: &'a mut GasMeter<E::T>,
) -> Self {
@@ -60,7 +60,7 @@ impl<'a, 'data, E: Ext + 'a> Runtime<'a, 'data, E> {
input_data,
output_data,
scratch_buf: Vec::new(),
config,
schedule,
memory,
gas_meter,
special_trap: None,
@@ -117,7 +117,7 @@ fn read_sandbox_memory<E: Ext>(
ptr: u32,
len: u32,
) -> Result<Vec<u8>, sandbox::HostError> {
let price = (ctx.config.sandbox_data_read_cost)
let price = (ctx.schedule.sandbox_data_read_cost)
.checked_mul(&<GasOf<E> as As<u32>>::sa(len))
.ok_or(sandbox::HostError)?;
charge_gas(ctx.gas_meter, price)?;
@@ -169,7 +169,7 @@ define_env!(init_env, <E: Ext>,
//
// - amount: How much gas is used.
gas(ctx, amount: u32) => {
let amount = <<<E as Ext>::T as Trait>::Gas as As<u32>>::sa(amount);
let amount = <<E::T as Trait>::Gas as As<u32>>::sa(amount);
charge_gas(&mut ctx.gas_meter, amount)?;
Ok(())
@@ -257,7 +257,7 @@ define_env!(init_env, <E: Ext>,
let nested_gas_limit = if gas == 0 {
ctx.gas_meter.gas_left()
} else {
<<<E as Ext>::T as Trait>::Gas as As<u64>>::sa(gas)
<<E::T as Trait>::Gas as As<u64>>::sa(gas)
};
let ext = &mut ctx.ext;
let scratch_buf = &mut ctx.scratch_buf;
@@ -316,7 +316,7 @@ define_env!(init_env, <E: Ext>,
let nested_gas_limit = if gas == 0 {
ctx.gas_meter.gas_left()
} else {
<<<E as Ext>::T as Trait>::Gas as As<u64>>::sa(gas)
<<E::T as Trait>::Gas as As<u64>>::sa(gas)
};
let ext = &mut ctx.ext;
let create_outcome = ctx.gas_meter.with_nested(nested_gas_limit, |nested_meter| {
@@ -339,8 +339,8 @@ define_env!(init_env, <E: Ext>,
// Save a data buffer as a result of the execution, terminate the execution and return a
// successful result to the caller.
ext_return(ctx, data_ptr: u32, data_len: u32) => {
let data_len_in_gas = <<<E as Ext>::T as Trait>::Gas as As<u64>>::sa(data_len as u64);
let price = (ctx.config.return_data_per_byte_cost)
let data_len_in_gas = <<E::T as Trait>::Gas as As<u64>>::sa(data_len as u64);
let price = (ctx.schedule.return_data_per_byte_cost)
.checked_mul(&data_len_in_gas)
.ok_or(sandbox::HostError)?;
@@ -382,7 +382,7 @@ define_env!(init_env, <E: Ext>,
// Finally, perform the write.
write_sandbox_memory(
ctx.config.sandbox_data_write_cost,
ctx.schedule.sandbox_data_write_cost,
ctx.gas_meter,
&ctx.memory,
dest_ptr,
@@ -414,7 +414,7 @@ define_env!(init_env, <E: Ext>,
// Finally, perform the write.
write_sandbox_memory(
ctx.config.sandbox_data_write_cost,
ctx.schedule.sandbox_data_write_cost,
ctx.gas_meter,
&ctx.memory,
dest_ptr,