diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs index f007ba41ccc61..b384003187569 100644 --- a/bin/node/executor/tests/basic.rs +++ b/bin/node/executor/tests/basic.rs @@ -602,23 +602,17 @@ fn deploying_wasm_contract_should_work() { CheckedExtrinsic { signed: Some((charlie(), signed_extra(0, 0))), function: Call::Contracts( - pallet_contracts::Call::put_code::(transfer_code) - ), - }, - CheckedExtrinsic { - signed: Some((charlie(), signed_extra(1, 0))), - function: Call::Contracts( - pallet_contracts::Call::instantiate::( - 1 * DOLLARS + subsistence, + pallet_contracts::Call::instantiate_with_code::( + 1000 * DOLLARS + subsistence, 500_000_000, - transfer_ch, + transfer_code, Vec::new(), Vec::new(), ) ), }, CheckedExtrinsic { - signed: Some((charlie(), signed_extra(2, 0))), + signed: Some((charlie(), signed_extra(1, 0))), function: Call::Contracts( pallet_contracts::Call::call::( sp_runtime::MultiAddress::Id(addr.clone()), diff --git a/frame/contracts/README.md b/frame/contracts/README.md index 4252bfc1d8434..8397d2f6bf005 100644 --- a/frame/contracts/README.md +++ b/frame/contracts/README.md @@ -42,23 +42,19 @@ fails, A can decide how to handle that failure, either proceeding or reverting A ### Dispatchable functions -* `put_code` - Stores the given binary Wasm code into the chain's storage and returns its `code_hash`. -* `instantiate` - Deploys a new contract from the given `code_hash`, optionally transferring some balance. -This instantiates a new smart contract account and calls its contract deploy handler to -initialize the contract. -* `call` - Makes a call to an account, optionally transferring some balance. +Those are documented in the reference documentation of the `Module`. ## Usage The Contract module is a work in progress. The following examples show how this Contract module can be used to instantiate and call contracts. -* [`ink`](https://github.com/paritytech/ink) is +- [`ink`](https://github.com/paritytech/ink) is an [`eDSL`](https://wiki.haskell.org/Embedded_domain_specific_language) that enables writing WebAssembly based smart contracts in the Rust programming language. This is a work in progress. ## Related Modules -* [Balances](https://docs.rs/pallet-balances/latest/pallet_balances/) +- [Balances](https://docs.rs/pallet-balances/latest/pallet_balances/) License: Apache-2.0 diff --git a/frame/contracts/fixtures/instantiate_return_code.wat b/frame/contracts/fixtures/instantiate_return_code.wat index cead1f1c9fa40..544489329cfad 100644 --- a/frame/contracts/fixtures/instantiate_return_code.wat +++ b/frame/contracts/fixtures/instantiate_return_code.wat @@ -10,8 +10,8 @@ (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32))) (import "env" "memory" (memory 1 1)) - ;; [0, 8) 100 balance - (data (i32.const 0) "\64\00\00\00\00\00\00\00") + ;; [0, 8) 10_000 balance + (data (i32.const 0) "\10\27\00\00\00\00\00\00") ;; [8, 12) here we store the return code of the transfer diff --git a/frame/contracts/src/benchmarking/code.rs b/frame/contracts/src/benchmarking/code.rs index 88e8b265a57e1..01ca7d3aac22d 100644 --- a/frame/contracts/src/benchmarking/code.rs +++ b/frame/contracts/src/benchmarking/code.rs @@ -103,7 +103,7 @@ pub struct ImportedFunction { pub return_type: Option, } -/// A wasm module ready to be put on chain with `put_code`. +/// A wasm module ready to be put on chain. #[derive(Clone)] pub struct WasmModule { pub code: Vec, @@ -245,16 +245,16 @@ where } /// Creates a wasm module of `target_bytes` size. Used to benchmark the performance of - /// `put_code` for different sizes of wasm modules. The generated module maximizes + /// `instantiate_with_code` for different sizes of wasm modules. The generated module maximizes /// instrumentation runtime by nesting blocks as deeply as possible given the byte budget. pub fn sized(target_bytes: u32) -> Self { use parity_wasm::elements::Instruction::{If, I32Const, Return, End}; - // Base size of a contract is 47 bytes and each expansion adds 6 bytes. + // Base size of a contract is 63 bytes and each expansion adds 6 bytes. // We do one expansion less to account for the code section and function body // size fields inside the binary wasm module representation which are leb128 encoded // and therefore grow in size when the contract grows. We are not allowed to overshoot - // because of the maximum code size that is enforced by `put_code`. - let expansions = (target_bytes.saturating_sub(47) / 6).saturating_sub(1); + // because of the maximum code size that is enforced by `instantiate_with_code`. + let expansions = (target_bytes.saturating_sub(63) / 6).saturating_sub(1); const EXPANSION: [Instruction; 4] = [ I32Const(0), If(BlockType::NoResult), @@ -263,6 +263,7 @@ where ]; ModuleDefinition { call_body: Some(body::repeated(expansions, &EXPANSION)), + memory: Some(ImportedMemory::max::()), .. Default::default() } .into() diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index 649d091880328..2034a17e922af 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -137,7 +137,7 @@ where // same block number. System::::set_block_number(1u32.into()); - Contracts::::put_code_raw(module.code)?; + Contracts::::store_code_raw(module.code)?; Contracts::::instantiate( RawOrigin::Signed(caller.clone()).into(), endowment, @@ -198,7 +198,7 @@ where /// Get the block number when this contract will be evicted. Returns an error when /// the rent collection won't happen because the contract has to much endowment. fn eviction_at(&self) -> Result { - let projection = Rent::::compute_projection(&self.account_id) + let projection = Rent::>::compute_projection(&self.account_id) .map_err(|_| "Invalid acc for rent")?; match projection { RentProjection::EvictionAt(at) => Ok(at), @@ -250,7 +250,7 @@ where /// Evict this contract. fn evict(&mut self) -> Result<(), &'static str> { self.set_block_num_for_eviction()?; - Rent::::try_eviction(&self.contract.account_id, Zero::zero())?; + Rent::>::try_eviction(&self.contract.account_id, Zero::zero())?; self.contract.ensure_tombstone() } } @@ -314,24 +314,34 @@ benchmarks! { // This constructs a contract that is maximal expensive to instrument. // It creates a maximum number of metering blocks per byte. - // `n`: Size of the code in kilobytes. - put_code { - let n in 0 .. Contracts::::current_schedule().limits.code_size / 1024; + // The size of the salt influences the runtime because is is hashed in order to + // determine the contract address. + // `c`: Size of the code in kilobytes. + // `s`: Size of the salt in kilobytes. + instantiate_with_code { + let c in 0 .. Contracts::::current_schedule().limits.code_size / 1024; + let s in 0 .. code::max_pages::() * 64; + let salt = vec![42u8; (s * 1024) as usize]; + let endowment = caller_funding::() / 3u32.into(); let caller = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, caller_funding::()); - let module = WasmModule::::sized(n * 1024); - let origin = RawOrigin::Signed(caller); - }: _(origin, module.code) + let WasmModule { code, hash, .. } = WasmModule::::sized(c * 1024); + let origin = RawOrigin::Signed(caller.clone()); + let addr = Contracts::::contract_address(&caller, &hash, &salt); + }: _(origin, endowment, Weight::max_value(), code, vec![], salt) + verify { + // endowment was removed from the caller + assert_eq!(T::Currency::free_balance(&caller), caller_funding::() - endowment); + // contract has the full endowment because no rent collection happended + assert_eq!(T::Currency::free_balance(&addr), endowment); + // instantiate should leave a alive contract + Contract::::address_alive_info(&addr)?; + } // Instantiate uses a dummy contract constructor to measure the overhead of the instantiate. - // The size of the input data influences the runtime because it is hashed in order to determine - // the contract address. - // `n`: Size of the data passed to constructor in kilobytes. // `s`: Size of the salt in kilobytes. instantiate { - let n in 0 .. code::max_pages::() * 64; let s in 0 .. code::max_pages::() * 64; - let data = vec![42u8; (n * 1024) as usize]; let salt = vec![42u8; (s * 1024) as usize]; let endowment = caller_funding::() / 3u32.into(); let caller = whitelisted_caller(); @@ -339,8 +349,8 @@ benchmarks! { let WasmModule { code, hash, .. } = WasmModule::::dummy_with_mem(); let origin = RawOrigin::Signed(caller.clone()); let addr = Contracts::::contract_address(&caller, &hash, &salt); - Contracts::::put_code_raw(code)?; - }: _(origin, endowment, Weight::max_value(), hash, data, salt) + Contracts::::store_code_raw(code)?; + }: _(origin, endowment, Weight::max_value(), hash, vec![], salt) verify { // endowment was removed from the caller assert_eq!(T::Currency::free_balance(&caller), caller_funding::() - endowment); @@ -1369,7 +1379,7 @@ benchmarks! { ])), .. Default::default() }); - Contracts::::put_code_raw(code.code)?; + Contracts::::store_code_raw(code.code)?; Ok(code.hash) }) .collect::, &'static str>>()?; @@ -1492,7 +1502,7 @@ benchmarks! { let hash = callee_code.hash.clone(); let hash_bytes = callee_code.hash.encode(); let hash_len = hash_bytes.len(); - Contracts::::put_code_raw(callee_code.code)?; + Contracts::::store_code_raw(callee_code.code)?; let inputs = (0..API_BENCHMARK_BATCH_SIZE).map(|x| x.encode()).collect::>(); let input_len = inputs.get(0).map(|x| x.len()).unwrap_or(0); let input_bytes = inputs.iter().cloned().flatten().collect::>(); @@ -2455,7 +2465,7 @@ mod tests { create_test!(on_initialize_per_queue_item); create_test!(update_schedule); - create_test!(put_code); + create_test!(instantiate_with_code); create_test!(instantiate); create_test!(call); create_test!(claim_surcharge); diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index c2ad48ca981ab..047d7aba192fc 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -18,13 +18,16 @@ use crate::{ CodeHash, ConfigCache, Event, RawEvent, Config, Module as Contracts, TrieId, BalanceOf, ContractInfo, gas::GasMeter, rent::Rent, storage::{self, Storage}, - Error, ContractInfoOf + Error, ContractInfoOf, Schedule, }; use sp_core::crypto::UncheckedFrom; -use sp_std::prelude::*; +use sp_std::{ + prelude::*, + marker::PhantomData, +}; use sp_runtime::traits::{Bounded, Zero, Convert, Saturating}; use frame_support::{ - dispatch::DispatchResult, + dispatch::{DispatchResult, DispatchError}, traits::{ExistenceRequirement, Currency, Time, Randomness}, weights::Weight, ensure, StorageMap, @@ -73,7 +76,7 @@ pub trait Ext { /// transferred from this to the newly created account (also known as endowment). fn instantiate( &mut self, - code: &CodeHash, + code: CodeHash, value: BalanceOf, gas_meter: &mut GasMeter, input_data: Vec, @@ -168,80 +171,111 @@ pub trait Ext { /// Returns the price for the specified amount of weight. fn get_weight_price(&self, weight: Weight) -> BalanceOf; + + /// Get a reference to the schedule used by the current call. + fn schedule(&self) -> &Schedule; } -/// Loader is a companion of the `Vm` trait. It loads an appropriate abstract -/// executable to be executed by an accompanying `Vm` implementation. -pub trait Loader { - type Executable; - - /// Load the initializer portion of the code specified by the `code_hash`. This - /// executable is called upon instantiation. - fn load_init(&self, code_hash: &CodeHash) -> Result; - /// Load the main portion of the code specified by the `code_hash`. This executable - /// is called for each call to a contract. - fn load_main(&self, code_hash: &CodeHash) -> Result; +/// Describes the different functions that can be exported by an [`Executable`]. +pub enum ExportedFunction { + /// The constructor function which is executed on deployment of a contract. + Constructor, + /// The function which is executed when a contract is called. + Call, } -/// A trait that represent a virtual machine. +/// A trait that represents something that can be executed. /// -/// You can view a virtual machine as something that takes code, an input data buffer, -/// queries it and/or performs actions on the given `Ext` and optionally -/// returns an output data buffer. The type of code depends on the particular virtual machine. -/// -/// Execution of code can end by either implicit termination (that is, reached the end of -/// executable), explicit termination via returning a buffer or termination due to a trap. -pub trait Vm { - type Executable; +/// In the on-chain environment this would be represented by a wasm module. This trait exists in +/// order to be able to mock the wasm logic for testing. +pub trait Executable: Sized { + /// Load the executable from storage. + fn from_storage(code_hash: CodeHash, schedule: &Schedule) -> Result; + + /// Load the module from storage without re-instrumenting it. + /// + /// A code module is re-instrumented on-load when it was originally instrumented with + /// an older schedule. This skips this step for cases where the code storage is + /// queried for purposes other than execution. + fn from_storage_noinstr(code_hash: CodeHash) -> Result; + + /// Decrements the refcount by one and deletes the code if it drops to zero. + fn drop_from_storage(self); + + /// Increment the refcount by one. Fails if the code does not exist on-chain. + fn add_user(code_hash: CodeHash) -> DispatchResult; + /// Decrement the refcount by one and remove the code when it drops to zero. + fn remove_user(code_hash: CodeHash); + + /// Execute the specified exported function and return the result. + /// + /// When the specified function is `Constructor` the executable is stored and its + /// refcount incremented. + /// + /// # Note + /// + /// This functions expects to be executed in a storage transaction that rolls back + /// all of its emitted storage changes. fn execute>( - &self, - exec: &Self::Executable, + self, ext: E, + function: &ExportedFunction, input_data: Vec, gas_meter: &mut GasMeter, ) -> ExecResult; + + /// The code hash of the executable. + fn code_hash(&self) -> &CodeHash; + + /// The storage that is occupied by the instrumented executable and its pristine source. + /// + /// The returned size is already divided by the number of users who share the code. + /// + /// # Note + /// + /// This works with the current in-memory value of refcount. When calling any contract + /// without refetching this from storage the result can be inaccurate as it might be + /// working with a stale value. Usually this inaccuracy is tolerable. + fn occupied_storage(&self) -> u32; } -pub struct ExecutionContext<'a, T: Config + 'a, V, L> { - pub caller: Option<&'a ExecutionContext<'a, T, V, L>>, +pub struct ExecutionContext<'a, T: Config + 'a, E> { + pub caller: Option<&'a ExecutionContext<'a, T, E>>, pub self_account: T::AccountId, pub self_trie_id: Option, pub depth: usize, pub config: &'a ConfigCache, - pub vm: &'a V, - pub loader: &'a L, pub timestamp: MomentOf, pub block_number: T::BlockNumber, + _phantom: PhantomData, } -impl<'a, T, E, V, L> ExecutionContext<'a, T, V, L> +impl<'a, T, E> ExecutionContext<'a, T, E> where T: Config, T::AccountId: UncheckedFrom + AsRef<[u8]>, - L: Loader, - V: Vm, + E: Executable, { /// Create the top level execution context. /// /// The specified `origin` address will be used as `sender` for. The `origin` must be a regular /// account (not a contract). - pub fn top_level(origin: T::AccountId, cfg: &'a ConfigCache, vm: &'a V, loader: &'a L) -> Self { + pub fn top_level(origin: T::AccountId, cfg: &'a ConfigCache) -> Self { ExecutionContext { caller: None, self_trie_id: None, self_account: origin, depth: 0, config: &cfg, - vm: &vm, - loader: &loader, timestamp: T::Time::now(), block_number: >::block_number(), + _phantom: Default::default(), } } fn nested<'b, 'c: 'b>(&'c self, dest: T::AccountId, trie_id: TrieId) - -> ExecutionContext<'b, T, V, L> + -> ExecutionContext<'b, T, E> { ExecutionContext { caller: Some(self), @@ -249,10 +283,9 @@ where self_account: dest, depth: self.depth + 1, config: self.config, - vm: self.vm, - loader: self.loader, timestamp: self.timestamp.clone(), block_number: self.block_number.clone(), + _phantom: Default::default(), } } @@ -268,16 +301,19 @@ where Err(Error::::MaxCallDepthReached)? } + let contract = >::get(&dest) + .and_then(|contract| contract.get_alive()) + .ok_or(Error::::NotCallable)?; + + let executable = E::from_storage(contract.code_hash, &self.config.schedule)?; + // This charges the rent and denies access to a contract that is in need of // eviction by returning `None`. We cannot evict eagerly here because those // changes would be rolled back in case this contract is called by another // contract. // See: https://github.com/paritytech/substrate/issues/6439#issuecomment-648754324 - let contract = if let Ok(Some(ContractInfo::Alive(info))) = Rent::::charge(&dest) { - info - } else { - Err(Error::::NotCallable)? - }; + let contract = Rent::::charge(&dest, contract, executable.occupied_storage())? + .ok_or(Error::::NotCallable)?; let transactor_kind = self.transactor_kind(); let caller = self.self_account.clone(); @@ -294,11 +330,9 @@ where )? } - let executable = nested.loader.load_main(&contract.code_hash) - .map_err(|_| Error::::CodeNotFound)?; - let output = nested.vm.execute( - &executable, + let output = executable.execute( nested.new_call_context(caller, value), + &ExportedFunction::Call, input_data, gas_meter, ).map_err(|e| ExecError { error: e.error, origin: ErrorOrigin::Callee })?; @@ -310,7 +344,7 @@ where &mut self, endowment: BalanceOf, gas_meter: &mut GasMeter, - code_hash: &CodeHash, + executable: E, input_data: Vec, salt: &[u8], ) -> Result<(T::AccountId, ExecReturnValue), ExecError> { @@ -320,7 +354,7 @@ where let transactor_kind = self.transactor_kind(); let caller = self.self_account.clone(); - let dest = Contracts::::contract_address(&caller, code_hash, salt); + let dest = Contracts::::contract_address(&caller, executable.code_hash(), salt); let output = frame_support::storage::with_transaction(|| { // Generate the trie id in a new transaction to only increment the counter on success. @@ -333,7 +367,7 @@ where .self_trie_id .clone() .expect("the nested context always has to have self_trie_id"), - code_hash.clone() + executable.code_hash().clone() )?; // Send funds unconditionally here. If the `endowment` is below existential_deposit @@ -347,25 +381,32 @@ where nested, )?; - let executable = nested.loader.load_init(&code_hash) - .map_err(|_| Error::::CodeNotFound)?; - let output = nested.vm - .execute( - &executable, - nested.new_call_context(caller.clone(), endowment), - input_data, - gas_meter, - ).map_err(|e| ExecError { error: e.error, origin: ErrorOrigin::Callee })?; - + // Cache the value before calling into the constructor because that + // consumes the value. If the constructor creates additional contracts using + // the same code hash we still charge the "1 block rent" as if they weren't + // spawned. This is OK as overcharging is always safe. + let occupied_storage = executable.occupied_storage(); + + let output = executable.execute( + nested.new_call_context(caller.clone(), endowment), + &ExportedFunction::Constructor, + input_data, + gas_meter, + ).map_err(|e| ExecError { error: e.error, origin: ErrorOrigin::Callee })?; + + // We need to re-fetch the contract because changes are written to storage + // eagerly during execution. + let contract = >::get(&dest) + .and_then(|contract| contract.get_alive()) + .ok_or(Error::::NotCallable)?; // Collect the rent for the first block to prevent the creation of very large // contracts that never intended to pay for even one block. // This also makes sure that it is above the subsistence threshold // in order to keep up the guarantuee that we always leave a tombstone behind // with the exception of a contract that called `seal_terminate`. - Rent::::charge(&dest)? - .and_then(|c| c.get_alive()) - .ok_or_else(|| Error::::NewContractNotFunded)?; + Rent::::charge(&dest, contract, occupied_storage)? + .ok_or(Error::::NewContractNotFunded)?; // Deposit an instantiation event. deposit_event::(vec![], RawEvent::Instantiated(caller.clone(), dest.clone())); @@ -387,7 +428,7 @@ where &'b mut self, caller: T::AccountId, value: BalanceOf, - ) -> CallContext<'b, 'a, T, V, L> { + ) -> CallContext<'b, 'a, T, E> { let timestamp = self.timestamp.clone(); let block_number = self.block_number.clone(); CallContext { @@ -396,13 +437,14 @@ where value_transferred: value, timestamp, block_number, + _phantom: Default::default(), } } /// Execute the given closure within a nested execution context. fn with_nested_context(&mut self, dest: T::AccountId, trie_id: TrieId, func: F) -> ExecResult - where F: FnOnce(&mut ExecutionContext) -> ExecResult + where F: FnOnce(&mut ExecutionContext) -> ExecResult { use frame_support::storage::TransactionOutcome::*; let mut nested = self.nested(dest, trie_id); @@ -447,16 +489,17 @@ enum TransferCause { /// is specified as `Terminate`. Otherwise, any transfer that would bring the sender below the /// subsistence threshold (for contracts) or the existential deposit (for plain accounts) /// results in an error. -fn transfer<'a, T: Config, V: Vm, L: Loader>( +fn transfer<'a, T: Config, E>( cause: TransferCause, origin: TransactorKind, transactor: &T::AccountId, dest: &T::AccountId, value: BalanceOf, - ctx: &mut ExecutionContext<'a, T, V, L>, + ctx: &mut ExecutionContext<'a, T, E>, ) -> DispatchResult where T::AccountId: UncheckedFrom + AsRef<[u8]>, + E: Executable, { use self::TransferCause::*; use self::TransactorKind::*; @@ -493,20 +536,20 @@ where /// implies that the control won't be returned to the contract anymore, but there is still some code /// on the path of the return from that call context. Therefore, care must be taken in these /// situations. -struct CallContext<'a, 'b: 'a, T: Config + 'b, V: Vm + 'b, L: Loader> { - ctx: &'a mut ExecutionContext<'b, T, V, L>, +struct CallContext<'a, 'b: 'a, T: Config + 'b, E> { + ctx: &'a mut ExecutionContext<'b, T, E>, caller: T::AccountId, value_transferred: BalanceOf, timestamp: MomentOf, block_number: T::BlockNumber, + _phantom: PhantomData, } -impl<'a, 'b: 'a, T, E, V, L> Ext for CallContext<'a, 'b, T, V, L> +impl<'a, 'b: 'a, T, E> Ext for CallContext<'a, 'b, T, E> where T: Config + 'b, T::AccountId: UncheckedFrom + AsRef<[u8]>, - V: Vm, - L: Loader, + E: Executable, { type T = T; @@ -537,13 +580,15 @@ where fn instantiate( &mut self, - code_hash: &CodeHash, + code_hash: CodeHash, endowment: BalanceOf, gas_meter: &mut GasMeter, input_data: Vec, salt: &[u8], ) -> Result<(AccountIdOf, ExecReturnValue), ExecError> { - self.ctx.instantiate(endowment, gas_meter, code_hash, input_data, salt) + let executable = E::from_storage(code_hash, &self.ctx.config.schedule)?; + let result = self.ctx.instantiate(endowment, gas_meter, executable, input_data, salt)?; + Ok(result) } fn transfer( @@ -582,6 +627,7 @@ where )?; if let Some(ContractInfo::Alive(info)) = ContractInfoOf::::take(&self_id) { Storage::::queue_trie_for_deletion(&info)?; + E::remove_user(info.code_hash); Contracts::::deposit_event(RawEvent::Terminated(self_id, beneficiary.clone())); Ok(()) } else { @@ -616,7 +662,7 @@ where } } - let result = Rent::::restore_to( + let result = Rent::::restore_to( self.ctx.self_account.clone(), dest.clone(), code_hash.clone(), @@ -701,6 +747,10 @@ where fn get_weight_price(&self, weight: Weight) -> BalanceOf { T::WeightPrice::convert(weight) } + + fn schedule(&self) -> &Schedule { + &self.ctx.config.schedule + } } fn deposit_event( @@ -720,25 +770,29 @@ fn deposit_event( /// wasm VM code. #[cfg(test)] mod tests { - use super::{ - BalanceOf, Event, ExecResult, ExecutionContext, Ext, Loader, - RawEvent, Vm, ReturnFlags, ExecError, ErrorOrigin, AccountIdOf, - }; + use super::*; use crate::{ gas::GasMeter, tests::{ExtBuilder, Test, MetaEvent}, - exec::ExecReturnValue, CodeHash, ConfigCache, gas::Gas, storage::Storage, - tests::{ALICE, BOB, CHARLIE}, + tests::{ + ALICE, BOB, CHARLIE, + test_utils::{place_contract, set_balance, get_balance}, + }, Error, }; - use crate::tests::test_utils::{place_contract, set_balance, get_balance}; use sp_runtime::DispatchError; use assert_matches::assert_matches; - use std::{cell::RefCell, collections::HashMap, marker::PhantomData, rc::Rc}; + use std::{cell::RefCell, collections::HashMap, rc::Rc}; + + type MockContext<'a> = ExecutionContext<'a, Test, MockExecutable>; const GAS_LIMIT: Gas = 10_000_000_000; + thread_local! { + static LOADER: RefCell = RefCell::new(MockLoader::default()); + } + fn events() -> Vec> { >::events() .into_iter() @@ -756,80 +810,74 @@ mod tests { } #[derive(Clone)] - struct MockExecutable<'a>(Rc ExecResult + 'a>); + struct MockExecutable(Rc ExecResult + 'static>, CodeHash); - impl<'a> MockExecutable<'a> { - fn new(f: impl Fn(MockCtx) -> ExecResult + 'a) -> Self { - MockExecutable(Rc::new(f)) - } - } - - struct MockLoader<'a> { - map: HashMap, MockExecutable<'a>>, + #[derive(Default)] + struct MockLoader { + map: HashMap, MockExecutable>, counter: u64, } - impl<'a> MockLoader<'a> { - fn empty() -> Self { - MockLoader { - map: HashMap::new(), - counter: 0, - } - } - - fn insert(&mut self, f: impl Fn(MockCtx) -> ExecResult + 'a) -> CodeHash { - // Generate code hashes as monotonically increasing values. - let code_hash = ::Hash::from_low_u64_be(self.counter); - - self.counter += 1; - self.map.insert(code_hash, MockExecutable::new(f)); - code_hash + impl MockLoader { + fn insert(f: impl Fn(MockCtx) -> ExecResult + 'static) -> CodeHash { + LOADER.with(|loader| { + let mut loader = loader.borrow_mut(); + // Generate code hashes as monotonically increasing values. + let hash = ::Hash::from_low_u64_be(loader.counter); + loader.counter += 1; + loader.map.insert(hash, MockExecutable (Rc::new(f), hash.clone())); + hash + }) } } - struct MockVm<'a> { - _marker: PhantomData<&'a ()>, - } + impl Executable for MockExecutable { + fn from_storage( + code_hash: CodeHash, + _schedule: &Schedule + ) -> Result { + Self::from_storage_noinstr(code_hash) + } - impl<'a> MockVm<'a> { - fn new() -> Self { - MockVm { _marker: PhantomData } + fn from_storage_noinstr(code_hash: CodeHash) -> Result { + LOADER.with(|loader| { + loader.borrow_mut() + .map + .get(&code_hash) + .cloned() + .ok_or(Error::::CodeNotFound.into()) + }) } - } - impl<'a> Loader for MockLoader<'a> { - type Executable = MockExecutable<'a>; + fn drop_from_storage(self) {} - fn load_init(&self, code_hash: &CodeHash) -> Result { - self.map - .get(code_hash) - .cloned() - .ok_or_else(|| "code not found") - } - fn load_main(&self, code_hash: &CodeHash) -> Result { - self.map - .get(code_hash) - .cloned() - .ok_or_else(|| "code not found") + fn add_user(_code_hash: CodeHash) -> DispatchResult { + Ok(()) } - } - impl<'a> Vm for MockVm<'a> { - type Executable = MockExecutable<'a>; + fn remove_user(_code_hash: CodeHash) {} fn execute>( - &self, - exec: &MockExecutable, + self, mut ext: E, + _function: &ExportedFunction, input_data: Vec, gas_meter: &mut GasMeter, ) -> ExecResult { - (exec.0)(MockCtx { + (self.0)(MockCtx { ext: &mut ext, input_data, gas_meter, }) } + + fn code_hash(&self) -> &CodeHash { + &self.1 + } + + fn occupied_storage(&self) -> u32 { + 0 + } } fn exec_success() -> ExecResult { @@ -838,32 +886,29 @@ mod tests { #[test] fn it_works() { + thread_local! { + static TEST_DATA: RefCell> = RefCell::new(vec![0]); + } + let value = Default::default(); let mut gas_meter = GasMeter::::new(GAS_LIMIT); - let data = vec![]; - - let vm = MockVm::new(); - - let test_data = Rc::new(RefCell::new(vec![0usize])); - - let mut loader = MockLoader::empty(); - let exec_ch = loader.insert(|_ctx| { - test_data.borrow_mut().push(1); + let exec_ch = MockLoader::insert(|_ctx| { + TEST_DATA.with(|data| data.borrow_mut().push(1)); exec_success() }); ExtBuilder::default().build().execute_with(|| { let cfg = ConfigCache::preload(); - let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); + let mut ctx = MockContext::top_level(ALICE, &cfg); place_contract(&BOB, exec_ch); assert_matches!( - ctx.call(BOB, value, &mut gas_meter, data), + ctx.call(BOB, value, &mut gas_meter, vec![]), Ok(_) ); }); - assert_eq!(&*test_data.borrow(), &vec![0, 1]); + TEST_DATA.with(|data| assert_eq!(*data.borrow(), vec![0, 1])); } #[test] @@ -873,12 +918,9 @@ mod tests { let origin = ALICE; let dest = BOB; - let vm = MockVm::new(); - let loader = MockLoader::empty(); - ExtBuilder::default().build().execute_with(|| { let cfg = ConfigCache::preload(); - let mut ctx = ExecutionContext::top_level(origin.clone(), &cfg, &vm, &loader); + let mut ctx = MockContext::top_level(origin.clone(), &cfg); set_balance(&origin, 100); set_balance(&dest, 0); @@ -903,15 +945,13 @@ mod tests { let origin = ALICE; let dest = BOB; - let vm = MockVm::new(); - let mut loader = MockLoader::empty(); - let return_ch = loader.insert( + let return_ch = MockLoader::insert( |_| Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: Vec::new() }) ); ExtBuilder::default().build().execute_with(|| { let cfg = ConfigCache::preload(); - let mut ctx = ExecutionContext::top_level(origin.clone(), &cfg, &vm, &loader); + let mut ctx = MockContext::top_level(origin.clone(), &cfg); place_contract(&BOB, return_ch); set_balance(&origin, 100); let balance = get_balance(&dest); @@ -938,12 +978,9 @@ mod tests { let origin = ALICE; let dest = BOB; - let vm = MockVm::new(); - let loader = MockLoader::empty(); - ExtBuilder::default().build().execute_with(|| { let cfg = ConfigCache::preload(); - let mut ctx = ExecutionContext::top_level(origin.clone(), &cfg, &vm, &loader); + let mut ctx = MockContext::top_level(origin.clone(), &cfg); set_balance(&origin, 0); let result = super::transfer( @@ -970,16 +1007,13 @@ mod tests { // is returned from the execution context. let origin = ALICE; let dest = BOB; - - let vm = MockVm::new(); - let mut loader = MockLoader::empty(); - let return_ch = loader.insert( + let return_ch = MockLoader::insert( |_| Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: vec![1, 2, 3, 4] }) ); ExtBuilder::default().build().execute_with(|| { let cfg = ConfigCache::preload(); - let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); + let mut ctx = MockContext::top_level(origin, &cfg); place_contract(&BOB, return_ch); let result = ctx.call( @@ -1001,16 +1035,13 @@ mod tests { // is returned from the execution context. let origin = ALICE; let dest = BOB; - - let vm = MockVm::new(); - let mut loader = MockLoader::empty(); - let return_ch = loader.insert( + let return_ch = MockLoader::insert( |_| Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: vec![1, 2, 3, 4] }) ); ExtBuilder::default().build().execute_with(|| { let cfg = ConfigCache::preload(); - let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); + let mut ctx = MockContext::top_level(origin, &cfg); place_contract(&BOB, return_ch); let result = ctx.call( @@ -1028,9 +1059,7 @@ mod tests { #[test] fn input_data_to_call() { - let vm = MockVm::new(); - let mut loader = MockLoader::empty(); - let input_data_ch = loader.insert(|ctx| { + let input_data_ch = MockLoader::insert(|ctx| { assert_eq!(ctx.input_data, &[1, 2, 3, 4]); exec_success() }); @@ -1038,7 +1067,7 @@ mod tests { // This one tests passing the input data into a contract via call. ExtBuilder::default().build().execute_with(|| { let cfg = ConfigCache::preload(); - let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); + let mut ctx = MockContext::top_level(ALICE, &cfg); place_contract(&BOB, input_data_ch); let result = ctx.call( @@ -1053,9 +1082,7 @@ mod tests { #[test] fn input_data_to_instantiate() { - let vm = MockVm::new(); - let mut loader = MockLoader::empty(); - let input_data_ch = loader.insert(|ctx| { + let input_data_ch = MockLoader::insert(|ctx| { assert_eq!(ctx.input_data, &[1, 2, 3, 4]); exec_success() }); @@ -1063,14 +1090,14 @@ mod tests { // This one tests passing the input data into a contract via instantiate. ExtBuilder::default().build().execute_with(|| { let cfg = ConfigCache::preload(); - let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); + let mut ctx = MockContext::top_level(ALICE, &cfg); set_balance(&ALICE, cfg.subsistence_threshold() * 10); let result = ctx.instantiate( cfg.subsistence_threshold() * 3, &mut GasMeter::::new(GAS_LIMIT), - &input_data_ch, + MockExecutable::from_storage(input_data_ch, &cfg.schedule).unwrap(), vec![1, 2, 3, 4], &[], ); @@ -1082,35 +1109,36 @@ mod tests { fn max_depth() { // This test verifies that when we reach the maximal depth creation of an // yet another context fails. + thread_local! { + static REACHED_BOTTOM: RefCell = RefCell::new(false); + } let value = Default::default(); - let reached_bottom = RefCell::new(false); - - let vm = MockVm::new(); - let mut loader = MockLoader::empty(); - let recurse_ch = loader.insert(|ctx| { + let recurse_ch = MockLoader::insert(|ctx| { // Try to call into yourself. let r = ctx.ext.call(&BOB, 0, ctx.gas_meter, vec![]); - let mut reached_bottom = reached_bottom.borrow_mut(); - if !*reached_bottom { - // We are first time here, it means we just reached bottom. - // Verify that we've got proper error and set `reached_bottom`. - assert_eq!( - r, - Err(Error::::MaxCallDepthReached.into()) - ); - *reached_bottom = true; - } else { - // We just unwinding stack here. - assert_matches!(r, Ok(_)); - } + REACHED_BOTTOM.with(|reached_bottom| { + let mut reached_bottom = reached_bottom.borrow_mut(); + if !*reached_bottom { + // We are first time here, it means we just reached bottom. + // Verify that we've got proper error and set `reached_bottom`. + assert_eq!( + r, + Err(Error::::MaxCallDepthReached.into()) + ); + *reached_bottom = true; + } else { + // We just unwinding stack here. + assert_matches!(r, Ok(_)); + } + }); exec_success() }); ExtBuilder::default().build().execute_with(|| { let cfg = ConfigCache::preload(); - let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); + let mut ctx = MockContext::top_level(ALICE, &cfg); set_balance(&BOB, 1); place_contract(&BOB, recurse_ch); @@ -1130,15 +1158,16 @@ mod tests { let origin = ALICE; let dest = BOB; - let vm = MockVm::new(); - - let witnessed_caller_bob = RefCell::new(None::>); - let witnessed_caller_charlie = RefCell::new(None::>); + thread_local! { + static WITNESSED_CALLER_BOB: RefCell>> = RefCell::new(None); + static WITNESSED_CALLER_CHARLIE: RefCell>> = RefCell::new(None); + } - let mut loader = MockLoader::empty(); - let bob_ch = loader.insert(|ctx| { + let bob_ch = MockLoader::insert(|ctx| { // Record the caller for bob. - *witnessed_caller_bob.borrow_mut() = Some(ctx.ext.caller().clone()); + WITNESSED_CALLER_BOB.with(|caller| + *caller.borrow_mut() = Some(ctx.ext.caller().clone()) + ); // Call into CHARLIE contract. assert_matches!( @@ -1147,16 +1176,18 @@ mod tests { ); exec_success() }); - let charlie_ch = loader.insert(|ctx| { + let charlie_ch = MockLoader::insert(|ctx| { // Record the caller for charlie. - *witnessed_caller_charlie.borrow_mut() = Some(ctx.ext.caller().clone()); + WITNESSED_CALLER_CHARLIE.with(|caller| + *caller.borrow_mut() = Some(ctx.ext.caller().clone()) + ); exec_success() }); ExtBuilder::default().build().execute_with(|| { let cfg = ConfigCache::preload(); - let mut ctx = ExecutionContext::top_level(origin.clone(), &cfg, &vm, &loader); + let mut ctx = MockContext::top_level(origin.clone(), &cfg); place_contract(&dest, bob_ch); place_contract(&CHARLIE, charlie_ch); @@ -1170,16 +1201,13 @@ mod tests { assert_matches!(result, Ok(_)); }); - assert_eq!(&*witnessed_caller_bob.borrow(), &Some(origin)); - assert_eq!(&*witnessed_caller_charlie.borrow(), &Some(dest)); + WITNESSED_CALLER_BOB.with(|caller| assert_eq!(*caller.borrow(), Some(origin))); + WITNESSED_CALLER_CHARLIE.with(|caller| assert_eq!(*caller.borrow(), Some(dest))); } #[test] fn address_returns_proper_values() { - let vm = MockVm::new(); - - let mut loader = MockLoader::empty(); - let bob_ch = loader.insert(|ctx| { + let bob_ch = MockLoader::insert(|ctx| { // Verify that address matches BOB. assert_eq!(*ctx.ext.address(), BOB); @@ -1190,14 +1218,14 @@ mod tests { ); exec_success() }); - let charlie_ch = loader.insert(|ctx| { + let charlie_ch = MockLoader::insert(|ctx| { assert_eq!(*ctx.ext.address(), CHARLIE); exec_success() }); ExtBuilder::default().build().execute_with(|| { let cfg = ConfigCache::preload(); - let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); + let mut ctx = MockContext::top_level(ALICE, &cfg); place_contract(&BOB, bob_ch); place_contract(&CHARLIE, charlie_ch); @@ -1214,20 +1242,17 @@ mod tests { #[test] fn refuse_instantiate_with_value_below_existential_deposit() { - let vm = MockVm::new(); - - let mut loader = MockLoader::empty(); - let dummy_ch = loader.insert(|_| exec_success()); + let dummy_ch = MockLoader::insert(|_| exec_success()); ExtBuilder::default().existential_deposit(15).build().execute_with(|| { let cfg = ConfigCache::preload(); - let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); + let mut ctx = MockContext::top_level(ALICE, &cfg); assert_matches!( ctx.instantiate( 0, // <- zero endowment &mut GasMeter::::new(GAS_LIMIT), - &dummy_ch, + MockExecutable::from_storage(dummy_ch, &cfg.schedule).unwrap(), vec![], &[], ), @@ -1238,23 +1263,20 @@ mod tests { #[test] fn instantiation_work_with_success_output() { - let vm = MockVm::new(); - - let mut loader = MockLoader::empty(); - let dummy_ch = loader.insert( + let dummy_ch = MockLoader::insert( |_| Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: vec![80, 65, 83, 83] }) ); ExtBuilder::default().existential_deposit(15).build().execute_with(|| { let cfg = ConfigCache::preload(); - let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); + let mut ctx = MockContext::top_level(ALICE, &cfg); set_balance(&ALICE, 1000); let instantiated_contract_address = assert_matches!( ctx.instantiate( 100, &mut GasMeter::::new(GAS_LIMIT), - &dummy_ch, + MockExecutable::from_storage(dummy_ch, &cfg.schedule).unwrap(), vec![], &[], ), @@ -1272,23 +1294,20 @@ mod tests { #[test] fn instantiation_fails_with_failing_output() { - let vm = MockVm::new(); - - let mut loader = MockLoader::empty(); - let dummy_ch = loader.insert( + let dummy_ch = MockLoader::insert( |_| Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: vec![70, 65, 73, 76] }) ); ExtBuilder::default().existential_deposit(15).build().execute_with(|| { let cfg = ConfigCache::preload(); - let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); + let mut ctx = MockContext::top_level(ALICE, &cfg); set_balance(&ALICE, 1000); let instantiated_contract_address = assert_matches!( ctx.instantiate( 100, &mut GasMeter::::new(GAS_LIMIT), - &dummy_ch, + MockExecutable::from_storage(dummy_ch, &cfg.schedule).unwrap(), vec![], &[], ), @@ -1303,18 +1322,15 @@ mod tests { #[test] fn instantiation_from_contract() { - let vm = MockVm::new(); - - let mut loader = MockLoader::empty(); - let dummy_ch = loader.insert(|_| exec_success()); + let dummy_ch = MockLoader::insert(|_| exec_success()); let instantiated_contract_address = Rc::new(RefCell::new(None::>)); - let instantiator_ch = loader.insert({ + let instantiator_ch = MockLoader::insert({ let dummy_ch = dummy_ch.clone(); let instantiated_contract_address = Rc::clone(&instantiated_contract_address); move |ctx| { // Instantiate a contract and save it's address in `instantiated_contract_address`. let (address, output) = ctx.ext.instantiate( - &dummy_ch, + dummy_ch, ConfigCache::::subsistence_threshold_uncached() * 3, ctx.gas_meter, vec![], @@ -1328,7 +1344,7 @@ mod tests { ExtBuilder::default().existential_deposit(15).build().execute_with(|| { let cfg = ConfigCache::preload(); - let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); + let mut ctx = MockContext::top_level(ALICE, &cfg); set_balance(&ALICE, cfg.subsistence_threshold() * 100); place_contract(&BOB, instantiator_ch); @@ -1350,19 +1366,16 @@ mod tests { #[test] fn instantiation_traps() { - let vm = MockVm::new(); - - let mut loader = MockLoader::empty(); - let dummy_ch = loader.insert( + let dummy_ch = MockLoader::insert( |_| Err("It's a trap!".into()) ); - let instantiator_ch = loader.insert({ + let instantiator_ch = MockLoader::insert({ let dummy_ch = dummy_ch.clone(); move |ctx| { // Instantiate a contract and save it's address in `instantiated_contract_address`. assert_matches!( ctx.ext.instantiate( - &dummy_ch, + dummy_ch, 15u64, ctx.gas_meter, vec![], @@ -1380,7 +1393,7 @@ mod tests { ExtBuilder::default().existential_deposit(15).build().execute_with(|| { let cfg = ConfigCache::preload(); - let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); + let mut ctx = MockContext::top_level(ALICE, &cfg); set_balance(&ALICE, 1000); set_balance(&BOB, 100); place_contract(&BOB, instantiator_ch); @@ -1398,11 +1411,7 @@ mod tests { #[test] fn termination_from_instantiate_fails() { - let vm = MockVm::new(); - - let mut loader = MockLoader::empty(); - - let terminate_ch = loader.insert(|ctx| { + let terminate_ch = MockLoader::insert(|ctx| { ctx.ext.terminate(&ALICE).unwrap(); exec_success() }); @@ -1412,18 +1421,18 @@ mod tests { .build() .execute_with(|| { let cfg = ConfigCache::preload(); - let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); + let mut ctx = MockContext::top_level(ALICE, &cfg); set_balance(&ALICE, 1000); assert_eq!( ctx.instantiate( 100, &mut GasMeter::::new(GAS_LIMIT), - &terminate_ch, + MockExecutable::from_storage(terminate_ch, &cfg.schedule).unwrap(), vec![], &[], ), - Err(Error::::NewContractNotFunded.into()) + Err(Error::::NotCallable.into()) ); assert_eq!( @@ -1435,9 +1444,7 @@ mod tests { #[test] fn rent_allowance() { - let vm = MockVm::new(); - let mut loader = MockLoader::empty(); - let rent_allowance_ch = loader.insert(|ctx| { + let rent_allowance_ch = MockLoader::insert(|ctx| { let allowance = ConfigCache::::subsistence_threshold_uncached() * 3; assert_eq!(ctx.ext.rent_allowance(), >::max_value()); ctx.ext.set_rent_allowance(allowance); @@ -1447,13 +1454,13 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let cfg = ConfigCache::preload(); - let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); + let mut ctx = MockContext::top_level(ALICE, &cfg); set_balance(&ALICE, cfg.subsistence_threshold() * 10); let result = ctx.instantiate( cfg.subsistence_threshold() * 5, &mut GasMeter::::new(GAS_LIMIT), - &rent_allowance_ch, + MockExecutable::from_storage(rent_allowance_ch, &cfg.schedule).unwrap(), vec![], &[], ); diff --git a/frame/contracts/src/gas.rs b/frame/contracts/src/gas.rs index 9bb6185e558ac..4bdfcdd577119 100644 --- a/frame/contracts/src/gas.rs +++ b/frame/contracts/src/gas.rs @@ -18,8 +18,9 @@ use crate::Config; use sp_std::marker::PhantomData; use sp_runtime::traits::Zero; -use frame_support::dispatch::{ - DispatchResultWithPostInfo, PostDispatchInfo, DispatchErrorWithPostInfo, +use frame_support::{ + dispatch::{DispatchResultWithPostInfo, PostDispatchInfo, DispatchErrorWithPostInfo}, + weights::Weight, }; use pallet_contracts_primitives::ExecError; @@ -27,7 +28,7 @@ use pallet_contracts_primitives::ExecError; use std::{any::Any, fmt::Debug}; // Gas is essentially the same as weight. It is a 1 to 1 correspondence. -pub type Gas = frame_support::weights::Weight; +pub type Gas = Weight; #[must_use] #[derive(Debug, PartialEq, Eq)] @@ -201,12 +202,15 @@ impl GasMeter { } /// Turn this GasMeter into a DispatchResult that contains the actually used gas. - pub fn into_dispatch_result(self, result: Result) -> DispatchResultWithPostInfo + pub fn into_dispatch_result( + self, result: Result, + base_weight: Weight, + ) -> DispatchResultWithPostInfo where E: Into, { let post_info = PostDispatchInfo { - actual_weight: Some(self.gas_spent()), + actual_weight: Some(self.gas_spent().saturating_add(base_weight)), pays_fee: Default::default(), }; diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 96ba7b32e2593..2dff15a184a64 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -59,10 +59,11 @@ //! //! ### Dispatchable functions //! -//! * `put_code` - Stores the given binary Wasm code into the chain's storage and returns its `code_hash`. -//! * `instantiate` - Deploys a new contract from the given `code_hash`, optionally transferring some balance. -//! This instantiates a new smart contract account and calls its contract deploy handler to -//! initialize the contract. +//! * `instantiate_with_code` - Deploys a new contract from the supplied wasm binary, optionally transferring +//! some balance. This instantiates a new smart contract account and calls its contract deploy +//! handler to initialize the contract. +//! * `instantiate` - The same as `instantiate_with_code` but instead of uploading new code an +//! existing `code_hash` is supplied. //! * `call` - Makes a call to an account, optionally transferring some balance. //! //! ## Usage @@ -98,13 +99,12 @@ mod tests; pub use crate::{ gas::{Gas, GasMeter}, - wasm::ReturnCode as RuntimeReturnCode, + wasm::{ReturnCode as RuntimeReturnCode, PrefabWasmModule}, weights::WeightInfo, schedule::{Schedule, HostFnWeights, InstructionWeights, Limits}, }; use crate::{ - exec::ExecutionContext, - wasm::{WasmLoader, WasmVm}, + exec::{ExecutionContext, Executable}, rent::Rent, storage::Storage, }; @@ -387,7 +387,8 @@ decl_error! { /// The contract that was called is either no contract at all (a plain account) /// or is a tombstone. NotCallable, - /// The code supplied to `put_code` exceeds the limit specified in the current schedule. + /// The code supplied to `instantiate_with_code` exceeds the limit specified in the + /// current schedule. CodeTooLarge, /// No code could be found at the supplied code hash. CodeNotFound, @@ -431,6 +432,8 @@ decl_error! { /// This can either happen when the accumulated storage in bytes is too large or /// when number of storage items is too large. StorageExhausted, + /// A contract with the same AccountId already exists. + DuplicateContract, } } @@ -528,23 +531,6 @@ decl_module! { Ok(()) } - /// Stores the given binary Wasm code into the chain's storage and returns its `codehash`. - /// You can instantiate contracts only with stored code. - #[weight = T::WeightInfo::put_code(code.len() as u32 / 1024)] - pub fn put_code( - origin, - code: Vec - ) -> DispatchResult { - ensure_signed(origin)?; - let schedule = >::current_schedule(); - ensure!(code.len() as u32 <= schedule.limits.code_size, Error::::CodeTooLarge); - let result = wasm::save_code::(code, &schedule); - if let Ok(code_hash) = result { - Self::deposit_event(RawEvent::CodeStored(code_hash)); - } - result.map(|_| ()).map_err(Into::into) - } - /// Makes a call to an account, optionally transferring some balance. /// /// * If the account is a smart-contract account, the associated code will be @@ -563,31 +549,73 @@ decl_module! { let origin = ensure_signed(origin)?; let dest = T::Lookup::lookup(dest)?; let mut gas_meter = GasMeter::new(gas_limit); - let result = Self::execute_wasm(origin, &mut gas_meter, |ctx, gas_meter| { ctx.call(dest, value, gas_meter, data) }); - gas_meter.into_dispatch_result(result) + gas_meter.into_dispatch_result(result, T::WeightInfo::call()) } - /// Instantiates a new contract from the `code_hash` generated by `put_code`, - /// optionally transferring some balance. + /// Instantiates a new contract from the supplied `code` optionally transferring + /// some balance. + /// + /// This is the only function that can deploy new code to the chain. /// - /// The supplied `salt` is used for contract address deriviation. See `fn contract_address`. + /// # Parameters + /// + /// * `endowment`: The balance to transfer from the `origin` to the newly created contract. + /// * `gas_limit`: The gas limit enforced when executing the constructor. + /// * `code`: The contract code to deploy in raw bytes. + /// * `data`: The input data to pass to the contract constructor. + /// * `salt`: Used for the address derivation. See [`Self::contract_address`]. /// /// Instantiation is executed as follows: /// + /// - The supplied `code` is instrumented, deployed, and a `code_hash` is created for that code. + /// - If the `code_hash` already exists on the chain the underlying `code` will be shared. /// - The destination address is computed based on the sender, code_hash and the salt. /// - The smart-contract account is created at the computed address. - /// - The `ctor_code` is executed in the context of the newly-created account. Buffer returned - /// after the execution is saved as the `code` of the account. That code will be invoked - /// upon any call received by this account. - /// - The contract is initialized. + /// - The `endowment` is transferred to the new account. + /// - The `deploy` function is executed in the context of the newly-created account. #[weight = - T::WeightInfo::instantiate( - data.len() as u32 / 1024, + T::WeightInfo::instantiate_with_code( + code.len() as u32 / 1024, salt.len() as u32 / 1024, - ).saturating_add(*gas_limit) + ) + .saturating_add(*gas_limit) + ] + pub fn instantiate_with_code( + origin, + #[compact] endowment: BalanceOf, + #[compact] gas_limit: Gas, + code: Vec, + data: Vec, + salt: Vec, + ) -> DispatchResultWithPostInfo { + let origin = ensure_signed(origin)?; + let schedule = >::current_schedule(); + let code_len = code.len() as u32; + ensure!(code_len <= schedule.limits.code_size, Error::::CodeTooLarge); + let mut gas_meter = GasMeter::new(gas_limit); + let result = Self::execute_wasm(origin, &mut gas_meter, |ctx, gas_meter| { + let executable = PrefabWasmModule::from_code(code, &schedule)?; + let result = ctx.instantiate(endowment, gas_meter, executable, data, &salt) + .map(|(_address, output)| output)?; + Ok(result) + }); + gas_meter.into_dispatch_result( + result, + T::WeightInfo::instantiate_with_code(code_len / 1024, salt.len() as u32 / 1024) + ) + } + + /// Instantiates a contract from a previously deployed wasm binary. + /// + /// This function is identical to [`Self::instantiate_with_code`] but without the + /// code deployment step. Instead, the `code_hash` of an on-chain deployed wasm binary + /// must be supplied. + #[weight = + T::WeightInfo::instantiate(salt.len() as u32 / 1024) + .saturating_add(*gas_limit) ] pub fn instantiate( origin, @@ -599,12 +627,16 @@ decl_module! { ) -> DispatchResultWithPostInfo { let origin = ensure_signed(origin)?; let mut gas_meter = GasMeter::new(gas_limit); - let result = Self::execute_wasm(origin, &mut gas_meter, |ctx, gas_meter| { - ctx.instantiate(endowment, gas_meter, &code_hash, data, &salt) - .map(|(_address, output)| output) + let executable = PrefabWasmModule::from_storage(code_hash, &ctx.config.schedule)?; + let result = ctx.instantiate(endowment, gas_meter, executable, data, &salt) + .map(|(_address, output)| output)?; + Ok(result) }); - gas_meter.into_dispatch_result(result) + gas_meter.into_dispatch_result( + result, + T::WeightInfo::instantiate(salt.len() as u32 / 1024) + ) } /// Allows block producers to claim a small reward for evicting a contract. If a block @@ -643,7 +675,9 @@ decl_module! { }; // If poking the contract has lead to eviction of the contract, give out the rewards. - if let Some(rent_payed) = Rent::::try_eviction(&dest, handicap)? { + if let Some(rent_payed) = + Rent::>::try_eviction(&dest, handicap)? + { T::Currency::deposit_into_existing( &rewarded, T::SurchargeReward::get().min(rent_payed), @@ -698,15 +732,15 @@ where } pub fn rent_projection(address: T::AccountId) -> RentProjectionResult { - Rent::::compute_projection(&address) + Rent::>::compute_projection(&address) } - /// Put code for benchmarks which does not check or instrument the code. + /// Store code for benchmarks which does not check nor instrument the code. #[cfg(feature = "runtime-benchmarks")] - pub fn put_code_raw(code: Vec) -> DispatchResult { + pub fn store_code_raw(code: Vec) -> DispatchResult { let schedule = >::current_schedule(); - let result = wasm::save_code_raw::(code, &schedule); - result.map(|_| ()).map_err(Into::into) + PrefabWasmModule::store_code_unchecked(code, &schedule)?; + Ok(()) } /// Determine the address of a contract, @@ -739,12 +773,13 @@ where fn execute_wasm( origin: T::AccountId, gas_meter: &mut GasMeter, - func: impl FnOnce(&mut ExecutionContext, WasmLoader>, &mut GasMeter) -> ExecResult, + func: impl FnOnce( + &mut ExecutionContext>, + &mut GasMeter, + ) -> ExecResult, ) -> ExecResult { let cfg = ConfigCache::preload(); - let vm = WasmVm::new(&cfg.schedule); - let loader = WasmLoader::new(&cfg.schedule); - let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); + let mut ctx = ExecutionContext::top_level(origin, &cfg); func(&mut ctx, gas_meter) } } @@ -807,6 +842,12 @@ decl_event! { /// - `data`: Data supplied by the contract. Metadata generated during contract /// compilation is needed to decode it. ContractEmitted(AccountId, Vec), + + /// A code with the specified hash was removed. + /// \[code_hash\] + /// + /// This happens when the last contract that uses this code hash was removed or evicted. + CodeRemoved(Hash), } } @@ -820,7 +861,7 @@ decl_storage! { /// A mapping from an original code hash to the original code, untouched by instrumentation. pub PristineCode: map hasher(identity) CodeHash => Option>; /// A mapping between an original code hash and instrumented wasm code, ready for execution. - pub CodeStorage: map hasher(identity) CodeHash => Option; + pub CodeStorage: map hasher(identity) CodeHash => Option>; /// The subtrie counter. pub AccountCounter: u64 = 0; /// The code associated with a given account. diff --git a/frame/contracts/src/rent.rs b/frame/contracts/src/rent.rs index 2075f6f757de2..145e6639c6086 100644 --- a/frame/contracts/src/rent.rs +++ b/frame/contracts/src/rent.rs @@ -20,7 +20,7 @@ use crate::{ AliveContractInfo, BalanceOf, ContractInfo, ContractInfoOf, Module, RawEvent, TombstoneContractInfo, Config, CodeHash, ConfigCache, Error, - storage::Storage, + storage::Storage, wasm::PrefabWasmModule, exec::Executable, }; use sp_std::prelude::*; use sp_io::hashing::blake2_256; @@ -86,12 +86,13 @@ enum Verdict { Charge { amount: OutstandingAmount }, } -pub struct Rent(sp_std::marker::PhantomData); +pub struct Rent(sp_std::marker::PhantomData<(T, E)>); -impl Rent +impl Rent where T: Config, - T::AccountId: UncheckedFrom + AsRef<[u8]> + T::AccountId: UncheckedFrom + AsRef<[u8]>, + E: Executable, { /// Returns a fee charged per block from the contract. /// @@ -99,10 +100,11 @@ where /// then the fee can drop to zero. fn compute_fee_per_block( free_balance: &BalanceOf, - contract: &AliveContractInfo + contract: &AliveContractInfo, + code_size_share: u32, ) -> BalanceOf { let uncovered_by_balance = T::DepositPerStorageByte::get() - .saturating_mul(contract.storage_size.into()) + .saturating_mul(contract.storage_size.saturating_add(code_size_share).into()) .saturating_add( T::DepositPerStorageItem::get() .saturating_mul(contract.pair_count.into()) @@ -148,6 +150,7 @@ where current_block_number: T::BlockNumber, handicap: T::BlockNumber, contract: &AliveContractInfo, + code_size: u32, ) -> Verdict { // How much block has passed since the last deduction for the contract. let blocks_passed = { @@ -164,7 +167,7 @@ where let free_balance = T::Currency::free_balance(account); // An amount of funds to charge per block for storage taken up by the contract. - let fee_per_block = Self::compute_fee_per_block(&free_balance, contract); + let fee_per_block = Self::compute_fee_per_block(&free_balance, contract, code_size); if fee_per_block.is_zero() { // The rent deposit offset reduced the fee to 0. This means that the contract // gets the rent for free. @@ -228,19 +231,22 @@ where /// Enacts the given verdict and returns the updated `ContractInfo`. /// /// `alive_contract_info` should be from the same address as `account`. + /// + /// # Note + /// + /// if `evictable_code` is `None` an `Evict` verdict will not be enacted. This is for + /// when calling this function during a `call` where access to the soon to be evicted + /// contract should be denied but storage should be left unmodified. fn enact_verdict( account: &T::AccountId, alive_contract_info: AliveContractInfo, current_block_number: T::BlockNumber, verdict: Verdict, - allow_eviction: bool, - ) -> Result>, DispatchError> { - match verdict { - Verdict::Exempt => return Ok(Some(ContractInfo::Alive(alive_contract_info))), - Verdict::Evict { amount: _ } if !allow_eviction => { - Ok(None) - } - Verdict::Evict { amount } => { + evictable_code: Option>, + ) -> Result>, DispatchError> { + match (verdict, evictable_code) { + (Verdict::Exempt, _) => return Ok(Some(alive_contract_info)), + (Verdict::Evict { amount }, Some(code)) => { // We need to remove the trie first because it is the only operation // that can fail and this function is called without a storage // transaction when called through `claim_surcharge`. @@ -261,19 +267,23 @@ where ); let tombstone_info = ContractInfo::Tombstone(tombstone); >::insert(account, &tombstone_info); + code.drop_from_storage(); >::deposit_event(RawEvent::Evicted(account.clone())); - Ok(Some(tombstone_info)) + Ok(None) + } + (Verdict::Evict { amount: _ }, None) => { + Ok(None) } - Verdict::Charge { amount } => { - let contract_info = ContractInfo::Alive(AliveContractInfo:: { + (Verdict::Charge { amount }, _) => { + let contract = ContractInfo::Alive(AliveContractInfo:: { rent_allowance: alive_contract_info.rent_allowance - amount.peek(), deduct_block: current_block_number, rent_payed: alive_contract_info.rent_payed.saturating_add(amount.peek()), ..alive_contract_info }); - >::insert(account, &contract_info); + >::insert(account, &contract); amount.withdraw(account); - Ok(Some(contract_info)) + Ok(Some(contract.get_alive().expect("We just constructed it as alive. qed"))) } } } @@ -283,21 +293,20 @@ where /// This functions does **not** evict the contract. It returns `None` in case the /// contract is in need of eviction. [`try_eviction`] must /// be called to perform the eviction. - pub fn charge(account: &T::AccountId) -> Result>, DispatchError> { - let contract_info = >::get(account); - let alive_contract_info = match contract_info { - None | Some(ContractInfo::Tombstone(_)) => return Ok(contract_info), - Some(ContractInfo::Alive(contract)) => contract, - }; - + pub fn charge( + account: &T::AccountId, + contract: AliveContractInfo, + code_size: u32, + ) -> Result>, DispatchError> { let current_block_number = >::block_number(); let verdict = Self::consider_case( account, current_block_number, Zero::zero(), - &alive_contract_info, + &contract, + code_size, ); - Self::enact_verdict(account, alive_contract_info, current_block_number, verdict, false) + Self::enact_verdict(account, contract, current_block_number, verdict, None) } /// Process a report that a contract under the given address should be evicted. @@ -322,12 +331,14 @@ where None | Some(ContractInfo::Tombstone(_)) => return Ok(None), Some(ContractInfo::Alive(contract)) => contract, }; + let module = PrefabWasmModule::::from_storage_noinstr(contract.code_hash)?; let current_block_number = >::block_number(); let verdict = Self::consider_case( account, current_block_number, handicap, &contract, + module.occupied_storage(), ); // Enact the verdict only if the contract gets removed. @@ -339,7 +350,9 @@ where .map(|a| a.peek()) .unwrap_or_else(|| >::zero()) .saturating_add(contract.rent_payed); - Self::enact_verdict(account, contract, current_block_number, verdict, true)?; + Self::enact_verdict( + account, contract, current_block_number, verdict, Some(module), + )?; Ok(Some(rent_payed)) } _ => Ok(None), @@ -367,26 +380,33 @@ where None | Some(ContractInfo::Tombstone(_)) => return Err(IsTombstone), Some(ContractInfo::Alive(contract)) => contract, }; + let module = PrefabWasmModule::from_storage_noinstr(alive_contract_info.code_hash) + .map_err(|_| IsTombstone)?; + let code_size = module.occupied_storage(); let current_block_number = >::block_number(); let verdict = Self::consider_case( account, current_block_number, Zero::zero(), &alive_contract_info, + code_size, + ); + let new_contract_info = Self::enact_verdict( + account, alive_contract_info, current_block_number, verdict, Some(module), ); - let new_contract_info = - Self::enact_verdict(account, alive_contract_info, current_block_number, verdict, false); // Check what happened after enaction of the verdict. let alive_contract_info = match new_contract_info.map_err(|_| IsTombstone)? { - None | Some(ContractInfo::Tombstone(_)) => return Err(IsTombstone), - Some(ContractInfo::Alive(contract)) => contract, + None => return Err(IsTombstone), + Some(contract) => contract, }; // Compute how much would the fee per block be with the *updated* balance. let total_balance = T::Currency::total_balance(account); let free_balance = T::Currency::free_balance(account); - let fee_per_block = Self::compute_fee_per_block(&free_balance, &alive_contract_info); + let fee_per_block = Self::compute_fee_per_block( + &free_balance, &alive_contract_info, code_size, + ); if fee_per_block.is_zero() { return Ok(RentProjection::NoEviction); } @@ -418,6 +438,7 @@ where /// Restores the destination account using the origin as prototype. /// /// The restoration will be performed iff: + /// - the supplied code_hash does still exist on-chain /// - origin exists and is alive, /// - the origin's storage is not written in the current block /// - the restored account has tombstone @@ -455,6 +476,9 @@ where origin_contract.last_write }; + // Fails if the code hash does not exist on chain + E::add_user(code_hash)?; + // We are allowed to eagerly modify storage even though the function can // fail later due to tombstones not matching. This is because the restoration // is always called from a contract and therefore in a storage transaction. @@ -483,6 +507,7 @@ where origin_contract.storage_size -= bytes_taken; >::remove(&origin); + E::remove_user(origin_contract.code_hash); >::insert(&dest, ContractInfo::Alive(AliveContractInfo:: { trie_id: origin_contract.trie_id, storage_size: origin_contract.storage_size, diff --git a/frame/contracts/src/schedule.rs b/frame/contracts/src/schedule.rs index 63e3f3c285898..3580fa2aae209 100644 --- a/frame/contracts/src/schedule.rs +++ b/frame/contracts/src/schedule.rs @@ -106,7 +106,7 @@ pub struct Limits { pub subject_len: u32, /// The maximum length of a contract code in bytes. This limit applies to the uninstrumented - /// and pristine form of the code as supplied to `put_code`. + /// and pristine form of the code as supplied to `instantiate_with_code`. pub code_size: u32, } diff --git a/frame/contracts/src/storage.rs b/frame/contracts/src/storage.rs index 5259b2a471265..2a2d5da225d61 100644 --- a/frame/contracts/src/storage.rs +++ b/frame/contracts/src/storage.rs @@ -164,29 +164,28 @@ where account: &AccountIdOf, trie_id: TrieId, ch: CodeHash, - ) -> Result<(), &'static str> { - >::mutate(account, |maybe_contract_info| { - if maybe_contract_info.is_some() { - return Err("Alive contract or tombstone already exists"); + ) -> DispatchResult { + >::try_mutate(account, |existing| { + if existing.is_some() { + return Err(Error::::DuplicateContract.into()); } - *maybe_contract_info = Some( - AliveContractInfo:: { - code_hash: ch, - storage_size: 0, - trie_id, - deduct_block: - // We want to charge rent for the first block in advance. Therefore we - // treat the contract as if it was created in the last block and then - // charge rent for it during instantiation. - >::block_number().saturating_sub(1u32.into()), - rent_allowance: >::max_value(), - rent_payed: >::zero(), - pair_count: 0, - last_write: None, - } - .into(), - ); + let contract = AliveContractInfo:: { + code_hash: ch, + storage_size: 0, + trie_id, + deduct_block: + // We want to charge rent for the first block in advance. Therefore we + // treat the contract as if it was created in the last block and then + // charge rent for it during instantiation. + >::block_number().saturating_sub(1u32.into()), + rent_allowance: >::max_value(), + rent_payed: >::zero(), + pair_count: 0, + last_write: None, + }; + + *existing = Some(contract.into()); Ok(()) }) diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 448df5b0de0f6..d80de6a5116c0 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -23,7 +23,7 @@ use crate::{ Result as ExtensionResult, Environment, ChainExtension, Ext, SysConfig, RetVal, UncheckedFrom, InitState, ReturnFlags, }, - exec::AccountIdOf, + exec::{AccountIdOf, Executable}, wasm::PrefabWasmModule, }; use assert_matches::assert_matches; use codec::Encode; @@ -42,6 +42,7 @@ use frame_support::{ storage::child, }; use frame_system::{self as system, EventRecord, Phase}; +use pretty_assertions::assert_eq; mod contracts { // Re-export contents of the root. This basically @@ -92,7 +93,7 @@ pub mod test_utils { pub fn place_contract(address: &AccountIdOf, code_hash: CodeHash) { let trie_id = Storage::::generate_trie_id(address); set_balance(address, ConfigCache::::subsistence_threshold_uncached() * 10); - Storage::::place_contract(&address, trie_id, code_hash).unwrap() + Storage::::place_contract(&address, trie_id, code_hash).unwrap(); } pub fn set_balance(who: &AccountIdOf, amount: u64) { let imbalance = Balances::deposit_creating(who, amount); @@ -107,6 +108,14 @@ pub mod test_utils { assert_eq!(u32::from_le_bytes($x.data[..].try_into().unwrap()), $y as u32); }} } + macro_rules! assert_refcount { + ( $code_hash:expr , $should:expr $(,)? ) => {{ + let is = crate::CodeStorage::::get($code_hash) + .map(|m| m.refcount()) + .unwrap_or(0); + assert_eq!(is, $should); + }} + } } thread_local! { @@ -352,12 +361,12 @@ where // Perform a call to a plain account. // The actual transfer fails because we can only call contracts. -// Then we check that no gas was used because the base costs for calling are either charged -// as part of the `call` extrinsic or by `seal_call`. +// Then we check that at least the base costs where charged (no runtime gas costs.) #[test] fn calling_plain_account_fails() { ExtBuilder::default().build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 100_000_000); + let base_cost = <::WeightInfo as crate::WeightInfo>::call(); assert_eq!( Contracts::call(Origin::signed(ALICE), BOB, 0, GAS_LIMIT, Vec::new()), @@ -365,7 +374,7 @@ fn calling_plain_account_fails() { DispatchErrorWithPostInfo { error: Error::::NotCallable.into(), post_info: PostDispatchInfo { - actual_weight: Some(0), + actual_weight: Some(base_cost), pays_fee: Default::default(), }, } @@ -460,20 +469,18 @@ fn instantiate_and_call_and_deposit_event() { let _ = Balances::deposit_creating(&ALICE, 1_000_000); let subsistence = ConfigCache::::subsistence_threshold_uncached(); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); - // Check at the end to get hash on error easily - let creation = Contracts::instantiate( + let creation = Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 3, + subsistence * 100, GAS_LIMIT, - code_hash.into(), + wasm, vec![], vec![], ); let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); - pretty_assertions::assert_eq!(System::events(), vec![ + assert_eq!(System::events(), vec![ EventRecord { phase: Phase::Initialization, event: MetaEvent::system(frame_system::Event::NewAccount(ALICE.clone())), @@ -488,26 +495,26 @@ fn instantiate_and_call_and_deposit_event() { }, EventRecord { phase: Phase::Initialization, - event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())), + event: MetaEvent::system(frame_system::Event::NewAccount(addr.clone())), topics: vec![], }, EventRecord { phase: Phase::Initialization, - event: MetaEvent::system(frame_system::Event::NewAccount(addr.clone())), + event: MetaEvent::balances( + pallet_balances::RawEvent::Endowed(addr.clone(), subsistence * 100) + ), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: MetaEvent::balances( - pallet_balances::RawEvent::Endowed(addr.clone(), subsistence * 3) + pallet_balances::RawEvent::Transfer(ALICE, addr.clone(), subsistence * 100) ), topics: vec![], }, EventRecord { phase: Phase::Initialization, - event: MetaEvent::balances( - pallet_balances::RawEvent::Transfer(ALICE, addr.clone(), subsistence * 3) - ), + event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())), topics: vec![], }, EventRecord { @@ -521,7 +528,7 @@ fn instantiate_and_call_and_deposit_event() { phase: Phase::Initialization, event: MetaEvent::contracts(RawEvent::Instantiated(ALICE, addr.clone())), topics: vec![], - } + }, ]); assert_ok!(creation); @@ -539,31 +546,16 @@ fn deposit_event_max_value_limit() { .execute_with(|| { // Create let _ = Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); - assert_ok!(Contracts::instantiate( + assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), 30_000, GAS_LIMIT, - code_hash.into(), + wasm, vec![], vec![], )); let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); - // The instantiation deducted the rent for one block immediately - let first_rent = ::RentFraction::get() - // base_deposit - free_balance - .mul_ceil(80_000 - 30_000) - // blocks to rent - * 1; - - // Check creation - let bob_contract = ContractInfoOf::::get(addr.clone()) - .unwrap() - .get_alive() - .unwrap(); - assert_eq!(bob_contract.rent_allowance, >::max_value() - first_rent); - // Call contract with allowed storage value. assert_ok!(Contracts::call( Origin::signed(ALICE), @@ -590,6 +582,7 @@ fn deposit_event_max_value_limit() { #[test] fn run_out_of_gas() { let (wasm, code_hash) = compile_module::("run_out_of_gas").unwrap(); + let subsistence = ConfigCache::::subsistence_threshold_uncached(); ExtBuilder::default() .existential_deposit(50) @@ -597,13 +590,11 @@ fn run_out_of_gas() { .execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); - - assert_ok!(Contracts::instantiate( + assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - 100, + 100 * subsistence, GAS_LIMIT, - code_hash.into(), + wasm, vec![], vec![], )); @@ -636,43 +627,6 @@ mod call { pub fn null() -> Vec { 3u32.to_le_bytes().to_vec() } } -/// Test correspondence of set_rent code and its hash. -/// Also test that encoded extrinsic in code correspond to the correct transfer -#[test] -fn test_set_rent_code_and_hash() { - let (wasm, code_hash) = compile_module::("set_rent").unwrap(); - - ExtBuilder::default() - .existential_deposit(50) - .build() - .execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); - - // If you ever need to update the wasm source this test will fail - // and will show you the actual hash. - assert_eq!(System::events(), vec![ - EventRecord { - phase: Phase::Initialization, - event: MetaEvent::system(frame_system::Event::NewAccount(ALICE)), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: MetaEvent::balances(pallet_balances::RawEvent::Endowed( - ALICE, 1_000_000 - )), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())), - topics: vec![], - }, - ]); - }); -} - #[test] fn storage_size() { let (wasm, code_hash) = compile_module::("set_rent").unwrap(); @@ -684,13 +638,13 @@ fn storage_size() { .execute_with(|| { // Create let _ = Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); - assert_ok!(Contracts::instantiate( + assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), 30_000, GAS_LIMIT, - code_hash.into(), - ::Balance::from(1_000u32).encode(), // rent allowance + wasm, + // rent_allowance + ::Balance::from(10_000u32).encode(), vec![], )); let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); @@ -757,12 +711,11 @@ fn empty_kv_pairs() { .build() .execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); - assert_ok!(Contracts::instantiate( + assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), 30_000, GAS_LIMIT, - code_hash.into(), + wasm, vec![], vec![], )); @@ -795,6 +748,8 @@ fn initialize_block(number: u64) { #[test] fn deduct_blocks() { let (wasm, code_hash) = compile_module::("set_rent").unwrap(); + let endowment: BalanceOf = 100_000; + let allowance: BalanceOf = 70_000; ExtBuilder::default() .existential_deposit(50) @@ -802,27 +757,33 @@ fn deduct_blocks() { .execute_with(|| { // Create let _ = Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); - assert_ok!(Contracts::instantiate( + assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - 30_000, - GAS_LIMIT, code_hash.into(), - ::Balance::from(1_000u32).encode(), // rent allowance + endowment, + GAS_LIMIT, + wasm, + allowance.encode(), vec![], )); let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); - ContractInfoOf::::get(&addr).unwrap().get_alive().unwrap(); + let contract = ContractInfoOf::::get(&addr).unwrap().get_alive().unwrap(); + let code_len: BalanceOf = + PrefabWasmModule::::from_storage_noinstr(contract.code_hash) + .unwrap() + .occupied_storage() + .into(); // The instantiation deducted the rent for one block immediately let rent0 = ::RentFraction::get() - // base_deposit + deploy_set_storage (4 bytes in 1 item) - free_balance - .mul_ceil(80_000 + 40_000 + 10_000 - 30_000) + // (base_deposit(8) + bytes in storage(4) + size of code) * byte_price + // + 1 storage item (10_000) - free_balance + .mul_ceil((8 + 4 + code_len) * 10_000 + 10_000 - endowment) // blocks to rent * 1; - let bob_contract = ContractInfoOf::::get(&addr).unwrap().get_alive().unwrap(); - assert_eq!(bob_contract.rent_allowance, 1_000 - rent0); - assert_eq!(bob_contract.deduct_block, 1); - assert_eq!(Balances::free_balance(&addr), 30_000 - rent0); + assert!(rent0 > 0); + assert_eq!(contract.rent_allowance, allowance - rent0); + assert_eq!(contract.deduct_block, 1); + assert_eq!(Balances::free_balance(&addr), endowment - rent0); // Advance 4 blocks initialize_block(5); @@ -834,17 +795,15 @@ fn deduct_blocks() { // Check result let rent = ::RentFraction::get() - // base_deposit + deploy_set_storage (4 bytes in 1 item) - free_balance - .mul_ceil(80_000 + 40_000 + 10_000 - (30_000 - rent0)) - // blocks to rent + .mul_ceil((8 + 4 + code_len) * 10_000 + 10_000 - (endowment - rent0)) * 4; - let bob_contract = ContractInfoOf::::get(&addr).unwrap().get_alive().unwrap(); - assert_eq!(bob_contract.rent_allowance, 1_000 - rent0 - rent); - assert_eq!(bob_contract.deduct_block, 5); - assert_eq!(Balances::free_balance(&addr), 30_000 - rent0 - rent); + let contract = ContractInfoOf::::get(&addr).unwrap().get_alive().unwrap(); + assert_eq!(contract.rent_allowance, allowance - rent0 - rent); + assert_eq!(contract.deduct_block, 5); + assert_eq!(Balances::free_balance(&addr), endowment - rent0 - rent); - // Advance 7 blocks more - initialize_block(12); + // Advance 2 blocks more + initialize_block(7); // Trigger rent through call assert_ok!( @@ -853,23 +812,21 @@ fn deduct_blocks() { // Check result let rent_2 = ::RentFraction::get() - // base_deposit + deploy_set_storage (4 bytes in 1 item) - free_balance - .mul_ceil(80_000 + 40_000 + 10_000 - (30_000 - rent0 - rent)) - // blocks to rent - * 7; - let bob_contract = ContractInfoOf::::get(&addr).unwrap().get_alive().unwrap(); - assert_eq!(bob_contract.rent_allowance, 1_000 - rent0 - rent - rent_2); - assert_eq!(bob_contract.deduct_block, 12); - assert_eq!(Balances::free_balance(&addr), 30_000 - rent0 - rent - rent_2); + .mul_ceil((8 + 4 + code_len) * 10_000 + 10_000 - (endowment - rent0 - rent)) + * 2; + let contract = ContractInfoOf::::get(&addr).unwrap().get_alive().unwrap(); + assert_eq!(contract.rent_allowance, allowance - rent0 - rent - rent_2); + assert_eq!(contract.deduct_block, 7); + assert_eq!(Balances::free_balance(&addr), endowment - rent0 - rent - rent_2); // Second call on same block should have no effect on rent assert_ok!( Contracts::call(Origin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, call::null()) ); - let bob_contract = ContractInfoOf::::get(&addr).unwrap().get_alive().unwrap(); - assert_eq!(bob_contract.rent_allowance, 1_000 - rent0 - rent - rent_2); - assert_eq!(bob_contract.deduct_block, 12); - assert_eq!(Balances::free_balance(&addr), 30_000 - rent0 - rent - rent_2) + let contract = ContractInfoOf::::get(&addr).unwrap().get_alive().unwrap(); + assert_eq!(contract.rent_allowance, allowance - rent0 - rent - rent_2); + assert_eq!(contract.deduct_block, 7); + assert_eq!(Balances::free_balance(&addr), endowment - rent0 - rent - rent_2) }); } @@ -886,16 +843,16 @@ fn signed_claim_surcharge_contract_removals() { #[test] fn claim_surcharge_malus() { // Test surcharge malus for inherent - claim_surcharge(27, |addr| Contracts::claim_surcharge(Origin::none(), addr, Some(ALICE)).is_ok(), true); - claim_surcharge(26, |addr| Contracts::claim_surcharge(Origin::none(), addr, Some(ALICE)).is_ok(), true); - claim_surcharge(25, |addr| Contracts::claim_surcharge(Origin::none(), addr, Some(ALICE)).is_ok(), true); - claim_surcharge(24, |addr| Contracts::claim_surcharge(Origin::none(), addr, Some(ALICE)).is_ok(), false); + claim_surcharge(9, |addr| Contracts::claim_surcharge(Origin::none(), addr, Some(ALICE)).is_ok(), true); + claim_surcharge(8, |addr| Contracts::claim_surcharge(Origin::none(), addr, Some(ALICE)).is_ok(), true); + claim_surcharge(7, |addr| Contracts::claim_surcharge(Origin::none(), addr, Some(ALICE)).is_ok(), true); + claim_surcharge(6, |addr| Contracts::claim_surcharge(Origin::none(), addr, Some(ALICE)).is_ok(), false); // Test surcharge malus for signed - claim_surcharge(27, |addr| Contracts::claim_surcharge(Origin::signed(ALICE), addr, None).is_ok(), true); - claim_surcharge(26, |addr| Contracts::claim_surcharge(Origin::signed(ALICE), addr, None).is_ok(), false); - claim_surcharge(25, |addr| Contracts::claim_surcharge(Origin::signed(ALICE), addr, None).is_ok(), false); - claim_surcharge(24, |addr| Contracts::claim_surcharge(Origin::signed(ALICE), addr, None).is_ok(), false); + claim_surcharge(9, |addr| Contracts::claim_surcharge(Origin::signed(ALICE), addr, None).is_ok(), true); + claim_surcharge(8, |addr| Contracts::claim_surcharge(Origin::signed(ALICE), addr, None).is_ok(), false); + claim_surcharge(7, |addr| Contracts::claim_surcharge(Origin::signed(ALICE), addr, None).is_ok(), false); + claim_surcharge(6, |addr| Contracts::claim_surcharge(Origin::signed(ALICE), addr, None).is_ok(), false); } /// Claim surcharge with the given trigger_call at the given blocks. @@ -909,12 +866,12 @@ fn claim_surcharge(blocks: u64, trigger_call: impl Fn(AccountIdOf) -> bool .execute_with(|| { // Create let _ = Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); - assert_ok!(Contracts::instantiate( + assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - 30_000, - GAS_LIMIT, code_hash.into(), - ::Balance::from(1_000u32).encode(), // rent allowance + 100_000, + GAS_LIMIT, + wasm, + ::Balance::from(30_000u32).encode(), // rent allowance vec![], )); let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); @@ -948,12 +905,12 @@ fn removals(trigger_call: impl Fn(AccountIdOf) -> bool) { .execute_with(|| { // Create let _ = Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm.clone())); - assert_ok!(Contracts::instantiate( + assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - 500, - GAS_LIMIT, code_hash.into(), - ::Balance::from(1_000u32).encode(), // rent allowance + 70_000, + GAS_LIMIT, + wasm.clone(), + ::Balance::from(100_000u32).encode(), // rent allowance vec![], )); let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); @@ -995,13 +952,12 @@ fn removals(trigger_call: impl Fn(AccountIdOf) -> bool) { .execute_with(|| { // Create let _ = Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm.clone())); - assert_ok!(Contracts::instantiate( + assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - 30_000, + 100_000, GAS_LIMIT, - code_hash.into(), - ::Balance::from(1000u32).encode(), // rent allowance + wasm.clone(), + ::Balance::from(70_000u32).encode(), // rent allowance vec![], )); let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); @@ -1031,7 +987,7 @@ fn removals(trigger_call: impl Fn(AccountIdOf) -> bool) { .get_tombstone() .is_some()); // Balance should be initial balance - initial rent_allowance - assert_eq!(Balances::free_balance(&addr), 29000); + assert_eq!(Balances::free_balance(&addr), 30_000); // Advance blocks initialize_block(20); @@ -1042,7 +998,7 @@ fn removals(trigger_call: impl Fn(AccountIdOf) -> bool) { .unwrap() .get_tombstone() .is_some()); - assert_eq!(Balances::free_balance(&addr), 29000); + assert_eq!(Balances::free_balance(&addr), 30_000); }); // Balance reached and inferior to subsistence threshold @@ -1051,15 +1007,14 @@ fn removals(trigger_call: impl Fn(AccountIdOf) -> bool) { .build() .execute_with(|| { // Create - let _ = Balances::deposit_creating(&ALICE, 1_000_000); let subsistence_threshold = ConfigCache::::subsistence_threshold_uncached(); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm.clone())); - assert_ok!(Contracts::instantiate( + let _ = Balances::deposit_creating(&ALICE, subsistence_threshold * 1000); + assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence_threshold * 3, + subsistence_threshold * 100, GAS_LIMIT, - code_hash.into(), - ::Balance::from(1_000u32).encode(), // rent allowance + wasm, + (subsistence_threshold * 100).encode(), // rent allowance vec![], )); let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); @@ -1082,7 +1037,7 @@ fn removals(trigger_call: impl Fn(AccountIdOf) -> bool) { balance, ); - // Make contract have exactly the subsitence threshold + // Make contract have exactly the subsistence threshold Balances::make_free_balance_be(&addr, subsistence_threshold); assert_eq!(Balances::free_balance(&addr), subsistence_threshold); @@ -1115,12 +1070,13 @@ fn call_removed_contract() { .execute_with(|| { // Create let _ = Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm.clone())); - assert_ok!(Contracts::instantiate( + assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), 30_000, - GAS_LIMIT, code_hash.into(), - ::Balance::from(1_000u32).encode(), // rent allowance + GAS_LIMIT, + wasm, + // rent allowance + ::Balance::from(10_000u32).encode(), vec![], )); let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); @@ -1163,27 +1119,29 @@ fn default_rent_allowance_on_instantiate() { .execute_with(|| { // Create let _ = Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); - assert_ok!(Contracts::instantiate( + assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), 30_000, GAS_LIMIT, - code_hash.into(), + wasm, vec![], vec![], )); let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); + let contract = ContractInfoOf::::get(&addr).unwrap().get_alive().unwrap(); + let code_len: BalanceOf = + PrefabWasmModule::::from_storage_noinstr(contract.code_hash) + .unwrap() + .occupied_storage() + .into(); // The instantiation deducted the rent for one block immediately let first_rent = ::RentFraction::get() - // base_deposit - free_balance - .mul_ceil(80_000 - 30_000) + // (base_deposit(8) + code_len) * byte_price - free_balance + .mul_ceil((8 + code_len) * 10_000 - 30_000) // blocks to rent * 1; - - // Check creation - let bob_contract = ContractInfoOf::::get(&addr).unwrap().get_alive().unwrap(); - assert_eq!(bob_contract.rent_allowance, >::max_value() - first_rent); + assert_eq!(contract.rent_allowance, >::max_value() - first_rent); // Advance blocks initialize_block(5); @@ -1194,46 +1152,64 @@ fn default_rent_allowance_on_instantiate() { ); // Check contract is still alive - let bob_contract = ContractInfoOf::::get(&addr).unwrap().get_alive(); - assert!(bob_contract.is_some()) + let contract = ContractInfoOf::::get(&addr).unwrap().get_alive(); + assert!(contract.is_some()) }); } #[test] fn restorations_dirty_storage_and_different_storage() { - restoration(true, true); + restoration(true, true, false); } #[test] fn restorations_dirty_storage() { - restoration(false, true); + restoration(false, true, false); } #[test] fn restoration_different_storage() { - restoration(true, false); + restoration(true, false, false); +} + +#[test] +fn restoration_code_evicted() { + restoration(false, false, true); } #[test] fn restoration_success() { - restoration(false, false); + restoration(false, false, false); } -fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: bool) { +fn restoration( + test_different_storage: bool, + test_restore_to_with_dirty_storage: bool, + test_code_evicted: bool +) { let (set_rent_wasm, set_rent_code_hash) = compile_module::("set_rent").unwrap(); let (restoration_wasm, restoration_code_hash) = compile_module::("restoration").unwrap(); + let allowance: ::Balance = 10_000; ExtBuilder::default() .existential_deposit(50) .build() .execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), restoration_wasm)); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), set_rent_wasm)); - // If you ever need to update the wasm source this test will fail - // and will show you the actual hash. - assert_eq!(System::events(), vec![ + // Create an account with address `BOB` with code `CODE_SET_RENT`. + // The input parameter sets the rent allowance to 0. + assert_ok!(Contracts::instantiate_with_code( + Origin::signed(ALICE), + 30_000, + GAS_LIMIT, + set_rent_wasm.clone(), + allowance.encode(), + vec![], + )); + let addr_bob = Contracts::contract_address(&ALICE, &set_rent_code_hash, &[]); + + let mut events = vec![ EventRecord { phase: Phase::Initialization, event: MetaEvent::system(frame_system::Event::NewAccount(ALICE)), @@ -1241,12 +1217,28 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: }, EventRecord { phase: Phase::Initialization, - event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(ALICE, 1_000_000)), + event: MetaEvent::balances( + pallet_balances::RawEvent::Endowed(ALICE, 1_000_000) + ), topics: vec![], }, EventRecord { phase: Phase::Initialization, - event: MetaEvent::contracts(RawEvent::CodeStored(restoration_code_hash.into())), + event: MetaEvent::system(frame_system::Event::NewAccount(addr_bob.clone())), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::balances( + pallet_balances::RawEvent::Endowed(addr_bob.clone(), 30_000) + ), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::balances( + pallet_balances::RawEvent::Transfer(ALICE, addr_bob.clone(), 30_000) + ), topics: vec![], }, EventRecord { @@ -1254,24 +1246,60 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: event: MetaEvent::contracts(RawEvent::CodeStored(set_rent_code_hash.into())), topics: vec![], }, - ]); + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::contracts(RawEvent::Instantiated(ALICE, addr_bob.clone())), + topics: vec![], + }, + ]; - // Create an account with address `BOB` with code `CODE_SET_RENT`. - // The input parameter sets the rent allowance to 0. - assert_ok!(Contracts::instantiate( - Origin::signed(ALICE), - 30_000, - GAS_LIMIT, - set_rent_code_hash.into(), - ::Balance::from(1_000u32).encode(), - vec![], - )); - let addr_bob = Contracts::contract_address(&ALICE, &set_rent_code_hash, &[]); + // Create another contract from the same code in order to increment the codes + // refcounter so that it stays on chain. + if !test_code_evicted { + assert_ok!(Contracts::instantiate_with_code( + Origin::signed(ALICE), + 20_000, + GAS_LIMIT, + set_rent_wasm, + allowance.encode(), + vec![1], + )); + assert_refcount!(set_rent_code_hash, 2); + let addr_dummy = Contracts::contract_address(&ALICE, &set_rent_code_hash, &[1]); + events.extend([ + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::system(frame_system::Event::NewAccount(addr_dummy.clone())), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::balances( + pallet_balances::RawEvent::Endowed(addr_dummy.clone(), 20_000) + ), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::balances( + pallet_balances::RawEvent::Transfer(ALICE, addr_dummy.clone(), 20_000) + ), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::contracts(RawEvent::Instantiated(ALICE, addr_dummy.clone())), + topics: vec![], + }, + ].iter().cloned()); + } + + assert_eq!(System::events(), events); // Check if `BOB` was created successfully and that the rent allowance is below what // we specified as the first rent was already collected. let bob_contract = ContractInfoOf::::get(&addr_bob).unwrap().get_alive().unwrap(); - assert!(bob_contract.rent_allowance < 5_000); + assert!(bob_contract.rent_allowance < allowance); if test_different_storage { assert_ok!(Contracts::call( @@ -1297,26 +1325,22 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: assert!(ContractInfoOf::::get(&addr_bob).unwrap().get_alive().is_some()); assert_ok!(Contracts::claim_surcharge(Origin::none(), addr_bob.clone(), Some(ALICE))); assert!(ContractInfoOf::::get(&addr_bob).unwrap().get_tombstone().is_some()); - assert_eq!(System::events(), vec![ - EventRecord { - phase: Phase::Initialization, - event: MetaEvent::contracts( - RawEvent::Evicted(addr_bob.clone()) - ), - topics: vec![], - }, - ]); + if test_code_evicted { + assert_refcount!(set_rent_code_hash, 0); + } else { + assert_refcount!(set_rent_code_hash, 1); + } // Create another account with the address `DJANGO` with `CODE_RESTORATION`. // // Note that we can't use `ALICE` for creating `DJANGO` so we create yet another // account `CHARLIE` and create `DJANGO` with it. let _ = Balances::deposit_creating(&CHARLIE, 1_000_000); - assert_ok!(Contracts::instantiate( + assert_ok!(Contracts::instantiate_with_code( Origin::signed(CHARLIE), 30_000, GAS_LIMIT, - restoration_code_hash.into(), + restoration_wasm, vec![], vec![], )); @@ -1358,7 +1382,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: key }; - if test_different_storage || test_restore_to_with_dirty_storage { + if test_different_storage || test_restore_to_with_dirty_storage || test_code_evicted { // Parametrization of the test imply restoration failure. Check that `DJANGO` aka // restoration contract is still in place and also that `BOB` doesn't exist. let result = perform_the_restoration(); @@ -1372,18 +1396,22 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: Storage::::read(&django_trie_id, &delta_key), Some(vec![40, 0, 0, 0]), ); - match (test_different_storage, test_restore_to_with_dirty_storage) { - (true, false) => { + match ( + test_different_storage, + test_restore_to_with_dirty_storage, + test_code_evicted + ) { + (true, false, false) => { assert_err_ignore_postinfo!( result, Error::::InvalidTombstone, ); assert_eq!(System::events(), vec![]); } - (_, true) => { + (_, true, false) => { assert_err_ignore_postinfo!( result, Error::::InvalidContractOrigin, ); - pretty_assertions::assert_eq!(System::events(), vec![ + assert_eq!(System::events(), vec![ EventRecord { phase: Phase::Initialization, event: MetaEvent::contracts(RawEvent::Evicted(addr_bob)), @@ -1416,17 +1444,31 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: ), topics: vec![], }, + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::contracts(RawEvent::CodeStored(restoration_code_hash)), + topics: vec![], + }, EventRecord { phase: Phase::Initialization, event: MetaEvent::contracts(RawEvent::Instantiated(CHARLIE, addr_django.clone())), topics: vec![], }, + ]); - } + }, + (false, false, true) => { + assert_err_ignore_postinfo!( + result, Error::::CodeNotFound, + ); + assert_refcount!(set_rent_code_hash, 0); + assert_eq!(System::events(), vec![]); + }, _ => unreachable!(), } } else { assert_ok!(perform_the_restoration()); + assert_refcount!(set_rent_code_hash, 2); // Here we expect that the restoration is succeeded. Check that the restoration // contract `DJANGO` ceased to exist and that `BOB` returned back. @@ -1439,6 +1481,11 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: assert!(ContractInfoOf::::get(&addr_django).is_none()); assert_matches!(Storage::::read(&django_trie_id, &delta_key), None); assert_eq!(System::events(), vec![ + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::contracts(RawEvent::CodeRemoved(restoration_code_hash)), + topics: vec![], + }, EventRecord { phase: Phase::Initialization, event: MetaEvent::system(system::Event::KilledAccount(addr_django.clone())), @@ -1466,12 +1513,11 @@ fn storage_max_value_limit() { .execute_with(|| { // Create let _ = Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); - assert_ok!(Contracts::instantiate( + assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), 30_000, GAS_LIMIT, - code_hash.into(), + wasm, vec![], vec![], )); @@ -1512,24 +1558,28 @@ fn deploy_and_call_other_contract() { .execute_with(|| { // Create let _ = Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), callee_wasm)); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), caller_wasm)); - - assert_ok!(Contracts::instantiate( + assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), 100_000, GAS_LIMIT, - caller_code_hash.into(), + caller_wasm, vec![], vec![], )); - let addr = Contracts::contract_address(&ALICE, &caller_code_hash, &[]); + assert_ok!(Contracts::instantiate_with_code( + Origin::signed(ALICE), + 100_000, + GAS_LIMIT, + callee_wasm, + 0u32.to_le_bytes().encode(), + vec![42], + )); // Call BOB contract, which attempts to instantiate and call the callee contract and // makes various assertions on the results from those calls. assert_ok!(Contracts::call( Origin::signed(ALICE), - addr, + Contracts::contract_address(&ALICE, &caller_code_hash, &[]), 0, GAS_LIMIT, callee_code_hash.as_ref().to_vec(), @@ -1545,14 +1595,13 @@ fn cannot_self_destruct_through_draning() { .build() .execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); // Instantiate the BOB contract. - assert_ok!(Contracts::instantiate( + assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), 100_000, GAS_LIMIT, - code_hash.into(), + wasm, vec![], vec![], )); @@ -1586,14 +1635,13 @@ fn cannot_self_destruct_while_live() { .build() .execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); // Instantiate the BOB contract. - assert_ok!(Contracts::instantiate( + assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), 100_000, GAS_LIMIT, - code_hash.into(), + wasm, vec![], vec![], )); @@ -1635,14 +1683,13 @@ fn self_destruct_works() { .execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); let _ = Balances::deposit_creating(&DJANGO, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); // Instantiate the BOB contract. - assert_ok!(Contracts::instantiate( + assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), 100_000, GAS_LIMIT, - code_hash.into(), + wasm, vec![], vec![], )); @@ -1680,10 +1727,15 @@ fn self_destruct_works() { EventRecord { phase: Phase::Initialization, event: MetaEvent::balances( - pallet_balances::RawEvent::Transfer(addr.clone(), DJANGO, 100_000) + pallet_balances::RawEvent::Transfer(addr.clone(), DJANGO, 93_654) ), topics: vec![], }, + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::contracts(RawEvent::CodeRemoved(code_hash)), + topics: vec![], + }, EventRecord { phase: Phase::Initialization, event: MetaEvent::contracts( @@ -1697,7 +1749,8 @@ fn self_destruct_works() { assert!(ContractInfoOf::::get(&addr).is_none()); // check that the beneficiary (django) got remaining balance - assert_eq!(Balances::free_balance(DJANGO), 1_100_000); + // some rent was deducted before termination + assert_eq!(Balances::free_balance(DJANGO), 1_093_654); }); } @@ -1714,16 +1767,22 @@ fn destroy_contract_and_transfer_funds() { .execute_with(|| { // Create let _ = Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), callee_wasm)); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), caller_wasm)); + assert_ok!(Contracts::instantiate_with_code( + Origin::signed(ALICE), + 200_000, + GAS_LIMIT, + callee_wasm, + vec![], + vec![42] + )); // This deploys the BOB contract, which in turn deploys the CHARLIE contract during // construction. - assert_ok!(Contracts::instantiate( + assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), 200_000, GAS_LIMIT, - caller_code_hash.into(), + caller_wasm, callee_code_hash.as_ref().to_vec(), vec![], )); @@ -1754,25 +1813,24 @@ fn destroy_contract_and_transfer_funds() { #[test] fn cannot_self_destruct_in_constructor() { - let (wasm, code_hash) = compile_module::("self_destructing_constructor").unwrap(); + let (wasm, _) = compile_module::("self_destructing_constructor").unwrap(); ExtBuilder::default() .existential_deposit(50) .build() .execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); // Fail to instantiate the BOB because the contructor calls seal_terminate. assert_err_ignore_postinfo!( - Contracts::instantiate( + Contracts::instantiate_with_code( Origin::signed(ALICE), 100_000, GAS_LIMIT, - code_hash.into(), + wasm, vec![], vec![], ), - Error::::NewContractNotFunded, + Error::::NotCallable, ); }); } @@ -1786,14 +1844,13 @@ fn crypto_hashes() { .build() .execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); // Instantiate the CRYPTO_HASHES contract. - assert_ok!(Contracts::instantiate( + assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), 100_000, GAS_LIMIT, - code_hash.into(), + wasm, vec![], vec![], )); @@ -1838,15 +1895,14 @@ fn transfer_return_code() { let (wasm, code_hash) = compile_module::("transfer_return_code").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let subsistence = ConfigCache::::subsistence_threshold_uncached(); - let _ = Balances::deposit_creating(&ALICE, 10 * subsistence); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); + let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); assert_ok!( - Contracts::instantiate( + Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 3, + subsistence * 100, GAS_LIMIT, - code_hash.into(), + wasm, vec![], vec![], ), @@ -1886,17 +1942,15 @@ fn call_return_code() { let (callee_code, callee_hash) = compile_module::("ok_trap_revert").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let subsistence = ConfigCache::::subsistence_threshold_uncached(); - let _ = Balances::deposit_creating(&ALICE, 10 * subsistence); - let _ = Balances::deposit_creating(&CHARLIE, 10 * subsistence); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), caller_code)); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), callee_code)); + let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); + let _ = Balances::deposit_creating(&CHARLIE, 1000 * subsistence); assert_ok!( - Contracts::instantiate( + Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 3, + subsistence * 100, GAS_LIMIT, - caller_hash.into(), + caller_code, vec![0], vec![], ), @@ -1915,11 +1969,11 @@ fn call_return_code() { assert_return_code!(result, RuntimeReturnCode::NotCallable); assert_ok!( - Contracts::instantiate( + Contracts::instantiate_with_code( Origin::signed(CHARLIE), - subsistence * 3, + subsistence * 100, GAS_LIMIT, - callee_hash.into(), + callee_code, vec![0], vec![], ), @@ -1981,18 +2035,27 @@ fn instantiate_return_code() { let (callee_code, callee_hash) = compile_module::("ok_trap_revert").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let subsistence = ConfigCache::::subsistence_threshold_uncached(); - let _ = Balances::deposit_creating(&ALICE, 10 * subsistence); - let _ = Balances::deposit_creating(&CHARLIE, 10 * subsistence); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), caller_code)); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), callee_code)); + let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); + let _ = Balances::deposit_creating(&CHARLIE, 1000 * subsistence); let callee_hash = callee_hash.as_ref().to_vec(); assert_ok!( - Contracts::instantiate( + Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 3, + subsistence * 100, GAS_LIMIT, - caller_hash.into(), + callee_code, + vec![], + vec![], + ), + ); + + assert_ok!( + Contracts::instantiate_with_code( + Origin::signed(ALICE), + subsistence * 100, + GAS_LIMIT, + caller_code, vec![], vec![], ), @@ -2006,26 +2069,26 @@ fn instantiate_return_code() { addr.clone(), 0, GAS_LIMIT, - vec![0; 33], + callee_hash.clone(), ).exec_result.unwrap(); assert_return_code!(result, RuntimeReturnCode::BelowSubsistenceThreshold); // Contract has enough total balance in order to not go below the subsistence - // threshold when transfering 100 balance but this balance is reserved so + // threshold when transfering the balance but this balance is reserved so // the transfer still fails but with another return code. - Balances::make_free_balance_be(&addr, subsistence + 100); - Balances::reserve(&addr, subsistence + 100).unwrap(); + Balances::make_free_balance_be(&addr, subsistence + 10_000); + Balances::reserve(&addr, subsistence + 10_000).unwrap(); let result = Contracts::bare_call( ALICE, addr.clone(), 0, GAS_LIMIT, - vec![0; 33], + callee_hash.clone(), ).exec_result.unwrap(); assert_return_code!(result, RuntimeReturnCode::TransferFailed); // Contract has enough balance but the passed code hash is invalid - Balances::make_free_balance_be(&addr, subsistence + 1000); + Balances::make_free_balance_be(&addr, subsistence + 10_000); let result = Contracts::bare_call( ALICE, addr.clone(), @@ -2063,11 +2126,18 @@ fn disabled_chain_extension_wont_deploy() { let (code, _hash) = compile_module::("chain_extension").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let subsistence = ConfigCache::::subsistence_threshold_uncached(); - let _ = Balances::deposit_creating(&ALICE, 10 * subsistence); + let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); TestExtension::disable(); - assert_eq!( - Contracts::put_code(Origin::signed(ALICE), code), - Err("module uses chain extensions but chain extensions are disabled".into()), + assert_err_ignore_postinfo!( + Contracts::instantiate_with_code( + Origin::signed(ALICE), + 3 * subsistence, + GAS_LIMIT, + code, + vec![], + vec![], + ), + "module uses chain extensions but chain extensions are disabled", ); }); } @@ -2077,20 +2147,19 @@ fn disabled_chain_extension_errors_on_call() { let (code, hash) = compile_module::("chain_extension").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let subsistence = ConfigCache::::subsistence_threshold_uncached(); - let _ = Balances::deposit_creating(&ALICE, 10 * subsistence); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), code)); - TestExtension::disable(); + let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); assert_ok!( - Contracts::instantiate( + Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 3, + subsistence * 100, GAS_LIMIT, - hash.into(), + code, vec![], vec![], ), ); let addr = Contracts::contract_address(&ALICE, &hash, &[]); + TestExtension::disable(); assert_err_ignore_postinfo!( Contracts::call( Origin::signed(ALICE), @@ -2109,14 +2178,13 @@ fn chain_extension_works() { let (code, hash) = compile_module::("chain_extension").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let subsistence = ConfigCache::::subsistence_threshold_uncached(); - let _ = Balances::deposit_creating(&ALICE, 10 * subsistence); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), code)); + let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); assert_ok!( - Contracts::instantiate( + Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 3, + subsistence * 100, GAS_LIMIT, - hash.into(), + code, vec![], vec![], ), @@ -2179,15 +2247,14 @@ fn lazy_removal_works() { let (code, hash) = compile_module::("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let subsistence = ConfigCache::::subsistence_threshold_uncached(); - let _ = Balances::deposit_creating(&ALICE, 10 * subsistence); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), code)); + let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); assert_ok!( - Contracts::instantiate( + Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 3, + subsistence * 100, GAS_LIMIT, - hash.into(), + code, vec![], vec![], ), @@ -2240,15 +2307,14 @@ fn lazy_removal_partial_remove_works() { let trie = ext.execute_with(|| { let subsistence = ConfigCache::::subsistence_threshold_uncached(); - let _ = Balances::deposit_creating(&ALICE, 10 * subsistence); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), code)); + let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); assert_ok!( - Contracts::instantiate( + Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 3, + subsistence * 100, GAS_LIMIT, - hash.into(), + code, vec![], vec![], ), @@ -2322,15 +2388,14 @@ fn lazy_removal_does_no_run_on_full_block() { let (code, hash) = compile_module::("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let subsistence = ConfigCache::::subsistence_threshold_uncached(); - let _ = Balances::deposit_creating(&ALICE, 10 * subsistence); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), code)); + let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); assert_ok!( - Contracts::instantiate( + Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 3, + subsistence * 100, GAS_LIMIT, - hash.into(), + code, vec![], vec![], ), @@ -2407,15 +2472,14 @@ fn lazy_removal_does_not_use_all_weight() { let (code, hash) = compile_module::("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let subsistence = ConfigCache::::subsistence_threshold_uncached(); - let _ = Balances::deposit_creating(&ALICE, 10 * subsistence); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), code)); + let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); assert_ok!( - Contracts::instantiate( + Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 3, + subsistence * 100, GAS_LIMIT, - hash.into(), + code, vec![], vec![], ), @@ -2478,15 +2542,14 @@ fn deletion_queue_full() { let (code, hash) = compile_module::("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let subsistence = ConfigCache::::subsistence_threshold_uncached(); - let _ = Balances::deposit_creating(&ALICE, 10 * subsistence); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), code)); + let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); assert_ok!( - Contracts::instantiate( + Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 3, + subsistence * 100, GAS_LIMIT, - hash.into(), + code, vec![], vec![], ), @@ -2540,12 +2603,12 @@ fn not_deployed_if_endowment_too_low_for_first_rent() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create let _ = Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); assert_storage_noop!(assert_err_ignore_postinfo!( - Contracts::instantiate( + Contracts::instantiate_with_code( Origin::signed(ALICE), 30_000, - GAS_LIMIT, code_hash.into(), + GAS_LIMIT, + wasm, (BalanceOf::::from(first_rent) - BalanceOf::::from(1u32)) .encode(), // rent allowance vec![], @@ -2562,12 +2625,12 @@ fn surcharge_reward_is_capped() { let (wasm, code_hash) = compile_module::("set_rent").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); - assert_ok!(Contracts::instantiate( + assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), 30_000, - GAS_LIMIT, code_hash.into(), - >::from(1_000u32).encode(), // rent allowance + GAS_LIMIT, + wasm, + >::from(10_000u32).encode(), // rent allowance vec![], )); let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); @@ -2598,3 +2661,75 @@ fn surcharge_reward_is_capped() { assert!(Balances::free_balance(&ALICE) < balance + reward); }); } + +#[test] +fn refcounter() { + let (wasm, code_hash) = compile_module::("self_destruct").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let subsistence = ConfigCache::::subsistence_threshold_uncached(); + + // Create two contracts with the same code and check that they do in fact share it. + assert_ok!(Contracts::instantiate_with_code( + Origin::signed(ALICE), + subsistence * 100, + GAS_LIMIT, + wasm.clone(), + vec![], + vec![0], + )); + assert_ok!(Contracts::instantiate_with_code( + Origin::signed(ALICE), + subsistence * 100, + GAS_LIMIT, + wasm.clone(), + vec![], + vec![1], + )); + assert_refcount!(code_hash, 2); + + // Sharing should also work with the usual instantiate call + assert_ok!(Contracts::instantiate( + Origin::signed(ALICE), + subsistence * 100, + GAS_LIMIT, + code_hash, + vec![], + vec![2], + )); + assert_refcount!(code_hash, 3); + + // addresses of all three existing contracts + let addr0 = Contracts::contract_address(&ALICE, &code_hash, &[0]); + let addr1 = Contracts::contract_address(&ALICE, &code_hash, &[1]); + let addr2 = Contracts::contract_address(&ALICE, &code_hash, &[2]); + + // Terminating one contract should decrement the refcount + assert_ok!(Contracts::call( + Origin::signed(ALICE), + addr0, + 0, + GAS_LIMIT, + vec![], + )); + assert_refcount!(code_hash, 2); + + // make remaining contracts eligible for eviction + initialize_block(40); + + // remove one of them + assert_ok!(Contracts::claim_surcharge(Origin::none(), addr1, Some(ALICE))); + assert_refcount!(code_hash, 1); + + // Pristine code should still be there + crate::PristineCode::::get(code_hash).unwrap(); + + // remove the last contract + assert_ok!(Contracts::claim_surcharge(Origin::none(), addr2, Some(ALICE))); + assert_refcount!(code_hash, 0); + + // all code should be gone + assert_matches!(crate::PristineCode::::get(code_hash), None); + assert_matches!(crate::CodeStorage::::get(code_hash), None); + }); +} diff --git a/frame/contracts/src/wasm/code_cache.rs b/frame/contracts/src/wasm/code_cache.rs index 3150ee4b7bde7..6166918c80c94 100644 --- a/frame/contracts/src/wasm/code_cache.rs +++ b/frame/contracts/src/wasm/code_cache.rs @@ -27,46 +27,84 @@ //! 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 crate::wasm::{prepare, runtime::Env, PrefabWasmModule}; -use crate::{CodeHash, CodeStorage, PristineCode, Schedule, Config}; -use sp_std::prelude::*; -use sp_runtime::traits::Hash; +use crate::{ + CodeHash, CodeStorage, PristineCode, Schedule, Config, Error, + wasm::{prepare, PrefabWasmModule}, Module as Contracts, RawEvent, +}; use sp_core::crypto::UncheckedFrom; -use frame_support::StorageMap; +use frame_support::{StorageMap, dispatch::{DispatchError, DispatchResult}}; -/// Put code in the storage. The hash of code is used as a key and is returned -/// as a result of this function. +/// Put the instrumented module in storage. /// -/// This function instruments the given code and caches it in the storage. -pub fn save( - original_code: Vec, - schedule: &Schedule, -) -> Result, &'static str> where T::AccountId: UncheckedFrom + AsRef<[u8]> { - let prefab_module = prepare::prepare_contract::(&original_code, schedule)?; - let code_hash = T::Hashing::hash(&original_code); +/// Increments the refcount of the in-storage `prefab_module` if it already exists in storage +/// under the specified `code_hash`. +pub fn store(mut prefab_module: PrefabWasmModule) +where + T::AccountId: UncheckedFrom + AsRef<[u8]> +{ + let code_hash = sp_std::mem::take(&mut prefab_module.code_hash); - >::insert(code_hash, prefab_module); - >::insert(code_hash, original_code); - - Ok(code_hash) + // original_code is only `Some` if the contract was instantiated from a new code + // but `None` if it was loaded from storage. + if let Some(code) = prefab_module.original_code.take() { + >::insert(&code_hash, code); + } + >::mutate(&code_hash, |existing| { + match existing { + Some(module) => increment_64(&mut module.refcount), + None => { + *existing = Some(prefab_module); + Contracts::::deposit_event(RawEvent::CodeStored(code_hash)) + } + } + }); } -/// Version of `save` to be used in runtime benchmarks. -// -/// This version neither checks nor instruments the passed in code. This is useful -/// when code needs to be benchmarked without the injected instrumentation. -#[cfg(feature = "runtime-benchmarks")] -pub fn save_raw( - original_code: Vec, - schedule: &Schedule, -) -> Result, &'static str> where T::AccountId: UncheckedFrom + AsRef<[u8]> { - let prefab_module = prepare::benchmarking::prepare_contract::(&original_code, schedule)?; - let code_hash = T::Hashing::hash(&original_code); +/// Decrement the refcount and store. +/// +/// Removes the code instead of storing it when the refcount drops to zero. +pub fn store_decremented(mut prefab_module: PrefabWasmModule) +where + T::AccountId: UncheckedFrom + AsRef<[u8]> +{ + prefab_module.refcount = prefab_module.refcount.saturating_sub(1); + if prefab_module.refcount > 0 { + >::insert(prefab_module.code_hash, prefab_module); + } else { + >::remove(prefab_module.code_hash); + finish_removal::(prefab_module.code_hash); + } +} - >::insert(code_hash, prefab_module); - >::insert(code_hash, original_code); +/// Increment the refcount of a code in-storage by one. +pub fn increment_refcount(code_hash: CodeHash) -> DispatchResult +where + T::AccountId: UncheckedFrom + AsRef<[u8]> +{ + >::mutate(code_hash, |existing| { + if let Some(module) = existing { + increment_64(&mut module.refcount); + Ok(()) + } else { + Err(Error::::CodeNotFound.into()) + } + }) +} - Ok(code_hash) +/// Decrement the refcount of a code in-storage by one and remove the code when it drops to zero. +pub fn decrement_refcount(code_hash: CodeHash) +where + T::AccountId: UncheckedFrom + AsRef<[u8]> +{ + >::mutate_exists(code_hash, |existing| { + if let Some(module) = existing { + module.refcount = module.refcount.saturating_sub(1); + if module.refcount == 0 { + *existing = None; + finish_removal::(code_hash); + } + } + }); } /// Load code with the given code hash. @@ -75,21 +113,51 @@ pub fn save_raw( /// the current one given as an argument, then this function will perform /// re-instrumentation and update the cache in the storage. pub fn load( - code_hash: &CodeHash, - schedule: &Schedule, -) -> Result where T::AccountId: UncheckedFrom + AsRef<[u8]> { - let mut prefab_module = - >::get(code_hash).ok_or_else(|| "code is not found")?; + code_hash: CodeHash, + schedule: Option<&Schedule>, +) -> Result, DispatchError> +where + T::AccountId: UncheckedFrom + AsRef<[u8]> +{ + let mut prefab_module = >::get(code_hash) + .ok_or_else(|| Error::::CodeNotFound)?; - 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 = - >::get(code_hash).ok_or_else(|| "pristine code is not found")?; - prefab_module = prepare::prepare_contract::(&original_code, schedule)?; - >::insert(&code_hash, &prefab_module); + if let Some(schedule) = schedule { + 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 = >::get(code_hash) + .ok_or_else(|| Error::::CodeNotFound)?; + prefab_module.code = prepare::reinstrument_contract::(original_code, schedule)?; + prefab_module.schedule_version = schedule.version; + >::insert(&code_hash, &prefab_module); + } } + prefab_module.code_hash = code_hash; Ok(prefab_module) } + +/// Finish removal of a code by deleting the pristine code and emitting an event. +fn finish_removal(code_hash: CodeHash) +where + T::AccountId: UncheckedFrom + AsRef<[u8]> +{ + >::remove(code_hash); + Contracts::::deposit_event(RawEvent::CodeRemoved(code_hash)) +} + +/// Increment the refcount panicking if it should ever overflow (which will not happen). +/// +/// We try hard to be infallible here because otherwise more storage transactions would be +/// necessary to account for failures in storing code for an already instantiated contract. +fn increment_64(refcount: &mut u64) { + *refcount = refcount.checked_add(1).expect(" + refcount is 64bit. Generating this overflow would require to store + _at least_ 18 exabyte of data assuming that a contract consumes only + one byte of data. Any node would run out of storage space before hitting + this overflow. + qed + "); +} diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index 45c927dfaa4b8..56be9f35313a6 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -18,114 +18,163 @@ //! This module provides a means for executing contracts //! represented in wasm. +#[macro_use] +mod env_def; +mod code_cache; +mod prepare; +mod runtime; + use crate::{ CodeHash, Schedule, Config, wasm::env_def::FunctionImplProvider, - exec::Ext, + exec::{Ext, Executable, ExportedFunction}, gas::GasMeter, }; use sp_std::prelude::*; use sp_core::crypto::UncheckedFrom; use codec::{Encode, Decode}; - -#[macro_use] -mod env_def; -mod code_cache; -mod prepare; -mod runtime; - -use self::code_cache::load as load_code; +use frame_support::dispatch::{DispatchError, DispatchResult}; use pallet_contracts_primitives::ExecResult; - -pub use self::code_cache::save as save_code; -#[cfg(feature = "runtime-benchmarks")] -pub use self::code_cache::save_raw as save_code_raw; pub use self::runtime::{ReturnCode, Runtime, RuntimeToken}; /// A prepared wasm module ready for execution. +/// +/// # Note +/// +/// This data structure is mostly immutable once created and stored. The exceptions that +/// can be changed by calling a contract are `refcount`, `schedule_version` and `code`. +/// `refcount` can change when a contract instantiates a new contract or self terminates. +/// `schedule_version` and `code` when a contract with an outdated instrumention is called. +/// Therefore one must be careful when holding any in-memory representation of this type while +/// calling into a contract as those fields can get out of date. #[derive(Clone, Encode, Decode)] -pub struct PrefabWasmModule { +pub struct PrefabWasmModule { /// Version of the schedule with which the code was instrumented. #[codec(compact)] schedule_version: u32, + /// Initial memory size of a contract's sandbox. #[codec(compact)] initial: u32, + /// The maximum memory size of a contract's sandbox. #[codec(compact)] maximum: u32, + /// The number of alive contracts that use this as their contract code. + /// + /// If this number drops to zero this module is removed from storage. + #[codec(compact)] + refcount: u64, /// This field is reserved for future evolution of format. /// - /// Basically, for now this field will be serialized as `None`. In the future - /// we would be able to extend this structure with. + /// For now this field is serialized as `None`. In the future we are able to change the + /// type parameter to a new struct that contains the fields that we want to add. + /// That new struct would also contain a reserved field for its future extensions. + /// This works because in SCALE `None` is encoded independently from the type parameter + /// of the option. _reserved: Option<()>, /// Code instrumented with the latest schedule. code: Vec, + /// The size of the uninstrumented code. + /// + /// We cache this value here in order to avoid the need to pull the pristine code + /// from storage when we only need its length for rent calculations. + original_code_len: u32, + /// The uninstrumented, pristine version of the code. + /// + /// It is not stored because the pristine code has its own storage item. The value + /// is only `Some` when this module was created from an `original_code` and `None` if + /// it was loaded from storage. + #[codec(skip)] + original_code: Option>, + /// The code hash of the stored code which is defined as the hash over the `original_code`. + /// + /// As the map key there is no need to store the hash in the value, too. It is set manually + /// when loading the module from storage. + #[codec(skip)] + code_hash: CodeHash, } -/// Wasm executable loaded by `WasmLoader` and executed by `WasmVm`. -pub struct WasmExecutable { - entrypoint_name: &'static str, - prefab_module: PrefabWasmModule, -} - -/// Loader which fetches `WasmExecutable` from the code cache. -pub struct WasmLoader<'a, T: Config> { - schedule: &'a Schedule, -} - -impl<'a, T: Config> WasmLoader<'a, T> where T::AccountId: UncheckedFrom + AsRef<[u8]> { - pub fn new(schedule: &'a Schedule) -> Self { - WasmLoader { schedule } +impl ExportedFunction { + /// The wasm export name for the function. + fn identifier(&self) -> &str { + match self { + Self::Constructor => "deploy", + Self::Call => "call", + } } } -impl<'a, T: Config> crate::exec::Loader for WasmLoader<'a, T> +impl PrefabWasmModule where T::AccountId: UncheckedFrom + AsRef<[u8]> { - type Executable = WasmExecutable; - - fn load_init(&self, code_hash: &CodeHash) -> Result { - let prefab_module = load_code::(code_hash, self.schedule)?; - Ok(WasmExecutable { - entrypoint_name: "deploy", - prefab_module, - }) + /// Create the module by checking and instrumenting `original_code`. + pub fn from_code( + original_code: Vec, + schedule: &Schedule + ) -> Result { + prepare::prepare_contract(original_code, schedule).map_err(Into::into) } - fn load_main(&self, code_hash: &CodeHash) -> Result { - let prefab_module = load_code::(code_hash, self.schedule)?; - Ok(WasmExecutable { - entrypoint_name: "call", - prefab_module, - }) - } -} -/// Implementation of `Vm` that takes `WasmExecutable` and executes it. -pub struct WasmVm<'a, T: Config> where T::AccountId: UncheckedFrom + AsRef<[u8]> { - schedule: &'a Schedule, -} + /// Create and store the module without checking nor instrumenting the passed code. + /// + /// # Note + /// + /// This is useful for benchmarking where we don't want instrumentation to skew + /// our results. + #[cfg(feature = "runtime-benchmarks")] + pub fn store_code_unchecked( + original_code: Vec, + schedule: &Schedule + ) -> DispatchResult { + let executable = prepare::benchmarking::prepare_contract(original_code, schedule) + .map_err::(Into::into)?; + code_cache::store(executable); + Ok(()) + } -impl<'a, T: Config> WasmVm<'a, T> where T::AccountId: UncheckedFrom + AsRef<[u8]> { - pub fn new(schedule: &'a Schedule) -> Self { - WasmVm { schedule } + /// Return the refcount of the module. + #[cfg(test)] + pub fn refcount(&self) -> u64 { + self.refcount } } -impl<'a, T: Config> crate::exec::Vm for WasmVm<'a, T> +impl Executable for PrefabWasmModule where T::AccountId: UncheckedFrom + AsRef<[u8]> { - type Executable = WasmExecutable; + fn from_storage( + code_hash: CodeHash, + schedule: &Schedule + ) -> Result { + code_cache::load(code_hash, Some(schedule)) + } + + fn from_storage_noinstr(code_hash: CodeHash) -> Result { + code_cache::load(code_hash, None) + } + + fn drop_from_storage(self) { + code_cache::store_decremented(self); + } + + fn add_user(code_hash: CodeHash) -> DispatchResult { + code_cache::increment_refcount::(code_hash) + } + + fn remove_user(code_hash: CodeHash) { + code_cache::decrement_refcount::(code_hash) + } fn execute>( - &self, - exec: &WasmExecutable, + self, mut ext: E, + function: &ExportedFunction, input_data: Vec, gas_meter: &mut GasMeter, ) -> ExecResult { let memory = - sp_sandbox::Memory::new(exec.prefab_module.initial, Some(exec.prefab_module.maximum)) + sp_sandbox::Memory::new(self.initial, Some(self.maximum)) .unwrap_or_else(|_| { // unlike `.expect`, explicit panic preserves the source location. // Needed as we can't use `RUST_BACKTRACE` in here. @@ -145,17 +194,34 @@ where let mut runtime = Runtime::new( &mut ext, input_data, - &self.schedule, memory, gas_meter, ); + // We store before executing so that the code hash is available in the constructor. + let code = self.code.clone(); + if let &ExportedFunction::Constructor = function { + code_cache::store(self) + } + // Instantiate the instance from the instrumented module code and invoke the contract // entrypoint. - let result = sp_sandbox::Instance::new(&exec.prefab_module.code, &imports, &mut runtime) - .and_then(|mut instance| instance.invoke(exec.entrypoint_name, &[], &mut runtime)); + let result = sp_sandbox::Instance::new(&code, &imports, &mut runtime) + .and_then(|mut instance| instance.invoke(function.identifier(), &[], &mut runtime)); + runtime.to_execution_result(result) } + + fn code_hash(&self) -> &CodeHash { + &self.code_hash + } + + fn occupied_storage(&self) -> u32 { + // We disregard the size of the struct itself as the size is completely + // dominated by the code size. + let len = self.original_code_len.saturating_add(self.code.len() as u32); + len.checked_div(self.refcount as u32).unwrap_or(len) + } } #[cfg(test)] @@ -163,10 +229,9 @@ mod tests { use super::*; use crate::{ CodeHash, BalanceOf, Error, Module as Contracts, - exec::{Ext, StorageKey, AccountIdOf}, + exec::{Ext, StorageKey, AccountIdOf, Executable}, gas::{Gas, GasMeter}, tests::{Test, Call, ALICE, BOB}, - wasm::prepare::prepare_contract, }; use std::collections::HashMap; use sp_core::H256; @@ -220,6 +285,7 @@ mod tests { restores: Vec, // (topics, data) events: Vec<(Vec, Vec)>, + schedule: Schedule, } impl Ext for MockExt { @@ -234,7 +300,7 @@ mod tests { } fn instantiate( &mut self, - code_hash: &CodeHash, + code_hash: CodeHash, endowment: u64, gas_meter: &mut GasMeter, data: Vec, @@ -248,7 +314,7 @@ mod tests { salt: salt.to_vec(), }); Ok(( - Contracts::::contract_address(&ALICE, code_hash, salt), + Contracts::::contract_address(&ALICE, &code_hash, salt), ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new(), @@ -355,6 +421,10 @@ mod tests { fn get_weight_price(&self, weight: Weight) -> BalanceOf { BalanceOf::::from(1312_u32).saturating_mul(weight.into()) } + + fn schedule(&self) -> &Schedule { + &self.schedule + } } impl Ext for &mut MockExt { @@ -368,7 +438,7 @@ mod tests { } fn instantiate( &mut self, - code: &CodeHash, + code: CodeHash, value: u64, gas_meter: &mut GasMeter, input_data: Vec, @@ -454,6 +524,9 @@ mod tests { fn get_weight_price(&self, weight: Weight) -> BalanceOf { (**self).get_weight_price(weight) } + fn schedule(&self) -> &Schedule { + (**self).schedule() + } } fn execute( @@ -466,23 +539,10 @@ mod tests { ::AccountId: UncheckedFrom<::Hash> + AsRef<[u8]> { - use crate::exec::Vm; - let wasm = wat::parse_str(wat).unwrap(); let schedule = crate::Schedule::default(); - let prefab_module = - prepare_contract::(&wasm, &schedule).unwrap(); - - let exec = WasmExecutable { - // Use a "call" convention. - entrypoint_name: "call", - prefab_module, - }; - - let cfg = Default::default(); - let vm = WasmVm::new(&cfg); - - vm.execute(&exec, ext, input_data, gas_meter) + let executable = PrefabWasmModule::::from_code(wasm, &schedule).unwrap(); + executable.execute(ext, &ExportedFunction::Call, input_data, gas_meter) } const CODE_TRANSFER: &str = r#" diff --git a/frame/contracts/src/wasm/prepare.rs b/frame/contracts/src/wasm/prepare.rs index e03eb3d39bc11..caf6ef88c1ba0 100644 --- a/frame/contracts/src/wasm/prepare.rs +++ b/frame/contracts/src/wasm/prepare.rs @@ -25,7 +25,7 @@ use crate::{ wasm::{PrefabWasmModule, env_def::ImportSatisfyCheck}, }; use parity_wasm::elements::{self, Internal, External, MemoryType, Type, ValueType}; -use pwasm_utils; +use sp_runtime::traits::Hash; use sp_std::prelude::*; /// Currently, all imported functions must be located inside this module. We might support @@ -407,22 +407,11 @@ fn get_memory_limits(module: Option<&MemoryType>, schedule: &Schedule } } -/// Loads the given module given in `original_code`, performs some checks on it and -/// does some preprocessing. -/// -/// The checks are: -/// -/// - provided code is a valid wasm module. -/// - the module doesn't define an internal memory instance, -/// - 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 fn prepare_contract( +fn check_and_instrument( original_code: &[u8], schedule: &Schedule, -) -> Result { - let mut contract_module = ContractModule::new(original_code, schedule)?; +) -> Result<(Vec, (u32, u32)), &'static str> { + let contract_module = ContractModule::new(&original_code, schedule)?; contract_module.scan_exports()?; contract_module.ensure_no_internal_memory()?; contract_module.ensure_table_size_limit(schedule.limits.table_size)?; @@ -438,19 +427,65 @@ pub fn prepare_contract( schedule )?; - contract_module = contract_module + let code = contract_module .inject_gas_metering()? - .inject_stack_height_metering()?; + .inject_stack_height_metering()? + .into_wasm_code()?; + Ok((code, memory_limits)) +} + +fn do_preparation( + original_code: Vec, + schedule: &Schedule, +) -> Result, &'static str> { + let (code, (initial, maximum)) = check_and_instrument::( + original_code.as_ref(), + schedule, + )?; Ok(PrefabWasmModule { schedule_version: schedule.version, - initial: memory_limits.0, - maximum: memory_limits.1, + initial, + maximum, _reserved: None, - code: contract_module.into_wasm_code()?, + code, + original_code_len: original_code.len() as u32, + refcount: 1, + code_hash: T::Hashing::hash(&original_code), + original_code: Some(original_code), }) } +/// Loads the given module given in `original_code`, performs some checks on it and +/// does some preprocessing. +/// +/// The checks are: +/// +/// - provided code is a valid wasm module. +/// - the module doesn't define an internal memory instance, +/// - 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 fn prepare_contract( + original_code: Vec, + schedule: &Schedule, +) -> Result, &'static str> { + do_preparation::(original_code, schedule) +} + +/// The same as [`prepare_contract`] but without constructing a new [`PrefabWasmModule`] +/// +/// # Note +/// +/// Use this when an existing contract should be re-instrumented with a newer schedule version. +pub fn reinstrument_contract( + original_code: Vec, + schedule: &Schedule, +) -> Result, &'static str> { + Ok(check_and_instrument::(&original_code, schedule)?.0) +} + /// Alternate (possibly unsafe) preparation functions used only for benchmarking. /// /// For benchmarking we need to construct special contracts that might not pass our @@ -459,9 +494,7 @@ pub fn prepare_contract( /// in production code. #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking { - use super::{ - Config, ContractModule, PrefabWasmModule, ImportSatisfyCheck, Schedule, get_memory_limits - }; + use super::*; use parity_wasm::elements::FunctionType; impl ImportSatisfyCheck for () { @@ -471,10 +504,10 @@ pub mod benchmarking { } /// Prepare function that neither checks nor instruments the passed in code. - pub fn prepare_contract(original_code: &[u8], schedule: &Schedule) - -> Result + pub fn prepare_contract(original_code: Vec, schedule: &Schedule) + -> Result, &'static str> { - let contract_module = ContractModule::new(original_code, schedule)?; + let contract_module = ContractModule::new(&original_code, schedule)?; let memory_limits = get_memory_limits(contract_module.scan_imports::<()>(&[])?, schedule)?; Ok(PrefabWasmModule { schedule_version: schedule.version, @@ -482,6 +515,10 @@ pub mod benchmarking { maximum: memory_limits.1, _reserved: None, code: contract_module.into_wasm_code()?, + original_code_len: original_code.len() as u32, + refcount: 1, + code_hash: T::Hashing::hash(&original_code), + original_code: Some(original_code), }) } } @@ -493,7 +530,7 @@ mod tests { use std::fmt; use assert_matches::assert_matches; - impl fmt::Debug for PrefabWasmModule { + impl fmt::Debug for PrefabWasmModule { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "PreparedContract {{ .. }}") } @@ -534,7 +571,7 @@ mod tests { }, .. Default::default() }; - let r = prepare_contract::(wasm.as_ref(), &schedule); + let r = do_preparation::(wasm, &schedule); assert_matches!(r, $($expected)*); } }; @@ -945,7 +982,7 @@ mod tests { ).unwrap(); let mut schedule = Schedule::default(); schedule.enable_println = true; - let r = prepare_contract::(wasm.as_ref(), &schedule); + let r = do_preparation::(wasm, &schedule); assert_matches!(r, Ok(_)); } } diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index bab347b30cfd0..9dd098e852666 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -18,7 +18,7 @@ //! Environment definition of the wasm smart-contract runtime. use crate::{ - HostFnWeights, Schedule, Config, CodeHash, BalanceOf, Error, + HostFnWeights, Config, CodeHash, BalanceOf, Error, exec::{Ext, StorageKey, TopicOf}, gas::{Gas, GasMeter, Token, GasMeterResult, ChargedAmount}, wasm::env_def::ConvertibleToWasm, @@ -300,7 +300,6 @@ fn has_duplicates>(items: &mut Vec) -> bool { pub struct Runtime<'a, E: Ext + 'a> { ext: &'a mut E, input_data: Option>, - schedule: &'a Schedule, memory: sp_sandbox::Memory, gas_meter: &'a mut GasMeter, trap_reason: Option, @@ -315,14 +314,12 @@ where pub fn new( ext: &'a mut E, input_data: Vec, - schedule: &'a Schedule, memory: sp_sandbox::Memory, gas_meter: &'a mut GasMeter, ) -> Self { Runtime { ext, input_data: Some(input_data), - schedule, memory, gas_meter, trap_reason: None, @@ -411,7 +408,7 @@ where where Tok: Token>, { - match self.gas_meter.charge(&self.schedule.host_fn_weights, token) { + match self.gas_meter.charge(&self.ext.schedule().host_fn_weights, token) { GasMeterResult::Proceed(amount) => Ok(amount), GasMeterResult::OutOfGas => Err(Error::::OutOfGas.into()) } @@ -425,7 +422,7 @@ where pub fn read_sandbox_memory(&self, ptr: u32, len: u32) -> Result, DispatchError> { - ensure!(len <= self.schedule.limits.max_memory_size(), Error::::OutOfBounds); + ensure!(len <= self.ext.schedule().limits.max_memory_size(), Error::::OutOfBounds); let mut buf = vec![0u8; len as usize]; self.memory.get(ptr, buf.as_mut_slice()) .map_err(|_| Error::::OutOfBounds)?; @@ -889,7 +886,7 @@ define_env!(Env, , match nested_meter { Some(nested_meter) => { ext.instantiate( - &code_hash, + code_hash, value, nested_meter, input_data, @@ -1094,7 +1091,7 @@ define_env!(Env, , // The data is encoded as T::Hash. seal_random(ctx, subject_ptr: u32, subject_len: u32, out_ptr: u32, out_len_ptr: u32) => { ctx.charge_gas(RuntimeToken::Random)?; - if subject_len > ctx.schedule.limits.subject_len { + if subject_len > ctx.ext.schedule().limits.subject_len { Err(Error::::RandomSubjectTooLong)?; } let subject_buf = ctx.read_sandbox_memory(subject_ptr, subject_len)?; @@ -1205,7 +1202,7 @@ define_env!(Env, , // allocator can handle. ensure!( delta_count - .saturating_mul(KEY_SIZE as u32) <= ctx.schedule.limits.max_memory_size(), + .saturating_mul(KEY_SIZE as u32) <= ctx.ext.schedule().limits.max_memory_size(), Error::::OutOfBounds, ); let mut delta = vec![[0; KEY_SIZE]; delta_count as usize]; @@ -1253,7 +1250,7 @@ define_env!(Env, , }; // If there are more than `event_topics`, then trap. - if topics.len() > ctx.schedule.limits.event_topics as usize { + if topics.len() > ctx.ext.schedule().limits.event_topics as usize { Err(Error::::TooManyTopics)?; } diff --git a/frame/contracts/src/weights.rs b/frame/contracts/src/weights.rs index 60d229101816e..3660220451824 100644 --- a/frame/contracts/src/weights.rs +++ b/frame/contracts/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_contracts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.1 -//! DATE: 2021-01-12, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] +//! DATE: 2021-01-25, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: @@ -48,8 +48,8 @@ pub trait WeightInfo { fn on_initialize_per_trie_key(k: u32, ) -> Weight; fn on_initialize_per_queue_item(q: u32, ) -> Weight; fn update_schedule() -> Weight; - fn put_code(n: u32, ) -> Weight; - fn instantiate(n: u32, s: u32, ) -> Weight; + fn instantiate_with_code(c: u32, s: u32, ) -> Weight; + fn instantiate(s: u32, ) -> Weight; fn call() -> Weight; fn claim_surcharge() -> Weight; fn seal_caller(r: u32, ) -> Weight; @@ -150,247 +150,247 @@ pub trait WeightInfo { pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { fn on_initialize() -> Weight { - (3_659_000 as Weight) + (3_697_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) } fn on_initialize_per_trie_key(k: u32, ) -> Weight { - (40_731_000 as Weight) - // Standard Error: 4_000 - .saturating_add((2_317_000 as Weight).saturating_mul(k as Weight)) + (45_767_000 as Weight) + // Standard Error: 5_000 + .saturating_add((2_294_000 as Weight).saturating_mul(k as Weight)) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(k as Weight))) } fn on_initialize_per_queue_item(q: u32, ) -> Weight { - (384_459_000 as Weight) - // Standard Error: 45_000 - .saturating_add((146_401_000 as Weight).saturating_mul(q as Weight)) + (270_383_000 as Weight) + // Standard Error: 42_000 + .saturating_add((146_901_000 as Weight).saturating_mul(q as Weight)) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn update_schedule() -> Weight { - (27_803_000 as Weight) + (26_819_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn put_code(n: u32, ) -> Weight { + fn instantiate_with_code(c: u32, s: u32, ) -> Weight { (0 as Weight) - // Standard Error: 208_000 - .saturating_add((110_774_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) + // Standard Error: 135_000 + .saturating_add((156_679_000 as Weight).saturating_mul(c as Weight)) + // Standard Error: 67_000 + .saturating_add((2_794_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(6 as Weight)) + .saturating_add(T::DbWeight::get().writes(5 as Weight)) } - fn instantiate(n: u32, s: u32, ) -> Weight { - (175_290_000 as Weight) + fn instantiate(s: u32, ) -> Weight { + (189_974_000 as Weight) // Standard Error: 1_000 - .saturating_add((3_000 as Weight).saturating_mul(n as Weight)) - // Standard Error: 1_000 - .saturating_add((2_244_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((2_250_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(6 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn call() -> Weight { - (161_225_000 as Weight) + (168_719_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn claim_surcharge() -> Weight { - (283_759_000 as Weight) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) + (294_458_000 as Weight) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn seal_caller(r: u32, ) -> Weight { - (118_373_000 as Weight) - // Standard Error: 337_000 - .saturating_add((250_358_000 as Weight).saturating_mul(r as Weight)) + (123_683_000 as Weight) + // Standard Error: 115_000 + .saturating_add((255_734_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_address(r: u32, ) -> Weight { - (125_126_000 as Weight) - // Standard Error: 127_000 - .saturating_add((248_900_000 as Weight).saturating_mul(r as Weight)) + (120_904_000 as Weight) + // Standard Error: 96_000 + .saturating_add((255_431_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_gas_left(r: u32, ) -> Weight { - (127_087_000 as Weight) - // Standard Error: 145_000 - .saturating_add((243_311_000 as Weight).saturating_mul(r as Weight)) + (124_210_000 as Weight) + // Standard Error: 124_000 + .saturating_add((251_138_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_balance(r: u32, ) -> Weight { - (123_879_000 as Weight) - // Standard Error: 227_000 - .saturating_add((521_306_000 as Weight).saturating_mul(r as Weight)) + (127_626_000 as Weight) + // Standard Error: 192_000 + .saturating_add((528_716_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_value_transferred(r: u32, ) -> Weight { - (121_348_000 as Weight) - // Standard Error: 125_000 - .saturating_add((244_379_000 as Weight).saturating_mul(r as Weight)) + (117_016_000 as Weight) + // Standard Error: 109_000 + .saturating_add((250_620_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_minimum_balance(r: u32, ) -> Weight { - (120_680_000 as Weight) - // Standard Error: 107_000 - .saturating_add((244_096_000 as Weight).saturating_mul(r as Weight)) + (123_945_000 as Weight) + // Standard Error: 290_000 + .saturating_add((252_225_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_tombstone_deposit(r: u32, ) -> Weight { - (117_310_000 as Weight) - // Standard Error: 130_000 - .saturating_add((245_096_000 as Weight).saturating_mul(r as Weight)) + (119_625_000 as Weight) + // Standard Error: 132_000 + .saturating_add((250_486_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_rent_allowance(r: u32, ) -> Weight { - (131_643_000 as Weight) - // Standard Error: 171_000 - .saturating_add((554_208_000 as Weight).saturating_mul(r as Weight)) + (131_962_000 as Weight) + // Standard Error: 187_000 + .saturating_add((555_772_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_block_number(r: u32, ) -> Weight { - (117_553_000 as Weight) - // Standard Error: 128_000 - .saturating_add((244_494_000 as Weight).saturating_mul(r as Weight)) + (120_356_000 as Weight) + // Standard Error: 107_000 + .saturating_add((249_743_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_now(r: u32, ) -> Weight { - (123_184_000 as Weight) - // Standard Error: 116_000 - .saturating_add((244_414_000 as Weight).saturating_mul(r as Weight)) + (109_890_000 as Weight) + // Standard Error: 252_000 + .saturating_add((253_638_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_weight_to_fee(r: u32, ) -> Weight { - (132_846_000 as Weight) - // Standard Error: 189_000 - .saturating_add((482_450_000 as Weight).saturating_mul(r as Weight)) + (128_014_000 as Weight) + // Standard Error: 207_000 + .saturating_add((481_167_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(6 as Weight)) } fn seal_gas(r: u32, ) -> Weight { - (113_681_000 as Weight) - // Standard Error: 116_000 - .saturating_add((120_711_000 as Weight).saturating_mul(r as Weight)) + (108_147_000 as Weight) + // Standard Error: 101_000 + .saturating_add((122_462_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_input(r: u32, ) -> Weight { - (118_826_000 as Weight) - // Standard Error: 89_000 - .saturating_add((6_650_000 as Weight).saturating_mul(r as Weight)) + (117_045_000 as Weight) + // Standard Error: 57_000 + .saturating_add((7_168_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_input_per_kb(n: u32, ) -> Weight { - (132_497_000 as Weight) + (127_286_000 as Weight) // Standard Error: 0 .saturating_add((278_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_return(r: u32, ) -> Weight { - (112_447_000 as Weight) - // Standard Error: 73_000 - .saturating_add((4_398_000 as Weight).saturating_mul(r as Weight)) + (111_673_000 as Weight) + // Standard Error: 88_000 + .saturating_add((4_768_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_return_per_kb(n: u32, ) -> Weight { - (120_288_000 as Weight) - // Standard Error: 0 - .saturating_add((787_000 as Weight).saturating_mul(n as Weight)) + (113_767_000 as Weight) + // Standard Error: 4_000 + .saturating_add((745_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_terminate(r: u32, ) -> Weight { - (118_973_000 as Weight) - // Standard Error: 124_000 - .saturating_add((75_967_000 as Weight).saturating_mul(r as Weight)) + (117_714_000 as Weight) + // Standard Error: 82_000 + .saturating_add((92_096_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(r as Weight))) - .saturating_add(T::DbWeight::get().writes((4 as Weight).saturating_mul(r as Weight))) + .saturating_add(T::DbWeight::get().writes((5 as Weight).saturating_mul(r as Weight))) } fn seal_restore_to(r: u32, ) -> Weight { - (207_295_000 as Weight) - // Standard Error: 385_000 - .saturating_add((103_584_000 as Weight).saturating_mul(r as Weight)) + (208_895_000 as Weight) + // Standard Error: 312_000 + .saturating_add((125_607_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) - .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(r as Weight))) - .saturating_add(T::DbWeight::get().writes((4 as Weight).saturating_mul(r as Weight))) + .saturating_add(T::DbWeight::get().reads((4 as Weight).saturating_mul(r as Weight))) + .saturating_add(T::DbWeight::get().writes((6 as Weight).saturating_mul(r as Weight))) } fn seal_restore_to_per_delta(d: u32, ) -> Weight { (0 as Weight) - // Standard Error: 2_349_000 - .saturating_add((3_693_440_000 as Weight).saturating_mul(d as Weight)) - .saturating_add(T::DbWeight::get().reads(7 as Weight)) + // Standard Error: 2_920_000 + .saturating_add((3_575_765_000 as Weight).saturating_mul(d as Weight)) + .saturating_add(T::DbWeight::get().reads(8 as Weight)) .saturating_add(T::DbWeight::get().reads((100 as Weight).saturating_mul(d as Weight))) - .saturating_add(T::DbWeight::get().writes(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(7 as Weight)) .saturating_add(T::DbWeight::get().writes((100 as Weight).saturating_mul(d as Weight))) } fn seal_random(r: u32, ) -> Weight { - (166_160_000 as Weight) - // Standard Error: 237_000 - .saturating_add((594_474_000 as Weight).saturating_mul(r as Weight)) + (120_578_000 as Weight) + // Standard Error: 196_000 + .saturating_add((604_126_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(6 as Weight)) } fn seal_deposit_event(r: u32, ) -> Weight { - (145_170_000 as Weight) - // Standard Error: 397_000 - .saturating_add((859_096_000 as Weight).saturating_mul(r as Weight)) + (142_228_000 as Weight) + // Standard Error: 476_000 + .saturating_add((885_528_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32, ) -> Weight { - (1_128_905_000 as Weight) - // Standard Error: 4_299_000 - .saturating_add((559_485_000 as Weight).saturating_mul(t as Weight)) - // Standard Error: 847_000 - .saturating_add((253_404_000 as Weight).saturating_mul(n as Weight)) + (1_157_284_000 as Weight) + // Standard Error: 2_081_000 + .saturating_add((547_132_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 410_000 + .saturating_add((243_458_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().reads((100 as Weight).saturating_mul(t as Weight))) .saturating_add(T::DbWeight::get().writes((100 as Weight).saturating_mul(t as Weight))) } fn seal_set_rent_allowance(r: u32, ) -> Weight { - (127_849_000 as Weight) - // Standard Error: 220_000 - .saturating_add((628_543_000 as Weight).saturating_mul(r as Weight)) + (142_691_000 as Weight) + // Standard Error: 237_000 + .saturating_add((662_375_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn seal_set_storage(r: u32, ) -> Weight { - (0 as Weight) - // Standard Error: 45_695_000 - .saturating_add((17_015_513_000 as Weight).saturating_mul(r as Weight)) + (1_111_700_000 as Weight) + // Standard Error: 15_818_000 + .saturating_add((16_429_245_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().reads((100 as Weight).saturating_mul(r as Weight))) .saturating_add(T::DbWeight::get().writes(1 as Weight)) .saturating_add(T::DbWeight::get().writes((100 as Weight).saturating_mul(r as Weight))) } fn seal_set_storage_per_kb(n: u32, ) -> Weight { - (1_632_351_000 as Weight) - // Standard Error: 399_000 - .saturating_add((73_694_000 as Weight).saturating_mul(n as Weight)) + (1_613_716_000 as Weight) + // Standard Error: 339_000 + .saturating_add((67_360_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn seal_clear_storage(r: u32, ) -> Weight { (0 as Weight) - // Standard Error: 2_632_000 - .saturating_add((2_148_012_000 as Weight).saturating_mul(r as Weight)) + // Standard Error: 2_384_000 + .saturating_add((2_125_855_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().reads((100 as Weight).saturating_mul(r as Weight))) .saturating_add(T::DbWeight::get().writes(1 as Weight)) .saturating_add(T::DbWeight::get().writes((100 as Weight).saturating_mul(r as Weight))) } fn seal_get_storage(r: u32, ) -> Weight { - (48_127_000 as Weight) - // Standard Error: 1_123_000 - .saturating_add((906_947_000 as Weight).saturating_mul(r as Weight)) + (88_908_000 as Weight) + // Standard Error: 657_000 + .saturating_add((894_111_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().reads((100 as Weight).saturating_mul(r as Weight))) } fn seal_get_storage_per_kb(n: u32, ) -> Weight { - (676_986_000 as Weight) - // Standard Error: 307_000 - .saturating_add((153_667_000 as Weight).saturating_mul(n as Weight)) + (680_626_000 as Weight) + // Standard Error: 256_000 + .saturating_add((146_686_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(6 as Weight)) } fn seal_transfer(r: u32, ) -> Weight { - (36_730_000 as Weight) - // Standard Error: 1_966_000 - .saturating_add((3_972_101_000 as Weight).saturating_mul(r as Weight)) + (0 as Weight) + // Standard Error: 1_355_000 + .saturating_add((5_086_065_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().reads((100 as Weight).saturating_mul(r as Weight))) .saturating_add(T::DbWeight::get().writes(1 as Weight)) @@ -398,591 +398,591 @@ impl WeightInfo for SubstrateWeight { } fn seal_call(r: u32, ) -> Weight { (0 as Weight) - // Standard Error: 10_776_000 - .saturating_add((9_860_978_000 as Weight).saturating_mul(r as Weight)) + // Standard Error: 8_018_000 + .saturating_add((9_737_605_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().reads((200 as Weight).saturating_mul(r as Weight))) } fn seal_call_per_transfer_input_output_kb(t: u32, i: u32, o: u32, ) -> Weight { - (9_838_971_000 as Weight) - // Standard Error: 112_906_000 - .saturating_add((3_413_715_000 as Weight).saturating_mul(t as Weight)) - // Standard Error: 40_000 - .saturating_add((60_054_000 as Weight).saturating_mul(i as Weight)) - // Standard Error: 43_000 - .saturating_add((82_629_000 as Weight).saturating_mul(o as Weight)) + (6_776_517_000 as Weight) + // Standard Error: 181_875_000 + .saturating_add((3_769_181_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 64_000 + .saturating_add((57_763_000 as Weight).saturating_mul(i as Weight)) + // Standard Error: 69_000 + .saturating_add((79_752_000 as Weight).saturating_mul(o as Weight)) .saturating_add(T::DbWeight::get().reads(206 as Weight)) .saturating_add(T::DbWeight::get().writes((101 as Weight).saturating_mul(t as Weight))) } fn seal_instantiate(r: u32, ) -> Weight { (0 as Weight) - // Standard Error: 36_803_000 - .saturating_add((18_211_156_000 as Weight).saturating_mul(r as Weight)) + // Standard Error: 32_551_000 + .saturating_add((19_948_011_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().reads((300 as Weight).saturating_mul(r as Weight))) .saturating_add(T::DbWeight::get().writes(2 as Weight)) - .saturating_add(T::DbWeight::get().writes((200 as Weight).saturating_mul(r as Weight))) + .saturating_add(T::DbWeight::get().writes((300 as Weight).saturating_mul(r as Weight))) } fn seal_instantiate_per_input_output_salt_kb(i: u32, o: u32, s: u32, ) -> Weight { - (15_975_563_000 as Weight) - // Standard Error: 167_000 - .saturating_add((60_759_000 as Weight).saturating_mul(i as Weight)) - // Standard Error: 167_000 - .saturating_add((83_681_000 as Weight).saturating_mul(o as Weight)) - // Standard Error: 167_000 - .saturating_add((284_260_000 as Weight).saturating_mul(s as Weight)) + (19_812_400_000 as Weight) + // Standard Error: 80_000 + .saturating_add((53_676_000 as Weight).saturating_mul(i as Weight)) + // Standard Error: 80_000 + .saturating_add((76_512_000 as Weight).saturating_mul(o as Weight)) + // Standard Error: 80_000 + .saturating_add((274_518_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(207 as Weight)) - .saturating_add(T::DbWeight::get().writes(202 as Weight)) + .saturating_add(T::DbWeight::get().writes(203 as Weight)) } fn seal_hash_sha2_256(r: u32, ) -> Weight { - (120_795_000 as Weight) - // Standard Error: 115_000 - .saturating_add((226_658_000 as Weight).saturating_mul(r as Weight)) + (123_385_000 as Weight) + // Standard Error: 128_000 + .saturating_add((231_897_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight { - (731_640_000 as Weight) - // Standard Error: 56_000 - .saturating_add((430_102_000 as Weight).saturating_mul(n as Weight)) + (399_641_000 as Weight) + // Standard Error: 46_000 + .saturating_add((427_165_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_hash_keccak_256(r: u32, ) -> Weight { - (121_490_000 as Weight) - // Standard Error: 144_000 - .saturating_add((242_726_000 as Weight).saturating_mul(r as Weight)) + (120_367_000 as Weight) + // Standard Error: 131_000 + .saturating_add((247_401_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight { - (624_029_000 as Weight) - // Standard Error: 36_000 - .saturating_add((344_476_000 as Weight).saturating_mul(n as Weight)) + (150_485_000 as Weight) + // Standard Error: 39_000 + .saturating_add((337_450_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_hash_blake2_256(r: u32, ) -> Weight { - (120_959_000 as Weight) - // Standard Error: 103_000 - .saturating_add((215_519_000 as Weight).saturating_mul(r as Weight)) + (117_139_000 as Weight) + // Standard Error: 138_000 + .saturating_add((221_115_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight { - (713_448_000 as Weight) - // Standard Error: 47_000 - .saturating_add((160_493_000 as Weight).saturating_mul(n as Weight)) + (428_440_000 as Weight) + // Standard Error: 36_000 + .saturating_add((153_427_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_hash_blake2_128(r: u32, ) -> Weight { - (122_428_000 as Weight) - // Standard Error: 111_000 - .saturating_add((213_863_000 as Weight).saturating_mul(r as Weight)) + (120_716_000 as Weight) + // Standard Error: 116_000 + .saturating_add((218_086_000 as Weight).saturating_mul(r as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight { - (757_838_000 as Weight) - // Standard Error: 47_000 - .saturating_add((160_245_000 as Weight).saturating_mul(n as Weight)) + (478_148_000 as Weight) + // Standard Error: 45_000 + .saturating_add((153_952_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) } fn instr_i64const(r: u32, ) -> Weight { - (24_075_000 as Weight) - // Standard Error: 18_000 - .saturating_add((3_122_000 as Weight).saturating_mul(r as Weight)) + (23_526_000 as Weight) + // Standard Error: 19_000 + .saturating_add((3_125_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64load(r: u32, ) -> Weight { - (26_406_000 as Weight) - // Standard Error: 31_000 - .saturating_add((159_539_000 as Weight).saturating_mul(r as Weight)) + (25_653_000 as Weight) + // Standard Error: 17_000 + .saturating_add((159_121_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64store(r: u32, ) -> Weight { - (26_266_000 as Weight) - // Standard Error: 3_229_000 - .saturating_add((238_726_000 as Weight).saturating_mul(r as Weight)) + (25_608_000 as Weight) + // Standard Error: 26_000 + .saturating_add((229_680_000 as Weight).saturating_mul(r as Weight)) } fn instr_select(r: u32, ) -> Weight { - (27_469_000 as Weight) - // Standard Error: 592_000 - .saturating_add((10_423_000 as Weight).saturating_mul(r as Weight)) + (24_053_000 as Weight) + // Standard Error: 19_000 + .saturating_add((11_768_000 as Weight).saturating_mul(r as Weight)) } fn instr_if(r: u32, ) -> Weight { - (24_627_000 as Weight) - // Standard Error: 29_000 - .saturating_add((11_999_000 as Weight).saturating_mul(r as Weight)) + (23_478_000 as Weight) + // Standard Error: 16_000 + .saturating_add((11_992_000 as Weight).saturating_mul(r as Weight)) } fn instr_br(r: u32, ) -> Weight { - (24_008_000 as Weight) - // Standard Error: 22_000 - .saturating_add((6_614_000 as Weight).saturating_mul(r as Weight)) + (23_418_000 as Weight) + // Standard Error: 15_000 + .saturating_add((5_936_000 as Weight).saturating_mul(r as Weight)) } fn instr_br_if(r: u32, ) -> Weight { - (24_040_000 as Weight) - // Standard Error: 20_000 - .saturating_add((14_190_000 as Weight).saturating_mul(r as Weight)) + (23_380_000 as Weight) + // Standard Error: 10_000 + .saturating_add((13_844_000 as Weight).saturating_mul(r as Weight)) } fn instr_br_table(r: u32, ) -> Weight { - (23_997_000 as Weight) - // Standard Error: 24_000 - .saturating_add((15_529_000 as Weight).saturating_mul(r as Weight)) + (23_509_000 as Weight) + // Standard Error: 11_000 + .saturating_add((14_912_000 as Weight).saturating_mul(r as Weight)) } fn instr_br_table_per_entry(e: u32, ) -> Weight { - (36_890_000 as Weight) + (36_616_000 as Weight) // Standard Error: 1_000 - .saturating_add((112_000 as Weight).saturating_mul(e as Weight)) + .saturating_add((104_000 as Weight).saturating_mul(e as Weight)) } fn instr_call(r: u32, ) -> Weight { - (24_266_000 as Weight) - // Standard Error: 198_000 - .saturating_add((99_702_000 as Weight).saturating_mul(r as Weight)) + (23_821_000 as Weight) + // Standard Error: 49_000 + .saturating_add((96_843_000 as Weight).saturating_mul(r as Weight)) } fn instr_call_indirect(r: u32, ) -> Weight { - (31_901_000 as Weight) - // Standard Error: 322_000 - .saturating_add((197_671_000 as Weight).saturating_mul(r as Weight)) + (31_502_000 as Weight) + // Standard Error: 523_000 + .saturating_add((196_243_000 as Weight).saturating_mul(r as Weight)) } fn instr_call_indirect_per_param(p: u32, ) -> Weight { - (239_803_000 as Weight) - // Standard Error: 5_000 - .saturating_add((3_474_000 as Weight).saturating_mul(p as Weight)) + (242_403_000 as Weight) + // Standard Error: 9_000 + .saturating_add((3_443_000 as Weight).saturating_mul(p as Weight)) } fn instr_local_get(r: u32, ) -> Weight { - (41_697_000 as Weight) - // Standard Error: 15_000 - .saturating_add((3_225_000 as Weight).saturating_mul(r as Weight)) + (40_816_000 as Weight) + // Standard Error: 20_000 + .saturating_add((3_178_000 as Weight).saturating_mul(r as Weight)) } fn instr_local_set(r: u32, ) -> Weight { - (41_698_000 as Weight) - // Standard Error: 13_000 - .saturating_add((3_458_000 as Weight).saturating_mul(r as Weight)) + (40_778_000 as Weight) + // Standard Error: 17_000 + .saturating_add((3_507_000 as Weight).saturating_mul(r as Weight)) } fn instr_local_tee(r: u32, ) -> Weight { - (41_715_000 as Weight) - // Standard Error: 19_000 - .saturating_add((4_684_000 as Weight).saturating_mul(r as Weight)) + (40_808_000 as Weight) + // Standard Error: 15_000 + .saturating_add((4_775_000 as Weight).saturating_mul(r as Weight)) } fn instr_global_get(r: u32, ) -> Weight { - (27_751_000 as Weight) - // Standard Error: 20_000 - .saturating_add((7_980_000 as Weight).saturating_mul(r as Weight)) + (26_983_000 as Weight) + // Standard Error: 32_000 + .saturating_add((8_878_000 as Weight).saturating_mul(r as Weight)) } fn instr_global_set(r: u32, ) -> Weight { - (27_632_000 as Weight) - // Standard Error: 21_000 - .saturating_add((12_050_000 as Weight).saturating_mul(r as Weight)) + (26_975_000 as Weight) + // Standard Error: 34_000 + .saturating_add((12_236_000 as Weight).saturating_mul(r as Weight)) } fn instr_memory_current(r: u32, ) -> Weight { - (26_302_000 as Weight) - // Standard Error: 25_000 - .saturating_add((3_480_000 as Weight).saturating_mul(r as Weight)) + (25_691_000 as Weight) + // Standard Error: 22_000 + .saturating_add((3_577_000 as Weight).saturating_mul(r as Weight)) } fn instr_memory_grow(r: u32, ) -> Weight { - (24_695_000 as Weight) - // Standard Error: 3_876_000 - .saturating_add((2_324_806_000 as Weight).saturating_mul(r as Weight)) + (24_245_000 as Weight) + // Standard Error: 3_933_000 + .saturating_add((2_305_850_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64clz(r: u32, ) -> Weight { - (24_043_000 as Weight) - // Standard Error: 13_000 - .saturating_add((5_187_000 as Weight).saturating_mul(r as Weight)) + (23_495_000 as Weight) + // Standard Error: 28_000 + .saturating_add((5_186_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ctz(r: u32, ) -> Weight { - (24_040_000 as Weight) - // Standard Error: 14_000 - .saturating_add((5_077_000 as Weight).saturating_mul(r as Weight)) + (23_441_000 as Weight) + // Standard Error: 16_000 + .saturating_add((5_224_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64popcnt(r: u32, ) -> Weight { - (23_995_000 as Weight) - // Standard Error: 18_000 - .saturating_add((5_801_000 as Weight).saturating_mul(r as Weight)) + (23_507_000 as Weight) + // Standard Error: 13_000 + .saturating_add((5_820_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64eqz(r: u32, ) -> Weight { - (24_010_000 as Weight) - // Standard Error: 12_000 - .saturating_add((5_221_000 as Weight).saturating_mul(r as Weight)) + (23_475_000 as Weight) + // Standard Error: 19_000 + .saturating_add((5_244_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64extendsi32(r: u32, ) -> Weight { - (24_073_000 as Weight) - // Standard Error: 18_000 - .saturating_add((5_205_000 as Weight).saturating_mul(r as Weight)) + (23_437_000 as Weight) + // Standard Error: 14_000 + .saturating_add((5_204_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64extendui32(r: u32, ) -> Weight { - (23_993_000 as Weight) - // Standard Error: 17_000 - .saturating_add((5_079_000 as Weight).saturating_mul(r as Weight)) + (23_434_000 as Weight) + // Standard Error: 16_000 + .saturating_add((5_211_000 as Weight).saturating_mul(r as Weight)) } fn instr_i32wrapi64(r: u32, ) -> Weight { - (24_008_000 as Weight) + (23_454_000 as Weight) // Standard Error: 16_000 - .saturating_add((5_077_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((5_181_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64eq(r: u32, ) -> Weight { - (23_991_000 as Weight) - // Standard Error: 17_000 - .saturating_add((7_248_000 as Weight).saturating_mul(r as Weight)) + (23_470_000 as Weight) + // Standard Error: 21_000 + .saturating_add((7_257_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ne(r: u32, ) -> Weight { - (23_983_000 as Weight) + (23_475_000 as Weight) // Standard Error: 21_000 - .saturating_add((7_303_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((7_132_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64lts(r: u32, ) -> Weight { - (23_991_000 as Weight) - // Standard Error: 21_000 - .saturating_add((7_106_000 as Weight).saturating_mul(r as Weight)) + (23_418_000 as Weight) + // Standard Error: 22_000 + .saturating_add((7_199_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ltu(r: u32, ) -> Weight { - (24_062_000 as Weight) + (23_478_000 as Weight) // Standard Error: 25_000 - .saturating_add((7_168_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((7_278_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64gts(r: u32, ) -> Weight { - (24_028_000 as Weight) - // Standard Error: 26_000 - .saturating_add((7_130_000 as Weight).saturating_mul(r as Weight)) + (23_471_000 as Weight) + // Standard Error: 25_000 + .saturating_add((7_134_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64gtu(r: u32, ) -> Weight { - (23_998_000 as Weight) - // Standard Error: 18_000 - .saturating_add((7_279_000 as Weight).saturating_mul(r as Weight)) + (23_448_000 as Weight) + // Standard Error: 20_000 + .saturating_add((7_260_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64les(r: u32, ) -> Weight { - (24_010_000 as Weight) - // Standard Error: 19_000 - .saturating_add((7_114_000 as Weight).saturating_mul(r as Weight)) + (23_409_000 as Weight) + // Standard Error: 26_000 + .saturating_add((7_064_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64leu(r: u32, ) -> Weight { - (24_003_000 as Weight) - // Standard Error: 13_000 - .saturating_add((7_052_000 as Weight).saturating_mul(r as Weight)) + (23_433_000 as Weight) + // Standard Error: 18_000 + .saturating_add((7_088_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ges(r: u32, ) -> Weight { - (23_948_000 as Weight) - // Standard Error: 15_000 - .saturating_add((7_236_000 as Weight).saturating_mul(r as Weight)) + (23_425_000 as Weight) + // Standard Error: 28_000 + .saturating_add((7_152_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64geu(r: u32, ) -> Weight { - (24_042_000 as Weight) - // Standard Error: 19_000 - .saturating_add((7_223_000 as Weight).saturating_mul(r as Weight)) + (23_474_000 as Weight) + // Standard Error: 17_000 + .saturating_add((7_204_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64add(r: u32, ) -> Weight { - (23_965_000 as Weight) - // Standard Error: 37_000 - .saturating_add((7_261_000 as Weight).saturating_mul(r as Weight)) + (23_431_000 as Weight) + // Standard Error: 24_000 + .saturating_add((7_105_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64sub(r: u32, ) -> Weight { - (24_023_000 as Weight) - // Standard Error: 26_000 - .saturating_add((7_170_000 as Weight).saturating_mul(r as Weight)) + (23_423_000 as Weight) + // Standard Error: 21_000 + .saturating_add((7_094_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64mul(r: u32, ) -> Weight { - (24_057_000 as Weight) - // Standard Error: 17_000 - .saturating_add((7_050_000 as Weight).saturating_mul(r as Weight)) + (23_407_000 as Weight) + // Standard Error: 16_000 + .saturating_add((7_149_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64divs(r: u32, ) -> Weight { - (24_038_000 as Weight) - // Standard Error: 15_000 - .saturating_add((12_934_000 as Weight).saturating_mul(r as Weight)) + (23_437_000 as Weight) + // Standard Error: 23_000 + .saturating_add((13_007_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64divu(r: u32, ) -> Weight { - (23_992_000 as Weight) - // Standard Error: 15_000 - .saturating_add((12_055_000 as Weight).saturating_mul(r as Weight)) + (23_405_000 as Weight) + // Standard Error: 22_000 + .saturating_add((12_259_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64rems(r: u32, ) -> Weight { - (24_082_000 as Weight) - // Standard Error: 18_000 - .saturating_add((12_898_000 as Weight).saturating_mul(r as Weight)) + (23_469_000 as Weight) + // Standard Error: 12_000 + .saturating_add((12_950_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64remu(r: u32, ) -> Weight { - (24_025_000 as Weight) + (23_460_000 as Weight) // Standard Error: 13_000 - .saturating_add((12_178_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((12_249_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64and(r: u32, ) -> Weight { - (23_984_000 as Weight) - // Standard Error: 18_000 - .saturating_add((7_214_000 as Weight).saturating_mul(r as Weight)) + (23_434_000 as Weight) + // Standard Error: 22_000 + .saturating_add((7_111_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64or(r: u32, ) -> Weight { - (24_012_000 as Weight) - // Standard Error: 16_000 - .saturating_add((7_183_000 as Weight).saturating_mul(r as Weight)) + (23_481_000 as Weight) + // Standard Error: 17_000 + .saturating_add((7_010_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64xor(r: u32, ) -> Weight { - (24_001_000 as Weight) - // Standard Error: 18_000 - .saturating_add((7_122_000 as Weight).saturating_mul(r as Weight)) + (23_500_000 as Weight) + // Standard Error: 34_000 + .saturating_add((7_074_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64shl(r: u32, ) -> Weight { - (23_973_000 as Weight) - // Standard Error: 13_000 - .saturating_add((7_251_000 as Weight).saturating_mul(r as Weight)) + (23_477_000 as Weight) + // Standard Error: 28_000 + .saturating_add((7_220_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64shrs(r: u32, ) -> Weight { - (23_969_000 as Weight) - // Standard Error: 14_000 - .saturating_add((7_289_000 as Weight).saturating_mul(r as Weight)) + (23_433_000 as Weight) + // Standard Error: 24_000 + .saturating_add((7_305_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64shru(r: u32, ) -> Weight { - (24_008_000 as Weight) - // Standard Error: 15_000 - .saturating_add((7_292_000 as Weight).saturating_mul(r as Weight)) + (23_413_000 as Weight) + // Standard Error: 18_000 + .saturating_add((7_299_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64rotl(r: u32, ) -> Weight { - (24_010_000 as Weight) - // Standard Error: 21_000 - .saturating_add((7_305_000 as Weight).saturating_mul(r as Weight)) + (23_468_000 as Weight) + // Standard Error: 22_000 + .saturating_add((7_204_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64rotr(r: u32, ) -> Weight { - (24_001_000 as Weight) - // Standard Error: 22_000 - .saturating_add((7_299_000 as Weight).saturating_mul(r as Weight)) + (23_434_000 as Weight) + // Standard Error: 32_000 + .saturating_add((7_255_000 as Weight).saturating_mul(r as Weight)) } } // For backwards compatibility and tests impl WeightInfo for () { fn on_initialize() -> Weight { - (3_659_000 as Weight) + (3_697_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) } fn on_initialize_per_trie_key(k: u32, ) -> Weight { - (40_731_000 as Weight) - // Standard Error: 4_000 - .saturating_add((2_317_000 as Weight).saturating_mul(k as Weight)) + (45_767_000 as Weight) + // Standard Error: 5_000 + .saturating_add((2_294_000 as Weight).saturating_mul(k as Weight)) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(k as Weight))) } fn on_initialize_per_queue_item(q: u32, ) -> Weight { - (384_459_000 as Weight) - // Standard Error: 45_000 - .saturating_add((146_401_000 as Weight).saturating_mul(q as Weight)) + (270_383_000 as Weight) + // Standard Error: 42_000 + .saturating_add((146_901_000 as Weight).saturating_mul(q as Weight)) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn update_schedule() -> Weight { - (27_803_000 as Weight) + (26_819_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn put_code(n: u32, ) -> Weight { + fn instantiate_with_code(c: u32, s: u32, ) -> Weight { (0 as Weight) - // Standard Error: 208_000 - .saturating_add((110_774_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + // Standard Error: 135_000 + .saturating_add((156_679_000 as Weight).saturating_mul(c as Weight)) + // Standard Error: 67_000 + .saturating_add((2_794_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(RocksDbWeight::get().reads(6 as Weight)) + .saturating_add(RocksDbWeight::get().writes(5 as Weight)) } - fn instantiate(n: u32, s: u32, ) -> Weight { - (175_290_000 as Weight) + fn instantiate(s: u32, ) -> Weight { + (189_974_000 as Weight) // Standard Error: 1_000 - .saturating_add((3_000 as Weight).saturating_mul(n as Weight)) - // Standard Error: 1_000 - .saturating_add((2_244_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((2_250_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn call() -> Weight { - (161_225_000 as Weight) + (168_719_000 as Weight) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn claim_surcharge() -> Weight { - (283_759_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + (294_458_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn seal_caller(r: u32, ) -> Weight { - (118_373_000 as Weight) - // Standard Error: 337_000 - .saturating_add((250_358_000 as Weight).saturating_mul(r as Weight)) + (123_683_000 as Weight) + // Standard Error: 115_000 + .saturating_add((255_734_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_address(r: u32, ) -> Weight { - (125_126_000 as Weight) - // Standard Error: 127_000 - .saturating_add((248_900_000 as Weight).saturating_mul(r as Weight)) + (120_904_000 as Weight) + // Standard Error: 96_000 + .saturating_add((255_431_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_gas_left(r: u32, ) -> Weight { - (127_087_000 as Weight) - // Standard Error: 145_000 - .saturating_add((243_311_000 as Weight).saturating_mul(r as Weight)) + (124_210_000 as Weight) + // Standard Error: 124_000 + .saturating_add((251_138_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_balance(r: u32, ) -> Weight { - (123_879_000 as Weight) - // Standard Error: 227_000 - .saturating_add((521_306_000 as Weight).saturating_mul(r as Weight)) + (127_626_000 as Weight) + // Standard Error: 192_000 + .saturating_add((528_716_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_value_transferred(r: u32, ) -> Weight { - (121_348_000 as Weight) - // Standard Error: 125_000 - .saturating_add((244_379_000 as Weight).saturating_mul(r as Weight)) + (117_016_000 as Weight) + // Standard Error: 109_000 + .saturating_add((250_620_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_minimum_balance(r: u32, ) -> Weight { - (120_680_000 as Weight) - // Standard Error: 107_000 - .saturating_add((244_096_000 as Weight).saturating_mul(r as Weight)) + (123_945_000 as Weight) + // Standard Error: 290_000 + .saturating_add((252_225_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_tombstone_deposit(r: u32, ) -> Weight { - (117_310_000 as Weight) - // Standard Error: 130_000 - .saturating_add((245_096_000 as Weight).saturating_mul(r as Weight)) + (119_625_000 as Weight) + // Standard Error: 132_000 + .saturating_add((250_486_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_rent_allowance(r: u32, ) -> Weight { - (131_643_000 as Weight) - // Standard Error: 171_000 - .saturating_add((554_208_000 as Weight).saturating_mul(r as Weight)) + (131_962_000 as Weight) + // Standard Error: 187_000 + .saturating_add((555_772_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_block_number(r: u32, ) -> Weight { - (117_553_000 as Weight) - // Standard Error: 128_000 - .saturating_add((244_494_000 as Weight).saturating_mul(r as Weight)) + (120_356_000 as Weight) + // Standard Error: 107_000 + .saturating_add((249_743_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_now(r: u32, ) -> Weight { - (123_184_000 as Weight) - // Standard Error: 116_000 - .saturating_add((244_414_000 as Weight).saturating_mul(r as Weight)) + (109_890_000 as Weight) + // Standard Error: 252_000 + .saturating_add((253_638_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_weight_to_fee(r: u32, ) -> Weight { - (132_846_000 as Weight) - // Standard Error: 189_000 - .saturating_add((482_450_000 as Weight).saturating_mul(r as Weight)) + (128_014_000 as Weight) + // Standard Error: 207_000 + .saturating_add((481_167_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) } fn seal_gas(r: u32, ) -> Weight { - (113_681_000 as Weight) - // Standard Error: 116_000 - .saturating_add((120_711_000 as Weight).saturating_mul(r as Weight)) + (108_147_000 as Weight) + // Standard Error: 101_000 + .saturating_add((122_462_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_input(r: u32, ) -> Weight { - (118_826_000 as Weight) - // Standard Error: 89_000 - .saturating_add((6_650_000 as Weight).saturating_mul(r as Weight)) + (117_045_000 as Weight) + // Standard Error: 57_000 + .saturating_add((7_168_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_input_per_kb(n: u32, ) -> Weight { - (132_497_000 as Weight) + (127_286_000 as Weight) // Standard Error: 0 .saturating_add((278_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_return(r: u32, ) -> Weight { - (112_447_000 as Weight) - // Standard Error: 73_000 - .saturating_add((4_398_000 as Weight).saturating_mul(r as Weight)) + (111_673_000 as Weight) + // Standard Error: 88_000 + .saturating_add((4_768_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_return_per_kb(n: u32, ) -> Weight { - (120_288_000 as Weight) - // Standard Error: 0 - .saturating_add((787_000 as Weight).saturating_mul(n as Weight)) + (113_767_000 as Weight) + // Standard Error: 4_000 + .saturating_add((745_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_terminate(r: u32, ) -> Weight { - (118_973_000 as Weight) - // Standard Error: 124_000 - .saturating_add((75_967_000 as Weight).saturating_mul(r as Weight)) + (117_714_000 as Weight) + // Standard Error: 82_000 + .saturating_add((92_096_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(r as Weight))) - .saturating_add(RocksDbWeight::get().writes((4 as Weight).saturating_mul(r as Weight))) + .saturating_add(RocksDbWeight::get().writes((5 as Weight).saturating_mul(r as Weight))) } fn seal_restore_to(r: u32, ) -> Weight { - (207_295_000 as Weight) - // Standard Error: 385_000 - .saturating_add((103_584_000 as Weight).saturating_mul(r as Weight)) + (208_895_000 as Weight) + // Standard Error: 312_000 + .saturating_add((125_607_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) - .saturating_add(RocksDbWeight::get().reads((3 as Weight).saturating_mul(r as Weight))) - .saturating_add(RocksDbWeight::get().writes((4 as Weight).saturating_mul(r as Weight))) + .saturating_add(RocksDbWeight::get().reads((4 as Weight).saturating_mul(r as Weight))) + .saturating_add(RocksDbWeight::get().writes((6 as Weight).saturating_mul(r as Weight))) } fn seal_restore_to_per_delta(d: u32, ) -> Weight { (0 as Weight) - // Standard Error: 2_349_000 - .saturating_add((3_693_440_000 as Weight).saturating_mul(d as Weight)) - .saturating_add(RocksDbWeight::get().reads(7 as Weight)) + // Standard Error: 2_920_000 + .saturating_add((3_575_765_000 as Weight).saturating_mul(d as Weight)) + .saturating_add(RocksDbWeight::get().reads(8 as Weight)) .saturating_add(RocksDbWeight::get().reads((100 as Weight).saturating_mul(d as Weight))) - .saturating_add(RocksDbWeight::get().writes(5 as Weight)) + .saturating_add(RocksDbWeight::get().writes(7 as Weight)) .saturating_add(RocksDbWeight::get().writes((100 as Weight).saturating_mul(d as Weight))) } fn seal_random(r: u32, ) -> Weight { - (166_160_000 as Weight) - // Standard Error: 237_000 - .saturating_add((594_474_000 as Weight).saturating_mul(r as Weight)) + (120_578_000 as Weight) + // Standard Error: 196_000 + .saturating_add((604_126_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) } fn seal_deposit_event(r: u32, ) -> Weight { - (145_170_000 as Weight) - // Standard Error: 397_000 - .saturating_add((859_096_000 as Weight).saturating_mul(r as Weight)) + (142_228_000 as Weight) + // Standard Error: 476_000 + .saturating_add((885_528_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32, ) -> Weight { - (1_128_905_000 as Weight) - // Standard Error: 4_299_000 - .saturating_add((559_485_000 as Weight).saturating_mul(t as Weight)) - // Standard Error: 847_000 - .saturating_add((253_404_000 as Weight).saturating_mul(n as Weight)) + (1_157_284_000 as Weight) + // Standard Error: 2_081_000 + .saturating_add((547_132_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 410_000 + .saturating_add((243_458_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().reads((100 as Weight).saturating_mul(t as Weight))) .saturating_add(RocksDbWeight::get().writes((100 as Weight).saturating_mul(t as Weight))) } fn seal_set_rent_allowance(r: u32, ) -> Weight { - (127_849_000 as Weight) - // Standard Error: 220_000 - .saturating_add((628_543_000 as Weight).saturating_mul(r as Weight)) + (142_691_000 as Weight) + // Standard Error: 237_000 + .saturating_add((662_375_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn seal_set_storage(r: u32, ) -> Weight { - (0 as Weight) - // Standard Error: 45_695_000 - .saturating_add((17_015_513_000 as Weight).saturating_mul(r as Weight)) + (1_111_700_000 as Weight) + // Standard Error: 15_818_000 + .saturating_add((16_429_245_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().reads((100 as Weight).saturating_mul(r as Weight))) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) .saturating_add(RocksDbWeight::get().writes((100 as Weight).saturating_mul(r as Weight))) } fn seal_set_storage_per_kb(n: u32, ) -> Weight { - (1_632_351_000 as Weight) - // Standard Error: 399_000 - .saturating_add((73_694_000 as Weight).saturating_mul(n as Weight)) + (1_613_716_000 as Weight) + // Standard Error: 339_000 + .saturating_add((67_360_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn seal_clear_storage(r: u32, ) -> Weight { (0 as Weight) - // Standard Error: 2_632_000 - .saturating_add((2_148_012_000 as Weight).saturating_mul(r as Weight)) + // Standard Error: 2_384_000 + .saturating_add((2_125_855_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().reads((100 as Weight).saturating_mul(r as Weight))) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) .saturating_add(RocksDbWeight::get().writes((100 as Weight).saturating_mul(r as Weight))) } fn seal_get_storage(r: u32, ) -> Weight { - (48_127_000 as Weight) - // Standard Error: 1_123_000 - .saturating_add((906_947_000 as Weight).saturating_mul(r as Weight)) + (88_908_000 as Weight) + // Standard Error: 657_000 + .saturating_add((894_111_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().reads((100 as Weight).saturating_mul(r as Weight))) } fn seal_get_storage_per_kb(n: u32, ) -> Weight { - (676_986_000 as Weight) - // Standard Error: 307_000 - .saturating_add((153_667_000 as Weight).saturating_mul(n as Weight)) + (680_626_000 as Weight) + // Standard Error: 256_000 + .saturating_add((146_686_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) } fn seal_transfer(r: u32, ) -> Weight { - (36_730_000 as Weight) - // Standard Error: 1_966_000 - .saturating_add((3_972_101_000 as Weight).saturating_mul(r as Weight)) + (0 as Weight) + // Standard Error: 1_355_000 + .saturating_add((5_086_065_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().reads((100 as Weight).saturating_mul(r as Weight))) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) @@ -990,343 +990,343 @@ impl WeightInfo for () { } fn seal_call(r: u32, ) -> Weight { (0 as Weight) - // Standard Error: 10_776_000 - .saturating_add((9_860_978_000 as Weight).saturating_mul(r as Weight)) + // Standard Error: 8_018_000 + .saturating_add((9_737_605_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().reads((200 as Weight).saturating_mul(r as Weight))) } fn seal_call_per_transfer_input_output_kb(t: u32, i: u32, o: u32, ) -> Weight { - (9_838_971_000 as Weight) - // Standard Error: 112_906_000 - .saturating_add((3_413_715_000 as Weight).saturating_mul(t as Weight)) - // Standard Error: 40_000 - .saturating_add((60_054_000 as Weight).saturating_mul(i as Weight)) - // Standard Error: 43_000 - .saturating_add((82_629_000 as Weight).saturating_mul(o as Weight)) + (6_776_517_000 as Weight) + // Standard Error: 181_875_000 + .saturating_add((3_769_181_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 64_000 + .saturating_add((57_763_000 as Weight).saturating_mul(i as Weight)) + // Standard Error: 69_000 + .saturating_add((79_752_000 as Weight).saturating_mul(o as Weight)) .saturating_add(RocksDbWeight::get().reads(206 as Weight)) .saturating_add(RocksDbWeight::get().writes((101 as Weight).saturating_mul(t as Weight))) } fn seal_instantiate(r: u32, ) -> Weight { (0 as Weight) - // Standard Error: 36_803_000 - .saturating_add((18_211_156_000 as Weight).saturating_mul(r as Weight)) + // Standard Error: 32_551_000 + .saturating_add((19_948_011_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().reads((300 as Weight).saturating_mul(r as Weight))) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes((200 as Weight).saturating_mul(r as Weight))) + .saturating_add(RocksDbWeight::get().writes((300 as Weight).saturating_mul(r as Weight))) } fn seal_instantiate_per_input_output_salt_kb(i: u32, o: u32, s: u32, ) -> Weight { - (15_975_563_000 as Weight) - // Standard Error: 167_000 - .saturating_add((60_759_000 as Weight).saturating_mul(i as Weight)) - // Standard Error: 167_000 - .saturating_add((83_681_000 as Weight).saturating_mul(o as Weight)) - // Standard Error: 167_000 - .saturating_add((284_260_000 as Weight).saturating_mul(s as Weight)) + (19_812_400_000 as Weight) + // Standard Error: 80_000 + .saturating_add((53_676_000 as Weight).saturating_mul(i as Weight)) + // Standard Error: 80_000 + .saturating_add((76_512_000 as Weight).saturating_mul(o as Weight)) + // Standard Error: 80_000 + .saturating_add((274_518_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(207 as Weight)) - .saturating_add(RocksDbWeight::get().writes(202 as Weight)) + .saturating_add(RocksDbWeight::get().writes(203 as Weight)) } fn seal_hash_sha2_256(r: u32, ) -> Weight { - (120_795_000 as Weight) - // Standard Error: 115_000 - .saturating_add((226_658_000 as Weight).saturating_mul(r as Weight)) + (123_385_000 as Weight) + // Standard Error: 128_000 + .saturating_add((231_897_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight { - (731_640_000 as Weight) - // Standard Error: 56_000 - .saturating_add((430_102_000 as Weight).saturating_mul(n as Weight)) + (399_641_000 as Weight) + // Standard Error: 46_000 + .saturating_add((427_165_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_hash_keccak_256(r: u32, ) -> Weight { - (121_490_000 as Weight) - // Standard Error: 144_000 - .saturating_add((242_726_000 as Weight).saturating_mul(r as Weight)) + (120_367_000 as Weight) + // Standard Error: 131_000 + .saturating_add((247_401_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight { - (624_029_000 as Weight) - // Standard Error: 36_000 - .saturating_add((344_476_000 as Weight).saturating_mul(n as Weight)) + (150_485_000 as Weight) + // Standard Error: 39_000 + .saturating_add((337_450_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_hash_blake2_256(r: u32, ) -> Weight { - (120_959_000 as Weight) - // Standard Error: 103_000 - .saturating_add((215_519_000 as Weight).saturating_mul(r as Weight)) + (117_139_000 as Weight) + // Standard Error: 138_000 + .saturating_add((221_115_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight { - (713_448_000 as Weight) - // Standard Error: 47_000 - .saturating_add((160_493_000 as Weight).saturating_mul(n as Weight)) + (428_440_000 as Weight) + // Standard Error: 36_000 + .saturating_add((153_427_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_hash_blake2_128(r: u32, ) -> Weight { - (122_428_000 as Weight) - // Standard Error: 111_000 - .saturating_add((213_863_000 as Weight).saturating_mul(r as Weight)) + (120_716_000 as Weight) + // Standard Error: 116_000 + .saturating_add((218_086_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight { - (757_838_000 as Weight) - // Standard Error: 47_000 - .saturating_add((160_245_000 as Weight).saturating_mul(n as Weight)) + (478_148_000 as Weight) + // Standard Error: 45_000 + .saturating_add((153_952_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) } fn instr_i64const(r: u32, ) -> Weight { - (24_075_000 as Weight) - // Standard Error: 18_000 - .saturating_add((3_122_000 as Weight).saturating_mul(r as Weight)) + (23_526_000 as Weight) + // Standard Error: 19_000 + .saturating_add((3_125_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64load(r: u32, ) -> Weight { - (26_406_000 as Weight) - // Standard Error: 31_000 - .saturating_add((159_539_000 as Weight).saturating_mul(r as Weight)) + (25_653_000 as Weight) + // Standard Error: 17_000 + .saturating_add((159_121_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64store(r: u32, ) -> Weight { - (26_266_000 as Weight) - // Standard Error: 3_229_000 - .saturating_add((238_726_000 as Weight).saturating_mul(r as Weight)) + (25_608_000 as Weight) + // Standard Error: 26_000 + .saturating_add((229_680_000 as Weight).saturating_mul(r as Weight)) } fn instr_select(r: u32, ) -> Weight { - (27_469_000 as Weight) - // Standard Error: 592_000 - .saturating_add((10_423_000 as Weight).saturating_mul(r as Weight)) + (24_053_000 as Weight) + // Standard Error: 19_000 + .saturating_add((11_768_000 as Weight).saturating_mul(r as Weight)) } fn instr_if(r: u32, ) -> Weight { - (24_627_000 as Weight) - // Standard Error: 29_000 - .saturating_add((11_999_000 as Weight).saturating_mul(r as Weight)) + (23_478_000 as Weight) + // Standard Error: 16_000 + .saturating_add((11_992_000 as Weight).saturating_mul(r as Weight)) } fn instr_br(r: u32, ) -> Weight { - (24_008_000 as Weight) - // Standard Error: 22_000 - .saturating_add((6_614_000 as Weight).saturating_mul(r as Weight)) + (23_418_000 as Weight) + // Standard Error: 15_000 + .saturating_add((5_936_000 as Weight).saturating_mul(r as Weight)) } fn instr_br_if(r: u32, ) -> Weight { - (24_040_000 as Weight) - // Standard Error: 20_000 - .saturating_add((14_190_000 as Weight).saturating_mul(r as Weight)) + (23_380_000 as Weight) + // Standard Error: 10_000 + .saturating_add((13_844_000 as Weight).saturating_mul(r as Weight)) } fn instr_br_table(r: u32, ) -> Weight { - (23_997_000 as Weight) - // Standard Error: 24_000 - .saturating_add((15_529_000 as Weight).saturating_mul(r as Weight)) + (23_509_000 as Weight) + // Standard Error: 11_000 + .saturating_add((14_912_000 as Weight).saturating_mul(r as Weight)) } fn instr_br_table_per_entry(e: u32, ) -> Weight { - (36_890_000 as Weight) + (36_616_000 as Weight) // Standard Error: 1_000 - .saturating_add((112_000 as Weight).saturating_mul(e as Weight)) + .saturating_add((104_000 as Weight).saturating_mul(e as Weight)) } fn instr_call(r: u32, ) -> Weight { - (24_266_000 as Weight) - // Standard Error: 198_000 - .saturating_add((99_702_000 as Weight).saturating_mul(r as Weight)) + (23_821_000 as Weight) + // Standard Error: 49_000 + .saturating_add((96_843_000 as Weight).saturating_mul(r as Weight)) } fn instr_call_indirect(r: u32, ) -> Weight { - (31_901_000 as Weight) - // Standard Error: 322_000 - .saturating_add((197_671_000 as Weight).saturating_mul(r as Weight)) + (31_502_000 as Weight) + // Standard Error: 523_000 + .saturating_add((196_243_000 as Weight).saturating_mul(r as Weight)) } fn instr_call_indirect_per_param(p: u32, ) -> Weight { - (239_803_000 as Weight) - // Standard Error: 5_000 - .saturating_add((3_474_000 as Weight).saturating_mul(p as Weight)) + (242_403_000 as Weight) + // Standard Error: 9_000 + .saturating_add((3_443_000 as Weight).saturating_mul(p as Weight)) } fn instr_local_get(r: u32, ) -> Weight { - (41_697_000 as Weight) - // Standard Error: 15_000 - .saturating_add((3_225_000 as Weight).saturating_mul(r as Weight)) + (40_816_000 as Weight) + // Standard Error: 20_000 + .saturating_add((3_178_000 as Weight).saturating_mul(r as Weight)) } fn instr_local_set(r: u32, ) -> Weight { - (41_698_000 as Weight) - // Standard Error: 13_000 - .saturating_add((3_458_000 as Weight).saturating_mul(r as Weight)) + (40_778_000 as Weight) + // Standard Error: 17_000 + .saturating_add((3_507_000 as Weight).saturating_mul(r as Weight)) } fn instr_local_tee(r: u32, ) -> Weight { - (41_715_000 as Weight) - // Standard Error: 19_000 - .saturating_add((4_684_000 as Weight).saturating_mul(r as Weight)) + (40_808_000 as Weight) + // Standard Error: 15_000 + .saturating_add((4_775_000 as Weight).saturating_mul(r as Weight)) } fn instr_global_get(r: u32, ) -> Weight { - (27_751_000 as Weight) - // Standard Error: 20_000 - .saturating_add((7_980_000 as Weight).saturating_mul(r as Weight)) + (26_983_000 as Weight) + // Standard Error: 32_000 + .saturating_add((8_878_000 as Weight).saturating_mul(r as Weight)) } fn instr_global_set(r: u32, ) -> Weight { - (27_632_000 as Weight) - // Standard Error: 21_000 - .saturating_add((12_050_000 as Weight).saturating_mul(r as Weight)) + (26_975_000 as Weight) + // Standard Error: 34_000 + .saturating_add((12_236_000 as Weight).saturating_mul(r as Weight)) } fn instr_memory_current(r: u32, ) -> Weight { - (26_302_000 as Weight) - // Standard Error: 25_000 - .saturating_add((3_480_000 as Weight).saturating_mul(r as Weight)) + (25_691_000 as Weight) + // Standard Error: 22_000 + .saturating_add((3_577_000 as Weight).saturating_mul(r as Weight)) } fn instr_memory_grow(r: u32, ) -> Weight { - (24_695_000 as Weight) - // Standard Error: 3_876_000 - .saturating_add((2_324_806_000 as Weight).saturating_mul(r as Weight)) + (24_245_000 as Weight) + // Standard Error: 3_933_000 + .saturating_add((2_305_850_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64clz(r: u32, ) -> Weight { - (24_043_000 as Weight) - // Standard Error: 13_000 - .saturating_add((5_187_000 as Weight).saturating_mul(r as Weight)) + (23_495_000 as Weight) + // Standard Error: 28_000 + .saturating_add((5_186_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ctz(r: u32, ) -> Weight { - (24_040_000 as Weight) - // Standard Error: 14_000 - .saturating_add((5_077_000 as Weight).saturating_mul(r as Weight)) + (23_441_000 as Weight) + // Standard Error: 16_000 + .saturating_add((5_224_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64popcnt(r: u32, ) -> Weight { - (23_995_000 as Weight) - // Standard Error: 18_000 - .saturating_add((5_801_000 as Weight).saturating_mul(r as Weight)) + (23_507_000 as Weight) + // Standard Error: 13_000 + .saturating_add((5_820_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64eqz(r: u32, ) -> Weight { - (24_010_000 as Weight) - // Standard Error: 12_000 - .saturating_add((5_221_000 as Weight).saturating_mul(r as Weight)) + (23_475_000 as Weight) + // Standard Error: 19_000 + .saturating_add((5_244_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64extendsi32(r: u32, ) -> Weight { - (24_073_000 as Weight) - // Standard Error: 18_000 - .saturating_add((5_205_000 as Weight).saturating_mul(r as Weight)) + (23_437_000 as Weight) + // Standard Error: 14_000 + .saturating_add((5_204_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64extendui32(r: u32, ) -> Weight { - (23_993_000 as Weight) - // Standard Error: 17_000 - .saturating_add((5_079_000 as Weight).saturating_mul(r as Weight)) + (23_434_000 as Weight) + // Standard Error: 16_000 + .saturating_add((5_211_000 as Weight).saturating_mul(r as Weight)) } fn instr_i32wrapi64(r: u32, ) -> Weight { - (24_008_000 as Weight) + (23_454_000 as Weight) // Standard Error: 16_000 - .saturating_add((5_077_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((5_181_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64eq(r: u32, ) -> Weight { - (23_991_000 as Weight) - // Standard Error: 17_000 - .saturating_add((7_248_000 as Weight).saturating_mul(r as Weight)) + (23_470_000 as Weight) + // Standard Error: 21_000 + .saturating_add((7_257_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ne(r: u32, ) -> Weight { - (23_983_000 as Weight) + (23_475_000 as Weight) // Standard Error: 21_000 - .saturating_add((7_303_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((7_132_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64lts(r: u32, ) -> Weight { - (23_991_000 as Weight) - // Standard Error: 21_000 - .saturating_add((7_106_000 as Weight).saturating_mul(r as Weight)) + (23_418_000 as Weight) + // Standard Error: 22_000 + .saturating_add((7_199_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ltu(r: u32, ) -> Weight { - (24_062_000 as Weight) + (23_478_000 as Weight) // Standard Error: 25_000 - .saturating_add((7_168_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((7_278_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64gts(r: u32, ) -> Weight { - (24_028_000 as Weight) - // Standard Error: 26_000 - .saturating_add((7_130_000 as Weight).saturating_mul(r as Weight)) + (23_471_000 as Weight) + // Standard Error: 25_000 + .saturating_add((7_134_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64gtu(r: u32, ) -> Weight { - (23_998_000 as Weight) - // Standard Error: 18_000 - .saturating_add((7_279_000 as Weight).saturating_mul(r as Weight)) + (23_448_000 as Weight) + // Standard Error: 20_000 + .saturating_add((7_260_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64les(r: u32, ) -> Weight { - (24_010_000 as Weight) - // Standard Error: 19_000 - .saturating_add((7_114_000 as Weight).saturating_mul(r as Weight)) + (23_409_000 as Weight) + // Standard Error: 26_000 + .saturating_add((7_064_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64leu(r: u32, ) -> Weight { - (24_003_000 as Weight) - // Standard Error: 13_000 - .saturating_add((7_052_000 as Weight).saturating_mul(r as Weight)) + (23_433_000 as Weight) + // Standard Error: 18_000 + .saturating_add((7_088_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ges(r: u32, ) -> Weight { - (23_948_000 as Weight) - // Standard Error: 15_000 - .saturating_add((7_236_000 as Weight).saturating_mul(r as Weight)) + (23_425_000 as Weight) + // Standard Error: 28_000 + .saturating_add((7_152_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64geu(r: u32, ) -> Weight { - (24_042_000 as Weight) - // Standard Error: 19_000 - .saturating_add((7_223_000 as Weight).saturating_mul(r as Weight)) + (23_474_000 as Weight) + // Standard Error: 17_000 + .saturating_add((7_204_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64add(r: u32, ) -> Weight { - (23_965_000 as Weight) - // Standard Error: 37_000 - .saturating_add((7_261_000 as Weight).saturating_mul(r as Weight)) + (23_431_000 as Weight) + // Standard Error: 24_000 + .saturating_add((7_105_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64sub(r: u32, ) -> Weight { - (24_023_000 as Weight) - // Standard Error: 26_000 - .saturating_add((7_170_000 as Weight).saturating_mul(r as Weight)) + (23_423_000 as Weight) + // Standard Error: 21_000 + .saturating_add((7_094_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64mul(r: u32, ) -> Weight { - (24_057_000 as Weight) - // Standard Error: 17_000 - .saturating_add((7_050_000 as Weight).saturating_mul(r as Weight)) + (23_407_000 as Weight) + // Standard Error: 16_000 + .saturating_add((7_149_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64divs(r: u32, ) -> Weight { - (24_038_000 as Weight) - // Standard Error: 15_000 - .saturating_add((12_934_000 as Weight).saturating_mul(r as Weight)) + (23_437_000 as Weight) + // Standard Error: 23_000 + .saturating_add((13_007_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64divu(r: u32, ) -> Weight { - (23_992_000 as Weight) - // Standard Error: 15_000 - .saturating_add((12_055_000 as Weight).saturating_mul(r as Weight)) + (23_405_000 as Weight) + // Standard Error: 22_000 + .saturating_add((12_259_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64rems(r: u32, ) -> Weight { - (24_082_000 as Weight) - // Standard Error: 18_000 - .saturating_add((12_898_000 as Weight).saturating_mul(r as Weight)) + (23_469_000 as Weight) + // Standard Error: 12_000 + .saturating_add((12_950_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64remu(r: u32, ) -> Weight { - (24_025_000 as Weight) + (23_460_000 as Weight) // Standard Error: 13_000 - .saturating_add((12_178_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((12_249_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64and(r: u32, ) -> Weight { - (23_984_000 as Weight) - // Standard Error: 18_000 - .saturating_add((7_214_000 as Weight).saturating_mul(r as Weight)) + (23_434_000 as Weight) + // Standard Error: 22_000 + .saturating_add((7_111_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64or(r: u32, ) -> Weight { - (24_012_000 as Weight) - // Standard Error: 16_000 - .saturating_add((7_183_000 as Weight).saturating_mul(r as Weight)) + (23_481_000 as Weight) + // Standard Error: 17_000 + .saturating_add((7_010_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64xor(r: u32, ) -> Weight { - (24_001_000 as Weight) - // Standard Error: 18_000 - .saturating_add((7_122_000 as Weight).saturating_mul(r as Weight)) + (23_500_000 as Weight) + // Standard Error: 34_000 + .saturating_add((7_074_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64shl(r: u32, ) -> Weight { - (23_973_000 as Weight) - // Standard Error: 13_000 - .saturating_add((7_251_000 as Weight).saturating_mul(r as Weight)) + (23_477_000 as Weight) + // Standard Error: 28_000 + .saturating_add((7_220_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64shrs(r: u32, ) -> Weight { - (23_969_000 as Weight) - // Standard Error: 14_000 - .saturating_add((7_289_000 as Weight).saturating_mul(r as Weight)) + (23_433_000 as Weight) + // Standard Error: 24_000 + .saturating_add((7_305_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64shru(r: u32, ) -> Weight { - (24_008_000 as Weight) - // Standard Error: 15_000 - .saturating_add((7_292_000 as Weight).saturating_mul(r as Weight)) + (23_413_000 as Weight) + // Standard Error: 18_000 + .saturating_add((7_299_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64rotl(r: u32, ) -> Weight { - (24_010_000 as Weight) - // Standard Error: 21_000 - .saturating_add((7_305_000 as Weight).saturating_mul(r as Weight)) + (23_468_000 as Weight) + // Standard Error: 22_000 + .saturating_add((7_204_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64rotr(r: u32, ) -> Weight { - (24_001_000 as Weight) - // Standard Error: 22_000 - .saturating_add((7_299_000 as Weight).saturating_mul(r as Weight)) + (23_434_000 as Weight) + // Standard Error: 32_000 + .saturating_add((7_255_000 as Weight).saturating_mul(r as Weight)) } }