diff --git a/Cargo.lock b/Cargo.lock index 8d168230b528e..fd4e1e7bd258e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4586,6 +4586,7 @@ dependencies = [ "sp-keystore", "sp-runtime", "sp-state-machine", + "sp-tracing", "sp-trie", "wat", ] diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml index 21785079c6c54..6fdd5f8d2351b 100644 --- a/bin/node/executor/Cargo.toml +++ b/bin/node/executor/Cargo.toml @@ -20,6 +20,7 @@ sc-executor = { version = "0.10.0-dev", path = "../../../client/executor" } sp-core = { version = "4.0.0-dev", path = "../../../primitives/core" } sp-keystore = { version = "0.10.0-dev", path = "../../../primitives/keystore" } sp-state-machine = { version = "0.10.0-dev", path = "../../../primitives/state-machine" } +sp-tracing = { version = "4.0.0-dev", path = "../../../primitives/tracing" } sp-trie = { version = "4.0.0-dev", path = "../../../primitives/trie" } frame-benchmarking = { version = "4.0.0-dev", path = "../../../frame/benchmarking" } diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs index bbb9339189b06..7c613843b4ee7 100644 --- a/bin/node/executor/tests/basic.rs +++ b/bin/node/executor/tests/basic.rs @@ -675,7 +675,7 @@ fn deploying_wasm_contract_should_work() { let addr = pallet_contracts::Pallet::::contract_address(&charlie(), &transfer_ch, &[]); - let subsistence = pallet_contracts::Pallet::::subsistence_threshold(); + let min_balance = ::Currency::minimum_balance(); let time = 42 * 1000; let b = construct_block( @@ -691,8 +691,9 @@ fn deploying_wasm_contract_should_work() { signed: Some((charlie(), signed_extra(0, 0))), function: Call::Contracts( pallet_contracts::Call::instantiate_with_code:: { - endowment: 1000 * DOLLARS + subsistence, + endowment: 1000 * DOLLARS + min_balance, gas_limit: 500_000_000, + storage_limit: None, code: transfer_code, data: Vec::new(), salt: Vec::new(), @@ -705,6 +706,7 @@ fn deploying_wasm_contract_should_work() { dest: sp_runtime::MultiAddress::Id(addr.clone()), value: 10, gas_limit: 500_000_000, + storage_limit: None, data: vec![0x00, 0x01, 0x02, 0x03], }), }, diff --git a/bin/node/executor/tests/common.rs b/bin/node/executor/tests/common.rs index d1c24c83c836d..ece27250e95b1 100644 --- a/bin/node/executor/tests/common.rs +++ b/bin/node/executor/tests/common.rs @@ -119,7 +119,7 @@ pub fn executor_call< hash: sp_core::blake2_256(&code).to_vec(), heap_pages: heap_pages.and_then(|hp| Decode::decode(&mut &hp[..]).ok()), }; - + sp_tracing::try_init_simple(); executor().call::(&mut t, &runtime_code, method, data, use_native, native_call) } diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 570abe53ed01f..bce39edba53d7 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -887,10 +887,8 @@ impl pallet_tips::Config for Runtime { } parameter_types! { - pub ContractDeposit: Balance = deposit( - 1, - >::contract_info_size(), - ); + pub const DepositPerItem: Balance = deposit(1, 0); + pub const DepositPerByte: Balance = deposit(0, 1); pub const MaxValueSize: u32 = 16 * 1024; // The lazy deletion runs inside on_initialize. pub DeletionWeightLimit: Weight = AVERAGE_ON_INITIALIZE_RATIO * @@ -917,7 +915,8 @@ impl pallet_contracts::Config for Runtime { /// change because that would break already deployed contracts. The `Call` structure itself /// is not allowed to change the indices of existing pallets, too. type CallFilter = Nothing; - type ContractDeposit = ContractDeposit; + type DepositPerItem = DepositPerItem; + type DepositPerByte = DepositPerByte; type CallStack = [pallet_contracts::Frame; 31]; type WeightPrice = pallet_transaction_payment::Pallet; type WeightInfo = pallet_contracts::weights::SubstrateWeight; @@ -1509,21 +1508,32 @@ impl_runtime_apis! { dest: AccountId, value: Balance, gas_limit: u64, + storage_limit: Option, input_data: Vec, - ) -> pallet_contracts_primitives::ContractExecResult { - Contracts::bare_call(origin, dest, value, gas_limit, input_data, true) + ) -> pallet_contracts_primitives::ContractExecResult { + Contracts::bare_call(origin, dest, value, gas_limit, storage_limit, input_data, true) } fn instantiate( origin: AccountId, endowment: Balance, gas_limit: u64, + storage_limit: Option, code: pallet_contracts_primitives::Code, data: Vec, salt: Vec, - ) -> pallet_contracts_primitives::ContractInstantiateResult + ) -> pallet_contracts_primitives::ContractInstantiateResult + { + Contracts::bare_instantiate(origin, endowment, gas_limit, storage_limit, code, data, salt, true) + } + + fn upload_code( + origin: AccountId, + code: Vec, + storage_limit: Option, + ) -> pallet_contracts_primitives::CodeUploadResult { - Contracts::bare_instantiate(origin, endowment, gas_limit, code, data, salt, true) + Contracts::bare_upload_code(origin, code, storage_limit) } fn get_storage( diff --git a/frame/contracts/common/src/lib.rs b/frame/contracts/common/src/lib.rs index c57f728c26b68..a7cf39ec70e20 100644 --- a/frame/contracts/common/src/lib.rs +++ b/frame/contracts/common/src/lib.rs @@ -22,7 +22,7 @@ use bitflags::bitflags; use codec::{Decode, Encode}; use sp_core::Bytes; -use sp_runtime::{DispatchError, RuntimeDebug}; +use sp_runtime::{traits::Zero, DispatchError, RuntimeDebug, traits::Saturating}; use sp_std::prelude::*; #[cfg(feature = "std")] @@ -34,7 +34,7 @@ use serde::{Deserialize, Serialize}; #[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] -pub struct ContractResult { +pub struct ContractResult { /// How much gas was consumed during execution. pub gas_consumed: u64, /// How much gas is required as gas limit in order to execute this call. @@ -45,7 +45,14 @@ pub struct ContractResult { /// /// This can only different from [`Self::gas_consumed`] when weight pre charging /// is used. Currently, only `seal_call_runtime` makes use of pre charging. + /// Additionally, any `seal_call` or `seal_instantiate` makes use of pre-charging + /// when a non-zero `gas_limit` argument is supplied. pub gas_required: u64, + /// How much balance was deposited and reserved during execution in order to pay for storage. + /// + /// The storage deposit is never actually charged from the caller in case of [`Self::result`] + /// is `Err`. This is because on error all storage changes are rolled back. + pub storage_deposit: StorageDeposit, /// An optional debug message. This message is only filled when explicitly requested /// by the code that calls into the contract. Otherwise it is empty. /// @@ -63,15 +70,20 @@ pub struct ContractResult { #[cfg_attr(feature = "std", serde(with = "as_string"))] pub debug_message: Vec, /// The execution result of the wasm code. - pub result: T, + pub result: R, } /// Result type of a `bare_call` call. -pub type ContractExecResult = ContractResult>; +pub type ContractExecResult = + ContractResult, Balance>; /// Result type of a `bare_instantiate` call. -pub type ContractInstantiateResult = - ContractResult, DispatchError>>; +pub type ContractInstantiateResult = + ContractResult, DispatchError>, Balance>; + +/// Result type of a `bare_code_upload` call. +pub type CodeUploadResult = + Result, DispatchError>; /// Result type of a `get_storage` call. pub type GetStorageResult = Result>, ContractAccessError>; @@ -123,6 +135,17 @@ pub struct InstantiateReturnValue { pub account_id: AccountId, } +/// The result of succesfully uploading a contract. +#[derive(PartialEq, Eq, Encode, Decode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] +pub struct CodeUploadReturnValue { + /// The key under which the new code is stored. + pub code_hash: CodeHash, + /// The deposit that was reserved at the caller. Is zero when the code already existed. + pub deposit: Balance, +} + /// Reference to an existing code hash or a new wasm module. #[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] @@ -134,6 +157,96 @@ pub enum Code { Existing(Hash), } +/// The amount of balance that was either charged or refunded in order to pay for storage. +#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, Clone)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] +pub enum StorageDeposit { + /// The transaction increased overall storage usage. + /// + /// This means that the specified amount of balance was transferred from the call origin + /// to the contracts involved. + Charge(Balance), + /// The transaction reduced storage consumption. + /// + /// This means that the specified amount of balance was transferred from the involved + /// contracts to the call origin. + Refund(Balance), +} + +impl Default for StorageDeposit { + fn default() -> Self { + Self::Charge(Zero::zero()) + } +} + +impl StorageDeposit { + /// Returns how much balance is charged or `0` in case of a refund. + pub fn charge_or_zero(self) -> Balance { + match self { + Self::Charge(amount) => amount, + Self::Refund(_) => Zero::zero(), + } + } +} + +impl StorageDeposit +where + Balance: Saturating + Ord + Copy +{ + /// This is essentially a saturating signed add. + pub fn saturating_add(&self, rhs: &Self) -> Self { + use StorageDeposit::*; + match (self, rhs) { + (Charge(lhs), Charge(rhs)) => Charge(lhs.saturating_add(*rhs)), + (Refund(lhs), Refund(rhs)) => Refund(lhs.saturating_add(*rhs)), + (Charge(lhs), Refund(rhs)) => if lhs >= rhs { + Charge(lhs.saturating_sub(*rhs)) + } else { + Refund(rhs.saturating_sub(*lhs)) + }, + (Refund(lhs), Charge(rhs)) => if lhs > rhs { + Refund(lhs.saturating_sub(*rhs)) + } else { + Charge(rhs.saturating_sub(*lhs)) + }, + } + } + + /// This is essentially a saturating signed sub. + pub fn saturating_sub(&self, rhs: &Self) -> Self { + use StorageDeposit::*; + match (self, rhs) { + (Charge(lhs), Refund(rhs)) => Charge(lhs.saturating_add(*rhs)), + (Refund(lhs), Charge(rhs)) => Refund(lhs.saturating_add(*rhs)), + (Charge(lhs), Charge(rhs)) => if lhs >= rhs { + Charge(lhs.saturating_sub(*rhs)) + } else { + Refund(rhs.saturating_sub(*lhs)) + }, + (Refund(lhs), Refund(rhs)) => if lhs > rhs { + Refund(lhs.saturating_sub(*rhs)) + } else { + Charge(rhs.saturating_sub(*lhs)) + }, + } + } + + /// If the amount of deposit (this type) is constrained by a `limit` this calcuates how + /// much balance (if any) is still available from this limit. + /// + /// # Note + /// + /// In case of a refund the return value can be larger than `limit`. + pub fn available(&self, limit: &Balance) -> Balance { + use StorageDeposit::*; + match self { + Charge(amount) => limit.saturating_sub(*amount), + Refund(amount) => limit.saturating_add(*amount), + } + } +} + #[cfg(feature = "std")] mod as_string { use super::*; diff --git a/frame/contracts/rpc/runtime-api/src/lib.rs b/frame/contracts/rpc/runtime-api/src/lib.rs index 20dfbe210e5ce..ac6f6475cc9a0 100644 --- a/frame/contracts/rpc/runtime-api/src/lib.rs +++ b/frame/contracts/rpc/runtime-api/src/lib.rs @@ -25,7 +25,7 @@ use codec::Codec; use pallet_contracts_primitives::{ - Code, ContractExecResult, ContractInstantiateResult, GetStorageResult, + Code, CodeUploadResult, ContractExecResult, ContractInstantiateResult, GetStorageResult, }; use sp_std::vec::Vec; @@ -45,8 +45,9 @@ sp_api::decl_runtime_apis! { dest: AccountId, value: Balance, gas_limit: u64, + storage_limit: Option, input_data: Vec, - ) -> ContractExecResult; + ) -> ContractExecResult; /// Instantiate a new contract. /// @@ -55,10 +56,21 @@ sp_api::decl_runtime_apis! { origin: AccountId, endowment: Balance, gas_limit: u64, + storage_limit: Option, code: Code, data: Vec, salt: Vec, - ) -> ContractInstantiateResult; + ) -> ContractInstantiateResult; + + + /// Upload new code without instantiating a contract from it. + /// + /// See `pallet_contracts::Pallet::upload_code`. + fn upload_code( + origin: AccountId, + code: Vec, + storage_limit: Option, + ) -> CodeUploadResult; /// Query a given storage key in a given contract. /// diff --git a/frame/contracts/rpc/src/lib.rs b/frame/contracts/rpc/src/lib.rs index e0796af056540..2077f016a3644 100644 --- a/frame/contracts/rpc/src/lib.rs +++ b/frame/contracts/rpc/src/lib.rs @@ -22,7 +22,9 @@ use std::sync::Arc; use codec::Codec; use jsonrpc_core::{Error, ErrorCode, Result}; use jsonrpc_derive::rpc; -use pallet_contracts_primitives::{Code, ContractExecResult, ContractInstantiateResult}; +use pallet_contracts_primitives::{ + Code, CodeUploadResult, ContractExecResult, ContractInstantiateResult, +}; use serde::{Deserialize, Serialize}; use sp_api::ProvideRuntimeApi; use sp_blockchain::HeaderBackend; @@ -79,6 +81,7 @@ pub struct CallRequest { dest: AccountId, value: NumberOrHex, gas_limit: NumberOrHex, + storage_limit: Option, input_data: Bytes, } @@ -90,11 +93,22 @@ pub struct InstantiateRequest { origin: AccountId, endowment: NumberOrHex, gas_limit: NumberOrHex, + storage_limit: Option, code: Code, data: Bytes, salt: Bytes, } +/// A struct that encodes RPC parameters required for a call to upload a new code. +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +pub struct CodeUploadRequest { + origin: AccountId, + code: Bytes, + storage_limit: Option, +} + /// Contracts RPC methods. #[rpc] pub trait ContractsApi { @@ -103,17 +117,18 @@ pub trait ContractsApi { /// This call is performed locally without submitting any transactions. Thus executing this /// won't change any state. Nonetheless, the calling state-changing contracts is still possible. /// - /// This method is useful for calling getter-like methods on contracts. + /// This method is useful for calling getter-like methods on contracts or to dry-run a + /// a contract call in order to determine the `gas_limit`. #[rpc(name = "contracts_call")] fn call( &self, call_request: CallRequest, at: Option, - ) -> Result; + ) -> Result>; /// Instantiate a new contract. /// - /// This call is performed locally without submitting any transactions. Thus the contract + /// This instantiate is performed locally without submitting any transactions. Thus the contract /// is not actually created. /// /// This method is useful for UIs to dry-run contract instantiations. @@ -122,7 +137,20 @@ pub trait ContractsApi { &self, instantiate_request: InstantiateRequest, at: Option, - ) -> Result>; + ) -> Result>; + + /// Upload new code without instantiating a contract from it. + /// + /// This upload is performed locally without submitting any transactions. Thus executing this + /// won't change any state. + /// + /// This method is useful for calling getter-like methods on contracts. + #[rpc(name = "contracts_upload_code")] + fn upload_code( + &self, + upload_request: CodeUploadRequest, + at: Option, + ) -> Result>; /// Returns the value under a specified storage `key` in a contract given by `address` param, /// or `None` if it is not set. @@ -173,47 +201,74 @@ where &self, call_request: CallRequest, at: Option<::Hash>, - ) -> Result { + ) -> Result> { let api = self.client.runtime_api(); let at = BlockId::hash(at.unwrap_or_else(|| // If the block hash is not supplied assume the best block. self.client.info().best_hash)); - let CallRequest { origin, dest, value, gas_limit, input_data } = call_request; + let CallRequest { origin, dest, value, gas_limit, storage_limit, input_data } = + call_request; let value: Balance = decode_hex(value, "balance")?; let gas_limit: Weight = decode_hex(gas_limit, "weight")?; + let storage_limit: Option = + storage_limit.map(|l| decode_hex(l, "balance")).transpose()?; limit_gas(gas_limit)?; - let exec_result = api - .call(&at, origin, dest, value, gas_limit, input_data.to_vec()) - .map_err(runtime_error_into_rpc_err)?; - - Ok(exec_result) + api.call(&at, origin, dest, value, gas_limit, storage_limit, input_data.to_vec()) + .map_err(runtime_error_into_rpc_err) } fn instantiate( &self, instantiate_request: InstantiateRequest, at: Option<::Hash>, - ) -> Result> { + ) -> Result> { let api = self.client.runtime_api(); let at = BlockId::hash(at.unwrap_or_else(|| // If the block hash is not supplied assume the best block. self.client.info().best_hash)); - let InstantiateRequest { origin, endowment, gas_limit, code, data, salt } = + let InstantiateRequest { origin, endowment, gas_limit, storage_limit, code, data, salt } = instantiate_request; let endowment: Balance = decode_hex(endowment, "balance")?; let gas_limit: Weight = decode_hex(gas_limit, "weight")?; + let storage_limit: Option = + storage_limit.map(|l| decode_hex(l, "balance")).transpose()?; limit_gas(gas_limit)?; - let exec_result = api - .instantiate(&at, origin, endowment, gas_limit, code, data.to_vec(), salt.to_vec()) - .map_err(runtime_error_into_rpc_err)?; + api.instantiate( + &at, + origin, + endowment, + gas_limit, + storage_limit, + code, + data.to_vec(), + salt.to_vec(), + ) + .map_err(runtime_error_into_rpc_err) + } + + fn upload_code( + &self, + upload_request: CodeUploadRequest, + at: Option<::Hash>, + ) -> Result> { + let api = self.client.runtime_api(); + let at = BlockId::hash(at.unwrap_or_else(|| + // If the block hash is not supplied assume the best block. + self.client.info().best_hash)); + + let CodeUploadRequest { origin, code, storage_limit } = upload_request; + + let storage_limit: Option = + storage_limit.map(|l| decode_hex(l, "balance")).transpose()?; - Ok(exec_result) + api.upload_code(&at, origin, code.to_vec(), storage_limit) + .map_err(runtime_error_into_rpc_err) } fn get_storage( @@ -288,12 +343,14 @@ mod tests { "dest": "5DRakbLVnjVrW6niwLfHGW24EeCEvDAFGEXrtaYS5M4ynoom", "value": "0x112210f4B16c1cb1", "gasLimit": 1000000000000, + "storageLimit": 5000, "inputData": "0x8c97db39" } "#, ) .unwrap(); assert_eq!(req.gas_limit.into_u256(), U256::from(0xe8d4a51000u64)); + assert_eq!(req.storage_limit.map(|l| l.into_u256()), Some(5000.into())); assert_eq!(req.value.into_u256(), U256::from(1234567890987654321u128)); } @@ -317,6 +374,7 @@ mod tests { assert_eq!(req.origin, "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL"); assert_eq!(req.endowment.into_u256(), 0x88.into()); assert_eq!(req.gas_limit.into_u256(), 42.into()); + assert_eq!(req.storage_limit, None); assert_eq!(&*req.data, [0x42, 0x99].as_ref()); assert_eq!(&*req.salt, [0x99, 0x88].as_ref()); let code = match req.code { @@ -326,10 +384,28 @@ mod tests { assert_eq!(&code, "0x1122"); } + #[test] + fn code_upload_request_should_serialize_deserialize_properly() { + type Req = CodeUploadRequest; + let req: Req = serde_json::from_str( + r#" + { + "origin": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + "code": "0x8c97db39", + "storageLimit": 5000 + } + "#, + ) + .unwrap(); + assert_eq!(req.origin, "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL"); + assert_eq!(&*req.code, [0x8c, 0x97, 0xdb, 0x39].as_ref()); + assert_eq!(req.storage_limit.map(|l| l.into_u256()), Some(5000.into())); + } + #[test] fn call_result_should_serialize_deserialize_properly() { fn test(expected: &str) { - let res: ContractExecResult = serde_json::from_str(expected).unwrap(); + let res: ContractExecResult = serde_json::from_str(expected).unwrap(); let actual = serde_json::to_string(&res).unwrap(); assert_eq!(actual, trim(expected).as_str()); } @@ -337,6 +413,7 @@ mod tests { r#"{ "gasConsumed": 5000, "gasRequired": 8000, + "storageDeposit": {"charge": 42000}, "debugMessage": "HelloWorld", "result": { "Ok": { @@ -350,6 +427,7 @@ mod tests { r#"{ "gasConsumed": 3400, "gasRequired": 5200, + "storageDeposit": {"refund": 12000}, "debugMessage": "HelloWorld", "result": { "Err": "BadOrigin" @@ -361,7 +439,8 @@ mod tests { #[test] fn instantiate_result_should_serialize_deserialize_properly() { fn test(expected: &str) { - let res: ContractInstantiateResult = serde_json::from_str(expected).unwrap(); + let res: ContractInstantiateResult = + serde_json::from_str(expected).unwrap(); let actual = serde_json::to_string(&res).unwrap(); assert_eq!(actual, trim(expected).as_str()); } @@ -369,6 +448,7 @@ mod tests { r#"{ "gasConsumed": 5000, "gasRequired": 8000, + "storageDeposit": {"refund": 12000}, "debugMessage": "HelloWorld", "result": { "Ok": { @@ -385,6 +465,7 @@ mod tests { r#"{ "gasConsumed": 3400, "gasRequired": 5200, + "storageDeposit": {"charge": 0}, "debugMessage": "HelloWorld", "result": { "Err": "BadOrigin" @@ -392,4 +473,26 @@ mod tests { }"#, ); } + + #[test] + fn code_upload_result_should_serialize_deserialize_properly() { + fn test(expected: &str) { + let res: CodeUploadResult = serde_json::from_str(expected).unwrap(); + let actual = serde_json::to_string(&res).unwrap(); + assert_eq!(actual, trim(expected).as_str()); + } + test( + r#"{ + "Ok": { + "codeHash": 4711, + "deposit": 99 + } + }"#, + ); + test( + r#"{ + "Err": "BadOrigin" + }"#, + ); + } } diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index 423f29b02badd..0abc86c7f55e3 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -41,7 +41,7 @@ use frame_support::weights::Weight; use frame_system::RawOrigin; use pwasm_utils::parity_wasm::elements::{BlockType, BrTableData, Instruction, ValueType}; use sp_runtime::{ - traits::{Bounded, Hash, Saturating}, + traits::{Bounded, Hash}, Perbill, }; use sp_std::{convert::TryInto, default::Default, vec, vec::Vec}; @@ -64,6 +64,7 @@ impl Contract where T: Config, T::AccountId: UncheckedFrom + AsRef<[u8]>, + as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, { /// Create new contract and use a default account id as instantiator. fn new(module: WasmModule, data: Vec) -> Result, &'static str> { @@ -85,16 +86,17 @@ where module: WasmModule, data: Vec, ) -> Result, &'static str> { - let endowment = contract_funding::(); + let endowment = T::Currency::minimum_balance(); T::Currency::make_free_balance_be(&caller, caller_funding::()); let salt = vec![0xff]; let addr = Contracts::::contract_address(&caller, &module.hash, &salt); - Contracts::::store_code_raw(module.code)?; + Contracts::::store_code_raw(module.code, caller.clone())?; Contracts::::instantiate( RawOrigin::Signed(caller.clone()).into(), endowment, - Weight::max_value(), + Weight::MAX, + None, module.hash, data, salt, @@ -134,9 +136,9 @@ where /// Store the supplied storage items into this contracts storage. fn store(&self, items: &Vec<(StorageKey, Vec)>) -> Result<(), &'static str> { - let mut info = self.info()?; + let info = self.info()?; for item in items { - Storage::::write(&mut info, &item.0, Some(item.1.clone())) + Storage::::write(&info.trie_id, &item.0, Some(item.1.clone()), None) .map_err(|_| "Failed to write storage to restoration dest")?; } >::insert(&self.account_id, info.clone()); @@ -152,6 +154,25 @@ where fn info(&self) -> Result, &'static str> { Self::address_info(&self.account_id) } + + /// Set the balance of the contract to the supplied amount. + fn set_balance(&self, balance: BalanceOf) { + T::Currency::make_free_balance_be(&self.account_id, balance); + } + + /// Returns `true` iff all storage entries related to code storage exist. + fn code_exists(hash: &CodeHash) -> bool { + >::contains_key(hash) && + >::contains_key(&hash) && + >::contains_key(&hash) + } + + /// Returns `true` iff no storage entry related to code storage exist. + fn code_removed(hash: &CodeHash) -> bool { + !>::contains_key(hash) && + !>::contains_key(&hash) && + !>::contains_key(&hash) + } } /// The funding that each account that either calls or instantiates contracts is funded with. @@ -159,11 +180,6 @@ fn caller_funding() -> BalanceOf { BalanceOf::::max_value() / 2u32.into() } -/// The funding used for contracts. It is less than `caller_funding` in purpose. -fn contract_funding() -> BalanceOf { - caller_funding::().saturating_sub(T::Currency::minimum_balance() * 100u32.into()) -} - /// Load the specified contract file from disk by including it into the runtime. /// /// We need to load a different version of ink! contracts when the benchmark is run as @@ -186,11 +202,12 @@ benchmarks! { where_clause { where T::AccountId: UncheckedFrom, T::AccountId: AsRef<[u8]>, + as codec::HasCompact>::Type: Clone + Eq + PartialEq + sp_std::fmt::Debug + scale_info::TypeInfo + codec::Encode, } // The base weight without any actual work performed apart from the setup costs. on_initialize {}: { - Storage::::process_deletion_queue_batch(Weight::max_value()) + Storage::::process_deletion_queue_batch(Weight::MAX) } #[skip_meta] @@ -199,7 +216,7 @@ benchmarks! { let instance = Contract::::with_storage(WasmModule::dummy(), k, T::Schedule::get().limits.payload_len)?; Storage::::queue_trie_for_deletion(&instance.info()?)?; }: { - Storage::::process_deletion_queue_batch(Weight::max_value()) + Storage::::process_deletion_queue_batch(Weight::MAX) } on_initialize_per_queue_item { @@ -210,7 +227,7 @@ benchmarks! { ContractInfoOf::::remove(instance.account_id); } }: { - Storage::::process_deletion_queue_batch(Weight::max_value()) + Storage::::process_deletion_queue_batch(Weight::MAX) } // This benchmarks the additional weight that is charged when a contract is executed the @@ -219,9 +236,10 @@ benchmarks! { instrument { let c in 0 .. T::Schedule::get().limits.code_len / 1024; let WasmModule { code, hash, .. } = WasmModule::::sized(c * 1024); - Contracts::::store_code_raw(code)?; - let mut module = PrefabWasmModule::from_storage_noinstr(hash)?; + Contracts::::store_code_raw(code, whitelisted_caller())?; let schedule = T::Schedule::get(); + let mut gas_meter = GasMeter::new(Weight::MAX); + let mut module = PrefabWasmModule::from_storage(hash, &schedule, &mut gas_meter)?; }: { Contracts::::reinstrument_module(&mut module, &schedule)?; } @@ -230,19 +248,11 @@ benchmarks! { code_load { let c in 0 .. T::Schedule::get().limits.code_len / 1024; let WasmModule { code, hash, .. } = WasmModule::::dummy_with_bytes(c * 1024); - Contracts::::store_code_raw(code)?; - }: { - >::from_storage_noinstr(hash)?; - } - - // The weight of changing the refcount of a contract's code per kilobyte. - code_refcount { - let c in 0 .. T::Schedule::get().limits.code_len / 1024; - let WasmModule { code, hash, .. } = WasmModule::::dummy_with_bytes(c * 1024); - Contracts::::store_code_raw(code)?; - let mut gas_meter = GasMeter::new(Weight::max_value()); + Contracts::::store_code_raw(code, whitelisted_caller())?; + let schedule = T::Schedule::get(); + let mut gas_meter = GasMeter::new(Weight::MAX); }: { - >::add_user(hash, &mut gas_meter)?; + >::from_storage(hash, &schedule, &mut gas_meter)?; } // This constructs a contract that is maximal expensive to instrument. @@ -260,16 +270,22 @@ benchmarks! { let c in 0 .. Perbill::from_percent(50).mul_ceil(T::Schedule::get().limits.code_len / 1024); let s in 0 .. code::max_pages::() * 64; let salt = vec![42u8; (s * 1024) as usize]; - let endowment = contract_funding::() / 3u32.into(); + let endowment = T::Currency::minimum_balance(); let caller = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, caller_funding::()); 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) + }: _(origin, endowment, Weight::MAX, None, code, vec![], salt) verify { - // endowment was removed from the caller - assert_eq!(T::Currency::free_balance(&caller), caller_funding::() - endowment); + // the contract itself does not trigger any reserves + let deposit = T::Currency::reserved_balance(&addr); + // uploading the code reserves some balance in the callers account + let code_deposit = T::Currency::reserved_balance(&caller); + assert_eq!( + T::Currency::free_balance(&caller), + caller_funding::() - endowment - deposit - code_deposit, + ); // contract has the full endowment assert_eq!(T::Currency::free_balance(&addr), endowment); // instantiate should leave a contract @@ -281,17 +297,19 @@ benchmarks! { instantiate { let s in 0 .. code::max_pages::() * 64; let salt = vec![42u8; (s * 1024) as usize]; - let endowment = contract_funding::() / 3u32.into(); + let endowment = T::Currency::minimum_balance(); let caller = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, caller_funding::()); let WasmModule { code, hash, .. } = WasmModule::::dummy(); let origin = RawOrigin::Signed(caller.clone()); let addr = Contracts::::contract_address(&caller, &hash, &salt); - Contracts::::store_code_raw(code)?; - }: _(origin, endowment, Weight::max_value(), hash, vec![], salt) + Contracts::::store_code_raw(code, caller.clone())?; + }: _(origin, endowment, Weight::MAX, None, hash, vec![], salt) verify { + // the contract itself does not trigger any reserves + let deposit = T::Currency::reserved_balance(&addr); // endowment was removed from the caller - assert_eq!(T::Currency::free_balance(&caller), caller_funding::() - endowment); + assert_eq!(T::Currency::free_balance(&caller), caller_funding::() - endowment - deposit); // contract has the full endowment assert_eq!(T::Currency::free_balance(&addr), endowment); // instantiate should leave a contract @@ -312,12 +330,14 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); let callee = instance.addr.clone(); let before = T::Currency::free_balance(&instance.account_id); - }: _(origin, callee, value, Weight::max_value(), data) + }: _(origin, callee, value, Weight::MAX, None, data) verify { + // the contract itself does not trigger any reserves + let deposit = T::Currency::reserved_balance(&instance.account_id); // endowment and value transfered via call should be removed from the caller assert_eq!( T::Currency::free_balance(&instance.caller), - caller_funding::() - instance.endowment - value, + caller_funding::() - instance.endowment - value - deposit, ); // contract should have received the value assert_eq!(T::Currency::free_balance(&instance.account_id), before + value); @@ -325,13 +345,53 @@ benchmarks! { instance.info()?; } + // This constructs a contract that is maximal expensive to instrument. + // It creates a maximum number of metering blocks per byte. + // `c`: Size of the code in kilobytes. + // + // # Note + // + // We cannot let `c` grow to the maximum code size because the code is not allowed + // to be larger than the maximum size **after instrumentation**. + upload_code { + let c in 0 .. Perbill::from_percent(50).mul_ceil(T::Schedule::get().limits.code_len / 1024); + let caller = whitelisted_caller(); + T::Currency::make_free_balance_be(&caller, caller_funding::()); + let WasmModule { code, hash, .. } = WasmModule::::sized(c * 1024); + let origin = RawOrigin::Signed(caller.clone()); + }: _(origin, code, None) + verify { + // uploading the code reserves some balance in the callers account + assert!(T::Currency::reserved_balance(&caller) > 0u32.into()); + assert!(>::code_exists(&hash)); + } + + // Removing code does not depend on the size of the contract because all the information + // needed to verify the removal claim (refcount, owner) is stored in a separate storage + // item (`OwnerInfoOf`). + remove_code { + let caller = whitelisted_caller(); + T::Currency::make_free_balance_be(&caller, caller_funding::()); + let WasmModule { code, hash, .. } = WasmModule::::dummy(); + let origin = RawOrigin::Signed(caller.clone()); + let uploaded = >::bare_upload_code(caller.clone(), code, None)?; + assert_eq!(uploaded.code_hash, hash); + assert_eq!(uploaded.deposit, T::Currency::reserved_balance(&caller)); + assert!(>::code_exists(&hash)); + }: _(origin, hash) + verify { + // removing the code should have unreserved the deposit + assert_eq!(T::Currency::reserved_balance(&caller), 0u32.into()); + assert!(>::code_removed(&hash)); + } + seal_caller { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( "seal_caller", r * API_BENCHMARK_BATCH_SIZE ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) seal_address { let r in 0 .. API_BENCHMARK_BATCHES; @@ -339,7 +399,7 @@ benchmarks! { "seal_address", r * API_BENCHMARK_BATCH_SIZE ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) seal_gas_left { let r in 0 .. API_BENCHMARK_BATCHES; @@ -347,7 +407,7 @@ benchmarks! { "seal_gas_left", r * API_BENCHMARK_BATCH_SIZE ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) seal_balance { let r in 0 .. API_BENCHMARK_BATCHES; @@ -355,7 +415,7 @@ benchmarks! { "seal_balance", r * API_BENCHMARK_BATCH_SIZE ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) seal_value_transferred { let r in 0 .. API_BENCHMARK_BATCHES; @@ -363,7 +423,7 @@ benchmarks! { "seal_value_transferred", r * API_BENCHMARK_BATCH_SIZE ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) seal_minimum_balance { let r in 0 .. API_BENCHMARK_BATCHES; @@ -371,7 +431,7 @@ benchmarks! { "seal_minimum_balance", r * API_BENCHMARK_BATCH_SIZE ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) seal_block_number { let r in 0 .. API_BENCHMARK_BATCHES; @@ -379,7 +439,7 @@ benchmarks! { "seal_block_number", r * API_BENCHMARK_BATCH_SIZE ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) seal_now { let r in 0 .. API_BENCHMARK_BATCHES; @@ -387,7 +447,7 @@ benchmarks! { "seal_now", r * API_BENCHMARK_BATCH_SIZE ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) seal_weight_to_fee { let r in 0 .. API_BENCHMARK_BATCHES; @@ -414,7 +474,7 @@ benchmarks! { }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) seal_gas { let r in 0 .. API_BENCHMARK_BATCHES; @@ -434,7 +494,7 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) seal_input { let r in 0 .. API_BENCHMARK_BATCHES; @@ -461,7 +521,7 @@ benchmarks! { }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) seal_input_per_kb { let n in 0 .. code::max_pages::() * 64; @@ -491,7 +551,7 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let data = vec![42u8; (n * 1024).min(buffer_size) as usize]; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), data) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, data) // We cannot call `seal_return` multiple times. Therefore our weight determination is not // as precise as with other APIs. Because this function can only be called once per @@ -516,7 +576,7 @@ benchmarks! { }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) seal_return_per_kb { let n in 0 .. code::max_pages::() * 64; @@ -539,7 +599,7 @@ benchmarks! { }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // The same argument as for `seal_return` is true here. seal_terminate { @@ -571,12 +631,13 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); assert_eq!(T::Currency::total_balance(&beneficiary), 0u32.into()); - assert_eq!(T::Currency::total_balance(&instance.account_id), contract_funding::()); - }: call(origin, instance.addr.clone(), 0u32.into(), Weight::max_value(), vec![]) + assert_eq!(T::Currency::free_balance(&instance.account_id), T::Currency::minimum_balance()); + assert_ne!(T::Currency::reserved_balance(&instance.account_id), 0u32.into()); + }: call(origin, instance.addr.clone(), 0u32.into(), Weight::MAX, None, vec![]) verify { if r > 0 { assert_eq!(T::Currency::total_balance(&instance.account_id), 0u32.into()); - assert_eq!(T::Currency::total_balance(&beneficiary), contract_funding::()); + assert_eq!(T::Currency::total_balance(&beneficiary), T::Currency::minimum_balance()); } } @@ -613,7 +674,7 @@ benchmarks! { }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // Overhead of calling the function without any topic. // We benchmark for the worst case (largest event). @@ -638,7 +699,7 @@ benchmarks! { }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // Benchmark the overhead that topics generate. // `t`: Number of topics @@ -676,7 +737,7 @@ benchmarks! { }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // The size of the supplied message does not influence the weight because as it is never // processed during on-chain execution: It is only ever read during debugging which happens @@ -702,7 +763,7 @@ benchmarks! { }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // Only the overhead of calling the function itself with minimal arguments. // The contract is a bit more complex because I needs to use different keys in order @@ -739,7 +800,7 @@ benchmarks! { }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) seal_set_storage_per_kb { let n in 0 .. T::Schedule::get().limits.payload_len / 1024; @@ -769,7 +830,7 @@ benchmarks! { }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // Similar to seal_set_storage. However, we store all the keys that we are about to // delete beforehand in order to prevent any optimizations that could occur when @@ -803,18 +864,19 @@ benchmarks! { .. Default::default() }); let instance = Contract::::new(code, vec![])?; - let mut info = instance.info()?; + let info = instance.info()?; for key in keys { Storage::::write( - &mut info, + &info.trie_id, key.as_slice().try_into().map_err(|e| "Key has wrong length")?, - Some(vec![42; T::Schedule::get().limits.payload_len as usize]) + Some(vec![42; T::Schedule::get().limits.payload_len as usize]), + None, ) .map_err(|_| "Failed to write to storage during setup.")?; } >::insert(&instance.account_id, info.clone()); let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // We make sure that all storage accesses are to unique keys. #[skip_meta] @@ -850,18 +912,19 @@ benchmarks! { .. Default::default() }); let instance = Contract::::new(code, vec![])?; - let mut info = instance.info()?; + let info = instance.info()?; for key in keys { Storage::::write( - &mut info, + &info.trie_id, key.as_slice().try_into().map_err(|e| "Key has wrong length")?, - Some(vec![]) + Some(vec![]), + None, ) .map_err(|_| "Failed to write to storage during setup.")?; } >::insert(&instance.account_id, info.clone()); let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) seal_get_storage_per_kb { let n in 0 .. T::Schedule::get().limits.payload_len / 1024; @@ -896,16 +959,17 @@ benchmarks! { .. Default::default() }); let instance = Contract::::new(code, vec![])?; - let mut info = instance.info()?; + let info = instance.info()?; Storage::::write( - &mut info, + &info.trie_id, key.as_slice().try_into().map_err(|e| "Key has wrong length")?, - Some(vec![42u8; (n * 1024) as usize]) + Some(vec![42u8; (n * 1024) as usize]), + None, ) .map_err(|_| "Failed to write to storage during setup.")?; >::insert(&instance.account_id, info.clone()); let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // We transfer to unique accounts. seal_transfer { @@ -948,11 +1012,12 @@ benchmarks! { .. Default::default() }); let instance = Contract::::new(code, vec![])?; + instance.set_balance(value * (r * API_BENCHMARK_BATCH_SIZE + 1).into()); let origin = RawOrigin::Signed(instance.caller.clone()); for account in &accounts { assert_eq!(T::Currency::total_balance(account), 0u32.into()); } - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) verify { for account in &accounts { assert_eq!(T::Currency::total_balance(account), value); @@ -1016,7 +1081,7 @@ benchmarks! { }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) seal_call_per_transfer_input_output_kb { let t in 0 .. 1; @@ -1101,7 +1166,7 @@ benchmarks! { }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // We assume that every instantiate sends at least the minimum balance. seal_instantiate { @@ -1119,14 +1184,14 @@ benchmarks! { ])), .. Default::default() }); - Contracts::::store_code_raw(code.code)?; + Contracts::::store_code_raw(code.code, whitelisted_caller())?; Ok(code.hash) }) .collect::, &'static str>>()?; let hash_len = hashes.get(0).map(|x| x.encode().len()).unwrap_or(0); let hashes_bytes = hashes.iter().flat_map(|x| x.encode()).collect::>(); let hashes_len = hashes_bytes.len(); - let value = contract_funding::() / (r * API_BENCHMARK_BATCH_SIZE + 2).into(); + let value = T::Currency::minimum_balance(); assert!(value > 0u32.into()); let value_bytes = value.encode(); let value_len = value_bytes.len(); @@ -1194,6 +1259,7 @@ benchmarks! { .. Default::default() }); let instance = Contract::::new(code, vec![])?; + instance.set_balance(value * (r * API_BENCHMARK_BATCH_SIZE + 1).into()); let origin = RawOrigin::Signed(instance.caller.clone()); let callee = instance.addr.clone(); let addresses = hashes @@ -1208,7 +1274,7 @@ benchmarks! { return Err("Expected that contract does not exist at this point.".into()); } } - }: call(origin, callee, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, callee, 0u32.into(), Weight::MAX, None, vec![]) verify { for addr in &addresses { ContractInfoOf::::get(&addr) @@ -1244,12 +1310,12 @@ benchmarks! { let hash = callee_code.hash.clone(); let hash_bytes = callee_code.hash.encode(); let hash_len = hash_bytes.len(); - Contracts::::store_code_raw(callee_code.code)?; + Contracts::::store_code_raw(callee_code.code, whitelisted_caller())?; 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::>(); let inputs_len = input_bytes.len(); - let value = contract_funding::() / (API_BENCHMARK_BATCH_SIZE + 2).into(); + let value = T::Currency::minimum_balance(); assert!(value > 0u32.into()); let value_bytes = value.encode(); let value_len = value_bytes.len(); @@ -1332,8 +1398,9 @@ benchmarks! { .. Default::default() }); let instance = Contract::::new(code, vec![])?; + instance.set_balance(value * (API_BENCHMARK_BATCH_SIZE + 1).into()); let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // Only the overhead of calling the function itself with minimal arguments. seal_hash_sha2_256 { @@ -1342,7 +1409,7 @@ benchmarks! { "seal_hash_sha2_256", r * API_BENCHMARK_BATCH_SIZE, 0, ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // `n`: Input to hash in kilobytes seal_hash_sha2_256_per_kb { @@ -1351,7 +1418,7 @@ benchmarks! { "seal_hash_sha2_256", API_BENCHMARK_BATCH_SIZE, n * 1024, ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // Only the overhead of calling the function itself with minimal arguments. seal_hash_keccak_256 { @@ -1360,7 +1427,7 @@ benchmarks! { "seal_hash_keccak_256", r * API_BENCHMARK_BATCH_SIZE, 0, ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // `n`: Input to hash in kilobytes seal_hash_keccak_256_per_kb { @@ -1369,7 +1436,7 @@ benchmarks! { "seal_hash_keccak_256", API_BENCHMARK_BATCH_SIZE, n * 1024, ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // Only the overhead of calling the function itself with minimal arguments. seal_hash_blake2_256 { @@ -1378,7 +1445,7 @@ benchmarks! { "seal_hash_blake2_256", r * API_BENCHMARK_BATCH_SIZE, 0, ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // `n`: Input to hash in kilobytes seal_hash_blake2_256_per_kb { @@ -1387,7 +1454,7 @@ benchmarks! { "seal_hash_blake2_256", API_BENCHMARK_BATCH_SIZE, n * 1024, ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // Only the overhead of calling the function itself with minimal arguments. seal_hash_blake2_128 { @@ -1396,7 +1463,7 @@ benchmarks! { "seal_hash_blake2_128", r * API_BENCHMARK_BATCH_SIZE, 0, ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // `n`: Input to hash in kilobytes seal_hash_blake2_128_per_kb { @@ -1405,7 +1472,7 @@ benchmarks! { "seal_hash_blake2_128", API_BENCHMARK_BATCH_SIZE, n * 1024, ), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // Only calling the function itself with valid arguments. // It generates different private keys and signatures for the message "Hello world". @@ -1459,7 +1526,7 @@ benchmarks! { }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) // We make the assumption that pushing a constant and dropping a value takes roughly // the same amount of time. We follow that `t.load` and `drop` both have the weight @@ -2266,6 +2333,7 @@ benchmarks! { instance.account_id, 0u32.into(), Weight::MAX, + None, data, false, ) @@ -2312,6 +2380,7 @@ benchmarks! { instance.account_id, 0u32.into(), Weight::MAX, + None, data, false, ) diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 6f24704cdd35f..1559f35695788 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -16,12 +16,15 @@ // limitations under the License. use crate::{ - gas::GasMeter, storage::Storage, AccountCounter, BalanceOf, CodeHash, Config, ContractInfo, - ContractInfoOf, Error, Event, Pallet as Contracts, Schedule, + gas::GasMeter, + storage::{self, Storage}, + AccountCounter, BalanceOf, CodeHash, Config, ContractInfo, ContractInfoOf, Error, Event, + Pallet as Contracts, Schedule, }; use frame_support::{ dispatch::{DispatchError, DispatchResult, DispatchResultWithPostInfo, Dispatchable}, ensure, + pallet_prelude::Encode, storage::{with_transaction, TransactionOutcome}, traits::{Contains, Currency, ExistenceRequirement, Get, OriginTrait, Randomness, Time}, weights::Weight, @@ -34,11 +37,6 @@ use sp_io::crypto::secp256k1_ecdsa_recover_compressed; use sp_runtime::traits::{Convert, Saturating}; use sp_std::{marker::PhantomData, mem, prelude::*}; -/// When fields are added to the [`ContractInfo`] that can change during execution this -/// variable needs to be set to true. This will also force changes to the -/// `in_memory_changes_not_discarded` test. -const CONTRACT_INFO_CAN_CHANGE: bool = false; - pub type AccountIdOf = ::AccountId; pub type MomentOf = <::Time as Time>::Moment; pub type SeedOf = ::Hash; @@ -191,6 +189,9 @@ pub trait Ext: sealing::Sealed { /// Get a mutable reference to the nested gas meter. fn gas_meter(&mut self) -> &mut GasMeter; + /// Get a mutable reference to the nested storage meter. + fn storage_meter(&mut self) -> &mut storage::meter::NestedMeter; + /// Append a string to the debug buffer. /// /// It is added as-is without any additional new line. @@ -206,6 +207,10 @@ pub trait Ext: sealing::Sealed { /// Recovers ECDSA compressed public key based on signature and message hash. fn ecdsa_recover(&self, signature: &[u8; 65], message_hash: &[u8; 32]) -> Result<[u8; 33], ()>; + + /// Tests sometimes need to modify and inspect the contract info directly. + #[cfg(test)] + fn contract_info(&mut self) -> &mut ContractInfo; } /// Describes the different functions that can be exported by an [`Executable`]. @@ -232,37 +237,12 @@ pub trait Executable: Sized { gas_meter: &mut GasMeter, ) -> 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. - /// - /// # Note - /// - /// Does not charge from the gas meter. Do not call in contexts where this is important. - fn from_storage_noinstr(code_hash: CodeHash) -> Result; - - /// Increment the refcount by one. Fails if the code does not exist on-chain. - /// - /// Returns the size of the original code. + /// Decrement the refcount by one if the code exists. /// /// # Note /// /// Charges weight proportional to the code size from the gas meter. - fn add_user(code_hash: CodeHash, gas_meter: &mut GasMeter) -> Result<(), DispatchError>; - - /// Decrement the refcount by one and remove the code when it drops to zero. - /// - /// Returns the size of the original code. - /// - /// # Note - /// - /// Charges weight proportional to the code size from the gas meter - fn remove_user( - code_hash: CodeHash, - gas_meter: &mut GasMeter, - ) -> Result<(), DispatchError>; + fn remove_user(code_hash: CodeHash) -> Result<(), DispatchError>; /// Execute the specified exported function and return the result. /// @@ -285,12 +265,6 @@ pub trait Executable: Sized { /// Size of the instrumented code in bytes. fn code_len(&self) -> u32; - - /// Sum of instrumented and pristine code len. - fn aggregate_code_len(&self) -> u32; - - // The number of contracts using this executable. - fn refcount(&self) -> u32; } /// The complete call stack of a contract execution. @@ -311,6 +285,8 @@ pub struct Stack<'a, T: Config, E> { schedule: &'a Schedule, /// The gas meter where costs are charged to. gas_meter: &'a mut GasMeter, + /// The storage meter makes sure that the storage limit is obeyed. + storage_meter: &'a mut storage::meter::Meter, /// The timestamp at the point of call stack instantiation. timestamp: MomentOf, /// The block number at the time of call stack instantiation. @@ -351,7 +327,9 @@ pub struct Frame { /// Determines whether this is a call or instantiate frame. entry_point: ExportedFunction, /// The gas meter capped to the supplied gas limit. - nested_meter: GasMeter, + nested_gas: GasMeter, + /// The storage meter for the individual call. + nested_storage: storage::meter::NestedMeter, /// If `false` the contract enabled its defense against reentrance attacks. allows_reentry: bool, } @@ -393,6 +371,26 @@ enum CachedContract { Terminated, } +impl CachedContract { + /// Return `Some(ContractInfo)` if the contract is in cached state. `None` otherwise. + fn into_contract(self) -> Option> { + if let CachedContract::Cached(contract) = self { + Some(contract) + } else { + None + } + } + + /// Return `Some(&mut ContractInfo)` if the contract is in cached state. `None` otherwise. + fn as_contract(&mut self) -> Option<&mut ContractInfo> { + if let CachedContract::Cached(contract) = self { + Some(contract) + } else { + None + } + } +} + impl Frame { /// Return the `contract_info` of the current contract. fn contract_info(&mut self) -> &mut ContractInfo { @@ -429,6 +427,26 @@ macro_rules! get_cached_or_panic_after_load { }}; } +/// Same as [`Stack::top_frame`]. +/// +/// We need this access as a macro because sometimes hiding the lifetimes behind +/// a function won't work out. +macro_rules! top_frame { + ($stack:expr) => { + $stack.frames.last().unwrap_or(&$stack.first_frame) + }; +} + +/// Same as [`Stack::top_frame_mut`]. +/// +/// We need this access as a macro because sometimes hiding the lifetimes behind +/// a function won't work out. +macro_rules! top_frame_mut { + ($stack:expr) => { + $stack.frames.last_mut().unwrap_or(&mut $stack.first_frame) + }; +} + impl CachedContract { /// Load the `contract_info` from storage if necessary. fn load(&mut self, account_id: &T::AccountId) { @@ -473,6 +491,7 @@ where origin: T::AccountId, dest: T::AccountId, gas_meter: &'a mut GasMeter, + storage_meter: &'a mut storage::meter::Meter, schedule: &'a Schedule, value: BalanceOf, input_data: Vec, @@ -482,6 +501,7 @@ where FrameArgs::Call { dest, cached_info: None }, origin, gas_meter, + storage_meter, schedule, value, debug_message, @@ -503,6 +523,7 @@ where origin: T::AccountId, executable: E, gas_meter: &'a mut GasMeter, + storage_meter: &'a mut storage::meter::Meter, schedule: &'a Schedule, value: BalanceOf, input_data: Vec, @@ -518,6 +539,7 @@ where }, origin, gas_meter, + storage_meter, schedule, value, debug_message, @@ -531,16 +553,18 @@ where args: FrameArgs, origin: T::AccountId, gas_meter: &'a mut GasMeter, + storage_meter: &'a mut storage::meter::Meter, schedule: &'a Schedule, value: BalanceOf, debug_message: Option<&'a mut Vec>, ) -> Result<(Self, E), ExecError> { let (first_frame, executable, account_counter) = - Self::new_frame(args, value, gas_meter, 0, &schedule)?; + Self::new_frame(args, value, gas_meter, storage_meter, 0, &schedule)?; let stack = Self { origin, schedule, gas_meter, + storage_meter, timestamp: T::Time::now(), block_number: >::block_number(), account_counter, @@ -557,13 +581,15 @@ where /// /// This does not take `self` because when constructing the first frame `self` is /// not initialized, yet. - fn new_frame( + fn new_frame( frame_args: FrameArgs, value_transferred: BalanceOf, gas_meter: &mut GasMeter, + storage_meter: &mut storage::meter::GenericMeter, gas_limit: Weight, schedule: &Schedule, ) -> Result<(Frame, E, Option), ExecError> { + let mut nested_storage = storage_meter.nested(); let (account_id, contract_info, executable, entry_point, account_counter) = match frame_args { FrameArgs::Call { dest, cached_info } => { @@ -586,6 +612,11 @@ where trie_id, executable.code_hash().clone(), )?; + nested_storage.charge(&storage::meter::Diff { + bytes_added: contract.encoded_size() as u32, + items_added: 1, + ..Default::default() + })?; (account_id, contract, executable, ExportedFunction::Constructor, Some(trie_seed)) }, }; @@ -595,7 +626,8 @@ where contract_info: CachedContract::Cached(contract_info), account_id, entry_point, - nested_meter: gas_meter.nested(gas_limit)?, + nested_gas: gas_meter.nested(gas_limit)?, + nested_storage, allows_reentry: true, }; @@ -613,23 +645,28 @@ where return Err(Error::::MaxCallDepthReached.into()) } - if CONTRACT_INFO_CAN_CHANGE { - // We need to make sure that changes made to the contract info are not discarded. - // See the `in_memory_changes_not_discarded` test for more information. - // We do not store on instantiate because we do not allow to call into a contract - // from its own constructor. - let frame = self.top_frame(); - if let (CachedContract::Cached(contract), ExportedFunction::Call) = - (&frame.contract_info, frame.entry_point) - { - >::insert(frame.account_id.clone(), contract.clone()); - } + // We need to make sure that changes made to the contract info are not discarded. + // See the `in_memory_changes_not_discarded` test for more information. + // We do not store on instantiate because we do not allow to call into a contract + // from its own constructor. + let frame = self.top_frame(); + if let (CachedContract::Cached(contract), ExportedFunction::Call) = + (&frame.contract_info, frame.entry_point) + { + >::insert(frame.account_id.clone(), contract.clone()); } - let nested_meter = - &mut self.frames.last_mut().unwrap_or(&mut self.first_frame).nested_meter; - let (frame, executable, _) = - Self::new_frame(frame_args, value_transferred, nested_meter, gas_limit, self.schedule)?; + let frame = top_frame_mut!(self); + let nested_gas = &mut frame.nested_gas; + let nested_storage = &mut frame.nested_storage; + let (frame, executable, _) = Self::new_frame( + frame_args, + value_transferred, + nested_gas, + nested_storage, + gas_limit, + self.schedule, + )?; self.frames.push(frame); Ok(executable) } @@ -699,15 +736,34 @@ where // A `None` means that we are returning from the `first_frame`. let frame = self.frames.pop(); - if let Some(frame) = frame { - let prev = self.top_frame_mut(); + // Both branches do essentially the same with the exception. The difference is that + // the else branch does consume the hardcoded `first_frame`. + if let Some(mut frame) = frame { let account_id = &frame.account_id; - prev.nested_meter.absorb_nested(frame.nested_meter); + let prev = top_frame_mut!(self); + + prev.nested_gas.absorb_nested(frame.nested_gas); + // Only gas counter changes are persisted in case of a failure. if !persist { return } - if let CachedContract::Cached(contract) = frame.contract_info { + + // Record the storage meter changes of the nested call into the parent meter. + // If the dropped frame's contract wasn't terminated we update the deposit counter + // in its contract info. The load is necessary to to pull it from storage in case + // it was invalidated. + frame.contract_info.load(account_id); + let mut contract = frame.contract_info.into_contract(); + prev.nested_storage.absorb( + frame.nested_storage, + &self.origin, + account_id, + contract.as_mut(), + ); + + // In case the contract wasn't terminated we need to persist changes made to it. + if let Some(contract) = contract { // optimization: Predecessor is the same contract. // We can just copy the contract into the predecessor without a storage write. // This is possible when there is no other contract in-between that could @@ -735,13 +791,18 @@ where core::str::from_utf8(msg).unwrap_or(""), ); } - // Write back to the root gas meter. - self.gas_meter.absorb_nested(mem::take(&mut self.first_frame.nested_meter)); - // Only gas counter changes are persisted in case of a failure. + self.gas_meter.absorb_nested(mem::take(&mut self.first_frame.nested_gas)); if !persist { return } - if let CachedContract::Cached(contract) = &self.first_frame.contract_info { + let mut contract = self.first_frame.contract_info.as_contract(); + self.storage_meter.absorb( + mem::take(&mut self.first_frame.nested_storage), + &self.origin, + &self.first_frame.account_id, + contract.as_deref_mut(), + ); + if let Some(contract) = contract { >::insert(&self.first_frame.account_id, contract); } if let Some(counter) = self.account_counter { @@ -770,6 +831,10 @@ where let existence_requirement = match (allow_death, sender_is_origin) { (true, _) => ExistenceRequirement::AllowDeath, (false, false) => { + // In case the caller is not the origin (i.e a contract) we make sure + // that the free balance alone keeps a contract's account alive. Otherwise + // it might be alive only due to some storage deposits which might remove the + // account when storage is removed. ensure!( T::Currency::free_balance(from).saturating_sub(value) >= T::Currency::minimum_balance(), @@ -807,12 +872,12 @@ where /// Reference to the current (top) frame. fn top_frame(&self) -> &Frame { - self.frames.last().unwrap_or(&self.first_frame) + top_frame!(self) } /// Mutable reference to the current (top) frame. fn top_frame_mut(&mut self) -> &mut Frame { - self.frames.last_mut().unwrap_or(&mut self.first_frame) + top_frame_mut!(self) } /// Iterator over all frames. @@ -934,6 +999,7 @@ where } let frame = self.top_frame_mut(); let info = frame.terminate(); + frame.nested_storage.terminate(&info); Storage::::queue_trie_for_deletion(&info)?; >::transfer( false, @@ -943,7 +1009,7 @@ where T::Currency::free_balance(&frame.account_id), )?; ContractInfoOf::::remove(&frame.account_id); - E::remove_user(info.code_hash, &mut frame.nested_meter)?; + E::remove_user(info.code_hash)?; Contracts::::deposit_event(Event::Terminated { contract: frame.account_id.clone(), beneficiary: beneficiary.clone(), @@ -961,7 +1027,12 @@ where fn set_storage(&mut self, key: StorageKey, value: Option>) -> DispatchResult { let frame = self.top_frame_mut(); - Storage::::write(frame.contract_info(), &key, value) + Storage::::write( + &frame.contract_info.get(&frame.account_id).trie_id, + &key, + value, + Some(&mut frame.nested_storage), + ) } fn address(&self) -> &T::AccountId { @@ -1016,7 +1087,11 @@ where } fn gas_meter(&mut self) -> &mut GasMeter { - &mut self.top_frame_mut().nested_meter + &mut self.top_frame_mut().nested_gas + } + + fn storage_meter(&mut self) -> &mut storage::meter::NestedMeter { + &mut self.top_frame_mut().nested_storage } fn append_debug_buffer(&mut self, msg: &str) -> bool { @@ -1039,6 +1114,11 @@ where fn ecdsa_recover(&self, signature: &[u8; 65], message_hash: &[u8; 32]) -> Result<[u8; 33], ()> { secp256k1_ecdsa_recover_compressed(&signature, &message_hash).map_err(|_| ()) } + + #[cfg(test)] + fn contract_info(&mut self) -> &mut ContractInfo { + self.top_frame_mut().contract_info() + } } fn deposit_event(topics: Vec, event: Event) { @@ -1076,9 +1156,9 @@ mod tests { storage::Storage, tests::{ test_utils::{get_balance, place_contract, set_balance}, - Call, Event as MetaEvent, ExtBuilder, Test, TestFilter, ALICE, BOB, CHARLIE, + Call, Event as MetaEvent, ExtBuilder, Test, TestFilter, ALICE, BOB, CHARLIE, GAS_LIMIT, }, - Error, Weight, + Error, }; use assert_matches::assert_matches; use codec::{Decode, Encode}; @@ -1094,8 +1174,6 @@ mod tests { type MockStack<'a> = Stack<'a, Test, MockExecutable>; - const GAS_LIMIT: Weight = 10_000_000_000; - thread_local! { static LOADER: RefCell = RefCell::new(MockLoader::default()); } @@ -1186,10 +1264,6 @@ mod tests { _schedule: &Schedule, _gas_meter: &mut GasMeter, ) -> Result { - Self::from_storage_noinstr(code_hash) - } - - fn from_storage_noinstr(code_hash: CodeHash) -> Result { LOADER.with(|loader| { loader .borrow_mut() @@ -1200,18 +1274,7 @@ mod tests { }) } - fn add_user( - code_hash: CodeHash, - _: &mut GasMeter, - ) -> Result<(), DispatchError> { - MockLoader::increment_refcount(code_hash); - Ok(()) - } - - fn remove_user( - code_hash: CodeHash, - _: &mut GasMeter, - ) -> Result<(), DispatchError> { + fn remove_user(code_hash: CodeHash) -> Result<(), DispatchError> { MockLoader::decrement_refcount(code_hash); Ok(()) } @@ -1239,14 +1302,6 @@ mod tests { fn code_len(&self) -> u32 { 0 } - - fn aggregate_code_len(&self) -> u32 { - 0 - } - - fn refcount(&self) -> u32 { - self.refcount as u32 - } } fn exec_success() -> ExecResult { @@ -1273,9 +1328,19 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let schedule = ::Schedule::get(); place_contract(&BOB, exec_ch); + let mut storage_meter = storage::meter::Meter::new(ALICE, 0).unwrap(); assert_matches!( - MockStack::run_call(ALICE, BOB, &mut gas_meter, &schedule, value, vec![], None,), + MockStack::run_call( + ALICE, + BOB, + &mut gas_meter, + &mut storage_meter, + &schedule, + value, + vec![], + None, + ), Ok(_) ); }); @@ -1317,11 +1382,13 @@ mod tests { place_contract(&dest, return_ch); set_balance(&origin, 100); let balance = get_balance(&dest); + let mut storage_meter = storage::meter::Meter::new(origin.clone(), 0).unwrap(); let output = MockStack::run_call( origin.clone(), dest.clone(), &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, &schedule, 55, vec![], @@ -1365,12 +1432,14 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let schedule = ::Schedule::get(); + let mut storage_meter = storage::meter::Meter::new(origin.clone(), 0).unwrap(); place_contract(&BOB, return_ch); let result = MockStack::run_call( origin, dest, &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, &schedule, 0, vec![], @@ -1396,11 +1465,13 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let schedule = ::Schedule::get(); place_contract(&BOB, return_ch); + let mut storage_meter = storage::meter::Meter::new(origin.clone(), 0).unwrap(); let result = MockStack::run_call( origin, dest, &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, &schedule, 0, vec![], @@ -1424,11 +1495,13 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let schedule = ::Schedule::get(); place_contract(&BOB, input_data_ch); + let mut storage_meter = storage::meter::Meter::new(ALICE, 0).unwrap(); let result = MockStack::run_call( ALICE, BOB, &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, &schedule, 0, vec![1, 2, 3, 4], @@ -1452,15 +1525,16 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); let executable = MockExecutable::from_storage(input_data_ch, &schedule, &mut gas_meter).unwrap(); - - set_balance(&ALICE, min_balance * 10); + set_balance(&ALICE, min_balance * 1000); + let mut storage_meter = storage::meter::Meter::new(ALICE, min_balance * 100).unwrap(); let result = MockStack::run_instantiate( ALICE, executable, &mut gas_meter, + &mut storage_meter, &schedule, - min_balance * 3, + min_balance, vec![1, 2, 3, 4], &[], None, @@ -1501,11 +1575,13 @@ mod tests { let schedule = ::Schedule::get(); set_balance(&BOB, 1); place_contract(&BOB, recurse_ch); + let mut storage_meter = storage::meter::Meter::new(ALICE, 0).unwrap(); let result = MockStack::run_call( ALICE, BOB, &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, &schedule, value, vec![], @@ -1546,11 +1622,13 @@ mod tests { let schedule = ::Schedule::get(); place_contract(&dest, bob_ch); place_contract(&CHARLIE, charlie_ch); + let mut storage_meter = storage::meter::Meter::new(origin.clone(), 0).unwrap(); let result = MockStack::run_call( origin.clone(), dest.clone(), &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, &schedule, 0, vec![], @@ -1583,11 +1661,13 @@ mod tests { let schedule = ::Schedule::get(); place_contract(&BOB, bob_ch); place_contract(&CHARLIE, charlie_ch); + let mut storage_meter = storage::meter::Meter::new(ALICE, 0).unwrap(); let result = MockStack::run_call( ALICE, BOB, &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, &schedule, 0, vec![], @@ -1607,12 +1687,14 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); let executable = MockExecutable::from_storage(dummy_ch, &schedule, &mut gas_meter).unwrap(); + let mut storage_meter = storage::meter::Meter::new(ALICE, 0).unwrap(); assert_matches!( MockStack::run_instantiate( ALICE, executable, &mut gas_meter, + &mut storage_meter, &schedule, 0, // <- zero endowment vec![], @@ -1632,18 +1714,21 @@ mod tests { ExtBuilder::default().existential_deposit(15).build().execute_with(|| { let schedule = ::Schedule::get(); + let min_balance = ::Currency::minimum_balance(); let mut gas_meter = GasMeter::::new(GAS_LIMIT); let executable = MockExecutable::from_storage(dummy_ch, &schedule, &mut gas_meter).unwrap(); - set_balance(&ALICE, 1000); + set_balance(&ALICE, min_balance * 1000); + let mut storage_meter = storage::meter::Meter::new(ALICE, min_balance * 100).unwrap(); let instantiated_contract_address = assert_matches!( MockStack::run_instantiate( ALICE, executable, &mut gas_meter, + &mut storage_meter, &schedule, - 100, + min_balance, vec![], &[], None, @@ -1672,18 +1757,21 @@ mod tests { ExtBuilder::default().existential_deposit(15).build().execute_with(|| { let schedule = ::Schedule::get(); + let min_balance = ::Currency::minimum_balance(); let mut gas_meter = GasMeter::::new(GAS_LIMIT); let executable = MockExecutable::from_storage(dummy_ch, &schedule, &mut gas_meter).unwrap(); - set_balance(&ALICE, 1000); + set_balance(&ALICE, min_balance * 1000); + let mut storage_meter = storage::meter::Meter::new(ALICE, min_balance * 100).unwrap(); let instantiated_contract_address = assert_matches!( MockStack::run_instantiate( ALICE, executable, &mut gas_meter, + &mut storage_meter, &schedule, - 100, + min_balance, vec![], &[], None, @@ -1711,7 +1799,7 @@ mod tests { .instantiate( 0, dummy_ch, - ::Currency::minimum_balance() * 3, + ::Currency::minimum_balance(), vec![], &[48, 49, 50], ) @@ -1724,16 +1812,19 @@ mod tests { ExtBuilder::default().existential_deposit(15).build().execute_with(|| { let schedule = ::Schedule::get(); - set_balance(&ALICE, ::Currency::minimum_balance() * 100); + let min_balance = ::Currency::minimum_balance(); + set_balance(&ALICE, min_balance * 100); place_contract(&BOB, instantiator_ch); + let mut storage_meter = storage::meter::Meter::new(ALICE, min_balance * 10).unwrap(); assert_matches!( MockStack::run_call( ALICE, BOB, &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, &schedule, - 20, + min_balance * 10, vec![], None, ), @@ -1786,14 +1877,16 @@ mod tests { set_balance(&ALICE, 1000); set_balance(&BOB, 100); place_contract(&BOB, instantiator_ch); + let mut storage_meter = storage::meter::Meter::new(ALICE, 100).unwrap(); assert_matches!( MockStack::run_call( ALICE, BOB, &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, &schedule, - 20, + 0, vec![], None, ), @@ -1819,12 +1912,14 @@ mod tests { let executable = MockExecutable::from_storage(terminate_ch, &schedule, &mut gas_meter).unwrap(); set_balance(&ALICE, 1000); + let mut storage_meter = storage::meter::Meter::new(ALICE, 100).unwrap(); assert_eq!( MockStack::run_instantiate( ALICE, executable, &mut gas_meter, + &mut storage_meter, &schedule, 100, vec![], @@ -1840,10 +1935,6 @@ mod tests { #[test] fn in_memory_changes_not_discarded() { - // Remove this assert and fill out the "DO" stubs once fields are added to the - // contract info that can be modified during exection. - assert!(!CONTRACT_INFO_CAN_CHANGE); - // Call stack: BOB -> CHARLIE (trap) -> BOB' (success) // This tests verfies some edge case of the contract info cache: // We change some value in our contract info before calling into a contract @@ -1854,9 +1945,11 @@ mod tests { // are made before calling into CHARLIE are not discarded. let code_bob = MockLoader::insert(Call, |ctx, _| { if ctx.input_data[0] == 0 { - // DO: modify medata (ContractInfo) of own contract through ctx.ext functions + let info = ctx.ext.contract_info(); + assert_eq!(info._reserved, None); + info._reserved = Some(()); assert_eq!(ctx.ext.call(0, CHARLIE, 0, vec![], true), exec_trapped()); - // DO: check that the value is not discarded (query via ctx.ext) + assert_eq!(ctx.ext.contract_info()._reserved, Some(())); } exec_success() }); @@ -1870,11 +1963,13 @@ mod tests { let schedule = ::Schedule::get(); place_contract(&BOB, code_bob); place_contract(&CHARLIE, code_charlie); + let mut storage_meter = storage::meter::Meter::new(ALICE, 0).unwrap(); let result = MockStack::run_call( ALICE, BOB, &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, &schedule, 0, vec![0], @@ -1900,15 +1995,16 @@ mod tests { let min_balance = ::Currency::minimum_balance(); let mut gas_meter = GasMeter::::new(GAS_LIMIT); let executable = MockExecutable::from_storage(code, &schedule, &mut gas_meter).unwrap(); - - set_balance(&ALICE, min_balance * 10); + set_balance(&ALICE, min_balance * 1000); + let mut storage_meter = storage::meter::Meter::new(ALICE, min_balance * 100).unwrap(); let result = MockStack::run_instantiate( ALICE, executable, &mut gas_meter, + &mut storage_meter, &schedule, - min_balance * 3, + min_balance, vec![], &[], None, @@ -1933,10 +2029,12 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); + let mut storage_meter = storage::meter::Meter::new(ALICE, 0).unwrap(); MockStack::run_call( ALICE, BOB, &mut gas_meter, + &mut storage_meter, &schedule, 0, vec![], @@ -1964,10 +2062,12 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); + let mut storage_meter = storage::meter::Meter::new(ALICE, 0).unwrap(); let result = MockStack::run_call( ALICE, BOB, &mut gas_meter, + &mut storage_meter, &schedule, 0, vec![], @@ -1993,12 +2093,14 @@ mod tests { let schedule = ::Schedule::get(); place_contract(&BOB, code_bob); place_contract(&CHARLIE, code_charlie); + let mut storage_meter = storage::meter::Meter::new(ALICE, 0).unwrap(); // Calling another contract should succeed assert_ok!(MockStack::run_call( ALICE, BOB, &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, &schedule, 0, CHARLIE.encode(), @@ -2011,6 +2113,7 @@ mod tests { ALICE, BOB, &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, &schedule, 0, BOB.encode(), @@ -2040,6 +2143,7 @@ mod tests { let schedule = ::Schedule::get(); place_contract(&BOB, code_bob); place_contract(&CHARLIE, code_charlie); + let mut storage_meter = storage::meter::Meter::new(ALICE, 0).unwrap(); // BOB -> CHARLIE -> BOB fails as BOB denies reentry. assert_err!( @@ -2047,6 +2151,7 @@ mod tests { ALICE, BOB, &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, &schedule, 0, vec![0], @@ -2074,8 +2179,19 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); + let mut storage_meter = storage::meter::Meter::new(ALICE, 0).unwrap(); System::reset_events(); - MockStack::run_call(ALICE, BOB, &mut gas_meter, &schedule, 0, vec![], None).unwrap(); + MockStack::run_call( + ALICE, + BOB, + &mut gas_meter, + &mut storage_meter, + &schedule, + 0, + vec![], + None, + ) + .unwrap(); let remark_hash = ::Hashing::hash(b"Hello World"); assert_eq!( @@ -2131,8 +2247,19 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); + let mut storage_meter = storage::meter::Meter::new(ALICE, 0).unwrap(); System::reset_events(); - MockStack::run_call(ALICE, BOB, &mut gas_meter, &schedule, 0, vec![], None).unwrap(); + MockStack::run_call( + ALICE, + BOB, + &mut gas_meter, + &mut storage_meter, + &schedule, + 0, + vec![], + None, + ) + .unwrap(); let remark_hash = ::Hashing::hash(b"Hello"); assert_eq!( @@ -2196,11 +2323,13 @@ mod tests { let succ_succ_executable = MockExecutable::from_storage(succ_succ_code, &schedule, &mut gas_meter).unwrap(); set_balance(&ALICE, min_balance * 1000); + let mut storage_meter = storage::meter::Meter::new(ALICE, min_balance * 500).unwrap(); MockStack::run_instantiate( ALICE, fail_executable, &mut gas_meter, + &mut storage_meter, &schedule, min_balance * 100, vec![], @@ -2214,6 +2343,7 @@ mod tests { ALICE, success_executable, &mut gas_meter, + &mut storage_meter, &schedule, min_balance * 100, vec![], @@ -2226,6 +2356,7 @@ mod tests { ALICE, succ_fail_executable, &mut gas_meter, + &mut storage_meter, &schedule, min_balance * 200, vec![], @@ -2238,6 +2369,7 @@ mod tests { ALICE, succ_succ_executable, &mut gas_meter, + &mut storage_meter, &schedule, min_balance * 200, vec![], diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index d70edad2dc007..a4bbd162a1aa6 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -106,24 +106,27 @@ pub use crate::{ use crate::{ exec::{AccountIdOf, ExecError, Executable, Stack as ExecStack}, gas::GasMeter, - storage::{ContractInfo, DeletedContract, Storage}, - wasm::PrefabWasmModule, + storage::{meter::Meter as StorageMeter, ContractInfo, DeletedContract, Storage}, + wasm::{OwnerInfo, PrefabWasmModule}, weights::WeightInfo, }; +use codec::{Encode, HasCompact}; use frame_support::{ dispatch::Dispatchable, ensure, - traits::{Contains, Currency, Get, Randomness, StorageVersion, Time}, - weights::{GetDispatchInfo, PostDispatchInfo, Weight}, + traits::{Contains, Currency, Get, Randomness, ReservableCurrency, StorageVersion, Time}, + weights::{GetDispatchInfo, Pays, PostDispatchInfo, Weight}, }; use frame_system::Pallet as System; use pallet_contracts_primitives::{ - Code, ContractAccessError, ContractExecResult, ContractInstantiateResult, ExecReturnValue, - GetStorageResult, InstantiateReturnValue, + Code, CodeUploadResult, CodeUploadReturnValue, ContractAccessError, ContractExecResult, + ContractInstantiateResult, ExecReturnValue, GetStorageResult, InstantiateReturnValue, + StorageDeposit, }; +use scale_info::TypeInfo; use sp_core::{crypto::UncheckedFrom, Bytes}; -use sp_runtime::traits::{Convert, Hash, StaticLookup}; -use sp_std::prelude::*; +use sp_runtime::traits::{CheckedSub, Convert, Hash, Saturating, StaticLookup}; +use sp_std::{fmt::Debug, prelude::*}; type CodeHash = ::Hash; type TrieId = Vec; @@ -148,7 +151,7 @@ pub mod pallet { type Randomness: Randomness; /// The currency in which fees are paid and contract balances are held. - type Currency: Currency; + type Currency: ReservableCurrency; /// The overarching event type. type Event: From> + IsType<::Event>; @@ -209,6 +212,21 @@ pub mod pallet { /// The maximum amount of weight that can be consumed per block for lazy trie removal. #[pallet::constant] type DeletionWeightLimit: Get; + + /// The amount of balance a caller has to pay for each byte of storage. + /// + /// # Note + /// + /// Changing this value for an existing chain might need a storage migration. + #[pallet::constant] + type DepositPerByte: Get>; + + /// The amount of balance a caller has to pay for each storage item. + /// # Note + /// + /// Changing this value for an existing chain might need a storage migration. + #[pallet::constant] + type DepositPerItem: Get>; } #[pallet::pallet] @@ -242,9 +260,19 @@ pub mod pallet { where T::AccountId: UncheckedFrom, T::AccountId: AsRef<[u8]>, + as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, { /// Makes a call to an account, optionally transferring some balance. /// + /// # Parameters + /// + /// * `dest`: Address of the contract to call. + /// * `value`: The balance to transfer from the `origin` to `dest`. + /// * `gas_limit`: The gas limit enforced when executing the constructor. + /// * `storage_limit`: The maximum amount of balance that can be charged from the caller to + /// pay for the storage consumed. + /// * `data`: The input data to pass to the contract. + /// /// * If the account is a smart-contract account, the associated code will be /// executed and any value will be transferred. /// * If the account is a regular account, any value will be transferred. @@ -256,23 +284,36 @@ pub mod pallet { dest: ::Source, #[pallet::compact] value: BalanceOf, #[pallet::compact] gas_limit: Weight, + storage_limit: Option< as codec::HasCompact>::Type>, data: Vec, ) -> DispatchResultWithPostInfo { let origin = ensure_signed(origin)?; let dest = T::Lookup::lookup(dest)?; - let output = Self::internal_call(origin, dest, value, gas_limit, data, None); + let output = Self::internal_call( + origin, + dest, + value, + gas_limit, + storage_limit.map(Into::into), + data, + None, + ); output.gas_meter.into_dispatch_result(output.result, T::WeightInfo::call()) } /// 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. + /// This dispatchable has the same effect as calling [`Self::upload_code`] + + /// [`Self::instantiate`]. Bundling them together provides efficiency gains. Please + /// also check the documentation of [`Self::upload_code`]. /// /// # Parameters /// /// * `endowment`: The balance to transfer from the `origin` to the newly created contract. /// * `gas_limit`: The gas limit enforced when executing the constructor. + /// * `storage_limit`: The maximum amount of balance that can be charged/reserved from the + /// caller to pay for the storage consumed. /// * `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 [`Pallet::contract_address`]. @@ -297,6 +338,7 @@ pub mod pallet { origin: OriginFor, #[pallet::compact] endowment: BalanceOf, #[pallet::compact] gas_limit: Weight, + storage_limit: Option< as codec::HasCompact>::Type>, code: Vec, data: Vec, salt: Vec, @@ -308,6 +350,7 @@ pub mod pallet { origin, endowment, gas_limit, + storage_limit.map(Into::into), Code::Upload(Bytes(code)), data, salt, @@ -331,6 +374,7 @@ pub mod pallet { origin: OriginFor, #[pallet::compact] endowment: BalanceOf, #[pallet::compact] gas_limit: Weight, + storage_limit: Option< as codec::HasCompact>::Type>, code_hash: CodeHash, data: Vec, salt: Vec, @@ -341,6 +385,7 @@ pub mod pallet { origin, endowment, gas_limit, + storage_limit.map(Into::into), Code::Existing(code_hash), data, salt, @@ -351,6 +396,43 @@ pub mod pallet { T::WeightInfo::instantiate(salt_len / 1024), ) } + + /// Upload new `code` without instantiating a contract from it. + /// + /// If the code does not already exist a deposit is reserved from the caller + /// and unreserved only when [`Self::remove_code`] is called. The size of the reserve + /// depends on the instrumented size of the the supplied `code`. + /// + /// # Note + /// + /// Anyone can instantiate a contract from any uploaded code and thus prevent its removal. + /// To avoid this situation a constructor could employ access control so that it can + /// only be instantiated by permissioned entities. The same is true when uploading + /// through [`Self::instantiate_with_code`]. + #[pallet::weight(0)] + pub fn upload_code( + origin: OriginFor, + code: Vec, + storage_limit: Option< as codec::HasCompact>::Type>, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + Self::bare_upload_code(origin, code, storage_limit.map(Into::into)).map(|_| ()) + } + + /// Remove the code stored under `code_hash` and refund the deposit to its owner. + /// + /// A code can only be removed by its original uploader (its owner) and only if it is + /// not used by any contract. + #[pallet::weight(0)] + pub fn remove_code( + origin: OriginFor, + code_hash: CodeHash, + ) -> DispatchResultWithPostInfo { + let origin = ensure_signed(origin)?; + >::remove(&origin, code_hash)?; + // we waive the fee because removing unused code is beneficial + Ok(Pays::No.into()) + } } #[pallet::event] @@ -375,12 +457,6 @@ pub mod pallet { /// Code with the specified hash has been stored. CodeStored { code_hash: T::Hash }, - /// Triggered when the current schedule is updated. - ScheduleUpdated { - /// The version of the newly set schedule. - version: u32, - }, - /// A custom event emitted by the contract. ContractEmitted { /// The contract that emitted the event. @@ -391,8 +467,6 @@ pub mod pallet { }, /// A code with the specified hash was removed. - /// - /// This happens when the last contract that uses this code hash was removed. CodeRemoved { code_hash: T::Hash }, } @@ -458,11 +532,6 @@ pub mod pallet { /// The queue is filled by deleting contracts and emptied by a fixed amount each block. /// Trying again during another block is the only way to resolve this issue. DeletionQueueFull, - /// A storage modification exhausted the 32bit type that holds the storage size. - /// - /// 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, /// A contract self destructed in its constructor. @@ -473,6 +542,12 @@ pub mod pallet { DebugMessageInvalidUTF8, /// A call tried to invoke a contract that is flagged as non-reentrant. ReentranceDenied, + /// Origin doesn't have enough balance to pay for the storage limit. + StorageLimitTooHigh, + /// More storage was created than allowed by the storage limit. + StorageExhausted, + /// Code removal was denied because the code is still in use by at least one contract. + CodeInUse, } /// A mapping from an original code hash to the original code, untouched by instrumentation. @@ -484,6 +559,10 @@ pub mod pallet { pub(crate) type CodeStorage = StorageMap<_, Identity, CodeHash, PrefabWasmModule>; + /// A mapping between an original code hash and its owner information. + #[pallet::storage] + pub(crate) type OwnerInfoOf = StorageMap<_, Identity, CodeHash, OwnerInfo>; + /// The subtrie counter. #[pallet::storage] pub(crate) type AccountCounter = StorageValue<_, u64, ValueQuery>; @@ -513,6 +592,8 @@ type InternalInstantiateOutput = InternalOutput, ExecRetur struct InternalOutput { /// The gas meter that was used to execute the call. gas_meter: GasMeter, + /// The storage deposit used by the call. + storage_deposit: StorageDeposit>, /// The result of the call. result: Result, } @@ -538,16 +619,25 @@ where dest: T::AccountId, value: BalanceOf, gas_limit: Weight, + storage_limit: Option>, data: Vec, debug: bool, - ) -> ContractExecResult { + ) -> ContractExecResult> { let mut debug_message = if debug { Some(Vec::new()) } else { None }; - let output = - Self::internal_call(origin, dest, value, gas_limit, data, debug_message.as_mut()); + let output = Self::internal_call( + origin, + dest, + value, + gas_limit, + storage_limit, + data, + debug_message.as_mut(), + ); ContractExecResult { result: output.result.map_err(|r| r.error), gas_consumed: output.gas_meter.gas_consumed(), gas_required: output.gas_meter.gas_required(), + storage_deposit: output.storage_deposit, debug_message: debug_message.unwrap_or_default(), } } @@ -559,7 +649,6 @@ where /// /// It returns the execution result, account id and the amount of used weight. /// - /// /// # Note /// /// `debug` should only ever be set to `true` when executing as an RPC because @@ -569,16 +658,18 @@ where origin: T::AccountId, endowment: BalanceOf, gas_limit: Weight, + storage_limit: Option>, code: Code>, data: Vec, salt: Vec, debug: bool, - ) -> ContractInstantiateResult { + ) -> ContractInstantiateResult> { let mut debug_message = if debug { Some(Vec::new()) } else { None }; let output = Self::internal_instantiate( origin, endowment, gas_limit, + storage_limit, code, data, salt, @@ -591,10 +682,31 @@ where .map_err(|e| e.error), gas_consumed: output.gas_meter.gas_consumed(), gas_required: output.gas_meter.gas_required(), + storage_deposit: output.storage_deposit, debug_message: debug_message.unwrap_or_default(), } } + /// Upload new code without instantiating a contract from it. + /// + /// This function is similar to [`Self::upload_code`], but doesn't perform any address lookups + /// and better suitable for calling directly from Rust. + pub fn bare_upload_code( + origin: T::AccountId, + code: Vec, + storage_limit: Option>, + ) -> CodeUploadResult, BalanceOf> { + let schedule = T::Schedule::get(); + let module = PrefabWasmModule::from_code(code, &schedule, origin)?; + let deposit = module.open_deposit(); + if let Some(storage_limit) = storage_limit { + ensure!(storage_limit <= deposit, >::StorageExhausted); + } + let result = CodeUploadReturnValue { code_hash: *module.code_hash(), deposit }; + module.store()?; + Ok(result) + } + /// Query storage of a specified contract under a specified key. pub fn get_storage(address: T::AccountId, key: [u8; 32]) -> GetStorageResult { let contract_info = @@ -629,9 +741,12 @@ where /// Store code for benchmarks which does not check nor instrument the code. #[cfg(feature = "runtime-benchmarks")] - fn store_code_raw(code: Vec) -> frame_support::dispatch::DispatchResult { + fn store_code_raw( + code: Vec, + owner: T::AccountId, + ) -> frame_support::dispatch::DispatchResult { let schedule = T::Schedule::get(); - PrefabWasmModule::store_code_unchecked(code, &schedule)?; + PrefabWasmModule::store_code_unchecked(code, &schedule, owner)?; Ok(()) } @@ -652,21 +767,34 @@ where dest: T::AccountId, value: BalanceOf, gas_limit: Weight, + storage_limit: Option>, data: Vec, debug_message: Option<&mut Vec>, ) -> InternalCallOutput { + let storage_limit = + storage_limit.unwrap_or_else(|| Self::max_storage_limit(&origin, value)); let mut gas_meter = GasMeter::new(gas_limit); + let mut storage_meter = match StorageMeter::new(origin.clone(), storage_limit) { + Ok(meter) => meter, + Err(err) => + return InternalCallOutput { + result: Err(err.into()), + gas_meter, + storage_deposit: Default::default(), + }, + }; let schedule = T::Schedule::get(); let result = ExecStack::>::run_call( origin, dest, &mut gas_meter, + &mut storage_meter, &schedule, value, data, debug_message, ); - InternalCallOutput { gas_meter, result } + InternalCallOutput { result, gas_meter, storage_deposit: storage_meter.into_deposit() } } /// Internal function that does the actual instantiation. @@ -676,41 +804,66 @@ where origin: T::AccountId, endowment: BalanceOf, gas_limit: Weight, + storage_limit: Option>, code: Code>, data: Vec, salt: Vec, debug_message: Option<&mut Vec>, ) -> InternalInstantiateOutput { + let mut storage_limit = + storage_limit.unwrap_or_else(|| Self::max_storage_limit(&origin, endowment)); + let mut storage_deposit = Default::default(); let mut gas_meter = GasMeter::new(gas_limit); - let schedule = T::Schedule::get(); let try_exec = || { - let executable = match code { + let schedule = T::Schedule::get(); + let (executable, extra_deposit) = match code { Code::Upload(Bytes(binary)) => { ensure!( binary.len() as u32 <= schedule.limits.code_len, >::CodeTooLarge ); - let executable = PrefabWasmModule::from_code(binary, &schedule)?; + let executable = + PrefabWasmModule::from_code(binary, &schedule, origin.clone())?; ensure!( executable.code_len() <= schedule.limits.code_len, >::CodeTooLarge ); - executable + // There might be an deposit that will be charged during execution when the + // uploaded module does not already exist. This deposit is not part of the + // storage meter because it is not transfered to the contract but + // reserved on the uploading account. + let deposit = executable.open_deposit(); + storage_limit = + storage_limit.checked_sub(&deposit).ok_or(>::StorageExhausted)?; + (executable, StorageDeposit::Charge(deposit)) }, - Code::Existing(hash) => + Code::Existing(hash) => ( PrefabWasmModule::from_storage(hash, &schedule, &mut gas_meter)?, + Default::default(), + ), }; - ExecStack::>::run_instantiate( + let mut storage_meter = StorageMeter::new(origin.clone(), storage_limit)?; + let result = ExecStack::>::run_instantiate( origin, executable, &mut gas_meter, + &mut storage_meter, &schedule, endowment, data, &salt, debug_message, - ) + ); + storage_deposit = storage_meter.into_deposit().saturating_add(&extra_deposit); + result }; - InternalInstantiateOutput { result: try_exec(), gas_meter } + InternalInstantiateOutput { result: try_exec(), gas_meter, storage_deposit } + } + + /// If no storage limit is specified we use this function. + fn max_storage_limit(origin: &T::AccountId, value: BalanceOf) -> BalanceOf { + T::Currency::free_balance(origin) + .saturating_sub(T::Currency::minimum_balance()) + .saturating_sub(value) } } diff --git a/frame/contracts/src/migration.rs b/frame/contracts/src/migration.rs index b7fa9575e23b5..d1f0164967683 100644 --- a/frame/contracts/src/migration.rs +++ b/frame/contracts/src/migration.rs @@ -54,10 +54,9 @@ mod v4 { /// V5: State rent is removed which obsoletes some fields in `ContractInfo`. mod v5 { use super::*; - use crate::{ - BalanceOf, CodeHash, ContractInfo, ContractInfoOf, DeletedContract, DeletionQueue, TrieId, - }; - use codec::Decode; + use crate::{BalanceOf, CodeHash, TrieId}; + use codec::{Decode, Encode}; + use frame_support::{codec, generate_storage_alias, Twox64Concat}; use sp_std::marker::PhantomData; type AliveContractInfo = @@ -95,6 +94,30 @@ mod v5 { trie_id: TrieId, } + type ContractInfo = RawContractInfo>; + + #[derive(Encode, Decode)] + struct RawContractInfo { + trie_id: TrieId, + code_hash: CodeHash, + _reserved: Option<()>, + } + + #[derive(Encode, Decode)] + struct DeletedContract { + trie_id: TrieId, + } + + generate_storage_alias!( + Contracts, + ContractInfoOf => Map<(Twox64Concat, T::AccountId), ContractInfo> + ); + + generate_storage_alias!( + Contracts, + DeletionQueue => Value> + ); + pub fn migrate() -> Weight { let mut weight: Weight = 0; @@ -110,7 +133,7 @@ mod v5 { } }); - >::translate(|old: Option>| { + DeletionQueue::translate(|old: Option>| { weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); old.map(|old| old.into_iter().map(|o| DeletedContract { trie_id: o.trie_id }).collect()) }) diff --git a/frame/contracts/src/storage.rs b/frame/contracts/src/storage.rs index 41db0796717e4..a97804e4d9e22 100644 --- a/frame/contracts/src/storage.rs +++ b/frame/contracts/src/storage.rs @@ -17,10 +17,12 @@ //! This module contains routines for accessing and altering a contract related state. +pub mod meter; + use crate::{ exec::{AccountIdOf, StorageKey}, weights::WeightInfo, - CodeHash, Config, ContractInfoOf, DeletionQueue, Error, TrieId, + BalanceOf, CodeHash, Config, ContractInfoOf, DeletionQueue, Error, TrieId, }; use codec::{Decode, Encode}; use frame_support::{ @@ -32,24 +34,29 @@ use frame_support::{ use scale_info::TypeInfo; use sp_core::crypto::UncheckedFrom; use sp_io::hashing::blake2_256; -use sp_runtime::{traits::Hash, RuntimeDebug}; +use sp_runtime::{ + traits::{Hash, Zero}, + RuntimeDebug, +}; use sp_std::{marker::PhantomData, prelude::*}; -pub type ContractInfo = RawContractInfo>; +pub type ContractInfo = RawContractInfo, BalanceOf>; /// Information for managing an account and its sub trie abstraction. /// This is the required info to cache for an account. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] -pub struct RawContractInfo { +pub struct RawContractInfo { /// Unique ID for the subtree encoded as a bytes vector. pub trie_id: TrieId, /// The code associated with a given account. pub code_hash: CodeHash, + /// The amount of balance that is currently deposited to pay for consumed storage. + pub storage_deposit: Balance, /// This field is reserved for future evolution of format. pub _reserved: Option<()>, } -impl RawContractInfo { +impl RawContractInfo { /// Associated child trie unique id is built from the hash part of the trie id. #[cfg(test)] pub fn child_trie_info(&self) -> ChildInfo { @@ -79,7 +86,7 @@ where /// The read is performed from the `trie_id` only. The `address` is not necessary. If the /// contract doesn't store under the given `key` `None` is returned. pub fn read(trie_id: &TrieId, key: &StorageKey) -> Option> { - child::get_raw(&child_trie_info(&trie_id), &blake2_256(key)) + child::get_raw(&child_trie_info(trie_id), &blake2_256(key)) } /// Update a storage entry into a contract's kv storage. @@ -90,15 +97,39 @@ where /// contract owns, the last block the storage was written to, etc. That's why, in contrast to /// `read`, this function also requires the `account` ID. pub fn write( - new_info: &mut ContractInfo, + trie_id: &TrieId, key: &StorageKey, - opt_new_value: Option>, + new_value: Option>, + storage_meter: Option<&mut meter::NestedMeter>, ) -> DispatchResult { let hashed_key = blake2_256(key); - let child_trie_info = &child_trie_info(&new_info.trie_id); + let child_trie_info = &child_trie_info(trie_id); + + if let Some(storage_meter) = storage_meter { + let mut diff = meter::Diff::default(); + let old_len = child::len(&child_trie_info, &hashed_key); + match (old_len, new_value.as_ref().map(|v| v.len() as u32)) { + (Some(old_len), Some(new_len)) => + if new_len > old_len { + diff.bytes_added = new_len - old_len; + } else { + diff.bytes_removed = old_len - new_len; + }, + (None, Some(new_len)) => { + diff.bytes_added = new_len; + diff.items_added = 1; + }, + (Some(old_len), None) => { + diff.bytes_removed = old_len; + diff.items_removed = 1; + }, + (None, None) => (), + } + storage_meter.charge(&diff)?; + } - match opt_new_value { - Some(new_value) => child::put_raw(&child_trie_info, &hashed_key, &new_value[..]), + match &new_value { + Some(new_value) => child::put_raw(&child_trie_info, &hashed_key, new_value), None => child::kill(&child_trie_info, &hashed_key), } @@ -118,7 +149,12 @@ where return Err(Error::::DuplicateContract.into()) } - let contract = ContractInfo:: { code_hash: ch, trie_id, _reserved: None }; + let contract = ContractInfo:: { + code_hash: ch, + trie_id, + storage_deposit: >::zero(), + _reserved: None, + }; Ok(contract) } diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs new file mode 100644 index 0000000000000..03eefacf729f3 --- /dev/null +++ b/frame/contracts/src/storage/meter.rs @@ -0,0 +1,359 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This module contains functions to meter the storage deposit. + +use crate::{storage::ContractInfo, BalanceOf, Config, Error}; +use frame_support::{ + dispatch::{DispatchError, DispatchResult}, + traits::{tokens::BalanceStatus, Get, ReservableCurrency}, + DefaultNoBound, +}; +use pallet_contracts_primitives::StorageDeposit as Deposit; +use sp_core::crypto::UncheckedFrom; +use sp_runtime::traits::Saturating; +use sp_std::marker::PhantomData; + +/// Deposit that uses the native currency's balance type. +pub type DepositOf = Deposit>; + +/// A production root storage meter that actually charges from its origin. +pub type Meter = RawMeter; + +/// A poduction nested storage meter that actually charges from its origin. +pub type NestedMeter = RawMeter; + +/// A poduction storage meter that actually charges from its origin. +/// +/// This can be used where we want to be generic over the state (Root vs. Nested). +pub type GenericMeter = RawMeter; + +/// A trait that allows to decouple the metering from the charging of balance. +/// +/// This mostly exists for testing so that the charging can be mocked. +pub trait Ext { + /// This will be called to inform the implementer about the `storage_limit` of the meter. + /// + /// It is necessary to reserve the balance so that the charge won't fail later on. Should fail + /// when `origin` does not have enough free balance. + /// + /// `origin`: The origin of the call stack from which is reponsible for putting down a deposit. + /// `limit`: The limit with which the meter was constructed. + fn reserve_limit(origin: &T::AccountId, limit: &BalanceOf) -> DispatchResult; + /// This called to inform the implementer that the metering is finished. + /// + /// This is should be used to unreserve the unused balance. The amount to unreserve can be + /// calculated from `limit` and `deposit`. + /// + /// `origin`: The origin of the call stack from which is reponsible for putting down a deposit. + /// `limit`: The limit with which the meter was constructed. + /// `deposit`: The amount of actually used balance during the life time of this meter. + fn unreserve_limit(origin: &T::AccountId, limit: &BalanceOf, deposit: &DepositOf); + /// This is called to inform the implementer that some balance should be charged due to + /// some interaction of the `origin` with a `contract`. + /// + /// The balance transfer can either flow from `origin` to `contract` or the other way + /// around depending on whether `amount` constitues a `Charge` or a `Refund`. + /// It is garantueed that that all the possible balance that can be charged from the `origin` + /// was reserved by a call to `reserve_limit`. This is why this function is infallible. + fn charge(origin: &T::AccountId, contract: &T::AccountId, amount: &DepositOf); +} + +/// This [`Ext`] is used for actual on-chain execution when balance needs to be charged. +/// +/// It uses [`ReservableCurrency`] in order to do accomplish the reserves. +pub enum ReservingExt {} + +/// Used to implement a type state pattern for the meter. +/// +/// It is sealed and cannot be implemented outside of this module. +pub trait State: private::Sealed {} + +/// State parameter that constitutes a meter that is in its root state. +pub enum Root {} + +/// State parameter that constitutes a meter that is in its nested state. +pub enum Nested {} + +impl State for Root {} +impl State for Nested {} + +/// A type that allows the metering of consumed or freed storage of a single contract call stack. +#[derive(DefaultNoBound)] +pub struct RawMeter, S: State> { + /// The origin is the account that instantiates a call stack. This is where the balance is + /// charged from and refunded to. + /// + /// # Note + /// + /// This is `Some` if and only if `S == Root`. + origin: Option, + /// The limit of how much balance this meter is allowed to consume. + limit: BalanceOf, + /// The amount of balance that was used in this meter and all of its already absorbed children. + total_deposit: DepositOf, + /// The amount of balance that was used in this meter alone. + own_deposit: DepositOf, + /// Type parameters are only used in impls. + _phantom: PhantomData<(E, S)>, +} + +/// This type is used to describe a storage change when charging from the meter. +#[derive(Default)] +pub struct Diff { + /// How many bytes were added to storage. + pub bytes_added: u32, + /// How many bytes were removed from storage. + pub bytes_removed: u32, + /// How many storage items were added to storage. + pub items_added: u32, + /// How many storage items were removed from storage. + pub items_removed: u32, +} + +impl Diff { + /// Calculate how much of a charge or refund results from applying the diff. + pub fn to_deposit(&self) -> DepositOf { + let mut deposit = Deposit::default(); + let per_byte = T::DepositPerByte::get(); + let per_item = T::DepositPerItem::get(); + + if self.bytes_added > self.bytes_removed { + deposit = deposit.saturating_add(&Deposit::Charge( + per_byte.saturating_mul((self.bytes_added - self.bytes_removed).into()), + )); + } else if self.bytes_removed > self.bytes_added { + deposit = deposit.saturating_add(&Deposit::Refund( + per_byte.saturating_mul((self.bytes_removed - self.bytes_added).into()), + )); + } + + if self.items_added > self.items_removed { + deposit = deposit.saturating_add(&Deposit::Charge( + per_item.saturating_mul((self.items_added - self.items_removed).into()), + )); + } else if self.bytes_removed > self.bytes_added { + deposit = deposit.saturating_add(&Deposit::Refund( + per_item.saturating_mul((self.items_removed - self.items_added).into()), + )); + } + + deposit + } +} + +impl Drop for RawMeter +where + T: Config, + E: Ext, + S: State, +{ + /// The drop implementation makes sure that the initial reserve is removed. + fn drop(&mut self) { + // Drop cannot be specialized: We need to do a runtime check. + // An origin exists if and only if this is a root meter. + if let Some(origin) = self.origin.as_ref() { + // you cannot charge to the root meter + debug_assert_eq!(self.own_deposit, >::default()); + E::unreserve_limit(origin, &self.limit, &self.total_deposit); + } + } +} + +/// Functions that apply to all states. +impl RawMeter +where + T: Config, + T::AccountId: UncheckedFrom + AsRef<[u8]>, + E: Ext, + S: State, +{ + /// Create a new child that has its `limit` set to whatever is remaining of it. + /// + /// This is called whenever a new subcall is initiated in order to track the storage + /// usage for this sub call separately. This is necessary because we want to exchange balance + /// with the current contract we are interacting with. + pub fn nested(&mut self) -> RawMeter { + RawMeter { + origin: None, + limit: self.available(), + total_deposit: Default::default(), + own_deposit: Default::default(), + _phantom: PhantomData, + } + } + + /// Absorb a child that was spawned to handle a sub call. + /// + /// This should be called whenever a sub call comes to its end and it is **not** reverted. + /// This does the actual balance transfer from/to `origin` and `contract` based on the overall + /// storage consumption of the call. It also updates the supplied contract info. + /// + /// In case a contract reverted the child meter should just be dropped in order to revert + /// any changes it recorded. + /// + /// # Parameters + /// + /// `absorbed`: The child storage meter that should be absorbed. + /// `origin`: The origin that spawned the original root meter. + /// `contract`: The contract that this sub call belings to. + /// `info`: The info of the contract in question. `None` if the contract was terminated. + pub fn absorb( + &mut self, + mut absorbed: RawMeter, + origin: &T::AccountId, + contract: &T::AccountId, + info: Option<&mut ContractInfo>, + ) { + // Absorbing from an exisiting (non terminated) contract. + if let Some(info) = info { + match &mut absorbed.own_deposit { + Deposit::Charge(amount) => + info.storage_deposit = info.storage_deposit.saturating_add(*amount), + Deposit::Refund(amount) => { + // We need to make sure to never refund more than what was deposited. + // This is relevant on runtime upgrades. + *amount = (*amount).min(info.storage_deposit); + info.storage_deposit = info.storage_deposit.saturating_sub(*amount); + }, + } + } + + self.total_deposit = self.total_deposit.saturating_add(&absorbed.total_deposit); + E::charge(origin, &contract, &absorbed.own_deposit); + } + + /// The amount of balance that is still available from the original `limit`. + fn available(&self) -> BalanceOf { + self.total_deposit.available(&self.limit) + } +} + +/// Functions that only apply to the root state. +impl RawMeter +where + T: Config, + T::AccountId: UncheckedFrom + AsRef<[u8]>, + E: Ext, +{ + /// Create new storage meter for the specified `origin` and `limit`. + /// + /// This tries to [`Ext::reserve_limit`] on `origin` and fails if this is not possible. + pub fn new(origin: T::AccountId, limit: BalanceOf) -> Result { + E::reserve_limit(&origin, &limit)?; + Ok(Self { + origin: Some(origin), + limit, + total_deposit: Default::default(), + own_deposit: Default::default(), + _phantom: PhantomData, + }) + } + + /// The total amount of deposit that should change hands as result of the execution + /// that this meter was passed into. + /// + /// This drops the root meter in order to make sure it is only called when the whole + /// execution did finish. + pub fn into_deposit(self) -> DepositOf { + // Clone is necessary because of the drop implementation. + self.total_deposit.clone() + } +} + +/// Functions that only apply to the nested state. +impl RawMeter +where + T: Config, + T::AccountId: UncheckedFrom + AsRef<[u8]>, + E: Ext, +{ + /// Try to charge the `diff` from the meter. Fails if this would exceed the original limit. + pub fn charge(&mut self, diff: &Diff) -> Result, DispatchError> { + let deposit = diff.to_deposit::(); + let total_deposit = self.total_deposit.saturating_add(&deposit); + if let Deposit::Charge(amount) = total_deposit { + if amount > self.limit { + return Err(>::StorageExhausted.into()) + } + } + self.total_deposit = total_deposit; + self.own_deposit = self.own_deposit.saturating_add(&deposit); + Ok(deposit) + } + + /// Call to tell the meter that the currently executing contract was executed. + /// + /// This will manipulate the meter so that all storage deposit accumulated in + /// `contract_info` will be refunded to the `origin` of the meter. + pub fn terminate(&mut self, contract_info: &ContractInfo) { + let refund = Deposit::Refund(contract_info.storage_deposit); + + // The deposit for `own_deposit` isn't persisted into the contract info until the current + // frame is dropped. This means that whatever changes were introduced during the + // current frame are dicarded when terminating. + self.total_deposit = + self.total_deposit.saturating_add(&refund).saturating_sub(&self.own_deposit); + self.own_deposit = refund; + } +} + +impl Ext for ReservingExt { + fn reserve_limit(origin: &T::AccountId, limit: &BalanceOf) -> DispatchResult { + T::Currency::reserve(origin, *limit) + } + + fn unreserve_limit(origin: &T::AccountId, limit: &BalanceOf, deposit: &DepositOf) { + T::Currency::unreserve(origin, deposit.available(&limit)); + } + + fn charge(origin: &T::AccountId, contract: &T::AccountId, amount: &DepositOf) { + let (slashed, beneficiary, amount) = match amount { + Deposit::Charge(amount) => (origin, contract, amount), + Deposit::Refund(amount) => (contract, origin, amount), + }; + + // For charge `Err` can never happen as a contract's account is required to exist + // at all times. The pallet enforces this invariant. Chain extensions or dispatchables + // that allow the removal of the contract's account are defunct. + // + // For refund `Err` can't happen because the initial value transfer from the + // origin to the contract has a keep alive existence requirement. + // + // There is nothing we can do when either `Err` or `Ok(> 0)` happens as this constitutes + // a bug in the runtime: Either the runtime does not hold up the invariant of never + // deleting a contract's account or it does not honor reserved balances. + // + // There is one exception: + // + // If a contract is terminated its account's free balance is completely removed and + // sent to the beneficiary. This could lead to the removal of the contract's account if + // the amount of reserved balance is below the existential deposit. + let _ = T::Currency::repatriate_reserved( + slashed, + beneficiary, + *amount, + BalanceStatus::Reserved, + ); + } +} + +mod private { + pub trait Sealed {} + impl Sealed for super::Root {} + impl Sealed for super::Nested {} +} diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index bf331e321d7fc..d67f97b405a84 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -21,7 +21,7 @@ use crate::{ ReturnFlags, SysConfig, UncheckedFrom, }, exec::Frame, - storage::{RawContractInfo, Storage}, + storage::Storage, wasm::{PrefabWasmModule, ReturnCode as RuntimeReturnCode}, weights::WeightInfo, BalanceOf, Config, ContractInfoOf, Error, Pallet, Schedule, @@ -33,7 +33,7 @@ use frame_support::{ dispatch::DispatchErrorWithPostInfo, parameter_types, storage::child, - traits::{Contains, Currency, OnInitialize, ReservableCurrency}, + traits::{BalanceStatus, Contains, Currency, OnInitialize, ReservableCurrency}, weights::{constants::WEIGHT_PER_SECOND, DispatchClass, PostDispatchInfo, Weight}, }; use frame_system::{self as system, EventRecord, Phase}; @@ -71,29 +71,16 @@ frame_support::construct_runtime!( pub mod test_utils { use super::{Balances, Test}; use crate::{ - exec::{AccountIdOf, StorageKey}, - storage::Storage, - AccountCounter, CodeHash, Config, ContractInfoOf, TrieId, + exec::AccountIdOf, storage::Storage, AccountCounter, CodeHash, Config, ContractInfoOf, }; use frame_support::traits::Currency; - pub fn set_storage(addr: &AccountIdOf, key: &StorageKey, value: Option>) { - let mut contract_info = >::get(&addr).unwrap(); - Storage::::write(&mut contract_info, key, value).unwrap(); - } - pub fn get_storage(addr: &AccountIdOf, key: &StorageKey) -> Option> { - let contract_info = >::get(&addr).unwrap(); - Storage::::read(&contract_info.trie_id, key) - } - pub fn generate_trie_id(address: &AccountIdOf) -> TrieId { + pub fn place_contract(address: &AccountIdOf, code_hash: CodeHash) { let seed = >::mutate(|counter| { *counter += 1; *counter }); - Storage::::generate_trie_id(address, seed) - } - pub fn place_contract(address: &AccountIdOf, code_hash: CodeHash) { - let trie_id = generate_trie_id(address); + let trie_id = Storage::::generate_trie_id(address, seed); set_balance(address, ::Currency::minimum_balance() * 10); let contract = Storage::::new_contract(&address, trie_id, code_hash).unwrap(); >::insert(address, contract); @@ -113,7 +100,7 @@ pub mod test_utils { } macro_rules! assert_refcount { ( $code_hash:expr , $should:expr $(,)? ) => {{ - let is = crate::CodeStorage::::get($code_hash).map(|m| m.refcount()).unwrap_or(0); + let is = crate::OwnerInfoOf::::get($code_hash).map(|m| m.refcount()).unwrap(); assert_eq!(is, $should); }}; } @@ -193,7 +180,7 @@ parameter_types! { pub const BlockHashCount: u64 = 250; pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights::simple_max(2 * WEIGHT_PER_SECOND); - pub static ExistentialDeposit: u64 = 0; + pub static ExistentialDeposit: u64 = 1; } impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; @@ -254,6 +241,8 @@ parameter_types! { pub const MaxCodeSize: u32 = 2 * 1024; pub MySchedule: Schedule = >::default(); pub const TransactionByteFee: u64 = 0; + pub const DepositPerByte: BalanceOf = 1; + pub const DepositPerItem: BalanceOf = 1; } impl Convert> for Test { @@ -295,6 +284,8 @@ impl Config for Test { type DeletionQueueDepth = DeletionQueueDepth; type DeletionWeightLimit = DeletionWeightLimit; type Schedule = MySchedule; + type DepositPerByte = DepositPerByte; + type DepositPerItem = DepositPerItem; } pub const ALICE: AccountId32 = AccountId32::new([1u8; 32]); @@ -302,14 +293,14 @@ pub const BOB: AccountId32 = AccountId32::new([2u8; 32]); pub const CHARLIE: AccountId32 = AccountId32::new([3u8; 32]); pub const DJANGO: AccountId32 = AccountId32::new([4u8; 32]); -const GAS_LIMIT: Weight = 10_000_000_000; +pub const GAS_LIMIT: Weight = 10_000_000_000; pub struct ExtBuilder { existential_deposit: u64, } impl Default for ExtBuilder { fn default() -> Self { - Self { existential_deposit: 1 } + Self { existential_deposit: ExistentialDeposit::get() } } } impl ExtBuilder { @@ -346,6 +337,30 @@ where Ok((wasm_binary, code_hash)) } +/// Extract the ordered list of deposit or refund amounts from a list of events. +fn deposits( + events: &[EventRecord::Hash>], +) -> impl Iterator> + '_ { + events.iter().filter_map(|event| { + if let EventRecord { + phase: Phase::Initialization, + event: + Event::Balances(pallet_balances::Event::ReserveRepatriated( + _, + _, + amount, + BalanceStatus::Reserved, + )), + topics: _, + } = event + { + Some(*amount) + } else { + None + } + }) +} + // Perform a call to a plain account. // The actual transfer fails because we can only call contracts. // Then we check that at least the base costs where charged (no runtime gas costs.) @@ -356,7 +371,7 @@ fn calling_plain_account_fails() { let base_cost = <::WeightInfo as WeightInfo>::call(); assert_eq!( - Contracts::call(Origin::signed(ALICE), BOB, 0, GAS_LIMIT, Vec::new()), + Contracts::call(Origin::signed(ALICE), BOB, 0, GAS_LIMIT, None, Vec::new()), Err(DispatchErrorWithPostInfo { error: Error::::ContractNotFound.into(), post_info: PostDispatchInfo { @@ -368,59 +383,6 @@ fn calling_plain_account_fails() { }); } -#[test] -fn account_removal_does_not_remove_storage() { - use self::test_utils::{get_storage, set_storage}; - - ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - let trie_id1 = test_utils::generate_trie_id(&ALICE); - let trie_id2 = test_utils::generate_trie_id(&BOB); - let key1 = &[1; 32]; - let key2 = &[2; 32]; - - // Set up two accounts with free balance above the existential threshold. - { - let alice_contract_info = RawContractInfo { - trie_id: trie_id1.clone(), - code_hash: H256::repeat_byte(1), - _reserved: None, - }; - let _ = Balances::deposit_creating(&ALICE, 110); - ContractInfoOf::::insert(ALICE, &alice_contract_info); - set_storage(&ALICE, &key1, Some(b"1".to_vec())); - set_storage(&ALICE, &key2, Some(b"2".to_vec())); - - let bob_contract_info = RawContractInfo { - trie_id: trie_id2.clone(), - code_hash: H256::repeat_byte(2), - _reserved: None, - }; - let _ = Balances::deposit_creating(&BOB, 110); - ContractInfoOf::::insert(BOB, &bob_contract_info); - set_storage(&BOB, &key1, Some(b"3".to_vec())); - set_storage(&BOB, &key2, Some(b"4".to_vec())); - } - - // Transfer funds from ALICE account of such amount that after this transfer - // the balance of the ALICE account will be below the existential threshold. - // - // This does not remove the contract storage as we are not notified about a - // account removal. This cannot happen in reality because a contract can only - // remove itself by `seal_terminate`. There is no external event that can remove - // the account appart from that. - assert_ok!(Balances::transfer(Origin::signed(ALICE), BOB, 20)); - - // Verify that no entries are removed. - { - assert_eq!(get_storage(&ALICE, key1), Some(b"1".to_vec())); - assert_eq!(get_storage(&ALICE, key2), Some(b"2".to_vec())); - - assert_eq!(get_storage(&BOB, key1), Some(b"3".to_vec())); - assert_eq!(get_storage(&BOB, key2), Some(b"4".to_vec())); - } - }); -} - #[test] fn instantiate_and_call_and_deposit_event() { let (wasm, code_hash) = compile_module::("return_from_start_fn").unwrap(); @@ -428,34 +390,41 @@ fn instantiate_and_call_and_deposit_event() { ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); let min_balance = ::Currency::minimum_balance(); + let endowment = min_balance * 100; + + // We determine the storage limit after uploading because it depends on ALICEs free + // balance which is changed by uploading a module. + assert_ok!(Contracts::upload_code(Origin::signed(ALICE), wasm, None)); + + // Drop previous events + initialize_block(2); // Check at the end to get hash on error easily - let creation = Contracts::instantiate_with_code( + let storage_limit = >::max_storage_limit(&ALICE, endowment); + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), - min_balance * 100, + endowment, GAS_LIMIT, - wasm, + None, + code_hash, vec![], vec![], - ); + )); let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); + assert!(ContractInfoOf::::contains_key(&addr)); + + // We instantiate a contract. This means the caller is charged for the storage + // that the contract itself occupies. + let events = System::events(); + let storage_cost = deposits(&events).next().unwrap(); + let unused = storage_limit - storage_cost; assert_eq!( - System::events(), + events, vec![ EventRecord { phase: Phase::Initialization, - event: Event::Balances(pallet_balances::Event::Deposit(ALICE, 1_000_000)), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: Event::System(frame_system::Event::NewAccount(ALICE.clone())), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: Event::Balances(pallet_balances::Event::Endowed(ALICE, 1_000_000)), + event: Event::Balances(pallet_balances::Event::Reserved(ALICE, storage_limit)), topics: vec![], }, EventRecord { @@ -480,13 +449,6 @@ fn instantiate_and_call_and_deposit_event() { )), topics: vec![], }, - EventRecord { - phase: Phase::Initialization, - event: Event::Contracts(crate::Event::CodeStored { - code_hash: code_hash.into() - }), - topics: vec![], - }, EventRecord { phase: Phase::Initialization, event: Event::Contracts(crate::Event::ContractEmitted { @@ -503,11 +465,23 @@ fn instantiate_and_call_and_deposit_event() { }), topics: vec![], }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::ReserveRepatriated( + ALICE, + addr.clone(), + storage_cost, + BalanceStatus::Reserved, + )), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Unreserved(ALICE, unused)), + topics: vec![], + }, ] ); - - assert_ok!(creation); - assert!(ContractInfoOf::::contains_key(&addr)); }); } @@ -522,6 +496,7 @@ fn deposit_event_max_value_limit() { Origin::signed(ALICE), 30_000, GAS_LIMIT, + None, wasm, vec![], vec![], @@ -534,6 +509,7 @@ fn deposit_event_max_value_limit() { addr.clone(), 0, GAS_LIMIT * 2, // we are copying a huge buffer, + None, ::Schedule::get().limits.payload_len.encode(), )); @@ -544,6 +520,7 @@ fn deposit_event_max_value_limit() { addr, 0, GAS_LIMIT, + None, (::Schedule::get().limits.payload_len + 1).encode(), ), Error::::ValueTooLarge, @@ -562,6 +539,7 @@ fn run_out_of_gas() { Origin::signed(ALICE), 100 * min_balance, GAS_LIMIT, + None, wasm, vec![], vec![], @@ -576,6 +554,7 @@ fn run_out_of_gas() { addr, // newly created account 0, 1_000_000_000_000, + None, vec![], ), Error::::OutOfGas, @@ -598,6 +577,7 @@ fn storage_max_value_limit() { Origin::signed(ALICE), 30_000, GAS_LIMIT, + None, wasm, vec![], vec![], @@ -611,6 +591,7 @@ fn storage_max_value_limit() { addr.clone(), 0, GAS_LIMIT * 2, // we are copying a huge buffer + None, ::Schedule::get().limits.payload_len.encode(), )); @@ -621,6 +602,7 @@ fn storage_max_value_limit() { addr, 0, GAS_LIMIT, + None, (::Schedule::get().limits.payload_len + 1).encode(), ), Error::::ValueTooLarge, @@ -640,6 +622,7 @@ fn deploy_and_call_other_contract() { Origin::signed(ALICE), 100_000, GAS_LIMIT, + None, caller_wasm, vec![], vec![], @@ -648,6 +631,7 @@ fn deploy_and_call_other_contract() { Origin::signed(ALICE), 100_000, GAS_LIMIT, + None, callee_wasm, 0u32.to_le_bytes().encode(), vec![42], @@ -660,6 +644,7 @@ fn deploy_and_call_other_contract() { Contracts::contract_address(&ALICE, &caller_code_hash, &[]), 0, GAS_LIMIT, + None, callee_code_hash.as_ref().to_vec(), )); }); @@ -676,6 +661,7 @@ fn cannot_self_destruct_through_draning() { Origin::signed(ALICE), 100_000, GAS_LIMIT, + None, wasm, vec![], vec![], @@ -687,7 +673,7 @@ fn cannot_self_destruct_through_draning() { // Call BOB which makes it send all funds to the zero address // The contract code asserts that the correct error value is returned. - assert_ok!(Contracts::call(Origin::signed(ALICE), addr, 0, GAS_LIMIT, vec![])); + assert_ok!(Contracts::call(Origin::signed(ALICE), addr, 0, GAS_LIMIT, None, vec![])); }); } @@ -702,6 +688,7 @@ fn cannot_self_destruct_while_live() { Origin::signed(ALICE), 100_000, GAS_LIMIT, + None, wasm, vec![], vec![], @@ -714,7 +701,7 @@ fn cannot_self_destruct_while_live() { // Call BOB with input data, forcing it make a recursive call to itself to // self-destruct, resulting in a trap. assert_err_ignore_postinfo!( - Contracts::call(Origin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, vec![0],), + Contracts::call(Origin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, None, vec![0],), Error::::ContractTrapped, ); @@ -735,6 +722,7 @@ fn self_destruct_works() { Origin::signed(ALICE), 100_000, GAS_LIMIT, + None, wasm, vec![], vec![], @@ -747,18 +735,39 @@ fn self_destruct_works() { // Drop all previous events initialize_block(2); + // We need to gather this before the call. Otherwise it was already reserved. + let storage_limit = >::max_storage_limit(&ALICE, 0); + + // There is only one user of this contract. + assert_refcount!(&code_hash, 1); + // Call BOB without input data which triggers termination. assert_matches!( - Contracts::call(Origin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, vec![],), + Contracts::call(Origin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, None, vec![],), Ok(_) ); + // Check that code is still there but refcount dropped to zero. + assert_refcount!(&code_hash, 0); + + // Check that account is gone + assert!(ContractInfoOf::::get(&addr).is_none()); + + // check that the beneficiary (django) got remaining balance + assert_eq!(Balances::free_balance(DJANGO), 1_000_000 + 100_000); + + // We delete a contract here. This means that the caller gets a refund over + // all the storage deposits of the contract. + let events = System::events(); + let storage_refund = deposits(&events).next().unwrap(); + let unreserved = storage_limit + storage_refund; + pretty_assertions::assert_eq!( - System::events(), + events, vec![ EventRecord { phase: Phase::Initialization, - event: Event::System(frame_system::Event::KilledAccount(addr.clone())), + event: Event::Balances(pallet_balances::Event::Reserved(ALICE, storage_limit)), topics: vec![], }, EventRecord { @@ -770,11 +779,6 @@ fn self_destruct_works() { )), topics: vec![], }, - EventRecord { - phase: Phase::Initialization, - event: Event::Contracts(crate::Event::CodeRemoved { code_hash }), - topics: vec![], - }, EventRecord { phase: Phase::Initialization, event: Event::Contracts(crate::Event::Terminated { @@ -783,15 +787,28 @@ fn self_destruct_works() { }), topics: vec![], }, + EventRecord { + phase: Phase::Initialization, + event: Event::System(frame_system::Event::KilledAccount(addr.clone())), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::ReserveRepatriated( + addr.clone(), + ALICE, + storage_refund, + BalanceStatus::Reserved, + )), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Unreserved(ALICE, unreserved)), + topics: vec![], + }, ], ); - - // Check that account is gone - assert!(ContractInfoOf::::get(&addr).is_none()); - - // check that the beneficiary (django) got remaining balance - // some rent was deducted before termination - assert_eq!(Balances::free_balance(DJANGO), 1_000_000 + 100_000); }); } @@ -809,6 +826,7 @@ fn destroy_contract_and_transfer_funds() { Origin::signed(ALICE), 200_000, GAS_LIMIT, + None, callee_wasm, vec![], vec![42] @@ -820,6 +838,7 @@ fn destroy_contract_and_transfer_funds() { Origin::signed(ALICE), 200_000, GAS_LIMIT, + None, caller_wasm, callee_code_hash.as_ref().to_vec(), vec![], @@ -836,6 +855,7 @@ fn destroy_contract_and_transfer_funds() { addr_bob, 0, GAS_LIMIT, + None, addr_charlie.encode(), )); @@ -856,6 +876,7 @@ fn cannot_self_destruct_in_constructor() { Origin::signed(ALICE), 100_000, GAS_LIMIT, + None, wasm, vec![], vec![], @@ -877,6 +898,7 @@ fn crypto_hashes() { Origin::signed(ALICE), 100_000, GAS_LIMIT, + None, wasm, vec![], vec![], @@ -904,7 +926,7 @@ fn crypto_hashes() { let mut params = vec![(n + 1) as u8]; params.extend_from_slice(input); let result = - >::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, params, false) + >::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, params, false) .result .unwrap(); assert!(result.is_success()); @@ -925,6 +947,7 @@ fn transfer_return_code() { Origin::signed(ALICE), min_balance * 100, GAS_LIMIT, + None, wasm, vec![], vec![], @@ -933,7 +956,7 @@ fn transfer_return_code() { // Contract has only the minimal balance so any transfer will fail. Balances::make_free_balance_be(&addr, min_balance); - let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, vec![], false) + let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, vec![], false) .result .unwrap(); assert_return_code!(result, RuntimeReturnCode::TransferFailed); @@ -943,7 +966,9 @@ fn transfer_return_code() { // the transfer still fails. Balances::make_free_balance_be(&addr, min_balance + 100); Balances::reserve(&addr, min_balance + 100).unwrap(); - let result = Contracts::bare_call(ALICE, addr, 0, GAS_LIMIT, vec![], false).result.unwrap(); + let result = Contracts::bare_call(ALICE, addr, 0, GAS_LIMIT, None, vec![], false) + .result + .unwrap(); assert_return_code!(result, RuntimeReturnCode::TransferFailed); }); } @@ -961,6 +986,7 @@ fn call_return_code() { Origin::signed(ALICE), min_balance * 100, GAS_LIMIT, + None, caller_code, vec![0], vec![], @@ -974,6 +1000,7 @@ fn call_return_code() { addr_bob.clone(), 0, GAS_LIMIT, + None, AsRef::<[u8]>::as_ref(&DJANGO).to_vec(), false, ) @@ -985,6 +1012,7 @@ fn call_return_code() { Origin::signed(CHARLIE), min_balance * 100, GAS_LIMIT, + None, callee_code, vec![0], vec![], @@ -998,6 +1026,7 @@ fn call_return_code() { addr_bob.clone(), 0, GAS_LIMIT, + None, AsRef::<[u8]>::as_ref(&addr_django) .iter() .chain(&0u32.to_le_bytes()) @@ -1019,6 +1048,7 @@ fn call_return_code() { addr_bob.clone(), 0, GAS_LIMIT, + None, AsRef::<[u8]>::as_ref(&addr_django) .iter() .chain(&0u32.to_le_bytes()) @@ -1037,6 +1067,7 @@ fn call_return_code() { addr_bob.clone(), 0, GAS_LIMIT, + None, AsRef::<[u8]>::as_ref(&addr_django) .iter() .chain(&1u32.to_le_bytes()) @@ -1054,6 +1085,7 @@ fn call_return_code() { addr_bob, 0, GAS_LIMIT, + None, AsRef::<[u8]>::as_ref(&addr_django) .iter() .chain(&2u32.to_le_bytes()) @@ -1081,6 +1113,7 @@ fn instantiate_return_code() { Origin::signed(ALICE), min_balance * 100, GAS_LIMIT, + None, callee_code, vec![], vec![], @@ -1090,6 +1123,7 @@ fn instantiate_return_code() { Origin::signed(ALICE), min_balance * 100, GAS_LIMIT, + None, caller_code, vec![], vec![], @@ -1098,10 +1132,17 @@ fn instantiate_return_code() { // Contract has only the minimal balance so any transfer will fail. Balances::make_free_balance_be(&addr, min_balance); - let result = - Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, callee_hash.clone(), false) - .result - .unwrap(); + let result = Contracts::bare_call( + ALICE, + addr.clone(), + 0, + GAS_LIMIT, + None, + callee_hash.clone(), + false, + ) + .result + .unwrap(); assert_return_code!(result, RuntimeReturnCode::TransferFailed); // Contract has enough total balance in order to not go below the min_balance @@ -1109,17 +1150,25 @@ fn instantiate_return_code() { // the transfer still fails. Balances::make_free_balance_be(&addr, min_balance + 10_000); Balances::reserve(&addr, min_balance + 10_000).unwrap(); - let result = - Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, callee_hash.clone(), false) - .result - .unwrap(); + let result = Contracts::bare_call( + ALICE, + addr.clone(), + 0, + GAS_LIMIT, + None, + callee_hash.clone(), + false, + ) + .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, min_balance + 10_000); - let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, vec![0; 33], false) - .result - .unwrap(); + let result = + Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, vec![0; 33], false) + .result + .unwrap(); assert_return_code!(result, RuntimeReturnCode::CodeNotFound); // Contract has enough balance but callee reverts because "1" is passed. @@ -1128,6 +1177,7 @@ fn instantiate_return_code() { addr.clone(), 0, GAS_LIMIT, + None, callee_hash.iter().chain(&1u32.to_le_bytes()).cloned().collect(), false, ) @@ -1141,6 +1191,7 @@ fn instantiate_return_code() { addr, 0, GAS_LIMIT, + None, callee_hash.iter().chain(&2u32.to_le_bytes()).cloned().collect(), false, ) @@ -1162,6 +1213,7 @@ fn disabled_chain_extension_wont_deploy() { Origin::signed(ALICE), 3 * min_balance, GAS_LIMIT, + None, code, vec![], vec![], @@ -1181,6 +1233,7 @@ fn disabled_chain_extension_errors_on_call() { Origin::signed(ALICE), min_balance * 100, GAS_LIMIT, + None, code, vec![], vec![], @@ -1188,7 +1241,7 @@ fn disabled_chain_extension_errors_on_call() { let addr = Contracts::contract_address(&ALICE, &hash, &[]); TestExtension::disable(); assert_err_ignore_postinfo!( - Contracts::call(Origin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, vec![],), + Contracts::call(Origin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, None, vec![],), Error::::NoChainExtension, ); }); @@ -1204,6 +1257,7 @@ fn chain_extension_works() { Origin::signed(ALICE), min_balance * 100, GAS_LIMIT, + None, code, vec![], vec![], @@ -1215,25 +1269,27 @@ fn chain_extension_works() { // func_id. // 0 = read input buffer and pass it through as output - let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, vec![0, 99], false); + let result = + Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, vec![0, 99], false); let gas_consumed = result.gas_consumed; assert_eq!(TestExtension::last_seen_buffer(), vec![0, 99]); assert_eq!(result.result.unwrap().data, Bytes(vec![0, 99])); // 1 = treat inputs as integer primitives and store the supplied integers - Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, vec![1], false) + Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, vec![1], false) .result .unwrap(); // those values passed in the fixture assert_eq!(TestExtension::last_seen_inputs(), (4, 1, 16, 12)); // 2 = charge some extra weight (amount supplied in second byte) - let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, vec![2, 42], false); + let result = + Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, vec![2, 42], false); assert_ok!(result.result); assert_eq!(result.gas_consumed, gas_consumed + 42); // 3 = diverging chain extension call that sets flags to 0x1 and returns a fixed buffer - let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, vec![3], false) + let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, vec![3], false) .result .unwrap(); assert_eq!(result.flags, ReturnFlags::REVERT); @@ -1252,6 +1308,7 @@ fn lazy_removal_works() { Origin::signed(ALICE), min_balance * 100, GAS_LIMIT, + None, code, vec![], vec![], @@ -1265,7 +1322,14 @@ fn lazy_removal_works() { child::put(trie, &[99], &42); // Terminate the contract - assert_ok!(Contracts::call(Origin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, vec![])); + assert_ok!(Contracts::call( + Origin::signed(ALICE), + addr.clone(), + 0, + GAS_LIMIT, + None, + vec![] + )); // Contract info should be gone assert!(!>::contains_key(&addr)); @@ -1303,22 +1367,30 @@ fn lazy_removal_partial_remove_works() { Origin::signed(ALICE), min_balance * 100, GAS_LIMIT, + None, code, vec![], vec![], ),); let addr = Contracts::contract_address(&ALICE, &hash, &[]); - let mut info = >::get(&addr).unwrap(); + let info = >::get(&addr).unwrap(); // Put value into the contracts child trie for val in &vals { - Storage::::write(&mut info, &val.0, Some(val.2.clone())).unwrap(); + Storage::::write(&info.trie_id, &val.0, Some(val.2.clone()), None).unwrap(); } >::insert(&addr, info.clone()); // Terminate the contract - assert_ok!(Contracts::call(Origin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, vec![])); + assert_ok!(Contracts::call( + Origin::signed(ALICE), + addr.clone(), + 0, + GAS_LIMIT, + None, + vec![] + )); // Contract info should be gone assert!(!>::contains_key(&addr)); @@ -1373,13 +1445,14 @@ fn lazy_removal_does_no_run_on_full_block() { Origin::signed(ALICE), min_balance * 100, GAS_LIMIT, + None, code, vec![], vec![], ),); let addr = Contracts::contract_address(&ALICE, &hash, &[]); - let mut info = >::get(&addr).unwrap(); + let info = >::get(&addr).unwrap(); let max_keys = 30; // Create some storage items for the contract. @@ -1389,12 +1462,19 @@ fn lazy_removal_does_no_run_on_full_block() { // Put value into the contracts child trie for val in &vals { - Storage::::write(&mut info, &val.0, Some(val.2.clone())).unwrap(); + Storage::::write(&info.trie_id, &val.0, Some(val.2.clone()), None).unwrap(); } >::insert(&addr, info.clone()); // Terminate the contract - assert_ok!(Contracts::call(Origin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, vec![])); + assert_ok!(Contracts::call( + Origin::signed(ALICE), + addr.clone(), + 0, + GAS_LIMIT, + None, + vec![] + )); // Contract info should be gone assert!(!>::contains_key(&addr)); @@ -1448,13 +1528,14 @@ fn lazy_removal_does_not_use_all_weight() { Origin::signed(ALICE), min_balance * 100, GAS_LIMIT, + None, code, vec![], vec![], ),); let addr = Contracts::contract_address(&ALICE, &hash, &[]); - let mut info = >::get(&addr).unwrap(); + let info = >::get(&addr).unwrap(); let (weight_per_key, max_keys) = Storage::::deletion_budget(1, weight_limit); // We create a contract with one less storage item than we can remove within the limit @@ -1464,12 +1545,19 @@ fn lazy_removal_does_not_use_all_weight() { // Put value into the contracts child trie for val in &vals { - Storage::::write(&mut info, &val.0, Some(val.2.clone())).unwrap(); + Storage::::write(&info.trie_id, &val.0, Some(val.2.clone()), None).unwrap(); } >::insert(&addr, info.clone()); // Terminate the contract - assert_ok!(Contracts::call(Origin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, vec![])); + assert_ok!(Contracts::call( + Origin::signed(ALICE), + addr.clone(), + 0, + GAS_LIMIT, + None, + vec![] + )); // Contract info should be gone assert!(!>::contains_key(&addr)); @@ -1513,6 +1601,7 @@ fn deletion_queue_full() { Origin::signed(ALICE), min_balance * 100, GAS_LIMIT, + None, code, vec![], vec![], @@ -1525,7 +1614,7 @@ fn deletion_queue_full() { // Terminate the contract should fail assert_err_ignore_postinfo!( - Contracts::call(Origin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, vec![],), + Contracts::call(Origin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, None, vec![],), Error::::DeletionQueueFull, ); @@ -1546,6 +1635,7 @@ fn refcounter() { Origin::signed(ALICE), min_balance * 100, GAS_LIMIT, + None, wasm.clone(), vec![], vec![0], @@ -1554,6 +1644,7 @@ fn refcounter() { Origin::signed(ALICE), min_balance * 100, GAS_LIMIT, + None, wasm.clone(), vec![], vec![1], @@ -1565,6 +1656,7 @@ fn refcounter() { Origin::signed(ALICE), min_balance * 100, GAS_LIMIT, + None, code_hash, vec![], vec![2], @@ -1577,23 +1669,23 @@ fn refcounter() { 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_ok!(Contracts::call(Origin::signed(ALICE), addr0, 0, GAS_LIMIT, None, vec![])); assert_refcount!(code_hash, 2); // remove another one - assert_ok!(Contracts::call(Origin::signed(ALICE), addr1, 0, GAS_LIMIT, vec![])); + assert_ok!(Contracts::call(Origin::signed(ALICE), addr1, 0, GAS_LIMIT, None, vec![])); assert_refcount!(code_hash, 1); // Pristine code should still be there crate::PristineCode::::get(code_hash).unwrap(); // remove the last contract - assert_ok!(Contracts::call(Origin::signed(ALICE), addr2, 0, GAS_LIMIT, vec![])); + assert_ok!(Contracts::call(Origin::signed(ALICE), addr2, 0, GAS_LIMIT, None, vec![])); 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); + // refcount is `0` but code should still exists because it needs to be removed manually + assert!(crate::PristineCode::::contains_key(&code_hash)); + assert!(crate::CodeStorage::::contains_key(&code_hash)); }); } @@ -1610,6 +1702,7 @@ fn reinstrument_does_charge() { Origin::signed(ALICE), min_balance * 100, GAS_LIMIT, + None, wasm, zero.clone(), vec![], @@ -1619,10 +1712,12 @@ fn reinstrument_does_charge() { // Call the contract two times without reinstrument - let result0 = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, zero.clone(), false); + let result0 = + Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, zero.clone(), false); assert!(result0.result.unwrap().is_success()); - let result1 = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, zero.clone(), false); + let result1 = + Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, zero.clone(), false); assert!(result1.result.unwrap().is_success()); // They should match because both where called with the same schedule. @@ -1635,7 +1730,8 @@ fn reinstrument_does_charge() { }); // This call should trigger reinstrumentation - let result2 = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, zero.clone(), false); + let result2 = + Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, zero.clone(), false); assert!(result2.result.unwrap().is_success()); assert!(result2.gas_consumed > result1.gas_consumed); assert_eq!( @@ -1655,12 +1751,13 @@ fn debug_message_works() { Origin::signed(ALICE), 30_000, GAS_LIMIT, + None, wasm, vec![], vec![], ),); let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); - let result = Contracts::bare_call(ALICE, addr, 0, GAS_LIMIT, vec![], true); + let result = Contracts::bare_call(ALICE, addr, 0, GAS_LIMIT, None, vec![], true); assert_matches!(result.result, Ok(_)); assert_eq!(std::str::from_utf8(&result.debug_message).unwrap(), "Hello World!"); @@ -1677,16 +1774,17 @@ fn debug_message_logging_disabled() { Origin::signed(ALICE), 30_000, GAS_LIMIT, + None, wasm, vec![], vec![], ),); let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); // disable logging by passing `false` - let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, vec![], false); + let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, vec![], false); assert_matches!(result.result, Ok(_)); // the dispatchables always run without debugging - assert_ok!(Contracts::call(Origin::signed(ALICE), addr, 0, GAS_LIMIT, vec![])); + assert_ok!(Contracts::call(Origin::signed(ALICE), addr, 0, GAS_LIMIT, None, vec![])); assert!(result.debug_message.is_empty()); }); } @@ -1701,12 +1799,13 @@ fn debug_message_invalid_utf8() { Origin::signed(ALICE), 30_000, GAS_LIMIT, + None, wasm, vec![], vec![], ),); let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); - let result = Contracts::bare_call(ALICE, addr, 0, GAS_LIMIT, vec![], true); + let result = Contracts::bare_call(ALICE, addr, 0, GAS_LIMIT, None, vec![], true); assert_err!(result.result, >::DebugMessageInvalidUTF8); }); } @@ -1724,6 +1823,7 @@ fn gas_estimation_nested_call_fixed_limit() { Origin::signed(ALICE), min_balance * 100, GAS_LIMIT, + None, caller_code, vec![], vec![0], @@ -1734,6 +1834,7 @@ fn gas_estimation_nested_call_fixed_limit() { Origin::signed(ALICE), min_balance * 100, GAS_LIMIT, + None, callee_code, vec![], vec![1], @@ -1747,15 +1848,32 @@ fn gas_estimation_nested_call_fixed_limit() { .collect(); // Call in order to determine the gas that is required for this call - let result = - Contracts::bare_call(ALICE, addr_caller.clone(), 0, GAS_LIMIT, input.clone(), false); + let result = Contracts::bare_call( + ALICE, + addr_caller.clone(), + 0, + GAS_LIMIT, + None, + input.clone(), + false, + ); assert_ok!(&result.result); + // We have a subcall with a fixed gas limit. This constitutes precharging. assert!(result.gas_required > result.gas_consumed); // Make the same call using the estimated gas. Should succeed. assert_ok!( - Contracts::bare_call(ALICE, addr_caller, 0, result.gas_required, input, false,).result + Contracts::bare_call( + ALICE, + addr_caller, + 0, + result.gas_required, + Some(result.storage_deposit.charge_or_zero()), + input, + false, + ) + .result ); }); } @@ -1774,6 +1892,7 @@ fn gas_estimation_call_runtime() { Origin::signed(ALICE), min_balance * 100, GAS_LIMIT, + None, caller_code, vec![], vec![0], @@ -1784,6 +1903,7 @@ fn gas_estimation_call_runtime() { Origin::signed(ALICE), min_balance * 100, GAS_LIMIT, + None, callee_code, vec![], vec![1], @@ -1796,18 +1916,34 @@ fn gas_estimation_call_runtime() { dest: addr_callee, value: 0, gas_limit: GAS_LIMIT / 3, + storage_limit: None, data: vec![], }); - let result = - Contracts::bare_call(ALICE, addr_caller.clone(), 0, GAS_LIMIT, call.encode(), false); + let result = Contracts::bare_call( + ALICE, + addr_caller.clone(), + 0, + GAS_LIMIT, + None, + call.encode(), + false, + ); assert_ok!(&result.result); assert!(result.gas_required > result.gas_consumed); // Make the same call using the required gas. Should succeed. assert_ok!( - Contracts::bare_call(ALICE, addr_caller, 0, result.gas_required, call.encode(), false,) - .result + Contracts::bare_call( + ALICE, + addr_caller, + 0, + result.gas_required, + None, + call.encode(), + false, + ) + .result ); }); } @@ -1825,6 +1961,7 @@ fn ecdsa_recover() { Origin::signed(ALICE), 100_000, GAS_LIMIT, + None, wasm, vec![], vec![], @@ -1854,9 +1991,10 @@ fn ecdsa_recover() { params.extend_from_slice(&signature); params.extend_from_slice(&message_hash); assert!(params.len() == 65 + 32); - let result = >::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, params, false) - .result - .unwrap(); + let result = + >::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, params, false) + .result + .unwrap(); assert!(result.is_success()); assert_eq!(result.data.as_ref(), &EXPECTED_COMPRESSED_PUBLIC_KEY); }) diff --git a/frame/contracts/src/wasm/code_cache.rs b/frame/contracts/src/wasm/code_cache.rs index afb68d4d81179..9b60f3344c733 100644 --- a/frame/contracts/src/wasm/code_cache.rs +++ b/frame/contracts/src/wasm/code_cache.rs @@ -28,177 +28,169 @@ //! 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. -#[cfg(feature = "runtime-benchmarks")] -pub use self::private::reinstrument; use crate::{ gas::{GasMeter, Token}, wasm::{prepare, PrefabWasmModule}, weights::WeightInfo, - CodeHash, CodeStorage, Config, Error, Event, Pallet as Contracts, PristineCode, Schedule, + CodeHash, CodeStorage, Config, Error, Event, OwnerInfoOf, Pallet, PristineCode, Schedule, Weight, }; -use frame_support::dispatch::DispatchError; +use frame_support::{ + dispatch::{DispatchError, DispatchResult}, + ensure, + storage::StorageMap, + traits::ReservableCurrency, +}; use sp_core::crypto::UncheckedFrom; +use sp_runtime::traits::BadOrigin; /// Put the instrumented module in storage. /// /// 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) +pub fn store(mut module: PrefabWasmModule, instantiated: bool) -> DispatchResult where T::AccountId: UncheckedFrom + AsRef<[u8]>, { - let code_hash = sp_std::mem::take(&mut prefab_module.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); - } + let code_hash = sp_std::mem::take(&mut module.code_hash); >::mutate(&code_hash, |existing| match existing { - Some(module) => increment_64(&mut module.refcount), + Some(existing) => { + // We instrument any uploaded contract anyways. We might as well store it to save + // a potential re-instrumentation later. + existing.code = module.code; + existing.instruction_weights_version = module.instruction_weights_version; + // When the code was merely uploaded but not instantiated we can skip this. + if instantiated { + >::mutate(&code_hash, |owner_info| { + if let Some(owner_info) = owner_info { + owner_info.refcount = owner_info.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 + ", + ); + } + }) + } + Ok(()) + }, None => { - *existing = Some(prefab_module); - Contracts::::deposit_event(Event::CodeStored { code_hash }) + let orig_code = module.original_code.take().expect( + " + If an executable isn't in storage it was uploaded. + If it was uploaded the original code must exist. qed + ", + ); + let mut owner_info = module.owner_info.take().expect( + "If an executable isn't in storage it was uploaded. + If it was uploaded the owner info was generated and attached. qed + ", + ); + // This `None` case happens only in freshly uploaded modules. This means that + // the `owner` is always the origin of the current transaction. + T::Currency::reserve(&owner_info.owner, owner_info.deposit)?; + owner_info.refcount = if instantiated { 1 } else { 0 }; + >::insert(&code_hash, orig_code); + >::insert(&code_hash, owner_info); + *existing = Some(module); + >::deposit_event(Event::CodeStored { code_hash }); + Ok(()) }, + }) +} + +/// Decrement the refcount of a code in-storage by one. +/// +/// # Note +/// +/// A contract whose refcount dropped to zero isn't automatically removed. A `remove_code` +/// transaction must be submitted by the original uploader to do so. +pub fn decrement_refcount(code_hash: CodeHash) -> Result<(), DispatchError> { + >::mutate(code_hash, |existing| { + if let Some(info) = existing { + info.refcount = info.refcount.saturating_sub(1); + } }); + Ok(()) } -/// Increment the refcount of a code in-storage by one. -pub fn increment_refcount( - code_hash: CodeHash, - gas_meter: &mut GasMeter, -) -> Result<(), DispatchError> -where - T::AccountId: UncheckedFrom + AsRef<[u8]>, -{ - gas_meter.charge(CodeToken::UpdateRefcount(estimate_code_size::(&code_hash)?))?; - >::mutate(code_hash, |existing| { - if let Some(module) = existing { - increment_64(&mut module.refcount); +/// Try to remove code together with all associated information. +pub fn try_remove(origin: &T::AccountId, code_hash: CodeHash) -> DispatchResult { + >::try_mutate_exists(&code_hash, |existing| { + if let Some(owner_info) = existing { + ensure!(owner_info.refcount == 0, >::CodeInUse); + ensure!(&owner_info.owner == origin, BadOrigin); + T::Currency::unreserve(&owner_info.owner, owner_info.deposit); + *existing = None; + >::remove(&code_hash); + >::remove(&code_hash); + >::deposit_event(Event::CodeRemoved { code_hash }); Ok(()) } else { - Err(Error::::CodeNotFound.into()) + Err(>::CodeNotFound.into()) } }) } -/// 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, - gas_meter: &mut GasMeter, -) -> Result<(), DispatchError> -where - T::AccountId: UncheckedFrom + AsRef<[u8]>, -{ - if let Ok(len) = estimate_code_size::(&code_hash) { - gas_meter.charge(CodeToken::UpdateRefcount(len))?; - } - >::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); - } - } - }); - Ok(()) -} - /// Load code with the given code hash. /// /// If the module was instrumented with a lower version of schedule than /// the current one given as an argument, then this function will perform /// re-instrumentation and update the cache in the storage. -/// -/// # Note -/// -/// If `reinstrument` is set it is assumed that the load is performed in the context of -/// a contract call: This means we charge the size based cased for loading the contract. pub fn load( code_hash: CodeHash, - mut reinstrument: Option<(&Schedule, &mut GasMeter)>, + schedule: &Schedule, + gas_meter: &mut GasMeter, ) -> Result, DispatchError> where T::AccountId: UncheckedFrom + AsRef<[u8]>, { - // The reinstrument case coincides with the cases where we need to charge extra - // based upon the code size: On-chain execution. - if let Some((_, gas_meter)) = &mut reinstrument { - gas_meter.charge(CodeToken::Load(estimate_code_size::(&code_hash)?))?; - } + gas_meter.charge(CodeToken::Load(estimate_code_size::, _>(&code_hash)?))?; let mut prefab_module = >::get(code_hash).ok_or_else(|| Error::::CodeNotFound)?; prefab_module.code_hash = code_hash; - if let Some((schedule, gas_meter)) = reinstrument { - if prefab_module.instruction_weights_version < schedule.instruction_weights.version { - // The instruction weights have changed. - // We need to re-instrument the code with the new instruction weights. - gas_meter.charge(CodeToken::Instrument(prefab_module.original_code_len))?; - private::reinstrument(&mut prefab_module, schedule)?; - } - } - Ok(prefab_module) -} - -mod private { - use super::*; - - /// Instruments the passed prefab wasm module with the supplied schedule. - pub fn reinstrument( - prefab_module: &mut PrefabWasmModule, - schedule: &Schedule, - ) -> Result<(), DispatchError> - where - T::AccountId: UncheckedFrom + AsRef<[u8]>, - { - let original_code = >::get(&prefab_module.code_hash) - .ok_or_else(|| Error::::CodeNotFound)?; - prefab_module.code = prepare::reinstrument_contract::(original_code, schedule)?; - prefab_module.instruction_weights_version = schedule.instruction_weights.version; - >::insert(&prefab_module.code_hash, &*prefab_module); - Ok(()) + if prefab_module.instruction_weights_version < schedule.instruction_weights.version { + // The instruction weights have changed. + // We need to re-instrument the code with the new instruction weights. + gas_meter.charge(CodeToken::Instrument(estimate_code_size::, _>( + &code_hash, + )?))?; + reinstrument(&mut prefab_module, schedule)?; } -} -/// 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(Event::CodeRemoved { code_hash }) + Ok(prefab_module) } -/// 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 - ", - ); +/// Instruments the passed prefab wasm module with the supplied schedule. +pub fn reinstrument( + prefab_module: &mut PrefabWasmModule, + schedule: &Schedule, +) -> Result<(), DispatchError> { + let original_code = + >::get(&prefab_module.code_hash).ok_or_else(|| Error::::CodeNotFound)?; + prefab_module.code = prepare::reinstrument_contract::(original_code, schedule)?; + prefab_module.instruction_weights_version = schedule.instruction_weights.version; + >::insert(&prefab_module.code_hash, &*prefab_module); + Ok(()) } -/// Get the size of the instrumented code stored at `code_hash` without loading it. +/// Get the size of the code stored at `code_hash` without loading it. /// -/// The returned value is slightly too large because it also contains the fields apart from -/// `code` which are located inside [`PrefabWasmModule`]. However, those are negligible when -/// compared to the code size. Additionally, charging too much weight is completely safe. -fn estimate_code_size(code_hash: &CodeHash) -> Result +/// The returned value is slightly too large when using it for the [`PrefabWasmModule`] +/// because it has other fields in addition to the code itself. However, those are negligible +/// when compared to the code size. Additionally, charging too much weight is completely safe. +fn estimate_code_size(code_hash: &CodeHash) -> Result where - T::AccountId: UncheckedFrom + AsRef<[u8]>, + T: Config, + M: StorageMap, V>, + V: codec::FullCodec, { - let key = >::hashed_key_for(code_hash); + let key = M::hashed_key_for(code_hash); let mut data = [0u8; 0]; let len = sp_io::storage::read(&key, &mut data, 0).ok_or_else(|| Error::::CodeNotFound)?; Ok(len) @@ -212,18 +204,12 @@ enum CodeToken { Instrument(u32), /// Weight for loading a contract per kilobyte. Load(u32), - /// Weight for changing the refcount of a contract per kilobyte. - UpdateRefcount(u32), } -impl Token for CodeToken -where - T: Config, - T::AccountId: UncheckedFrom + AsRef<[u8]>, -{ +impl Token for CodeToken { fn weight(&self) -> Weight { use self::CodeToken::*; - // In case of `Load` and `UpdateRefcount` we already covered the general costs of + // In case of `Load` we already covered the general costs of // accessing the storage but still need to account for the actual size of the // contract code. This is why we substract `T::*::(0)`. We need to do this at this // point because when charging the general weight we do not know the size of @@ -232,8 +218,6 @@ where Instrument(len) => T::WeightInfo::instrument(len / 1024), Load(len) => T::WeightInfo::code_load(len / 1024).saturating_sub(T::WeightInfo::code_load(0)), - UpdateRefcount(len) => T::WeightInfo::code_refcount(len / 1024) - .saturating_sub(T::WeightInfo::code_refcount(0)), } } } diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index 802b5db85902e..edf3191abff32 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -31,10 +31,13 @@ use crate::{ exec::{ExecResult, Executable, ExportedFunction, Ext}, gas::GasMeter, wasm::env_def::FunctionImplProvider, - CodeHash, Config, Schedule, + AccountIdOf, BalanceOf, CodeHash, CodeStorage, Config, Schedule, }; use codec::{Decode, Encode}; -use frame_support::dispatch::DispatchError; +use frame_support::{ + dispatch::{DispatchError, DispatchResult}, + pallet_prelude::MaxEncodedLen, +}; use sp_core::crypto::UncheckedFrom; use sp_std::prelude::*; #[cfg(test)] @@ -45,10 +48,9 @@ pub use tests::MockExt; /// # Note /// /// This data structure is mostly immutable once created and stored. The exceptions that -/// can be changed by calling a contract are `refcount`, `instruction_weights_version` and `code`. -/// `refcount` can change when a contract instantiates a new contract or self terminates. -/// `instruction_weights_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 +/// can be changed by calling a contract are `instruction_weights_version` and `code`. +/// `instruction_weights_version` and `code` change 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, scale_info::TypeInfo)] #[scale_info(skip_type_params(T))] @@ -62,11 +64,6 @@ pub struct PrefabWasmModule { /// The maximum memory size of a contract's sandbox. #[codec(compact)] maximum: u32, - /// The number of 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. /// /// For now this field is serialized as `None`. In the future we are able to change the @@ -77,11 +74,6 @@ pub struct PrefabWasmModule { _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 @@ -95,6 +87,27 @@ pub struct PrefabWasmModule { /// when loading the module from storage. #[codec(skip)] code_hash: CodeHash, + // This isn't needed for contract execution and does not get loaded from storage by defaut. + // It is `Some` if and only if this struct was generated from code. + #[codec(skip)] + owner_info: Option>, +} + +/// Information that belongs to a [`PrefabWasmModule`] but is stored separately. +/// +/// It is stored in a seperate storage entry to avoid loading the code when not necessary. +#[derive(Clone, Encode, Decode, scale_info::TypeInfo, MaxEncodedLen)] +#[codec(mel_bound(T: Config))] +#[scale_info(skip_type_params(T))] +pub struct OwnerInfo { + /// The account that has deployed the contract and hence is allowed to remove it. + owner: AccountIdOf, + /// The amount of balance that was deposited by the owner in order to deploy it. + #[codec(compact)] + deposit: BalanceOf, + /// The number of contracts that use this as their code. + #[codec(compact)] + refcount: u64, } impl ExportedFunction { @@ -112,11 +125,43 @@ where T::AccountId: UncheckedFrom + AsRef<[u8]>, { /// Create the module by checking and instrumenting `original_code`. + /// + /// This does **not** store the module. For this one need to either call [`Self::store`] + /// or [`::execute`]. pub fn from_code( original_code: Vec, schedule: &Schedule, + owner: AccountIdOf, ) -> Result { - prepare::prepare_contract(original_code, schedule).map_err(Into::into) + prepare::prepare_contract(original_code, schedule, owner).map_err(Into::into) + } + + /// Store the code without instantiating it. + /// + /// Otherwise the code is stored when [`::execute`] is called. + pub fn store(self) -> DispatchResult { + code_cache::store(self, false) + } + + /// Remove the code from storage and refund the deposit to its owner. + /// + /// Applies all necessary checks before removing the code. + pub fn remove(origin: &T::AccountId, code_hash: CodeHash) -> DispatchResult { + code_cache::try_remove::(origin, code_hash) + } + + /// Returns whether there is an deposit to be payed for this module. + /// + /// Returns `0` if the module is already in storage and hence no deposit will + /// be charged when storing it. + pub fn open_deposit(&self) -> BalanceOf { + if >::contains_key(&self.code_hash) { + 0u32.into() + } else { + // Only already in-storage contracts have their `owner_info` set to `None`. + // Therefore is is correct to return `0` in this case. + self.owner_info.as_ref().map(|i| i.deposit).unwrap_or_default() + } } /// Create and store the module without checking nor instrumenting the passed code. @@ -124,22 +169,16 @@ where /// # Note /// /// This is useful for benchmarking where we don't want instrumentation to skew - /// our results. + /// our results. This also does not collect any deposit from the `owner`. #[cfg(feature = "runtime-benchmarks")] pub fn store_code_unchecked( original_code: Vec, schedule: &Schedule, - ) -> Result<(), DispatchError> { - let executable = prepare::benchmarking::prepare_contract(original_code, schedule) + owner: T::AccountId, + ) -> DispatchResult { + let executable = prepare::benchmarking::prepare_contract(original_code, schedule, owner) .map_err::(Into::into)?; - code_cache::store(executable); - Ok(()) - } - - /// Return the refcount of the module. - #[cfg(test)] - pub fn refcount(&self) -> u64 { - self.refcount + code_cache::store(executable, false) } /// Decrement instruction_weights_version by 1. Panics if it is already 0. @@ -149,6 +188,14 @@ where } } +impl OwnerInfo { + /// Return the refcount of the module. + #[cfg(test)] + pub fn refcount(&self) -> u64 { + self.refcount + } +} + impl Executable for PrefabWasmModule where T::AccountId: UncheckedFrom + AsRef<[u8]>, @@ -158,22 +205,11 @@ where schedule: &Schedule, gas_meter: &mut GasMeter, ) -> Result { - code_cache::load(code_hash, Some((schedule, gas_meter))) - } - - fn from_storage_noinstr(code_hash: CodeHash) -> Result { - code_cache::load(code_hash, None) - } - - fn add_user(code_hash: CodeHash, gas_meter: &mut GasMeter) -> Result<(), DispatchError> { - code_cache::increment_refcount::(code_hash, gas_meter) + code_cache::load(code_hash, schedule, gas_meter) } - fn remove_user( - code_hash: CodeHash, - gas_meter: &mut GasMeter, - ) -> Result<(), DispatchError> { - code_cache::decrement_refcount::(code_hash, gas_meter) + fn remove_user(code_hash: CodeHash) -> Result<(), DispatchError> { + code_cache::decrement_refcount::(code_hash) } fn execute>( @@ -199,16 +235,15 @@ where imports.add_host_func(module, name, func_ptr); }); - let mut runtime = Runtime::new(ext, input_data, memory); - // 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) + code_cache::store(self, true)?; } // Instantiate the instance from the instrumented module code and invoke the contract // entrypoint. + let mut runtime = Runtime::new(ext, input_data, memory); let result = sp_sandbox::Instance::new(&code, &imports, &mut runtime) .and_then(|mut instance| instance.invoke(function.identifier(), &[], &mut runtime)); @@ -222,14 +257,6 @@ where fn code_len(&self) -> u32 { self.code.len() as u32 } - - fn aggregate_code_len(&self) -> u32 { - self.original_code_len.saturating_add(self.code_len()) - } - - fn refcount(&self) -> u32 { - self.refcount as u32 - } } #[cfg(test)] @@ -240,6 +267,7 @@ mod tests { AccountIdOf, BlockNumberOf, ErrorOrigin, ExecError, Executable, Ext, SeedOf, StorageKey, }, gas::GasMeter, + storage, tests::{Call, Test, ALICE, BOB}, BalanceOf, CodeHash, Error, Pallet as Contracts, }; @@ -410,6 +438,9 @@ mod tests { fn gas_meter(&mut self) -> &mut GasMeter { &mut self.gas_meter } + fn storage_meter(&mut self) -> &mut storage::meter::NestedMeter { + unimplemented!() + } fn append_debug_buffer(&mut self, msg: &str) -> bool { self.debug_buffer.extend(msg.as_bytes()); true @@ -418,7 +449,6 @@ mod tests { self.runtime_calls.borrow_mut().push(call); Ok(Default::default()) } - fn ecdsa_recover( &self, signature: &[u8; 65], @@ -427,13 +457,16 @@ mod tests { self.ecdsa_recover.borrow_mut().push((signature.clone(), message_hash.clone())); Ok([3; 33]) } + fn contract_info(&mut self) -> &mut crate::ContractInfo { + unimplemented!() + } } fn execute>(wat: &str, input_data: Vec, mut ext: E) -> ExecResult { let wasm = wat::parse_str(wat).unwrap(); let schedule = crate::Schedule::default(); let executable = - PrefabWasmModule::<::T>::from_code(wasm, &schedule).unwrap(); + PrefabWasmModule::<::T>::from_code(wasm, &schedule, ALICE).unwrap(); executable.execute(ext.borrow_mut(), &ExportedFunction::Call, input_data) } diff --git a/frame/contracts/src/wasm/prepare.rs b/frame/contracts/src/wasm/prepare.rs index c766914f3d46e..0347e12dd6785 100644 --- a/frame/contracts/src/wasm/prepare.rs +++ b/frame/contracts/src/wasm/prepare.rs @@ -21,9 +21,12 @@ use crate::{ chain_extension::ChainExtension, - wasm::{env_def::ImportSatisfyCheck, PrefabWasmModule}, - Config, Schedule, + storage::meter::Diff, + wasm::{env_def::ImportSatisfyCheck, OwnerInfo, PrefabWasmModule}, + AccountIdOf, Config, Schedule, }; +use codec::Encode; +use frame_support::pallet_prelude::MaxEncodedLen; use pwasm_utils::parity_wasm::elements::{self, External, Internal, MemoryType, Type, ValueType}; use sp_runtime::traits::Hash; use sp_std::prelude::*; @@ -395,20 +398,36 @@ fn check_and_instrument( fn do_preparation( original_code: Vec, schedule: &Schedule, + owner: AccountIdOf, ) -> Result, &'static str> { let (code, (initial, maximum)) = check_and_instrument::(original_code.as_ref(), schedule)?; - Ok(PrefabWasmModule { + let original_code_len = original_code.len(); + + let mut module = PrefabWasmModule { instruction_weights_version: schedule.instruction_weights.version, initial, maximum, _reserved: None, code, - original_code_len: original_code.len() as u32, - refcount: 1, code_hash: T::Hashing::hash(&original_code), original_code: Some(original_code), - }) + owner_info: None, + }; + + // We need to add the sizes of the `#[codec(skip)]` fields which are stored in different + // storage items. This is also why we have `3` items addeded and not only one. + let bytes_added = module + .encoded_size() + .saturating_add(original_code_len) + .saturating_add(>::max_encoded_len()) as u32; + let deposit = Diff { bytes_added, items_added: 3, ..Default::default() } + .to_deposit::() + .charge_or_zero(); + + module.owner_info = Some(OwnerInfo { owner, deposit, refcount: 0 }); + + Ok(module) } /// Loads the given module given in `original_code`, performs some checks on it and @@ -425,8 +444,9 @@ fn do_preparation( pub fn prepare_contract( original_code: Vec, schedule: &Schedule, + owner: AccountIdOf, ) -> Result, &'static str> { - do_preparation::(original_code, schedule) + do_preparation::(original_code, schedule, owner) } /// The same as [`prepare_contract`] but without constructing a new [`PrefabWasmModule`] @@ -461,6 +481,7 @@ pub mod benchmarking { pub fn prepare_contract( original_code: Vec, schedule: &Schedule, + owner: AccountIdOf, ) -> Result, &'static str> { let contract_module = ContractModule::new(&original_code, schedule)?; let memory_limits = get_memory_limits(contract_module.scan_imports::<()>(&[])?, schedule)?; @@ -470,10 +491,14 @@ 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), + owner_info: Some(OwnerInfo { + owner, + // this is a helper function for benchmarking which skips deposit collection + deposit: Default::default(), + refcount: 0, + }), }) } } @@ -481,10 +506,14 @@ pub mod benchmarking { #[cfg(test)] mod tests { use super::*; - use crate::{exec::Ext, schedule::Limits}; + use crate::{ + exec::Ext, + schedule::Limits, + tests::{Test, ALICE}, + }; use std::fmt; - impl fmt::Debug for PrefabWasmModule { + impl fmt::Debug for PrefabWasmModule { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "PreparedContract {{ .. }}") } @@ -526,7 +555,7 @@ mod tests { }, .. Default::default() }; - let r = do_preparation::(wasm, &schedule); + let r = do_preparation::(wasm, &schedule, ALICE); assert_matches::assert_matches!(r, $($expected)*); } }; diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 602f55bfdb9b4..35a1e734b25d0 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -1065,7 +1065,7 @@ define_env!(Env, , ctx.terminate(beneficiary_ptr) }, - // Remove the calling account and transfer remaining balance. + // Remove the calling account and transfer remaining **free** balance. // // This function never returns. Either the termination was successful and the // execution of the destroyed contract is halted. Or it failed during the termination diff --git a/frame/contracts/src/weights.rs b/frame/contracts/src/weights.rs index 4b6c40764ad0a..0e205efae4e27 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 4.0.0-dev -//! DATE: 2021-10-28, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2021-10-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: @@ -50,17 +50,17 @@ pub trait WeightInfo { fn on_initialize_per_queue_item(q: u32, ) -> Weight; fn instrument(c: u32, ) -> Weight; fn code_load(c: u32, ) -> Weight; - fn code_refcount(c: u32, ) -> Weight; fn instantiate_with_code(c: u32, s: u32, ) -> Weight; fn instantiate(s: u32, ) -> Weight; fn call() -> Weight; + fn upload_code(c: u32, ) -> Weight; + fn remove_code() -> Weight; fn seal_caller(r: u32, ) -> Weight; fn seal_address(r: u32, ) -> Weight; fn seal_gas_left(r: u32, ) -> Weight; fn seal_balance(r: u32, ) -> Weight; fn seal_value_transferred(r: u32, ) -> Weight; fn seal_minimum_balance(r: u32, ) -> Weight; - fn seal_tombstone_deposit(r: u32, ) -> Weight; fn seal_block_number(r: u32, ) -> Weight; fn seal_now(r: u32, ) -> Weight; fn seal_weight_to_fee(r: u32, ) -> Weight; @@ -151,75 +151,69 @@ pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { // Storage: Contracts DeletionQueue (r:1 w:0) fn on_initialize() -> Weight { - (2_987_000 as Weight) + (2_969_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) } // Storage: Skipped Metadata (r:0 w:0) fn on_initialize_per_trie_key(k: u32, ) -> Weight { (0 as Weight) // Standard Error: 2_000 - .saturating_add((2_201_000 as Weight).saturating_mul(k as Weight)) + .saturating_add((2_184_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))) } // Storage: Contracts DeletionQueue (r:1 w:0) fn on_initialize_per_queue_item(q: u32, ) -> Weight { - (97_470_000 as Weight) + (97_477_000 as Weight) // Standard Error: 2_000 - .saturating_add((322_000 as Weight).saturating_mul(q as Weight)) + .saturating_add((319_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)) } // Storage: Contracts PristineCode (r:1 w:0) // Storage: Contracts CodeStorage (r:0 w:1) fn instrument(c: u32, ) -> Weight { - (28_804_000 as Weight) - // Standard Error: 84_000 - .saturating_add((71_838_000 as Weight).saturating_mul(c as Weight)) + (30_226_000 as Weight) + // Standard Error: 93_000 + .saturating_add((74_603_000 as Weight).saturating_mul(c as Weight)) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } // Storage: Contracts CodeStorage (r:1 w:0) fn code_load(c: u32, ) -> Weight { - (5_658_000 as Weight) + (8_062_000 as Weight) // Standard Error: 0 - .saturating_add((1_425_000 as Weight).saturating_mul(c as Weight)) + .saturating_add((1_455_000 as Weight).saturating_mul(c as Weight)) .saturating_add(T::DbWeight::get().reads(1 as Weight)) } // Storage: Contracts CodeStorage (r:1 w:1) - fn code_refcount(c: u32, ) -> Weight { - (9_001_000 as Weight) - // Standard Error: 1_000 - .saturating_add((2_281_000 as Weight).saturating_mul(c as Weight)) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - } - // Storage: Contracts AccountCounter (r:1 w:1) + // Storage: Contracts AccountCounter (r:1 w:0) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Timestamp Now (r:1 w:0) // Storage: System Account (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:1) // Storage: Contracts PristineCode (r:0 w:1) + // Storage: Contracts OwnerInfoOf (r:0 w:1) fn instantiate_with_code(c: u32, s: u32, ) -> Weight { - (499_349_000 as Weight) - // Standard Error: 199_000 - .saturating_add((174_439_000 as Weight).saturating_mul(c as Weight)) - // Standard Error: 13_000 - .saturating_add((2_096_000 as Weight).saturating_mul(s as Weight)) + (501_313_000 as Weight) + // Standard Error: 139_000 + .saturating_add((174_299_000 as Weight).saturating_mul(c as Weight)) + // Standard Error: 9_000 + .saturating_add((2_169_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(5 as Weight)) } // Storage: Contracts CodeStorage (r:1 w:1) - // Storage: Contracts AccountCounter (r:1 w:1) + // Storage: Contracts AccountCounter (r:1 w:0) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Timestamp Now (r:1 w:0) // Storage: System Account (r:1 w:1) + // Storage: Contracts OwnerInfoOf (r:1 w:1) fn instantiate(s: u32, ) -> Weight { - (181_151_000 as Weight) + (263_013_000 as Weight) // Standard Error: 2_000 - .saturating_add((2_025_000 as Weight).saturating_mul(s as Weight)) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add((1_991_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } // Storage: Contracts ContractInfoOf (r:1 w:1) @@ -227,685 +221,726 @@ impl WeightInfo for SubstrateWeight { // Storage: Timestamp Now (r:1 w:0) // Storage: System Account (r:1 w:1) fn call() -> Weight { - (153_830_000 as Weight) + (210_423_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: Contracts CodeStorage (r:1 w:1) + // Storage: Contracts PristineCode (r:0 w:1) + // Storage: Contracts OwnerInfoOf (r:0 w:1) + fn upload_code(c: u32, ) -> Weight { + (84_744_000 as Weight) + // Standard Error: 95_000 + .saturating_add((73_018_000 as Weight).saturating_mul(c as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + // Storage: Contracts OwnerInfoOf (r:1 w:1) + // Storage: Contracts CodeStorage (r:0 w:1) + // Storage: Contracts PristineCode (r:0 w:1) + fn remove_code() -> Weight { + (41_641_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_caller(r: u32, ) -> Weight { - (423_222_000 as Weight) - // Standard Error: 169_000 - .saturating_add((114_763_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (459_561_000 as Weight) + // Standard Error: 172_000 + .saturating_add((114_881_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_address(r: u32, ) -> Weight { - (420_731_000 as Weight) - // Standard Error: 165_000 - .saturating_add((115_213_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (458_035_000 as Weight) + // Standard Error: 174_000 + .saturating_add((116_818_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_gas_left(r: u32, ) -> Weight { - (422_407_000 as Weight) - // Standard Error: 176_000 - .saturating_add((113_935_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (459_535_000 as Weight) + // Standard Error: 164_000 + .saturating_add((114_361_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) - // Storage: System Account (r:1 w:0) fn seal_balance(r: u32, ) -> Weight { - (425_698_000 as Weight) - // Standard Error: 210_000 - .saturating_add((335_171_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (457_456_000 as Weight) + // Standard Error: 225_000 + .saturating_add((343_777_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_value_transferred(r: u32, ) -> Weight { - (410_218_000 as Weight) - // Standard Error: 187_000 - .saturating_add((115_360_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - } - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - fn seal_minimum_balance(r: u32, ) -> Weight { - (402_765_000 as Weight) + (470_778_000 as Weight) // Standard Error: 169_000 - .saturating_add((116_553_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add((114_043_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) - fn seal_tombstone_deposit(r: u32, ) -> Weight { - (404_817_000 as Weight) - // Standard Error: 173_000 - .saturating_add((115_894_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + fn seal_minimum_balance(r: u32, ) -> Weight { + (470_607_000 as Weight) + // Standard Error: 180_000 + .saturating_add((114_061_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_block_number(r: u32, ) -> Weight { - (405_604_000 as Weight) - // Standard Error: 193_000 - .saturating_add((115_757_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (460_130_000 as Weight) + // Standard Error: 377_000 + .saturating_add((116_803_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_now(r: u32, ) -> Weight { - (413_577_000 as Weight) - // Standard Error: 166_000 - .saturating_add((115_115_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (478_140_000 as Weight) + // Standard Error: 158_000 + .saturating_add((112_277_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) // Storage: TransactionPayment NextFeeMultiplier (r:1 w:0) fn seal_weight_to_fee(r: u32, ) -> Weight { - (413_932_000 as Weight) - // Standard Error: 201_000 - .saturating_add((272_742_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (474_028_000 as Weight) + // Standard Error: 249_000 + .saturating_add((283_763_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_gas(r: u32, ) -> Weight { - (144_109_000 as Weight) - // Standard Error: 96_000 - .saturating_add((52_461_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (184_619_000 as Weight) + // Standard Error: 120_000 + .saturating_add((52_888_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_input(r: u32, ) -> Weight { - (422_584_000 as Weight) - // Standard Error: 158_000 - .saturating_add((98_316_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (455_537_000 as Weight) + // Standard Error: 199_000 + .saturating_add((99_547_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_input_per_kb(n: u32, ) -> Weight { - (549_530_000 as Weight) - // Standard Error: 8_000 - .saturating_add((38_025_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (592_179_000 as Weight) + // Standard Error: 6_000 + .saturating_add((38_070_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_return(r: u32, ) -> Weight { - (403_711_000 as Weight) - // Standard Error: 114_000 - .saturating_add((2_996_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (440_955_000 as Weight) + // Standard Error: 177_000 + .saturating_add((15_497_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_return_per_kb(n: u32, ) -> Weight { - (408_252_000 as Weight) + (441_001_000 as Weight) // Standard Error: 1_000 - .saturating_add((630_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add((654_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) // Storage: Contracts DeletionQueue (r:1 w:1) - // Storage: System Account (r:2 w:2) + // Storage: Contracts OwnerInfoOf (r:1 w:1) fn seal_terminate(r: u32, ) -> Weight { - (412_619_000 as Weight) - // Standard Error: 896_000 - .saturating_add((66_155_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(r as Weight))) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (455_397_000 as Weight) + // Standard Error: 3_901_000 + .saturating_add((100_527_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().reads((4 as Weight).saturating_mul(r as Weight))) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) .saturating_add(T::DbWeight::get().writes((4 as Weight).saturating_mul(r as Weight))) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) fn seal_random(r: u32, ) -> Weight { - (416_604_000 as Weight) - // Standard Error: 274_000 - .saturating_add((366_304_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (461_076_000 as Weight) + // Standard Error: 298_000 + .saturating_add((383_206_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_deposit_event(r: u32, ) -> Weight { - (417_326_000 as Weight) - // Standard Error: 457_000 - .saturating_add((640_211_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (446_683_000 as Weight) + // Standard Error: 350_000 + .saturating_add((640_909_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) // Storage: System EventTopics (r:100 w:100) fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32, ) -> Weight { - (1_121_348_000 as Weight) - // Standard Error: 2_483_000 - .saturating_add((463_498_000 as Weight).saturating_mul(t as Weight)) - // Standard Error: 489_000 - .saturating_add((167_147_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) + (1_194_739_000 as Weight) + // Standard Error: 2_357_000 + .saturating_add((471_118_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 464_000 + .saturating_add((164_785_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().reads((100 as Weight).saturating_mul(t as Weight))) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) .saturating_add(T::DbWeight::get().writes((100 as Weight).saturating_mul(t as Weight))) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_debug_message(r: u32, ) -> Weight { - (159_880_000 as Weight) - // Standard Error: 138_000 - .saturating_add((67_837_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (206_163_000 as Weight) + // Standard Error: 132_000 + .saturating_add((69_314_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } // Storage: Skipped Metadata (r:0 w:0) fn seal_set_storage(r: u32, ) -> Weight { - (389_400_000 as Weight) - // Standard Error: 239_000 - .saturating_add((238_933_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (492_580_000 as Weight) + // Standard Error: 515_000 + .saturating_add((379_332_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().reads((100 as Weight).saturating_mul(r as Weight))) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) .saturating_add(T::DbWeight::get().writes((100 as Weight).saturating_mul(r as Weight))) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) - // Storage: unknown [0x7afa01283080ef247df84e0ba38ea5a587d25ce6633a6bfbba02068c14023441] (r:0 w:1) + // Storage: unknown [0x7afa01283080ef247df84e0ba38ea5a587d25ce6633a6bfbba02068c14023441] (r:1 w:1) fn seal_set_storage_per_kb(n: u32, ) -> Weight { - (611_980_000 as Weight) - // Standard Error: 234_000 - .saturating_add((72_047_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) + (823_649_000 as Weight) + // Standard Error: 307_000 + .saturating_add((74_809_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads(6 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) } // Storage: Skipped Metadata (r:0 w:0) fn seal_clear_storage(r: u32, ) -> Weight { - (436_588_000 as Weight) - // Standard Error: 222_000 - .saturating_add((209_734_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (226_639_000 as Weight) + // Standard Error: 1_501_000 + .saturating_add((867_367_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(3 as Weight)) .saturating_add(T::DbWeight::get().writes((100 as Weight).saturating_mul(r as Weight))) } // Storage: Skipped Metadata (r:0 w:0) fn seal_get_storage(r: u32, ) -> Weight { - (285_689_000 as Weight) - // Standard Error: 742_000 - .saturating_add((496_745_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) + (343_416_000 as Weight) + // Standard Error: 848_000 + .saturating_add((511_880_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(4 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(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) // Storage: unknown [0x7afa01283080ef247df84e0ba38ea5a587d25ce6633a6bfbba02068c14023441] (r:1 w:0) fn seal_get_storage_per_kb(n: u32, ) -> Weight { - (693_967_000 as Weight) - // Standard Error: 226_000 - .saturating_add((111_370_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (760_951_000 as Weight) + // Standard Error: 255_000 + .saturating_add((112_137_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) - // Storage: System Account (r:101 w:101) fn seal_transfer(r: u32, ) -> Weight { - (332_032_000 as Weight) - // Standard Error: 2_537_000 - .saturating_add((4_071_041_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) + (326_749_000 as Weight) + // Standard Error: 2_723_000 + .saturating_add((4_099_913_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(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) .saturating_add(T::DbWeight::get().writes((100 as Weight).saturating_mul(r as Weight))) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_call(r: u32, ) -> Weight { - (0 as Weight) - // Standard Error: 10_806_000 - .saturating_add((39_442_275_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) + (2_843_530_000 as Weight) + // Standard Error: 20_758_000 + .saturating_add((38_200_118_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(2 as Weight)) .saturating_add(T::DbWeight::get().writes((100 as Weight).saturating_mul(r as Weight))) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:101 w:101) // Storage: Contracts CodeStorage (r:2 w:0) // Storage: Timestamp Now (r:1 w:0) - // Storage: System Account (r:101 w:101) fn seal_call_per_transfer_input_output_kb(t: u32, i: u32, o: u32, ) -> Weight { - (38_600_435_000 as Weight) - // Standard Error: 53_014_000 - .saturating_add((3_392_887_000 as Weight).saturating_mul(t as Weight)) - // Standard Error: 18_000 - .saturating_add((63_348_000 as Weight).saturating_mul(i as Weight)) - // Standard Error: 20_000 - .saturating_add((101_366_000 as Weight).saturating_mul(o as Weight)) - .saturating_add(T::DbWeight::get().reads(104 as Weight)) - .saturating_add(T::DbWeight::get().reads((101 as Weight).saturating_mul(t as Weight))) - .saturating_add(T::DbWeight::get().writes(101 as Weight)) - .saturating_add(T::DbWeight::get().writes((101 as Weight).saturating_mul(t as Weight))) + (137_204_029_000 as Weight) + // Standard Error: 8_000 + .saturating_add((62_996_000 as Weight).saturating_mul(i as Weight)) + // Standard Error: 9_000 + .saturating_add((42_000 as Weight).saturating_mul(o as Weight)) + .saturating_add(T::DbWeight::get().reads(105 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(t as Weight))) + .saturating_add(T::DbWeight::get().writes(102 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) // Storage: Contracts AccountCounter (r:1 w:1) - // Storage: System Account (r:101 w:101) + // Storage: Contracts OwnerInfoOf (r:100 w:100) fn seal_instantiate(r: u32, ) -> Weight { - (643_999_000 as Weight) - // Standard Error: 37_244_000 - .saturating_add((45_559_839_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) - .saturating_add(T::DbWeight::get().reads((300 as Weight).saturating_mul(r as Weight))) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) - .saturating_add(T::DbWeight::get().writes((300 as Weight).saturating_mul(r as Weight))) + (0 as Weight) + // Standard Error: 42_300_000 + .saturating_add((51_184_882_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(6 as Weight)) + .saturating_add(T::DbWeight::get().reads((400 as Weight).saturating_mul(r as Weight))) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + .saturating_add(T::DbWeight::get().writes((400 as Weight).saturating_mul(r as Weight))) } + // Storage: System Account (r:102 w:102) // Storage: Contracts ContractInfoOf (r:101 w:101) // Storage: Contracts CodeStorage (r:2 w:1) // Storage: Timestamp Now (r:1 w:0) // Storage: Contracts AccountCounter (r:1 w:1) - // Storage: System Account (r:101 w:101) + // Storage: Contracts OwnerInfoOf (r:1 w:1) fn seal_instantiate_per_input_output_salt_kb(i: u32, o: u32, s: u32, ) -> Weight { - (45_415_035_000 as Weight) + (47_809_145_000 as Weight) // Standard Error: 30_000 - .saturating_add((63_567_000 as Weight).saturating_mul(i as Weight)) + .saturating_add((64_218_000 as Weight).saturating_mul(i as Weight)) // Standard Error: 30_000 - .saturating_add((100_900_000 as Weight).saturating_mul(o as Weight)) + .saturating_add((100_891_000 as Weight).saturating_mul(o as Weight)) // Standard Error: 30_000 - .saturating_add((201_139_000 as Weight).saturating_mul(s as Weight)) - .saturating_add(T::DbWeight::get().reads(206 as Weight)) - .saturating_add(T::DbWeight::get().writes(204 as Weight)) + .saturating_add((201_407_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(208 as Weight)) + .saturating_add(T::DbWeight::get().writes(206 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_sha2_256(r: u32, ) -> Weight { - (417_335_000 as Weight) - // Standard Error: 174_000 - .saturating_add((126_268_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (456_042_000 as Weight) + // Standard Error: 153_000 + .saturating_add((129_938_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight { - (700_565_000 as Weight) - // Standard Error: 68_000 - .saturating_add((499_898_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (764_274_000 as Weight) + // Standard Error: 24_000 + .saturating_add((499_753_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_keccak_256(r: u32, ) -> Weight { - (416_014_000 as Weight) - // Standard Error: 168_000 - .saturating_add((134_320_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (460_114_000 as Weight) + // Standard Error: 186_000 + .saturating_add((134_203_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight { - (534_466_000 as Weight) - // Standard Error: 19_000 - .saturating_add((346_588_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (575_294_000 as Weight) + // Standard Error: 20_000 + .saturating_add((346_682_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_blake2_256(r: u32, ) -> Weight { - (414_278_000 as Weight) - // Standard Error: 164_000 - .saturating_add((106_210_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (456_380_000 as Weight) + // Standard Error: 196_000 + .saturating_add((110_306_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight { - (569_659_000 as Weight) - // Standard Error: 16_000 - .saturating_add((163_989_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (535_192_000 as Weight) + // Standard Error: 18_000 + .saturating_add((164_174_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_blake2_128(r: u32, ) -> Weight { - (421_251_000 as Weight) - // Standard Error: 166_000 - .saturating_add((104_678_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (459_977_000 as Weight) + // Standard Error: 171_000 + .saturating_add((107_852_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight { - (568_490_000 as Weight) - // Standard Error: 21_000 - .saturating_add((163_999_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (553_463_000 as Weight) + // Standard Error: 16_000 + .saturating_add((164_158_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_ecdsa_recover(r: u32, ) -> Weight { - (361_122_000 as Weight) - // Standard Error: 1_172_000 - .saturating_add((15_591_590_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (486_280_000 as Weight) + // Standard Error: 1_265_000 + .saturating_add((15_591_161_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn instr_i64const(r: u32, ) -> Weight { - (46_003_000 as Weight) - // Standard Error: 10_000 - .saturating_add((1_185_000 as Weight).saturating_mul(r as Weight)) + (53_752_000 as Weight) + // Standard Error: 14_000 + .saturating_add((853_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64load(r: u32, ) -> Weight { - (42_908_000 as Weight) - // Standard Error: 13_000 - .saturating_add((2_570_000 as Weight).saturating_mul(r as Weight)) + (48_323_000 as Weight) + // Standard Error: 9_000 + .saturating_add((2_426_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64store(r: u32, ) -> Weight { - (42_739_000 as Weight) - // Standard Error: 13_000 - .saturating_add((2_791_000 as Weight).saturating_mul(r as Weight)) + (48_636_000 as Weight) + // Standard Error: 10_000 + .saturating_add((2_633_000 as Weight).saturating_mul(r as Weight)) } fn instr_select(r: u32, ) -> Weight { - (47_543_000 as Weight) - // Standard Error: 8_000 - .saturating_add((2_834_000 as Weight).saturating_mul(r as Weight)) + (51_403_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_369_000 as Weight).saturating_mul(r as Weight)) } fn instr_if(r: u32, ) -> Weight { - (50_540_000 as Weight) - // Standard Error: 13_000 - .saturating_add((2_663_000 as Weight).saturating_mul(r as Weight)) + (48_766_000 as Weight) + // Standard Error: 12_000 + .saturating_add((2_412_000 as Weight).saturating_mul(r as Weight)) } fn instr_br(r: u32, ) -> Weight { - (47_732_000 as Weight) - // Standard Error: 8_000 - .saturating_add((1_771_000 as Weight).saturating_mul(r as Weight)) + (51_707_000 as Weight) + // Standard Error: 16_000 + .saturating_add((1_391_000 as Weight).saturating_mul(r as Weight)) } fn instr_br_if(r: u32, ) -> Weight { - (49_005_000 as Weight) - // Standard Error: 17_000 - .saturating_add((2_072_000 as Weight).saturating_mul(r as Weight)) + (45_136_000 as Weight) + // Standard Error: 18_000 + .saturating_add((2_123_000 as Weight).saturating_mul(r as Weight)) } fn instr_br_table(r: u32, ) -> Weight { - (45_975_000 as Weight) - // Standard Error: 17_000 - .saturating_add((2_492_000 as Weight).saturating_mul(r as Weight)) + (38_198_000 as Weight) + // Standard Error: 19_000 + .saturating_add((2_786_000 as Weight).saturating_mul(r as Weight)) } - fn instr_br_table_per_entry(_e: u32, ) -> Weight { - (55_461_000 as Weight) + fn instr_br_table_per_entry(e: u32, ) -> Weight { + (47_401_000 as Weight) + // Standard Error: 3_000 + .saturating_add((15_000 as Weight).saturating_mul(e as Weight)) } fn instr_call(r: u32, ) -> Weight { - (41_932_000 as Weight) - // Standard Error: 29_000 - .saturating_add((19_800_000 as Weight).saturating_mul(r as Weight)) + (40_645_000 as Weight) + // Standard Error: 25_000 + .saturating_add((20_039_000 as Weight).saturating_mul(r as Weight)) } fn instr_call_indirect(r: u32, ) -> Weight { - (56_550_000 as Weight) - // Standard Error: 34_000 - .saturating_add((28_414_000 as Weight).saturating_mul(r as Weight)) + (57_228_000 as Weight) + // Standard Error: 33_000 + .saturating_add((29_388_000 as Weight).saturating_mul(r as Weight)) } fn instr_call_indirect_per_param(p: u32, ) -> Weight { - (93_172_000 as Weight) - // Standard Error: 6_000 - .saturating_add((1_018_000 as Weight).saturating_mul(p as Weight)) + (89_382_000 as Weight) + // Standard Error: 5_000 + .saturating_add((1_143_000 as Weight).saturating_mul(p as Weight)) } fn instr_local_get(r: u32, ) -> Weight { - (54_603_000 as Weight) - // Standard Error: 14_000 - .saturating_add((764_000 as Weight).saturating_mul(r as Weight)) + (48_789_000 as Weight) + // Standard Error: 11_000 + .saturating_add((692_000 as Weight).saturating_mul(r as Weight)) } fn instr_local_set(r: u32, ) -> Weight { - (54_763_000 as Weight) - // Standard Error: 14_000 - .saturating_add((878_000 as Weight).saturating_mul(r as Weight)) + (49_067_000 as Weight) + // Standard Error: 12_000 + .saturating_add((803_000 as Weight).saturating_mul(r as Weight)) } fn instr_local_tee(r: u32, ) -> Weight { - (56_137_000 as Weight) + (45_765_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_194_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((1_395_000 as Weight).saturating_mul(r as Weight)) } fn instr_global_get(r: u32, ) -> Weight { - (69_513_000 as Weight) - // Standard Error: 11_000 - .saturating_add((1_125_000 as Weight).saturating_mul(r as Weight)) + (61_552_000 as Weight) + // Standard Error: 20_000 + .saturating_add((1_440_000 as Weight).saturating_mul(r as Weight)) } fn instr_global_set(r: u32, ) -> Weight { - (69_120_000 as Weight) - // Standard Error: 12_000 - .saturating_add((1_215_000 as Weight).saturating_mul(r as Weight)) + (56_926_000 as Weight) + // Standard Error: 22_000 + .saturating_add((1_741_000 as Weight).saturating_mul(r as Weight)) } fn instr_memory_current(r: u32, ) -> Weight { - (46_021_000 as Weight) - // Standard Error: 10_000 - .saturating_add((1_103_000 as Weight).saturating_mul(r as Weight)) + (53_287_000 as Weight) + // Standard Error: 14_000 + .saturating_add((769_000 as Weight).saturating_mul(r as Weight)) } fn instr_memory_grow(r: u32, ) -> Weight { - (52_245_000 as Weight) - // Standard Error: 4_119_000 - .saturating_add((619_498_000 as Weight).saturating_mul(r as Weight)) + (38_009_000 as Weight) + // Standard Error: 2_445_000 + .saturating_add((637_570_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64clz(r: u32, ) -> Weight { - (47_314_000 as Weight) - // Standard Error: 9_000 - .saturating_add((1_720_000 as Weight).saturating_mul(r as Weight)) + (55_460_000 as Weight) + // Standard Error: 10_000 + .saturating_add((1_222_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ctz(r: u32, ) -> Weight { - (47_855_000 as Weight) - // Standard Error: 9_000 - .saturating_add((1_701_000 as Weight).saturating_mul(r as Weight)) + (55_058_000 as Weight) + // Standard Error: 13_000 + .saturating_add((1_251_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64popcnt(r: u32, ) -> Weight { - (47_704_000 as Weight) - // Standard Error: 10_000 - .saturating_add((1_708_000 as Weight).saturating_mul(r as Weight)) + (55_308_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_229_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64eqz(r: u32, ) -> Weight { - (47_656_000 as Weight) - // Standard Error: 9_000 - .saturating_add((1_705_000 as Weight).saturating_mul(r as Weight)) + (55_303_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_231_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64extendsi32(r: u32, ) -> Weight { - (55_202_000 as Weight) + (44_768_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_229_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((1_428_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64extendui32(r: u32, ) -> Weight { - (55_193_000 as Weight) - // Standard Error: 9_000 - .saturating_add((1_223_000 as Weight).saturating_mul(r as Weight)) + (44_806_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_429_000 as Weight).saturating_mul(r as Weight)) } fn instr_i32wrapi64(r: u32, ) -> Weight { - (48_125_000 as Weight) - // Standard Error: 10_000 - .saturating_add((1_704_000 as Weight).saturating_mul(r as Weight)) + (55_388_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_228_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64eq(r: u32, ) -> Weight { - (49_162_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_241_000 as Weight).saturating_mul(r as Weight)) + (50_942_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_826_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ne(r: u32, ) -> Weight { - (48_635_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_262_000 as Weight).saturating_mul(r as Weight)) + (50_964_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_825_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64lts(r: u32, ) -> Weight { - (48_550_000 as Weight) - // Standard Error: 9_000 - .saturating_add((2_267_000 as Weight).saturating_mul(r as Weight)) + (50_749_000 as Weight) + // Standard Error: 10_000 + .saturating_add((1_830_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ltu(r: u32, ) -> Weight { - (49_135_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_219_000 as Weight).saturating_mul(r as Weight)) + (50_581_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_837_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64gts(r: u32, ) -> Weight { - (49_638_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_206_000 as Weight).saturating_mul(r as Weight)) + (50_720_000 as Weight) + // Standard Error: 10_000 + .saturating_add((1_831_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64gtu(r: u32, ) -> Weight { - (49_889_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_201_000 as Weight).saturating_mul(r as Weight)) + (50_686_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_832_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64les(r: u32, ) -> Weight { - (49_763_000 as Weight) - // Standard Error: 9_000 - .saturating_add((2_210_000 as Weight).saturating_mul(r as Weight)) + (50_914_000 as Weight) + // Standard Error: 12_000 + .saturating_add((1_828_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64leu(r: u32, ) -> Weight { - (49_607_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_207_000 as Weight).saturating_mul(r as Weight)) + (51_126_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_820_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ges(r: u32, ) -> Weight { - (49_664_000 as Weight) - // Standard Error: 9_000 - .saturating_add((2_213_000 as Weight).saturating_mul(r as Weight)) + (50_960_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_829_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64geu(r: u32, ) -> Weight { - (49_718_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_206_000 as Weight).saturating_mul(r as Weight)) + (50_796_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_831_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64add(r: u32, ) -> Weight { - (49_513_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_208_000 as Weight).saturating_mul(r as Weight)) + (51_011_000 as Weight) + // Standard Error: 12_000 + .saturating_add((1_826_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64sub(r: u32, ) -> Weight { - (49_837_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_201_000 as Weight).saturating_mul(r as Weight)) + (50_892_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_828_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64mul(r: u32, ) -> Weight { - (49_684_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_210_000 as Weight).saturating_mul(r as Weight)) + (50_980_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_825_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64divs(r: u32, ) -> Weight { - (48_749_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_872_000 as Weight).saturating_mul(r as Weight)) + (51_245_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_427_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64divu(r: u32, ) -> Weight { - (49_134_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_630_000 as Weight).saturating_mul(r as Weight)) + (51_083_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_218_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64rems(r: u32, ) -> Weight { - (48_981_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_861_000 as Weight).saturating_mul(r as Weight)) + (50_861_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_446_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64remu(r: u32, ) -> Weight { - (49_195_000 as Weight) - // Standard Error: 8_000 - .saturating_add((2_593_000 as Weight).saturating_mul(r as Weight)) + (51_001_000 as Weight) + // Standard Error: 10_000 + .saturating_add((2_183_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64and(r: u32, ) -> Weight { - (49_304_000 as Weight) - // Standard Error: 8_000 - .saturating_add((2_238_000 as Weight).saturating_mul(r as Weight)) + (50_819_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_829_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64or(r: u32, ) -> Weight { - (48_636_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_259_000 as Weight).saturating_mul(r as Weight)) + (51_368_000 as Weight) + // Standard Error: 12_000 + .saturating_add((1_814_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64xor(r: u32, ) -> Weight { - (48_761_000 as Weight) - // Standard Error: 8_000 - .saturating_add((2_262_000 as Weight).saturating_mul(r as Weight)) + (50_952_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_828_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64shl(r: u32, ) -> Weight { - (48_492_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_263_000 as Weight).saturating_mul(r as Weight)) + (50_996_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_827_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64shrs(r: u32, ) -> Weight { - (48_736_000 as Weight) - // Standard Error: 8_000 - .saturating_add((2_256_000 as Weight).saturating_mul(r as Weight)) + (51_061_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_826_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64shru(r: u32, ) -> Weight { - (48_675_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_256_000 as Weight).saturating_mul(r as Weight)) + (50_931_000 as Weight) + // Standard Error: 12_000 + .saturating_add((1_832_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64rotl(r: u32, ) -> Weight { - (48_703_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_257_000 as Weight).saturating_mul(r as Weight)) + (50_652_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_832_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64rotr(r: u32, ) -> Weight { - (48_758_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_259_000 as Weight).saturating_mul(r as Weight)) + (50_817_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_830_000 as Weight).saturating_mul(r as Weight)) } } @@ -913,75 +948,69 @@ impl WeightInfo for SubstrateWeight { impl WeightInfo for () { // Storage: Contracts DeletionQueue (r:1 w:0) fn on_initialize() -> Weight { - (2_987_000 as Weight) + (2_969_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) } // Storage: Skipped Metadata (r:0 w:0) fn on_initialize_per_trie_key(k: u32, ) -> Weight { (0 as Weight) // Standard Error: 2_000 - .saturating_add((2_201_000 as Weight).saturating_mul(k as Weight)) + .saturating_add((2_184_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))) } // Storage: Contracts DeletionQueue (r:1 w:0) fn on_initialize_per_queue_item(q: u32, ) -> Weight { - (97_470_000 as Weight) + (97_477_000 as Weight) // Standard Error: 2_000 - .saturating_add((322_000 as Weight).saturating_mul(q as Weight)) + .saturating_add((319_000 as Weight).saturating_mul(q as Weight)) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } // Storage: Contracts PristineCode (r:1 w:0) // Storage: Contracts CodeStorage (r:0 w:1) fn instrument(c: u32, ) -> Weight { - (28_804_000 as Weight) - // Standard Error: 84_000 - .saturating_add((71_838_000 as Weight).saturating_mul(c as Weight)) + (30_226_000 as Weight) + // Standard Error: 93_000 + .saturating_add((74_603_000 as Weight).saturating_mul(c as Weight)) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } // Storage: Contracts CodeStorage (r:1 w:0) fn code_load(c: u32, ) -> Weight { - (5_658_000 as Weight) + (8_062_000 as Weight) // Standard Error: 0 - .saturating_add((1_425_000 as Weight).saturating_mul(c as Weight)) + .saturating_add((1_455_000 as Weight).saturating_mul(c as Weight)) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) } // Storage: Contracts CodeStorage (r:1 w:1) - fn code_refcount(c: u32, ) -> Weight { - (9_001_000 as Weight) - // Standard Error: 1_000 - .saturating_add((2_281_000 as Weight).saturating_mul(c as Weight)) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - } - // Storage: Contracts AccountCounter (r:1 w:1) + // Storage: Contracts AccountCounter (r:1 w:0) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Timestamp Now (r:1 w:0) // Storage: System Account (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:1) // Storage: Contracts PristineCode (r:0 w:1) + // Storage: Contracts OwnerInfoOf (r:0 w:1) fn instantiate_with_code(c: u32, s: u32, ) -> Weight { - (499_349_000 as Weight) - // Standard Error: 199_000 - .saturating_add((174_439_000 as Weight).saturating_mul(c as Weight)) - // Standard Error: 13_000 - .saturating_add((2_096_000 as Weight).saturating_mul(s as Weight)) + (501_313_000 as Weight) + // Standard Error: 139_000 + .saturating_add((174_299_000 as Weight).saturating_mul(c as Weight)) + // Standard Error: 9_000 + .saturating_add((2_169_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(5 as Weight)) } // Storage: Contracts CodeStorage (r:1 w:1) - // Storage: Contracts AccountCounter (r:1 w:1) + // Storage: Contracts AccountCounter (r:1 w:0) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Timestamp Now (r:1 w:0) // Storage: System Account (r:1 w:1) + // Storage: Contracts OwnerInfoOf (r:1 w:1) fn instantiate(s: u32, ) -> Weight { - (181_151_000 as Weight) + (263_013_000 as Weight) // Standard Error: 2_000 - .saturating_add((2_025_000 as Weight).saturating_mul(s as Weight)) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + .saturating_add((1_991_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } // Storage: Contracts ContractInfoOf (r:1 w:1) @@ -989,684 +1018,725 @@ impl WeightInfo for () { // Storage: Timestamp Now (r:1 w:0) // Storage: System Account (r:1 w:1) fn call() -> Weight { - (153_830_000 as Weight) + (210_423_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: Contracts CodeStorage (r:1 w:1) + // Storage: Contracts PristineCode (r:0 w:1) + // Storage: Contracts OwnerInfoOf (r:0 w:1) + fn upload_code(c: u32, ) -> Weight { + (84_744_000 as Weight) + // Standard Error: 95_000 + .saturating_add((73_018_000 as Weight).saturating_mul(c as Weight)) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + } + // Storage: Contracts OwnerInfoOf (r:1 w:1) + // Storage: Contracts CodeStorage (r:0 w:1) + // Storage: Contracts PristineCode (r:0 w:1) + fn remove_code() -> Weight { + (41_641_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_caller(r: u32, ) -> Weight { - (423_222_000 as Weight) - // Standard Error: 169_000 - .saturating_add((114_763_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (459_561_000 as Weight) + // Standard Error: 172_000 + .saturating_add((114_881_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_address(r: u32, ) -> Weight { - (420_731_000 as Weight) - // Standard Error: 165_000 - .saturating_add((115_213_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (458_035_000 as Weight) + // Standard Error: 174_000 + .saturating_add((116_818_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_gas_left(r: u32, ) -> Weight { - (422_407_000 as Weight) - // Standard Error: 176_000 - .saturating_add((113_935_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (459_535_000 as Weight) + // Standard Error: 164_000 + .saturating_add((114_361_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) - // Storage: System Account (r:1 w:0) fn seal_balance(r: u32, ) -> Weight { - (425_698_000 as Weight) - // Standard Error: 210_000 - .saturating_add((335_171_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (457_456_000 as Weight) + // Standard Error: 225_000 + .saturating_add((343_777_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_value_transferred(r: u32, ) -> Weight { - (410_218_000 as Weight) - // Standard Error: 187_000 - .saturating_add((115_360_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - } - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - fn seal_minimum_balance(r: u32, ) -> Weight { - (402_765_000 as Weight) + (470_778_000 as Weight) // Standard Error: 169_000 - .saturating_add((116_553_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + .saturating_add((114_043_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) - fn seal_tombstone_deposit(r: u32, ) -> Weight { - (404_817_000 as Weight) - // Standard Error: 173_000 - .saturating_add((115_894_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + fn seal_minimum_balance(r: u32, ) -> Weight { + (470_607_000 as Weight) + // Standard Error: 180_000 + .saturating_add((114_061_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_block_number(r: u32, ) -> Weight { - (405_604_000 as Weight) - // Standard Error: 193_000 - .saturating_add((115_757_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (460_130_000 as Weight) + // Standard Error: 377_000 + .saturating_add((116_803_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_now(r: u32, ) -> Weight { - (413_577_000 as Weight) - // Standard Error: 166_000 - .saturating_add((115_115_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (478_140_000 as Weight) + // Standard Error: 158_000 + .saturating_add((112_277_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) // Storage: TransactionPayment NextFeeMultiplier (r:1 w:0) fn seal_weight_to_fee(r: u32, ) -> Weight { - (413_932_000 as Weight) - // Standard Error: 201_000 - .saturating_add((272_742_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (474_028_000 as Weight) + // Standard Error: 249_000 + .saturating_add((283_763_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_gas(r: u32, ) -> Weight { - (144_109_000 as Weight) - // Standard Error: 96_000 - .saturating_add((52_461_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (184_619_000 as Weight) + // Standard Error: 120_000 + .saturating_add((52_888_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_input(r: u32, ) -> Weight { - (422_584_000 as Weight) - // Standard Error: 158_000 - .saturating_add((98_316_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (455_537_000 as Weight) + // Standard Error: 199_000 + .saturating_add((99_547_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_input_per_kb(n: u32, ) -> Weight { - (549_530_000 as Weight) - // Standard Error: 8_000 - .saturating_add((38_025_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (592_179_000 as Weight) + // Standard Error: 6_000 + .saturating_add((38_070_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_return(r: u32, ) -> Weight { - (403_711_000 as Weight) - // Standard Error: 114_000 - .saturating_add((2_996_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (440_955_000 as Weight) + // Standard Error: 177_000 + .saturating_add((15_497_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_return_per_kb(n: u32, ) -> Weight { - (408_252_000 as Weight) + (441_001_000 as Weight) // Standard Error: 1_000 - .saturating_add((630_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + .saturating_add((654_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) // Storage: Contracts DeletionQueue (r:1 w:1) - // Storage: System Account (r:2 w:2) + // Storage: Contracts OwnerInfoOf (r:1 w:1) fn seal_terminate(r: u32, ) -> Weight { - (412_619_000 as Weight) - // Standard Error: 896_000 - .saturating_add((66_155_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().reads((3 as Weight).saturating_mul(r as Weight))) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (455_397_000 as Weight) + // Standard Error: 3_901_000 + .saturating_add((100_527_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().reads((4 as Weight).saturating_mul(r as Weight))) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) .saturating_add(RocksDbWeight::get().writes((4 as Weight).saturating_mul(r as Weight))) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) fn seal_random(r: u32, ) -> Weight { - (416_604_000 as Weight) - // Standard Error: 274_000 - .saturating_add((366_304_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (461_076_000 as Weight) + // Standard Error: 298_000 + .saturating_add((383_206_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_deposit_event(r: u32, ) -> Weight { - (417_326_000 as Weight) - // Standard Error: 457_000 - .saturating_add((640_211_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (446_683_000 as Weight) + // Standard Error: 350_000 + .saturating_add((640_909_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) // Storage: System EventTopics (r:100 w:100) fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32, ) -> Weight { - (1_121_348_000 as Weight) - // Standard Error: 2_483_000 - .saturating_add((463_498_000 as Weight).saturating_mul(t as Weight)) - // Standard Error: 489_000 - .saturating_add((167_147_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + (1_194_739_000 as Weight) + // Standard Error: 2_357_000 + .saturating_add((471_118_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 464_000 + .saturating_add((164_785_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().reads((100 as Weight).saturating_mul(t as Weight))) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) .saturating_add(RocksDbWeight::get().writes((100 as Weight).saturating_mul(t as Weight))) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_debug_message(r: u32, ) -> Weight { - (159_880_000 as Weight) - // Standard Error: 138_000 - .saturating_add((67_837_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (206_163_000 as Weight) + // Standard Error: 132_000 + .saturating_add((69_314_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } // Storage: Skipped Metadata (r:0 w:0) fn seal_set_storage(r: u32, ) -> Weight { - (389_400_000 as Weight) - // Standard Error: 239_000 - .saturating_add((238_933_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (492_580_000 as Weight) + // Standard Error: 515_000 + .saturating_add((379_332_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().reads((100 as Weight).saturating_mul(r as Weight))) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) .saturating_add(RocksDbWeight::get().writes((100 as Weight).saturating_mul(r as Weight))) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) - // Storage: unknown [0x7afa01283080ef247df84e0ba38ea5a587d25ce6633a6bfbba02068c14023441] (r:0 w:1) + // Storage: unknown [0x7afa01283080ef247df84e0ba38ea5a587d25ce6633a6bfbba02068c14023441] (r:1 w:1) fn seal_set_storage_per_kb(n: u32, ) -> Weight { - (611_980_000 as Weight) - // Standard Error: 234_000 - .saturating_add((72_047_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + (823_649_000 as Weight) + // Standard Error: 307_000 + .saturating_add((74_809_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(RocksDbWeight::get().reads(6 as Weight)) + .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } // Storage: Skipped Metadata (r:0 w:0) fn seal_clear_storage(r: u32, ) -> Weight { - (436_588_000 as Weight) - // Standard Error: 222_000 - .saturating_add((209_734_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (226_639_000 as Weight) + // Standard Error: 1_501_000 + .saturating_add((867_367_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(3 as Weight)) .saturating_add(RocksDbWeight::get().writes((100 as Weight).saturating_mul(r as Weight))) } // Storage: Skipped Metadata (r:0 w:0) fn seal_get_storage(r: u32, ) -> Weight { - (285_689_000 as Weight) - // Standard Error: 742_000 - .saturating_add((496_745_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + (343_416_000 as Weight) + // Standard Error: 848_000 + .saturating_add((511_880_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 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(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) // Storage: unknown [0x7afa01283080ef247df84e0ba38ea5a587d25ce6633a6bfbba02068c14023441] (r:1 w:0) fn seal_get_storage_per_kb(n: u32, ) -> Weight { - (693_967_000 as Weight) - // Standard Error: 226_000 - .saturating_add((111_370_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (760_951_000 as Weight) + // Standard Error: 255_000 + .saturating_add((112_137_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) - // Storage: System Account (r:101 w:101) fn seal_transfer(r: u32, ) -> Weight { - (332_032_000 as Weight) - // Standard Error: 2_537_000 - .saturating_add((4_071_041_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + (326_749_000 as Weight) + // Standard Error: 2_723_000 + .saturating_add((4_099_913_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(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) .saturating_add(RocksDbWeight::get().writes((100 as Weight).saturating_mul(r as Weight))) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_call(r: u32, ) -> Weight { - (0 as Weight) - // Standard Error: 10_806_000 - .saturating_add((39_442_275_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + (2_843_530_000 as Weight) + // Standard Error: 20_758_000 + .saturating_add((38_200_118_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(2 as Weight)) .saturating_add(RocksDbWeight::get().writes((100 as Weight).saturating_mul(r as Weight))) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:101 w:101) // Storage: Contracts CodeStorage (r:2 w:0) // Storage: Timestamp Now (r:1 w:0) - // Storage: System Account (r:101 w:101) fn seal_call_per_transfer_input_output_kb(t: u32, i: u32, o: u32, ) -> Weight { - (38_600_435_000 as Weight) - // Standard Error: 53_014_000 - .saturating_add((3_392_887_000 as Weight).saturating_mul(t as Weight)) - // Standard Error: 18_000 - .saturating_add((63_348_000 as Weight).saturating_mul(i as Weight)) - // Standard Error: 20_000 - .saturating_add((101_366_000 as Weight).saturating_mul(o as Weight)) - .saturating_add(RocksDbWeight::get().reads(104 as Weight)) - .saturating_add(RocksDbWeight::get().reads((101 as Weight).saturating_mul(t as Weight))) - .saturating_add(RocksDbWeight::get().writes(101 as Weight)) - .saturating_add(RocksDbWeight::get().writes((101 as Weight).saturating_mul(t as Weight))) + (137_204_029_000 as Weight) + // Standard Error: 8_000 + .saturating_add((62_996_000 as Weight).saturating_mul(i as Weight)) + // Standard Error: 9_000 + .saturating_add((42_000 as Weight).saturating_mul(o as Weight)) + .saturating_add(RocksDbWeight::get().reads(105 as Weight)) + .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(t as Weight))) + .saturating_add(RocksDbWeight::get().writes(102 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) // Storage: Contracts AccountCounter (r:1 w:1) - // Storage: System Account (r:101 w:101) + // Storage: Contracts OwnerInfoOf (r:100 w:100) fn seal_instantiate(r: u32, ) -> Weight { - (643_999_000 as Weight) - // Standard Error: 37_244_000 - .saturating_add((45_559_839_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) - .saturating_add(RocksDbWeight::get().reads((300 as Weight).saturating_mul(r as Weight))) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes((300 as Weight).saturating_mul(r as Weight))) + (0 as Weight) + // Standard Error: 42_300_000 + .saturating_add((51_184_882_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(6 as Weight)) + .saturating_add(RocksDbWeight::get().reads((400 as Weight).saturating_mul(r as Weight))) + .saturating_add(RocksDbWeight::get().writes(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes((400 as Weight).saturating_mul(r as Weight))) } + // Storage: System Account (r:102 w:102) // Storage: Contracts ContractInfoOf (r:101 w:101) // Storage: Contracts CodeStorage (r:2 w:1) // Storage: Timestamp Now (r:1 w:0) // Storage: Contracts AccountCounter (r:1 w:1) - // Storage: System Account (r:101 w:101) + // Storage: Contracts OwnerInfoOf (r:1 w:1) fn seal_instantiate_per_input_output_salt_kb(i: u32, o: u32, s: u32, ) -> Weight { - (45_415_035_000 as Weight) + (47_809_145_000 as Weight) // Standard Error: 30_000 - .saturating_add((63_567_000 as Weight).saturating_mul(i as Weight)) + .saturating_add((64_218_000 as Weight).saturating_mul(i as Weight)) // Standard Error: 30_000 - .saturating_add((100_900_000 as Weight).saturating_mul(o as Weight)) + .saturating_add((100_891_000 as Weight).saturating_mul(o as Weight)) // Standard Error: 30_000 - .saturating_add((201_139_000 as Weight).saturating_mul(s as Weight)) - .saturating_add(RocksDbWeight::get().reads(206 as Weight)) - .saturating_add(RocksDbWeight::get().writes(204 as Weight)) + .saturating_add((201_407_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(RocksDbWeight::get().reads(208 as Weight)) + .saturating_add(RocksDbWeight::get().writes(206 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_sha2_256(r: u32, ) -> Weight { - (417_335_000 as Weight) - // Standard Error: 174_000 - .saturating_add((126_268_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (456_042_000 as Weight) + // Standard Error: 153_000 + .saturating_add((129_938_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight { - (700_565_000 as Weight) - // Standard Error: 68_000 - .saturating_add((499_898_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (764_274_000 as Weight) + // Standard Error: 24_000 + .saturating_add((499_753_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_keccak_256(r: u32, ) -> Weight { - (416_014_000 as Weight) - // Standard Error: 168_000 - .saturating_add((134_320_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (460_114_000 as Weight) + // Standard Error: 186_000 + .saturating_add((134_203_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight { - (534_466_000 as Weight) - // Standard Error: 19_000 - .saturating_add((346_588_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (575_294_000 as Weight) + // Standard Error: 20_000 + .saturating_add((346_682_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_blake2_256(r: u32, ) -> Weight { - (414_278_000 as Weight) - // Standard Error: 164_000 - .saturating_add((106_210_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (456_380_000 as Weight) + // Standard Error: 196_000 + .saturating_add((110_306_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight { - (569_659_000 as Weight) - // Standard Error: 16_000 - .saturating_add((163_989_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (535_192_000 as Weight) + // Standard Error: 18_000 + .saturating_add((164_174_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_blake2_128(r: u32, ) -> Weight { - (421_251_000 as Weight) - // Standard Error: 166_000 - .saturating_add((104_678_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (459_977_000 as Weight) + // Standard Error: 171_000 + .saturating_add((107_852_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight { - (568_490_000 as Weight) - // Standard Error: 21_000 - .saturating_add((163_999_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (553_463_000 as Weight) + // Standard Error: 16_000 + .saturating_add((164_158_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + // Storage: System Account (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_ecdsa_recover(r: u32, ) -> Weight { - (361_122_000 as Weight) - // Standard Error: 1_172_000 - .saturating_add((15_591_590_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (486_280_000 as Weight) + // Standard Error: 1_265_000 + .saturating_add((15_591_161_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn instr_i64const(r: u32, ) -> Weight { - (46_003_000 as Weight) - // Standard Error: 10_000 - .saturating_add((1_185_000 as Weight).saturating_mul(r as Weight)) + (53_752_000 as Weight) + // Standard Error: 14_000 + .saturating_add((853_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64load(r: u32, ) -> Weight { - (42_908_000 as Weight) - // Standard Error: 13_000 - .saturating_add((2_570_000 as Weight).saturating_mul(r as Weight)) + (48_323_000 as Weight) + // Standard Error: 9_000 + .saturating_add((2_426_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64store(r: u32, ) -> Weight { - (42_739_000 as Weight) - // Standard Error: 13_000 - .saturating_add((2_791_000 as Weight).saturating_mul(r as Weight)) + (48_636_000 as Weight) + // Standard Error: 10_000 + .saturating_add((2_633_000 as Weight).saturating_mul(r as Weight)) } fn instr_select(r: u32, ) -> Weight { - (47_543_000 as Weight) - // Standard Error: 8_000 - .saturating_add((2_834_000 as Weight).saturating_mul(r as Weight)) + (51_403_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_369_000 as Weight).saturating_mul(r as Weight)) } fn instr_if(r: u32, ) -> Weight { - (50_540_000 as Weight) - // Standard Error: 13_000 - .saturating_add((2_663_000 as Weight).saturating_mul(r as Weight)) + (48_766_000 as Weight) + // Standard Error: 12_000 + .saturating_add((2_412_000 as Weight).saturating_mul(r as Weight)) } fn instr_br(r: u32, ) -> Weight { - (47_732_000 as Weight) - // Standard Error: 8_000 - .saturating_add((1_771_000 as Weight).saturating_mul(r as Weight)) + (51_707_000 as Weight) + // Standard Error: 16_000 + .saturating_add((1_391_000 as Weight).saturating_mul(r as Weight)) } fn instr_br_if(r: u32, ) -> Weight { - (49_005_000 as Weight) - // Standard Error: 17_000 - .saturating_add((2_072_000 as Weight).saturating_mul(r as Weight)) + (45_136_000 as Weight) + // Standard Error: 18_000 + .saturating_add((2_123_000 as Weight).saturating_mul(r as Weight)) } fn instr_br_table(r: u32, ) -> Weight { - (45_975_000 as Weight) - // Standard Error: 17_000 - .saturating_add((2_492_000 as Weight).saturating_mul(r as Weight)) + (38_198_000 as Weight) + // Standard Error: 19_000 + .saturating_add((2_786_000 as Weight).saturating_mul(r as Weight)) } - fn instr_br_table_per_entry(_e: u32, ) -> Weight { - (55_461_000 as Weight) + fn instr_br_table_per_entry(e: u32, ) -> Weight { + (47_401_000 as Weight) + // Standard Error: 3_000 + .saturating_add((15_000 as Weight).saturating_mul(e as Weight)) } fn instr_call(r: u32, ) -> Weight { - (41_932_000 as Weight) - // Standard Error: 29_000 - .saturating_add((19_800_000 as Weight).saturating_mul(r as Weight)) + (40_645_000 as Weight) + // Standard Error: 25_000 + .saturating_add((20_039_000 as Weight).saturating_mul(r as Weight)) } fn instr_call_indirect(r: u32, ) -> Weight { - (56_550_000 as Weight) - // Standard Error: 34_000 - .saturating_add((28_414_000 as Weight).saturating_mul(r as Weight)) + (57_228_000 as Weight) + // Standard Error: 33_000 + .saturating_add((29_388_000 as Weight).saturating_mul(r as Weight)) } fn instr_call_indirect_per_param(p: u32, ) -> Weight { - (93_172_000 as Weight) - // Standard Error: 6_000 - .saturating_add((1_018_000 as Weight).saturating_mul(p as Weight)) + (89_382_000 as Weight) + // Standard Error: 5_000 + .saturating_add((1_143_000 as Weight).saturating_mul(p as Weight)) } fn instr_local_get(r: u32, ) -> Weight { - (54_603_000 as Weight) - // Standard Error: 14_000 - .saturating_add((764_000 as Weight).saturating_mul(r as Weight)) + (48_789_000 as Weight) + // Standard Error: 11_000 + .saturating_add((692_000 as Weight).saturating_mul(r as Weight)) } fn instr_local_set(r: u32, ) -> Weight { - (54_763_000 as Weight) - // Standard Error: 14_000 - .saturating_add((878_000 as Weight).saturating_mul(r as Weight)) + (49_067_000 as Weight) + // Standard Error: 12_000 + .saturating_add((803_000 as Weight).saturating_mul(r as Weight)) } fn instr_local_tee(r: u32, ) -> Weight { - (56_137_000 as Weight) + (45_765_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_194_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((1_395_000 as Weight).saturating_mul(r as Weight)) } fn instr_global_get(r: u32, ) -> Weight { - (69_513_000 as Weight) - // Standard Error: 11_000 - .saturating_add((1_125_000 as Weight).saturating_mul(r as Weight)) + (61_552_000 as Weight) + // Standard Error: 20_000 + .saturating_add((1_440_000 as Weight).saturating_mul(r as Weight)) } fn instr_global_set(r: u32, ) -> Weight { - (69_120_000 as Weight) - // Standard Error: 12_000 - .saturating_add((1_215_000 as Weight).saturating_mul(r as Weight)) + (56_926_000 as Weight) + // Standard Error: 22_000 + .saturating_add((1_741_000 as Weight).saturating_mul(r as Weight)) } fn instr_memory_current(r: u32, ) -> Weight { - (46_021_000 as Weight) - // Standard Error: 10_000 - .saturating_add((1_103_000 as Weight).saturating_mul(r as Weight)) + (53_287_000 as Weight) + // Standard Error: 14_000 + .saturating_add((769_000 as Weight).saturating_mul(r as Weight)) } fn instr_memory_grow(r: u32, ) -> Weight { - (52_245_000 as Weight) - // Standard Error: 4_119_000 - .saturating_add((619_498_000 as Weight).saturating_mul(r as Weight)) + (38_009_000 as Weight) + // Standard Error: 2_445_000 + .saturating_add((637_570_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64clz(r: u32, ) -> Weight { - (47_314_000 as Weight) - // Standard Error: 9_000 - .saturating_add((1_720_000 as Weight).saturating_mul(r as Weight)) + (55_460_000 as Weight) + // Standard Error: 10_000 + .saturating_add((1_222_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ctz(r: u32, ) -> Weight { - (47_855_000 as Weight) - // Standard Error: 9_000 - .saturating_add((1_701_000 as Weight).saturating_mul(r as Weight)) + (55_058_000 as Weight) + // Standard Error: 13_000 + .saturating_add((1_251_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64popcnt(r: u32, ) -> Weight { - (47_704_000 as Weight) - // Standard Error: 10_000 - .saturating_add((1_708_000 as Weight).saturating_mul(r as Weight)) + (55_308_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_229_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64eqz(r: u32, ) -> Weight { - (47_656_000 as Weight) - // Standard Error: 9_000 - .saturating_add((1_705_000 as Weight).saturating_mul(r as Weight)) + (55_303_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_231_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64extendsi32(r: u32, ) -> Weight { - (55_202_000 as Weight) + (44_768_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_229_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((1_428_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64extendui32(r: u32, ) -> Weight { - (55_193_000 as Weight) - // Standard Error: 9_000 - .saturating_add((1_223_000 as Weight).saturating_mul(r as Weight)) + (44_806_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_429_000 as Weight).saturating_mul(r as Weight)) } fn instr_i32wrapi64(r: u32, ) -> Weight { - (48_125_000 as Weight) - // Standard Error: 10_000 - .saturating_add((1_704_000 as Weight).saturating_mul(r as Weight)) + (55_388_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_228_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64eq(r: u32, ) -> Weight { - (49_162_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_241_000 as Weight).saturating_mul(r as Weight)) + (50_942_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_826_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ne(r: u32, ) -> Weight { - (48_635_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_262_000 as Weight).saturating_mul(r as Weight)) + (50_964_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_825_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64lts(r: u32, ) -> Weight { - (48_550_000 as Weight) - // Standard Error: 9_000 - .saturating_add((2_267_000 as Weight).saturating_mul(r as Weight)) + (50_749_000 as Weight) + // Standard Error: 10_000 + .saturating_add((1_830_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ltu(r: u32, ) -> Weight { - (49_135_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_219_000 as Weight).saturating_mul(r as Weight)) + (50_581_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_837_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64gts(r: u32, ) -> Weight { - (49_638_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_206_000 as Weight).saturating_mul(r as Weight)) + (50_720_000 as Weight) + // Standard Error: 10_000 + .saturating_add((1_831_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64gtu(r: u32, ) -> Weight { - (49_889_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_201_000 as Weight).saturating_mul(r as Weight)) + (50_686_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_832_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64les(r: u32, ) -> Weight { - (49_763_000 as Weight) - // Standard Error: 9_000 - .saturating_add((2_210_000 as Weight).saturating_mul(r as Weight)) + (50_914_000 as Weight) + // Standard Error: 12_000 + .saturating_add((1_828_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64leu(r: u32, ) -> Weight { - (49_607_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_207_000 as Weight).saturating_mul(r as Weight)) + (51_126_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_820_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ges(r: u32, ) -> Weight { - (49_664_000 as Weight) - // Standard Error: 9_000 - .saturating_add((2_213_000 as Weight).saturating_mul(r as Weight)) + (50_960_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_829_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64geu(r: u32, ) -> Weight { - (49_718_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_206_000 as Weight).saturating_mul(r as Weight)) + (50_796_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_831_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64add(r: u32, ) -> Weight { - (49_513_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_208_000 as Weight).saturating_mul(r as Weight)) + (51_011_000 as Weight) + // Standard Error: 12_000 + .saturating_add((1_826_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64sub(r: u32, ) -> Weight { - (49_837_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_201_000 as Weight).saturating_mul(r as Weight)) + (50_892_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_828_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64mul(r: u32, ) -> Weight { - (49_684_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_210_000 as Weight).saturating_mul(r as Weight)) + (50_980_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_825_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64divs(r: u32, ) -> Weight { - (48_749_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_872_000 as Weight).saturating_mul(r as Weight)) + (51_245_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_427_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64divu(r: u32, ) -> Weight { - (49_134_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_630_000 as Weight).saturating_mul(r as Weight)) + (51_083_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_218_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64rems(r: u32, ) -> Weight { - (48_981_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_861_000 as Weight).saturating_mul(r as Weight)) + (50_861_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_446_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64remu(r: u32, ) -> Weight { - (49_195_000 as Weight) - // Standard Error: 8_000 - .saturating_add((2_593_000 as Weight).saturating_mul(r as Weight)) + (51_001_000 as Weight) + // Standard Error: 10_000 + .saturating_add((2_183_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64and(r: u32, ) -> Weight { - (49_304_000 as Weight) - // Standard Error: 8_000 - .saturating_add((2_238_000 as Weight).saturating_mul(r as Weight)) + (50_819_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_829_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64or(r: u32, ) -> Weight { - (48_636_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_259_000 as Weight).saturating_mul(r as Weight)) + (51_368_000 as Weight) + // Standard Error: 12_000 + .saturating_add((1_814_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64xor(r: u32, ) -> Weight { - (48_761_000 as Weight) - // Standard Error: 8_000 - .saturating_add((2_262_000 as Weight).saturating_mul(r as Weight)) + (50_952_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_828_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64shl(r: u32, ) -> Weight { - (48_492_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_263_000 as Weight).saturating_mul(r as Weight)) + (50_996_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_827_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64shrs(r: u32, ) -> Weight { - (48_736_000 as Weight) - // Standard Error: 8_000 - .saturating_add((2_256_000 as Weight).saturating_mul(r as Weight)) + (51_061_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_826_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64shru(r: u32, ) -> Weight { - (48_675_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_256_000 as Weight).saturating_mul(r as Weight)) + (50_931_000 as Weight) + // Standard Error: 12_000 + .saturating_add((1_832_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64rotl(r: u32, ) -> Weight { - (48_703_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_257_000 as Weight).saturating_mul(r as Weight)) + (50_652_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_832_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64rotr(r: u32, ) -> Weight { - (48_758_000 as Weight) - // Standard Error: 7_000 - .saturating_add((2_259_000 as Weight).saturating_mul(r as Weight)) + (50_817_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_830_000 as Weight).saturating_mul(r as Weight)) } }