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
+2
View File
@@ -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,
+5 -5
View File
@@ -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 {
+16 -23
View File
@@ -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
+20 -19
View File
@@ -14,14 +14,13 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
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<Event<T>>,
pub config: &'a Config<T>,
}
impl<'a, T: Trait> ExecutionContext<'a, T> {
@@ -51,12 +51,11 @@ impl<'a, T: Trait> ExecutionContext<'a, T> {
data: &[u8],
output_data: &mut Vec<u8>,
) -> Result<CallReceipt, &'static str> {
if self.depth == <MaxDepth<T>>::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 = <Module<T>>::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<CreateReceipt<T>, &'static str> {
if self.depth == <MaxDepth<T>>::get() as usize {
if self.depth == self.config.max_depth as usize {
return Err("reached maximum depth, cannot create");
}
let create_base_fee = <Module<T>>::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 {
<Module<T>>::contract_fee()
} else {
if would_create {
<balances::Module<T>>::creation_fee()
} else {
<balances::Module<T>>::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 < <balances::Module<T>>::existential_deposit() {
if would_create && value < ctx.config.existential_deposit {
return Err("value too low to create account");
}
<T as balances::Trait>::EnsureAccountLiquid::ensure_account_liquid(transactor)?;
+80 -1
View File
@@ -168,11 +168,13 @@ decl_module! {
// paying for the gas.
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 mut output_data = Vec::new();
@@ -221,11 +223,13 @@ decl_module! {
// paying for the gas.
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);
@@ -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<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.
}
@@ -310,3 +315,77 @@ impl<T: Trait> balances::OnFreeBalanceZero<T::AccountId> for Module<T> {
<StorageOf<T>>::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<T: Trait> {
pub schedule: Schedule<T::Gas>,
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<T: Trait> Config<T> {
fn preload() -> Config<T> {
Config {
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(),
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(),
}
}
}
/// 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> {
/// 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<Gas: As<u64>> Default for Schedule<Gas> {
fn default() -> Schedule<Gas> {
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,
}
}
}
+1
View File
@@ -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,
);
@@ -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,