diff --git a/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm b/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm index fe88a3463d..cb5c02bbb9 100644 Binary files a/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm and b/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm differ diff --git a/substrate/node/cli/src/chain_spec.rs b/substrate/node/cli/src/chain_spec.rs index 1efef9e84b..d8bc5bad5d 100644 --- a/substrate/node/cli/src/chain_spec.rs +++ b/substrate/node/cli/src/chain_spec.rs @@ -124,6 +124,7 @@ fn staging_testnet_config_genesis() -> GenesisConfig { gas_price: 1 * MILLICENTS, max_depth: 1024, block_gas_limit: 10_000_000, + current_schedule: Default::default(), }), upgrade_key: Some(UpgradeKeyConfig { key: endowed_accounts[0].clone(), @@ -241,6 +242,7 @@ pub fn testnet_genesis( gas_price: 1, max_depth: 1024, block_gas_limit: 10_000_000, + current_schedule: Default::default(), }), upgrade_key: Some(UpgradeKeyConfig { key: upgrade_key, diff --git a/substrate/node/executor/src/lib.rs b/substrate/node/executor/src/lib.rs index fd68aadd51..fe491c7cfd 100644 --- a/substrate/node/executor/src/lib.rs +++ b/substrate/node/executor/src/lib.rs @@ -295,9 +295,9 @@ mod tests { 1, GENESIS_HASH.into(), if support_changes_trie { - hex!("978a3ff733a86638da39d36a349c693b5cf562bcc8db30fec6c2b6c40f925a9b").into() + hex!("a998cf2956b526aecc0887903df66457e640bb2debfd7976b5c7696da31cdaef").into() } else { - hex!("7bbad534e3de3db3c8cda015c4e8ed8ba10dde7e3fca21f4fd4fbc686e6c1410").into() + hex!("2caffd5fcc42934e6b758613ff0a7e624a8c5b7c67b7c405bf6985a7e3a19701").into() }, if support_changes_trie { Some(hex!("1f8f44dcae8982350c14dee720d34b147e73279f5a2ce1f9781195a991970978").into()) @@ -321,7 +321,7 @@ mod tests { construct_block( 2, block1(false).1, - hex!("7be30152ee2ee909047cffad5f0a28bf8c2b0a97c124b500aeac112f6917738e").into(), + hex!("72b2afc379ce2161aef95ef6f86a2321867f12b046703ea0af5aed158c2a4f30").into(), None, vec![ CheckedExtrinsic { @@ -344,7 +344,7 @@ mod tests { construct_block( 1, GENESIS_HASH.into(), - hex!("325a73726dc640af41becb42938e7152e218f130219c0695aae35b6a156f93f3").into(), + hex!("5f4461c584ce91dd6862313fd075ffc26dc702fcc1183634ee7b0c5de8b5b4d1").into(), None, vec![ CheckedExtrinsic { @@ -626,7 +626,7 @@ mod tests { let b = construct_block( 1, GENESIS_HASH.into(), - hex!("cf0fee74c87ecff646804984bbdf85832a788b3ca2a2aa33e20da61fa7182b37").into(), + hex!("9885d4297ce0341ec07957d1de32848460565a17ef2ea400df0e2326634914ae").into(), None, vec![ CheckedExtrinsic { diff --git a/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm b/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm index 8d53eefe47..94c8b1f3b8 100644 Binary files a/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm and b/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm differ diff --git a/substrate/srml/contract/COMPLEXITY.md b/substrate/srml/contract/COMPLEXITY.md index f805eb45f0..a6f1c3dca7 100644 --- a/substrate/srml/contract/COMPLEXITY.md +++ b/substrate/srml/contract/COMPLEXITY.md @@ -132,53 +132,48 @@ Consists of dropping (in the Rust sense) of the `AccountDb`. This function performs the following steps: 1. Querying source and destination balances from an overlay (see `get_balance`), -2. Querying fee for the case. (This hits DB unless pre-loaded) -2. Querying `existential_deposit`. (This hits DB unless pre-loaded) +2. Querying `existential_deposit`. 3. Executing `ensure_account_liquid` hook. 4. Updating source and destination balance in the overlay (see `set_balance`). **Note** that the complexity of executing `ensure_account_liquid` hook should be considered separately. -In the course of the execution this function can perform up to 4 DB reads: 2x `get_balance`, fee and `existential_deposit`. The last two can be pre-loaded pushing the cost of loading to a higher level and making it a one time. It can also induce up to 2 DB writes via `set_balance` if flushed to the storage. +In the course of the execution this function can perform up to 2 DB reads to `get_balance` of source and destination accounts. It can also induce up to 2 DB writes via `set_balance` if flushed to the storage. Moreover, if the source balance goes below `existential_deposit` then the account will be deleted along with all its storage which requires time proportional to the number of storage entries of that account. Assuming marshaled size of a balance value is of the constant size we can neglect its effect on the performance. -**complexity**: up to 4 DB reads and up to 2 DB writes (if flushed to the storage) in the standard case. If removal of the source account takes place then it will additionally perform a DB write per one storage entry that the account has. For the current `AccountDb` implementation computing complexity also depends on the depth of the `AccountDb` cascade. Memorywise it can be assumed to be constant. +**complexity**: up to 2 DB reads and up to 2 DB writes (if flushed to the storage) in the standard case. If removal of the source account takes place then it will additionally perform a DB write per one storage entry that the account has. For the current `AccountDb` implementation computing complexity also depends on the depth of the `AccountDb` cascade. Memorywise it can be assumed to be constant. ## Call This function receives input data for the contract execution. The execution consists of the following steps: -1. Querying `MaxDepth` and `call_base_fee`. (These hit DB unless pre-loaded) -2. Loading code from the DB. -3. `transfer`-ing funds between the caller and the destination account. -4. Executing the code of the destination account. -5. Committing overlayed changed to the underlying `AccountDb`. +1. Loading code from the DB. +2. `transfer`-ing funds between the caller and the destination account. +3. Executing the code of the destination account. +4. Committing overlayed changed to the underlying `AccountDb`. **Note** that the complexity of executing the contract code should be considered separately. -The execution of this function will involve 2 DB reads for querying `MaxDepth` and `MaxDepth` constants. These values can be pre-loaded pushing the cost of loading to a higher level and making it a one time. - Loading code most probably will trigger a DB read, since the code is immutable and therefore will not get into the cache (unless a suicide removes it). -Also, `transfer` can make up to 4 DB reads and up to 2 DB writes (if flushed to the storage) in the standard case. If removal of the source account takes place then it will additionally perform a DB write per one storage entry that the account has. +Also, `transfer` can make up to 2 DB reads and up to 2 DB writes (if flushed to the storage) in the standard case. If removal of the source account takes place then it will additionally perform a DB write per one storage entry that the account has. Finally, all changes are `commit`-ted into the underlying overlay. The complexity of this depends on the number of changes performed by the code. Thus, the pricing of storage modification should account for that. -**complexity**: Up to 7 DB reads. DB read of the code is of dynamic size. There can also be up to 2 DB writes (if flushed to the storage). Additionally, if the source account removal takes place a DB write will be performed per one storage entry that the account has. +**complexity**: Up to 3 DB reads. DB read of the code is of dynamic size. There can also be up to 2 DB writes (if flushed to the storage). Additionally, if the source account removal takes place a DB write will be performed per one storage entry that the account has. ## Create This function takes the code of the constructor and input data. Creation of a contract consists of the following steps: -1. Querying `MaxDepth` and `create_base_fee`. (These hit DB unless pre-loaded) -2. Calling `DetermineContractAddress` hook to determine an address for the contract, -3. `transfer`-ing funds between self and the newly created contract. -4. Executing the constructor code. This will yield the final code of the code. -5. Storing the code for the newly created contract in the overlay. -6. Committing overlayed changed to the underlying `AccountDb`. +1. Calling `DetermineContractAddress` hook to determine an address for the contract, +2. `transfer`-ing funds between self and the newly created contract. +3. Executing the constructor code. This will yield the final code of the code. +4. Storing the code for the newly created contract in the overlay. +5. Committing overlayed changed to the underlying `AccountDb`. **Note** that the complexity of executing the constructor code should be considered separately. @@ -186,15 +181,13 @@ This function takes the code of the constructor and input data. Creation of a co **Note** that the constructor returns code in the owned form and it's obtained via return facilities, which should have take fee for the return value. -The execution of this function involves 2 DB reads for querying `create_base_fee` and `MaxDepth` constants. These values can be pre-loaded pushing the cost of loading to a higher level and making it a one time. - -Also, `transfer` can make up to 4 DB reads and up to 2 DB writes (if flushed to the storage) in the standard case. If removal of the source account takes place then it will additionally perform a DB write per one storage entry that the account has. +Also, `transfer` can make up to 2 DB reads and up to 2 DB writes (if flushed to the storage) in the standard case. If removal of the source account takes place then it will additionally perform a DB write per one storage entry that the account has. Storing the code in the overlay may induce another DB write (if flushed to the storage) with the size proportional to the size of the constructor code. Finally, all changes are `commit`-ted into the underlying overlay. The complexity of this depends on the number of changes performed by the constructor code. Thus, the pricing of storage modification should account for that. -**complexity**: Up to 6 DB reads and induces up to 3 DB writes (if flushed to the storage), one of which is dependent on the size of the code. Additionally, if the source account removal takes place a DB write will be performed per one storage entry that the account has. +**complexity**: Up to 2 DB reads and induces up to 3 DB writes (if flushed to the storage), one of which is dependent on the size of the code. Additionally, if the source account removal takes place a DB write will be performed per one storage entry that the account has. # Externalities diff --git a/substrate/srml/contract/src/exec.rs b/substrate/srml/contract/src/exec.rs index cc74142947..f3cc2139bf 100644 --- a/substrate/srml/contract/src/exec.rs +++ b/substrate/srml/contract/src/exec.rs @@ -14,14 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use super::{MaxDepth, ContractAddressFor, Module, Trait, Event, RawEvent}; +use super::{ContractAddressFor, Trait, Event, RawEvent, Config}; use account_db::{AccountDb, OverlayAccountDb}; use gas::GasMeter; use vm; use rstd::prelude::*; use runtime_primitives::traits::{Zero, CheckedAdd, CheckedSub}; -use runtime_support::StorageValue; use balances::{self, EnsureAccountLiquid}; // TODO: Add logs @@ -38,6 +37,7 @@ pub struct ExecutionContext<'a, T: Trait + 'a> { pub overlay: OverlayAccountDb<'a, T>, pub depth: usize, pub events: Vec>, + pub config: &'a Config, } impl<'a, T: Trait> ExecutionContext<'a, T> { @@ -51,12 +51,11 @@ impl<'a, T: Trait> ExecutionContext<'a, T> { data: &[u8], output_data: &mut Vec, ) -> Result { - if self.depth == >::get() as usize { + if self.depth == self.config.max_depth as usize { return Err("reached maximum depth, cannot make a call"); } - let call_base_fee = >::call_base_fee(); - if gas_meter.charge(call_base_fee).is_out_of_gas() { + if gas_meter.charge(self.config.call_base_fee).is_out_of_gas() { return Err("not enough gas to pay base call fee"); } @@ -70,6 +69,7 @@ impl<'a, T: Trait> ExecutionContext<'a, T> { self_account: dest.clone(), depth: self.depth + 1, events: Vec::new(), + config: self.config, }; if value > T::Balance::zero() { @@ -92,7 +92,7 @@ impl<'a, T: Trait> ExecutionContext<'a, T> { ctx: &mut nested, _caller: caller, }, - &::vm::Config::default(), + &self.config.schedule, gas_meter, ).map_err(|_| "vm execute returned error while call")?; } @@ -114,12 +114,11 @@ impl<'a, T: Trait> ExecutionContext<'a, T> { init_code: &[u8], data: &[u8], ) -> Result, &'static str> { - if self.depth == >::get() as usize { + if self.depth == self.config.max_depth as usize { return Err("reached maximum depth, cannot create"); } - let create_base_fee = >::create_base_fee(); - if gas_meter.charge(create_base_fee).is_out_of_gas() { + if gas_meter.charge(self.config.create_base_fee).is_out_of_gas() { return Err("not enough gas to pay base create fee"); } @@ -138,6 +137,7 @@ impl<'a, T: Trait> ExecutionContext<'a, T> { self_account: dest.clone(), depth: self.depth + 1, events: Vec::new(), + config: self.config, }; if endowment > T::Balance::zero() { @@ -160,7 +160,7 @@ impl<'a, T: Trait> ExecutionContext<'a, T> { ctx: &mut nested, _caller: caller, }, - &::vm::Config::default(), + &self.config.schedule, gas_meter, ).map_err(|_| "vm execute returned error while create")?; @@ -213,14 +213,15 @@ fn transfer<'a, T: Trait>( // `contract_create` will be `false` but `would_create` will be `true`. let would_create = to_balance.is_zero(); - let fee: T::Balance = if contract_create { - >::contract_fee() - } else { - if would_create { - >::creation_fee() - } else { - >::transfer_fee() - } + let fee: T::Balance = match (contract_create, would_create) { + // If this function is called from `CREATE` routine, then we always + // charge contract account creation fee. + (true, _) => ctx.config.contract_account_create_fee, + + // Otherwise the fee depends on whether we create a new account or transfer + // to an existing one. + (false, true) => ctx.config.account_create_fee, + (false, false) => ctx.config.transfer_fee, }; if gas_meter.charge_by_balance(fee).is_out_of_gas() { @@ -233,7 +234,7 @@ fn transfer<'a, T: Trait>( Some(b) => b, None => return Err("balance too low to send value"), }; - if would_create && value < >::existential_deposit() { + if would_create && value < ctx.config.existential_deposit { return Err("value too low to create account"); } ::EnsureAccountLiquid::ensure_account_liquid(transactor)?; diff --git a/substrate/srml/contract/src/lib.rs b/substrate/srml/contract/src/lib.rs index e3f6a600b3..e87c289ed8 100644 --- a/substrate/srml/contract/src/lib.rs +++ b/substrate/srml/contract/src/lib.rs @@ -168,11 +168,13 @@ decl_module! { // paying for the gas. let mut gas_meter = gas::buy_gas::(&origin, gas_limit)?; + let cfg = Config::preload(); let mut ctx = ExecutionContext { self_account: origin.clone(), depth: 0, overlay: OverlayAccountDb::::new(&account_db::DirectAccountDb), events: Vec::new(), + config: &cfg, }; let mut output_data = Vec::new(); @@ -221,11 +223,13 @@ decl_module! { // paying for the gas. let mut gas_meter = gas::buy_gas::(&origin, gas_limit)?; + let cfg = Config::preload(); let mut ctx = ExecutionContext { self_account: origin.clone(), depth: 0, overlay: OverlayAccountDb::::new(&account_db::DirectAccountDb), events: Vec::new(), + config: &cfg, }; let result = ctx.create(origin.clone(), endowment, &mut gas_meter, &ctor_code, &data); @@ -284,7 +288,8 @@ decl_storage! { 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 an account. pub CodeOf: map T::AccountId => Vec; // TODO Vec values should be optimised to not do a length prefix. } @@ -310,3 +315,77 @@ impl balances::OnFreeBalanceZero for Module { >::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_create_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, +} + +impl Config { + fn preload() -> Config { + Config { + schedule: >::current_schedule(), + existential_deposit: >::existential_deposit(), + max_depth: >::max_depth(), + contract_account_create_fee: >::contract_fee(), + account_create_fee: >::creation_fee(), + transfer_fee: >::transfer_fee(), + call_base_fee: >::call_base_fee(), + create_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)] +pub struct Schedule { + /// 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. + sandbox_data_read_cost: Gas, + + /// Gas cost per one byte written to the sandbox memory. + 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 { + 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, + } + } +} diff --git a/substrate/srml/contract/src/tests.rs b/substrate/srml/contract/src/tests.rs index 62683e0a95..27d6e59a95 100644 --- a/substrate/srml/contract/src/tests.rs +++ b/substrate/srml/contract/src/tests.rs @@ -142,6 +142,7 @@ impl ExtBuilder { gas_price: self.gas_price, max_depth: 100, block_gas_limit: self.block_gas_limit, + current_schedule: Default::default(), }.build_storage() .unwrap().0, ); diff --git a/substrate/srml/contract/src/vm/env_def/macros.rs b/substrate/srml/contract/src/vm/env_def/macros.rs index 16ba669daf..c751bd0c12 100644 --- a/substrate/srml/contract/src/vm/env_def/macros.rs +++ b/substrate/srml/contract/src/vm/env_def/macros.rs @@ -219,7 +219,7 @@ mod tests { #[test] fn macro_define_func() { define_func!( ext_gas (_ctx, amount: u32) => { - let amount = <<::T as Trait>::Gas as As>::sa(amount); + let amount = <::Gas as As>::sa(amount); if !amount.is_zero() { Ok(()) } else { @@ -269,7 +269,7 @@ mod tests { fn macro_define_env() { define_env!(init_env, , ext_gas( _ctx, amount: u32 ) => { - let amount = <<::T as Trait>::Gas as As>::sa(amount); + let amount = <::Gas as As>::sa(amount); if !amount.is_zero() { Ok(()) } else { diff --git a/substrate/srml/contract/src/vm/mod.rs b/substrate/srml/contract/src/vm/mod.rs index 63cdc5861d..dc59154266 100644 --- a/substrate/srml/contract/src/vm/mod.rs +++ b/substrate/srml/contract/src/vm/mod.rs @@ -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 = ::Balance; @@ -118,7 +117,7 @@ pub fn execute<'a, E: Ext>( input_data: &[u8], output_data: &mut Vec, ext: &'a mut E, - config: &Config, + schedule: &Schedule<::Gas>, gas_meter: &mut GasMeter, ) -> 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 { - /// 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 Default for Config { - fn default() -> Config { - 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::::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(); diff --git a/substrate/srml/contract/src/vm/prepare.rs b/substrate/srml/contract/src/vm/prepare.rs index b79ce8336d..8ec056dfc7 100644 --- a/substrate/srml/contract/src/vm/prepare.rs +++ b/substrate/srml/contract/src/vm/prepare.rs @@ -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, - config: &'a Config, + schedule: &'a Schedule, } -impl<'a, T: Trait> ContractModule<'a, T> { - fn new(original_code: &[u8], config: &'a Config) -> Result, Error> { +impl<'a, Gas: 'a + As + Clone> ContractModule<'a, Gas> { + fn new(original_code: &[u8], schedule: &'a Schedule) -> Result, 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( original_code: &[u8], - config: &Config, + schedule: &Schedule<::Gas>, env: &HostFunctionSet, ) -> Result { - 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( // 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( 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 { let wasm = wabt::Wat2Wasm::new().validate(false).convert(wat).unwrap(); - let config = Config::::default(); + let schedule = Schedule::::default(); let env = ::vm::runtime::init_env(); - prepare_contract::(wasm.as_ref(), &config, &env) + prepare_contract::(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::::default().max_memory_pages, 16); + assert_eq!(Schedule::::default().max_memory_pages, 16); let r = parse_and_prepare_wat(r#"(module (import "env" "memory" (memory 1 1)))"#); assert_matches!(r, Ok(_)); diff --git a/substrate/srml/contract/src/vm/runtime.rs b/substrate/srml/contract/src/vm/runtime.rs index 3bdc9bf12a..c56953fcdb 100644 --- a/substrate/srml/contract/src/vm/runtime.rs +++ b/substrate/srml/contract/src/vm/runtime.rs @@ -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, scratch_buf: Vec, - config: &'a Config, + schedule: &'a Schedule<::Gas>, memory: sandbox::Memory, gas_meter: &'a mut GasMeter, special_trap: Option, @@ -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, - config: &'a Config, + schedule: &'a Schedule<::Gas>, memory: sandbox::Memory, gas_meter: &'a mut GasMeter, ) -> 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( ptr: u32, len: u32, ) -> Result, sandbox::HostError> { - let price = (ctx.config.sandbox_data_read_cost) + let price = (ctx.schedule.sandbox_data_read_cost) .checked_mul(& as As>::sa(len)) .ok_or(sandbox::HostError)?; charge_gas(ctx.gas_meter, price)?; @@ -169,7 +169,7 @@ define_env!(init_env, , // // - amount: How much gas is used. gas(ctx, amount: u32) => { - let amount = <<::T as Trait>::Gas as As>::sa(amount); + let amount = <::Gas as As>::sa(amount); charge_gas(&mut ctx.gas_meter, amount)?; Ok(()) @@ -257,7 +257,7 @@ define_env!(init_env, , let nested_gas_limit = if gas == 0 { ctx.gas_meter.gas_left() } else { - <<::T as Trait>::Gas as As>::sa(gas) + <::Gas as As>::sa(gas) }; let ext = &mut ctx.ext; let scratch_buf = &mut ctx.scratch_buf; @@ -316,7 +316,7 @@ define_env!(init_env, , let nested_gas_limit = if gas == 0 { ctx.gas_meter.gas_left() } else { - <<::T as Trait>::Gas as As>::sa(gas) + <::Gas as As>::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, , // 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 = <<::T as Trait>::Gas as As>::sa(data_len as u64); - let price = (ctx.config.return_data_per_byte_cost) + let data_len_in_gas = <::Gas as As>::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, , // 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, , // 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,