From 27599f9b56e721e00c22e4882deed92ccb73b3ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Thu, 16 Sep 2021 14:32:07 +0200 Subject: [PATCH 01/34] Frame no longer needs to be mutable (refactoring artifact) --- frame/contracts/src/exec.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 7ef1aec2dfc60..a43e91a376389 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -653,18 +653,20 @@ where // Additional work needs to be performed in case of an instantiation. if output.is_success() && entry_point == ExportedFunction::Constructor { - let frame = self.top_frame_mut(); - let account_id = frame.account_id.clone(); + let frame = self.top_frame(); // It is not allowed to terminate a contract inside its constructor. - if let CachedContract::Terminated = frame.contract_info { + if matches!(frame.contract_info, CachedContract::Terminated) { return Err(Error::::TerminatedInConstructor.into()) } // Deposit an instantiation event. deposit_event::( vec![], - Event::Instantiated { deployer: self.caller().clone(), contract: account_id }, + Event::Instantiated { + deployer: self.caller().clone(), + contract: frame.account_id.clone(), + }, ); } @@ -743,7 +745,7 @@ where return } if let CachedContract::Cached(contract) = &self.first_frame.contract_info { - >::insert(&self.first_frame.account_id, contract.clone()); + >::insert(&self.first_frame.account_id, contract); } if let Some(counter) = self.account_counter { >::set(counter); From d3abb6f51a44048294e29d8454d60da40d552c1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Fri, 17 Sep 2021 12:18:34 +0200 Subject: [PATCH 02/34] Remove Contract/Tombstone deposit --- frame/contracts/fixtures/drain.wat | 4 +- frame/contracts/src/benchmarking/mod.rs | 14 +- frame/contracts/src/exec.rs | 73 +++++----- frame/contracts/src/lib.rs | 63 ++------- frame/contracts/src/schedule.rs | 4 - frame/contracts/src/tests.rs | 173 ++++++++++++------------ frame/contracts/src/wasm/mod.rs | 48 ------- frame/contracts/src/wasm/runtime.rs | 68 +++------- 8 files changed, 156 insertions(+), 291 deletions(-) diff --git a/frame/contracts/fixtures/drain.wat b/frame/contracts/fixtures/drain.wat index 546026ac95986..3e2a68805a8af 100644 --- a/frame/contracts/fixtures/drain.wat +++ b/frame/contracts/fixtures/drain.wat @@ -33,7 +33,7 @@ ) ) - ;; Self-destruct by sending full balance to the 0 address. + ;; Try to self-destruct by sending full balance to the 0 address. (call $assert (i32.eq (call $seal_transfer @@ -42,7 +42,7 @@ (i32.const 0) ;; Pointer to the buffer with value to transfer (i32.const 8) ;; Length of the buffer with value to transfer ) - (i32.const 4) ;; ReturnCode::BelowSubsistenceThreshold + (i32.const 5) ;; ReturnCode::TransferFailed ) ) ) diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index 5c753c2d95558..423f29b02badd 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}, + traits::{Bounded, Hash, Saturating}, Perbill, }; use sp_std::{convert::TryInto, default::Default, vec, vec::Vec}; @@ -373,14 +373,6 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) - seal_tombstone_deposit { - let r in 0 .. API_BENCHMARK_BATCHES; - let instance = Contract::::new(WasmModule::getter( - "seal_tombstone_deposit", r * API_BENCHMARK_BATCH_SIZE - ), vec![])?; - let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) - seal_block_number { let r in 0 .. API_BENCHMARK_BATCHES; let instance = Contract::::new(WasmModule::getter( @@ -923,7 +915,7 @@ benchmarks! { .collect::>(); let account_len = accounts.get(0).map(|i| i.encode().len()).unwrap_or(0); let account_bytes = accounts.iter().flat_map(|x| x.encode()).collect(); - let value = Contracts::::subsistence_threshold(); + let value = T::Currency::minimum_balance(); assert!(value > 0u32.into()); let value_bytes = value.encode(); let value_len = value_bytes.len(); @@ -1111,7 +1103,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![]) - // We assume that every instantiate sends at least the subsistence amount. + // We assume that every instantiate sends at least the minimum balance. seal_instantiate { let r in 0 .. API_BENCHMARK_BATCHES; let hashes = (0..r * API_BENCHMARK_BATCH_SIZE) diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index a43e91a376389..6f24704cdd35f 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -168,9 +168,6 @@ pub trait Ext: sealing::Sealed { /// Returns the minimum balance that is required for creating an account. fn minimum_balance(&self) -> BalanceOf; - /// Returns the deposit required to instantiate a contract. - fn contract_deposit(&self) -> BalanceOf; - /// Returns a random number for the current block with the given subject. fn random(&self, subject: &[u8]) -> (SeedOf, BlockNumberOf); @@ -760,7 +757,7 @@ where /// subsistence threshold (for contracts) or the existential deposit (for plain accounts) /// results in an error. fn transfer( - sender_is_contract: bool, + sender_is_origin: bool, allow_death: bool, from: &T::AccountId, to: &T::AccountId, @@ -770,17 +767,17 @@ where return Ok(()) } - let existence_requirement = match (allow_death, sender_is_contract) { + let existence_requirement = match (allow_death, sender_is_origin) { (true, _) => ExistenceRequirement::AllowDeath, - (false, true) => { + (false, false) => { ensure!( - T::Currency::total_balance(from).saturating_sub(value) >= - Contracts::::subsistence_threshold(), - Error::::BelowSubsistenceThreshold, + T::Currency::free_balance(from).saturating_sub(value) >= + T::Currency::minimum_balance(), + Error::::TransferFailed, ); ExistenceRequirement::KeepAlive }, - (false, false) => ExistenceRequirement::KeepAlive, + (false, true) => ExistenceRequirement::KeepAlive, }; T::Currency::transfer(from, to, value, existence_requirement) @@ -793,13 +790,11 @@ where fn initial_transfer(&self) -> DispatchResult { let frame = self.top_frame(); let value = frame.value_transferred; - let subsistence_threshold = >::subsistence_threshold(); + let min_balance = T::Currency::minimum_balance(); - // If the value transferred to a new contract is less than the subsistence threshold - // we can error out early. This avoids executing the constructor in cases where - // we already know that the contract has too little balance. - if frame.entry_point == ExportedFunction::Constructor && value < subsistence_threshold { - return Err(>::NewContractNotFunded.into()) + // New contracts must receive at least the minimum balance as endowment. + if frame.entry_point == ExportedFunction::Constructor && value < min_balance { + return Err(>::EndowmentTooLow.into()) } Self::transfer(self.caller_is_origin(), false, self.caller(), &frame.account_id, value) @@ -807,7 +802,7 @@ where /// Wether the caller is the initiator of the call stack. fn caller_is_origin(&self) -> bool { - !self.frames.is_empty() + self.frames.is_empty() } /// Reference to the current (top) frame. @@ -941,7 +936,7 @@ where let info = frame.terminate(); Storage::::queue_trie_for_deletion(&info)?; >::transfer( - true, + false, true, &frame.account_id, beneficiary, @@ -957,7 +952,7 @@ where } fn transfer(&mut self, to: &T::AccountId, value: BalanceOf) -> DispatchResult { - Self::transfer(true, false, &self.top_frame().account_id, to, value) + Self::transfer(false, false, &self.top_frame().account_id, to, value) } fn get_storage(&mut self, key: &StorageKey) -> Option> { @@ -997,10 +992,6 @@ where T::Currency::minimum_balance() } - fn contract_deposit(&self) -> BalanceOf { - T::ContractDeposit::get() - } - fn deposit_event(&mut self, topics: Vec, data: Vec) { deposit_event::( topics, @@ -1354,7 +1345,7 @@ mod tests { ExtBuilder::default().build().execute_with(|| { set_balance(&origin, 0); - let result = MockStack::transfer(false, false, &origin, &dest, 100); + let result = MockStack::transfer(true, false, &origin, &dest, 100); assert_eq!(result, Err(Error::::TransferFailed.into())); assert_eq!(get_balance(&origin), 0); @@ -1457,19 +1448,19 @@ mod tests { // This one tests passing the input data into a contract via instantiate. ExtBuilder::default().build().execute_with(|| { let schedule = ::Schedule::get(); - let subsistence = Contracts::::subsistence_threshold(); + let min_balance = ::Currency::minimum_balance(); let mut gas_meter = GasMeter::::new(GAS_LIMIT); let executable = MockExecutable::from_storage(input_data_ch, &schedule, &mut gas_meter).unwrap(); - set_balance(&ALICE, subsistence * 10); + set_balance(&ALICE, min_balance * 10); let result = MockStack::run_instantiate( ALICE, executable, &mut gas_meter, &schedule, - subsistence * 3, + min_balance * 3, vec![1, 2, 3, 4], &[], None, @@ -1720,7 +1711,7 @@ mod tests { .instantiate( 0, dummy_ch, - Contracts::::subsistence_threshold() * 3, + ::Currency::minimum_balance() * 3, vec![], &[48, 49, 50], ) @@ -1733,7 +1724,7 @@ mod tests { ExtBuilder::default().existential_deposit(15).build().execute_with(|| { let schedule = ::Schedule::get(); - set_balance(&ALICE, Contracts::::subsistence_threshold() * 100); + set_balance(&ALICE, ::Currency::minimum_balance() * 100); place_contract(&BOB, instantiator_ch); assert_matches!( @@ -1776,7 +1767,7 @@ mod tests { ctx.ext.instantiate( 0, dummy_ch, - Contracts::::subsistence_threshold(), + ::Currency::minimum_balance(), vec![], &[], ), @@ -1906,18 +1897,18 @@ mod tests { // This one tests passing the input data into a contract via instantiate. ExtBuilder::default().build().execute_with(|| { let schedule = ::Schedule::get(); - let subsistence = Contracts::::subsistence_threshold(); + 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, subsistence * 10); + set_balance(&ALICE, min_balance * 10); let result = MockStack::run_instantiate( ALICE, executable, &mut gas_meter, &schedule, - subsistence * 3, + min_balance * 3, vec![], &[], None, @@ -1937,10 +1928,10 @@ mod tests { let mut debug_buffer = Vec::new(); ExtBuilder::default().build().execute_with(|| { - let subsistence = Contracts::::subsistence_threshold(); + let min_balance = ::Currency::minimum_balance(); let schedule = ::Schedule::get(); let mut gas_meter = GasMeter::::new(GAS_LIMIT); - set_balance(&ALICE, subsistence * 10); + set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); MockStack::run_call( ALICE, @@ -1968,10 +1959,10 @@ mod tests { let mut debug_buffer = Vec::new(); ExtBuilder::default().build().execute_with(|| { - let subsistence = Contracts::::subsistence_threshold(); + let min_balance = ::Currency::minimum_balance(); let schedule = ::Schedule::get(); let mut gas_meter = GasMeter::::new(GAS_LIMIT); - set_balance(&ALICE, subsistence * 10); + set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); let result = MockStack::run_call( ALICE, @@ -2078,10 +2069,10 @@ mod tests { }); ExtBuilder::default().build().execute_with(|| { - let subsistence = Contracts::::subsistence_threshold(); + let min_balance = ::Currency::minimum_balance(); let schedule = ::Schedule::get(); let mut gas_meter = GasMeter::::new(GAS_LIMIT); - set_balance(&ALICE, subsistence * 10); + set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); System::reset_events(); MockStack::run_call(ALICE, BOB, &mut gas_meter, &schedule, 0, vec![], None).unwrap(); @@ -2135,10 +2126,10 @@ mod tests { }); ExtBuilder::default().build().execute_with(|| { - let subsistence = Contracts::::subsistence_threshold(); + let min_balance = ::Currency::minimum_balance(); let schedule = ::Schedule::get(); let mut gas_meter = GasMeter::::new(GAS_LIMIT); - set_balance(&ALICE, subsistence * 10); + set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); System::reset_events(); MockStack::run_call(ALICE, BOB, &mut gas_meter, &schedule, 0, vec![], None).unwrap(); diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 62b74b9b7b954..d70edad2dc007 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -122,7 +122,7 @@ use pallet_contracts_primitives::{ GetStorageResult, InstantiateReturnValue, }; use sp_core::{crypto::UncheckedFrom, Bytes}; -use sp_runtime::traits::{Convert, Hash, Saturating, StaticLookup}; +use sp_runtime::traits::{Convert, Hash, StaticLookup}; use sp_std::prelude::*; type CodeHash = ::Hash; @@ -165,12 +165,6 @@ pub mod pallet { /// This is applied in **addition** to [`frame_system::Config::BaseCallFilter`]. /// It is recommended to treat this as a whitelist. /// - /// # Subsistence Threshold - /// - /// The runtime **must** make sure that any allowed dispatchable makes sure that the - /// `total_balance` of the contract stays above [`Pallet::subsistence_threshold()`]. - /// Otherwise users could clutter the storage with contracts. - /// /// # Stability /// /// The runtime **must** make sure that all dispatchables that are callable by @@ -201,13 +195,6 @@ pub mod pallet { #[pallet::constant] type Schedule: Get>; - /// The deposit that must be placed into the contract's account to instantiate it. - /// This is in **addition** to the [`pallet_balances::Pallet::ExistenialDeposit`]. - /// The minimum balance for a contract's account can be queried using - /// [`Pallet::subsistence_threshold`]. - #[pallet::constant] - type ContractDeposit: Get>; - /// The type of the call stack determines the maximum nesting depth of contract calls. /// /// The allowed depth is `CallStack::size() + 1`. @@ -417,16 +404,20 @@ pub mod pallet { OutOfGas, /// The output buffer supplied to a contract API call was too small. OutputBufferTooSmall, - /// Performing the requested transfer would have brought the contract below - /// the subsistence threshold. No transfer is allowed to do this. Use `seal_terminate` - /// to recover a deposit. - BelowSubsistenceThreshold, - /// The newly created contract is below the subsistence threshold after executing - /// its contructor. No contracts are allowed to exist below that threshold. - NewContractNotFunded, - /// Performing the requested transfer failed for a reason originating in the - /// chosen currency implementation of the runtime. Most probably the balance is - /// too low or locks are placed on it. + /// When creating a new contract at least the minimum balance must be provided + /// as endowment. If that is not the case this error is returned. + EndowmentTooLow, + /// Performing the requested transfer failed. Most probably the transfer would have + /// brought the contract's account below the minimum balance. Other possible reasons + /// are balance locks or reservations on the contract's account. + /// + /// # Note + /// + /// Any transfer must leave the minimum balance as **free** balance in the contract. + /// This is different from a usual keep alive transfer where the reserved balance + /// also counts into the minimum balance. This is enforced because otherwise a refund + /// of a storage deposit could remove a contract's account as it was only kept alive + /// by this deposit. TransferFailed, /// Performing a call was denied because the calling depth reached the limit /// of what is specified in the schedule. @@ -636,30 +627,6 @@ where UncheckedFrom::unchecked_from(T::Hashing::hash(&buf)) } - /// Subsistence threshold is the extension of the minimum balance (aka existential deposit) - /// by the contract deposit. It is the minimum balance any contract must hold. - /// - /// Any contract initiated balance transfer mechanism cannot make the balance lower - /// than the subsistence threshold. The only way to recover the balance is to remove - /// contract using `seal_terminate`. - pub fn subsistence_threshold() -> BalanceOf { - T::Currency::minimum_balance().saturating_add(T::ContractDeposit::get()) - } - - /// The in-memory size in bytes of the data structure associated with each contract. - /// - /// The data structure is also put into storage for each contract. The in-storage size - /// is never larger than the in-memory representation and usually smaller due to compact - /// encoding and lack of padding. - /// - /// # Note - /// - /// This returns the in-memory size because the in-storage size (SCALE encoded) cannot - /// be efficiently determined. Treat this as an upper bound of the in-storage size. - pub fn contract_info_size() -> u32 { - sp_std::mem::size_of::>() as u32 - } - /// 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 { diff --git a/frame/contracts/src/schedule.rs b/frame/contracts/src/schedule.rs index c14165b4c6aec..b9acc9d49204f 100644 --- a/frame/contracts/src/schedule.rs +++ b/frame/contracts/src/schedule.rs @@ -271,9 +271,6 @@ pub struct HostFnWeights { /// Weight of calling `seal_minimum_balance`. pub minimum_balance: Weight, - /// Weight of calling `seal_contract_deposit`. - pub contract_deposit: Weight, - /// Weight of calling `seal_block_number`. pub block_number: Weight, @@ -561,7 +558,6 @@ impl Default for HostFnWeights { balance: cost_batched!(seal_balance), value_transferred: cost_batched!(seal_value_transferred), minimum_balance: cost_batched!(seal_minimum_balance), - contract_deposit: cost_batched!(seal_tombstone_deposit), block_number: cost_batched!(seal_block_number), now: cost_batched!(seal_now), weight_to_fee: cost_batched!(seal_weight_to_fee), diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index bd5dbae5b34a6..bf331e321d7fc 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -73,7 +73,7 @@ pub mod test_utils { use crate::{ exec::{AccountIdOf, StorageKey}, storage::Storage, - AccountCounter, CodeHash, ContractInfoOf, Pallet as Contracts, TrieId, + AccountCounter, CodeHash, Config, ContractInfoOf, TrieId, }; use frame_support::traits::Currency; @@ -94,7 +94,7 @@ pub mod test_utils { } pub fn place_contract(address: &AccountIdOf, code_hash: CodeHash) { let trie_id = generate_trie_id(address); - set_balance(address, Contracts::::subsistence_threshold() * 10); + set_balance(address, ::Currency::minimum_balance() * 10); let contract = Storage::::new_contract(&address, trie_id, code_hash).unwrap(); >::insert(address, contract); } @@ -248,7 +248,6 @@ impl pallet_utility::Config for Test { type WeightInfo = (); } parameter_types! { - pub const ContractDeposit: u64 = 16; pub const MaxValueSize: u32 = 16_384; pub const DeletionQueueDepth: u32 = 1024; pub const DeletionWeightLimit: Weight = 500_000_000_000; @@ -289,7 +288,6 @@ impl Config for Test { type Event = Event; type Call = Call; type CallFilter = TestFilter; - type ContractDeposit = ContractDeposit; type CallStack = [Frame; 31]; type WeightPrice = Self; type WeightInfo = (); @@ -429,12 +427,12 @@ fn instantiate_and_call_and_deposit_event() { ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); - let subsistence = Pallet::::subsistence_threshold(); + let min_balance = ::Currency::minimum_balance(); // Check at the end to get hash on error easily let creation = Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 100, + min_balance * 100, GAS_LIMIT, wasm, vec![], @@ -469,7 +467,7 @@ fn instantiate_and_call_and_deposit_event() { phase: Phase::Initialization, event: Event::Balances(pallet_balances::Event::Endowed( addr.clone(), - subsistence * 100 + min_balance * 100 )), topics: vec![], }, @@ -478,7 +476,7 @@ fn instantiate_and_call_and_deposit_event() { event: Event::Balances(pallet_balances::Event::Transfer( ALICE, addr.clone(), - subsistence * 100 + min_balance * 100 )), topics: vec![], }, @@ -556,14 +554,13 @@ fn deposit_event_max_value_limit() { #[test] fn run_out_of_gas() { let (wasm, code_hash) = compile_module::("run_out_of_gas").unwrap(); - let subsistence = Pallet::::subsistence_threshold(); - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); let _ = Balances::deposit_creating(&ALICE, 1_000_000); assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - 100 * subsistence, + 100 * min_balance, GAS_LIMIT, wasm, vec![], @@ -921,12 +918,12 @@ fn crypto_hashes() { fn transfer_return_code() { let (wasm, code_hash) = compile_module::("transfer_return_code").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let subsistence = Pallet::::subsistence_threshold(); - let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); + let min_balance = ::Currency::minimum_balance(); + let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 100, + min_balance * 100, GAS_LIMIT, wasm, vec![], @@ -934,18 +931,18 @@ fn transfer_return_code() { ),); let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); - // Contract has only the minimal balance so any transfer will return BelowSubsistence. - Balances::make_free_balance_be(&addr, subsistence); + // 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) .result .unwrap(); - assert_return_code!(result, RuntimeReturnCode::BelowSubsistenceThreshold); + assert_return_code!(result, RuntimeReturnCode::TransferFailed); - // Contract has enough total balance in order to not go below the subsistence + // Contract has enough total balance in order to not go below the min balance // threshold when transfering 100 balance but this balance is reserved so - // the transfer still fails but with another return code. - Balances::make_free_balance_be(&addr, subsistence + 100); - Balances::reserve(&addr, subsistence + 100).unwrap(); + // 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(); assert_return_code!(result, RuntimeReturnCode::TransferFailed); }); @@ -956,20 +953,20 @@ fn call_return_code() { let (caller_code, caller_hash) = compile_module::("call_return_code").unwrap(); let (callee_code, callee_hash) = compile_module::("ok_trap_revert").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let subsistence = Pallet::::subsistence_threshold(); - let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); - let _ = Balances::deposit_creating(&CHARLIE, 1000 * subsistence); + let min_balance = ::Currency::minimum_balance(); + let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = Balances::deposit_creating(&CHARLIE, 1000 * min_balance); assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 100, + min_balance * 100, GAS_LIMIT, caller_code, vec![0], vec![], ),); let addr_bob = Contracts::contract_address(&ALICE, &caller_hash, &[]); - Balances::make_free_balance_be(&addr_bob, subsistence); + Balances::make_free_balance_be(&addr_bob, min_balance); // Contract calls into Django which is no valid contract let result = Contracts::bare_call( @@ -986,16 +983,16 @@ fn call_return_code() { assert_ok!(Contracts::instantiate_with_code( Origin::signed(CHARLIE), - subsistence * 100, + min_balance * 100, GAS_LIMIT, callee_code, vec![0], vec![], ),); let addr_django = Contracts::contract_address(&CHARLIE, &callee_hash, &[]); - Balances::make_free_balance_be(&addr_django, subsistence); + Balances::make_free_balance_be(&addr_django, min_balance); - // Contract has only the minimal balance so any transfer will return BelowSubsistence. + // Contract has only the minimal balance so any transfer will fail. let result = Contracts::bare_call( ALICE, addr_bob.clone(), @@ -1010,13 +1007,13 @@ fn call_return_code() { ) .result .unwrap(); - assert_return_code!(result, RuntimeReturnCode::BelowSubsistenceThreshold); + assert_return_code!(result, RuntimeReturnCode::TransferFailed); - // Contract has enough total balance in order to not go below the subsistence + // Contract has enough total balance in order to not go below the min balance // threshold when transfering 100 balance but this balance is reserved so - // the transfer still fails but with another return code. - Balances::make_free_balance_be(&addr_bob, subsistence + 100); - Balances::reserve(&addr_bob, subsistence + 100).unwrap(); + // the transfer still fails. + Balances::make_free_balance_be(&addr_bob, min_balance + 100); + Balances::reserve(&addr_bob, min_balance + 100).unwrap(); let result = Contracts::bare_call( ALICE, addr_bob.clone(), @@ -1034,7 +1031,7 @@ fn call_return_code() { assert_return_code!(result, RuntimeReturnCode::TransferFailed); // Contract has enough balance but callee reverts because "1" is passed. - Balances::make_free_balance_be(&addr_bob, subsistence + 1000); + Balances::make_free_balance_be(&addr_bob, min_balance + 1000); let result = Contracts::bare_call( ALICE, addr_bob.clone(), @@ -1075,14 +1072,14 @@ fn instantiate_return_code() { let (caller_code, caller_hash) = compile_module::("instantiate_return_code").unwrap(); let (callee_code, callee_hash) = compile_module::("ok_trap_revert").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let subsistence = Pallet::::subsistence_threshold(); - let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); - let _ = Balances::deposit_creating(&CHARLIE, 1000 * subsistence); + let min_balance = ::Currency::minimum_balance(); + let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = Balances::deposit_creating(&CHARLIE, 1000 * min_balance); let callee_hash = callee_hash.as_ref().to_vec(); assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 100, + min_balance * 100, GAS_LIMIT, callee_code, vec![], @@ -1091,7 +1088,7 @@ fn instantiate_return_code() { assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 100, + min_balance * 100, GAS_LIMIT, caller_code, vec![], @@ -1099,19 +1096,19 @@ fn instantiate_return_code() { ),); let addr = Contracts::contract_address(&ALICE, &caller_hash, &[]); - // Contract has only the minimal balance so any transfer will return BelowSubsistence. - Balances::make_free_balance_be(&addr, subsistence); + // 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(); - assert_return_code!(result, RuntimeReturnCode::BelowSubsistenceThreshold); + assert_return_code!(result, RuntimeReturnCode::TransferFailed); - // Contract has enough total balance in order to not go below the subsistence + // Contract has enough total balance in order to not go below the min_balance // threshold when transfering the balance but this balance is reserved so - // the transfer still fails but with another return code. - Balances::make_free_balance_be(&addr, subsistence + 10_000); - Balances::reserve(&addr, subsistence + 10_000).unwrap(); + // 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 @@ -1119,7 +1116,7 @@ fn instantiate_return_code() { assert_return_code!(result, RuntimeReturnCode::TransferFailed); // Contract has enough balance but the passed code hash is invalid - Balances::make_free_balance_be(&addr, subsistence + 10_000); + 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(); @@ -1157,13 +1154,13 @@ fn instantiate_return_code() { fn disabled_chain_extension_wont_deploy() { let (code, _hash) = compile_module::("chain_extension").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let subsistence = Pallet::::subsistence_threshold(); - let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); + let min_balance = ::Currency::minimum_balance(); + let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); TestExtension::disable(); assert_err_ignore_postinfo!( Contracts::instantiate_with_code( Origin::signed(ALICE), - 3 * subsistence, + 3 * min_balance, GAS_LIMIT, code, vec![], @@ -1178,11 +1175,11 @@ fn disabled_chain_extension_wont_deploy() { fn disabled_chain_extension_errors_on_call() { let (code, hash) = compile_module::("chain_extension").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let subsistence = Pallet::::subsistence_threshold(); - let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); + let min_balance = ::Currency::minimum_balance(); + let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 100, + min_balance * 100, GAS_LIMIT, code, vec![], @@ -1201,11 +1198,11 @@ fn disabled_chain_extension_errors_on_call() { fn chain_extension_works() { let (code, hash) = compile_module::("chain_extension").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let subsistence = Pallet::::subsistence_threshold(); - let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); + let min_balance = ::Currency::minimum_balance(); + let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 100, + min_balance * 100, GAS_LIMIT, code, vec![], @@ -1248,12 +1245,12 @@ fn chain_extension_works() { fn lazy_removal_works() { let (code, hash) = compile_module::("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let subsistence = Pallet::::subsistence_threshold(); - let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); + let min_balance = ::Currency::minimum_balance(); + let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 100, + min_balance * 100, GAS_LIMIT, code, vec![], @@ -1299,12 +1296,12 @@ fn lazy_removal_partial_remove_works() { let mut ext = ExtBuilder::default().existential_deposit(50).build(); let trie = ext.execute_with(|| { - let subsistence = Pallet::::subsistence_threshold(); - let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); + let min_balance = ::Currency::minimum_balance(); + let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 100, + min_balance * 100, GAS_LIMIT, code, vec![], @@ -1369,12 +1366,12 @@ fn lazy_removal_partial_remove_works() { fn lazy_removal_does_no_run_on_full_block() { let (code, hash) = compile_module::("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let subsistence = Pallet::::subsistence_threshold(); - let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); + let min_balance = ::Currency::minimum_balance(); + let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 100, + min_balance * 100, GAS_LIMIT, code, vec![], @@ -1444,12 +1441,12 @@ fn lazy_removal_does_not_use_all_weight() { let mut ext = ExtBuilder::default().existential_deposit(50).build(); let (trie, vals, weight_per_key) = ext.execute_with(|| { - let subsistence = Pallet::::subsistence_threshold(); - let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); + let min_balance = ::Currency::minimum_balance(); + let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 100, + min_balance * 100, GAS_LIMIT, code, vec![], @@ -1509,12 +1506,12 @@ fn lazy_removal_does_not_use_all_weight() { fn deletion_queue_full() { let (code, hash) = compile_module::("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let subsistence = Pallet::::subsistence_threshold(); - let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); + let min_balance = ::Currency::minimum_balance(); + let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 100, + min_balance * 100, GAS_LIMIT, code, vec![], @@ -1542,12 +1539,12 @@ fn refcounter() { let (wasm, code_hash) = compile_module::("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); - let subsistence = Pallet::::subsistence_threshold(); + let min_balance = ::Currency::minimum_balance(); // Create two contracts with the same code and check that they do in fact share it. assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 100, + min_balance * 100, GAS_LIMIT, wasm.clone(), vec![], @@ -1555,7 +1552,7 @@ fn refcounter() { )); assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 100, + min_balance * 100, GAS_LIMIT, wasm.clone(), vec![], @@ -1566,7 +1563,7 @@ fn refcounter() { // Sharing should also work with the usual instantiate call assert_ok!(Contracts::instantiate( Origin::signed(ALICE), - subsistence * 100, + min_balance * 100, GAS_LIMIT, code_hash, vec![], @@ -1605,13 +1602,13 @@ fn reinstrument_does_charge() { let (wasm, code_hash) = compile_module::("return_with_data").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); - let subsistence = Pallet::::subsistence_threshold(); + let min_balance = ::Currency::minimum_balance(); let zero = 0u32.to_le_bytes().encode(); let code_len = wasm.len() as u32; assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 100, + min_balance * 100, GAS_LIMIT, wasm, zero.clone(), @@ -1719,13 +1716,13 @@ fn gas_estimation_nested_call_fixed_limit() { let (caller_code, caller_hash) = compile_module::("call_with_limit").unwrap(); let (callee_code, callee_hash) = compile_module::("dummy").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let subsistence = Pallet::::subsistence_threshold(); - let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); - let _ = Balances::deposit_creating(&CHARLIE, 1000 * subsistence); + let min_balance = ::Currency::minimum_balance(); + let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = Balances::deposit_creating(&CHARLIE, 1000 * min_balance); assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 100, + min_balance * 100, GAS_LIMIT, caller_code, vec![], @@ -1735,7 +1732,7 @@ fn gas_estimation_nested_call_fixed_limit() { assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 100, + min_balance * 100, GAS_LIMIT, callee_code, vec![], @@ -1769,13 +1766,13 @@ fn gas_estimation_call_runtime() { let (caller_code, caller_hash) = compile_module::("call_runtime").unwrap(); let (callee_code, callee_hash) = compile_module::("dummy").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let subsistence = Pallet::::subsistence_threshold(); - let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); - let _ = Balances::deposit_creating(&CHARLIE, 1000 * subsistence); + let min_balance = ::Currency::minimum_balance(); + let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = Balances::deposit_creating(&CHARLIE, 1000 * min_balance); assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 100, + min_balance * 100, GAS_LIMIT, caller_code, vec![], @@ -1785,7 +1782,7 @@ fn gas_estimation_call_runtime() { assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - subsistence * 100, + min_balance * 100, GAS_LIMIT, callee_code, vec![], diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index 855cb6e45091f..802b5db85902e 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -389,9 +389,6 @@ mod tests { fn minimum_balance(&self) -> u64 { 666 } - fn contract_deposit(&self) -> u64 { - 16 - } fn random(&self, subject: &[u8]) -> (SeedOf, BlockNumberOf) { (H256::from_slice(subject), 42) } @@ -1395,51 +1392,6 @@ mod tests { assert_ok!(execute(CODE_MINIMUM_BALANCE, vec![], MockExt::default())); } - const CODE_CONTRACT_DEPOSIT: &str = r#" -(module - (import "seal0" "seal_contract_deposit" (func $seal_contract_deposit (param i32 i32))) - (import "env" "memory" (memory 1 1)) - - ;; size of our buffer is 32 bytes - (data (i32.const 32) "\20") - - (func $assert (param i32) - (block $ok - (br_if $ok - (get_local 0) - ) - (unreachable) - ) - ) - - (func (export "call") - (call $seal_contract_deposit (i32.const 0) (i32.const 32)) - - ;; assert len == 8 - (call $assert - (i32.eq - (i32.load (i32.const 32)) - (i32.const 8) - ) - ) - - ;; assert that contents of the buffer is equal to the i64 value of 16. - (call $assert - (i64.eq - (i64.load (i32.const 0)) - (i64.const 16) - ) - ) - ) - (func (export "deploy")) -) -"#; - - #[test] - fn contract_deposit() { - assert_ok!(execute(CODE_CONTRACT_DEPOSIT, vec![], MockExt::default())); - } - const CODE_RANDOM: &str = r#" (module (import "seal0" "seal_random" (func $seal_random (param i32 i32 i32 i32))) diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 52b864bf18eac..602f55bfdb9b4 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -31,7 +31,7 @@ use pallet_contracts_primitives::{ExecReturnValue, ReturnFlags}; use pwasm_utils::parity_wasm::elements::ValueType; use sp_core::{crypto::UncheckedFrom, Bytes}; use sp_io::hashing::{blake2_128, blake2_256, keccak_256, sha2_256}; -use sp_runtime::traits::Bounded; +use sp_runtime::traits::{Bounded, Zero}; use sp_std::prelude::*; /// Every error that can be returned to a contract when it calls any of the host functions. @@ -54,15 +54,12 @@ pub enum ReturnCode { CalleeReverted = 2, /// The passed key does not exist in storage. KeyNotFound = 3, - /// Transfer failed because it would have brought the sender's total balance below the - /// subsistence threshold. - BelowSubsistenceThreshold = 4, - /// Transfer failed for other reasons. Most probably reserved or locked balance of the - /// sender prevents the transfer. + /// Deprecated and no longer returned: There is only the minimum balance. + _BelowSubsistenceThreshold = 4, + /// See [`Error::TransferFailed`]. TransferFailed = 5, - /// The newly created contract is below the subsistence threshold after executing - /// its constructor. - NewContractNotFunded = 6, + /// See [`Error::EndowmentTooLow`]. + EndowmentTooLow = 6, /// No code could be found at the supplied code hash. CodeNotFound = 7, /// The contract that was called is no contract (a plain account). @@ -150,8 +147,6 @@ pub enum RuntimeCosts { ValueTransferred, /// Weight of calling `seal_minimum_balance`. MinimumBalance, - /// Weight of calling `seal_contract_deposit`. - ContractDeposit, /// Weight of calling `seal_block_number`. BlockNumber, /// Weight of calling `seal_now`. @@ -230,7 +225,6 @@ impl RuntimeCosts { Balance => s.balance, ValueTransferred => s.value_transferred, MinimumBalance => s.minimum_balance, - ContractDeposit => s.contract_deposit, BlockNumber => s.block_number, Now => s.now, WeightToFee => s.weight_to_fee, @@ -606,16 +600,14 @@ where fn err_into_return_code(from: DispatchError) -> Result { use ReturnCode::*; - let below_sub = Error::::BelowSubsistenceThreshold.into(); let transfer_failed = Error::::TransferFailed.into(); - let not_funded = Error::::NewContractNotFunded.into(); + let endowment_too_low = Error::::EndowmentTooLow.into(); let no_code = Error::::CodeNotFound.into(); let not_found = Error::::ContractNotFound.into(); match from { - x if x == below_sub => Ok(BelowSubsistenceThreshold), x if x == transfer_failed => Ok(TransferFailed), - x if x == not_funded => Ok(NewContractNotFunded), + x if x == endowment_too_low => Ok(EndowmentTooLow), x if x == no_code => Ok(CodeNotFound), x if x == not_found => Ok(NotCallable), err => Err(err), @@ -832,7 +824,6 @@ define_env!(Env, , // // # Errors // - // `ReturnCode::BelowSubsistenceThreshold` // `ReturnCode::TransferFailed` [seal0] seal_transfer( ctx, @@ -920,7 +911,6 @@ define_env!(Env, , // // `ReturnCode::CalleeReverted`: Output buffer is returned. // `ReturnCode::CalleeTrapped` - // `ReturnCode::BelowSubsistenceThreshold` // `ReturnCode::TransferFailed` // `ReturnCode::NotCallable` [__unstable__] seal_call( @@ -997,9 +987,8 @@ define_env!(Env, , // length to `output_len_ptr`. The copy of the output buffer and address can be skipped by // supplying the sentinel value of `u32::MAX` to `output_ptr` or `address_ptr`. // - // After running the constructor it is verified that the contract account holds at - // least the subsistence threshold. If that is not the case the instantiation fails and - // the contract is not created. + // `value` must be at least the minimum balance. Otherwise the instantiation fails and the + // contract is not created. // // # Parameters // @@ -1028,9 +1017,8 @@ define_env!(Env, , // // `ReturnCode::CalleeReverted`: Output buffer is returned. // `ReturnCode::CalleeTrapped` - // `ReturnCode::BelowSubsistenceThreshold` // `ReturnCode::TransferFailed` - // `ReturnCode::NewContractNotFunded` + // `ReturnCode::EndowmentTooLow` // `ReturnCode::CodeNotFound` [seal1] seal_instantiate( ctx, @@ -1210,7 +1198,7 @@ define_env!(Env, , )?) }, - // Stores the balance of the current account into the supplied buffer. + // Stores the **free* balance of the current account into the supplied buffer. // // The value is stored to linear memory at the address pointed to by `out_ptr`. // `out_len_ptr` must point to a u32 value that describes the available space at @@ -1318,38 +1306,20 @@ define_env!(Env, , )?) }, - // Stores the contract deposit into the supplied buffer. - // - // # Deprecation - // - // This is equivalent to calling `seal_contract_deposit` and only exists for backwards - // compatibility. See that function for documentation. - [seal0] seal_tombstone_deposit(ctx, out_ptr: u32, out_len_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::ContractDeposit)?; - Ok(ctx.write_sandbox_output( - out_ptr, out_len_ptr, &ctx.ext.contract_deposit().encode(), false, already_charged - )?) - }, - - // Stores the contract deposit into the supplied buffer. + // Stores the tombstone deposit into the supplied buffer. // // The value is stored to linear memory at the address pointed to by `out_ptr`. // `out_len_ptr` must point to a u32 value that describes the available space at // `out_ptr`. This call overwrites it with the size of the value. If the available // space at `out_ptr` is less than the size of the value a trap is triggered. // - // The data is encoded as T::Balance. - // - // # Note + // # Deprecation // - // The contract deposit is on top of the existential deposit. The sum - // is commonly referred as subsistence threshold in code. No contract initiated - // balance transfer can go below this threshold. - [seal0] seal_contract_deposit(ctx, out_ptr: u32, out_len_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::ContractDeposit)?; - Ok(ctx.write_sandbox_output( - out_ptr, out_len_ptr, &ctx.ext.contract_deposit().encode(), false, already_charged - )?) + // There is no longer a tombstone deposit. This function always returns 0. + [seal0] seal_tombstone_deposit(ctx, out_ptr: u32, out_len_ptr: u32) => { + ctx.charge_gas(RuntimeCosts::Balance)?; + let deposit = >::zero().encode(); + Ok(ctx.write_sandbox_output(out_ptr, out_len_ptr, &deposit, false, already_charged)?) }, // Was used to restore the given destination contract sacrificing the caller. From 8b92865b6e12b2464da33daca602beca7d3cc39d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Mon, 20 Sep 2021 12:02:39 +0200 Subject: [PATCH 03/34] Add StorageMeter --- Cargo.lock | 1 + bin/node/executor/Cargo.toml | 1 + bin/node/executor/tests/basic.rs | 6 +- bin/node/executor/tests/common.rs | 2 +- bin/node/runtime/src/lib.rs | 28 +- frame/contracts/common/src/lib.rs | 125 +- frame/contracts/rpc/runtime-api/src/lib.rs | 18 +- frame/contracts/rpc/src/lib.rs | 143 +- frame/contracts/src/benchmarking/mod.rs | 251 +-- frame/contracts/src/exec.rs | 378 +++-- frame/contracts/src/lib.rs | 233 ++- frame/contracts/src/migration.rs | 33 +- frame/contracts/src/storage.rs | 60 +- frame/contracts/src/storage/meter.rs | 359 +++++ frame/contracts/src/tests.rs | 482 ++++-- frame/contracts/src/wasm/code_cache.rs | 242 ++- frame/contracts/src/wasm/mod.rs | 143 +- frame/contracts/src/wasm/prepare.rs | 53 +- frame/contracts/src/wasm/runtime.rs | 2 +- frame/contracts/src/weights.rs | 1660 ++++++++++---------- 20 files changed, 2744 insertions(+), 1476 deletions(-) create mode 100644 frame/contracts/src/storage/meter.rs diff --git a/Cargo.lock b/Cargo.lock index eeb1012b1d1d3..e3d6b901e3e3c 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)) } } From b91e08a72164bf451719a80e891106b138aaaa06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Sun, 7 Nov 2021 14:11:52 +0100 Subject: [PATCH 04/34] cargo fmt --- frame/contracts/common/src/lib.rs | 51 ++++++++++++++++++------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/frame/contracts/common/src/lib.rs b/frame/contracts/common/src/lib.rs index a7cf39ec70e20..cef739f46b4ef 100644 --- a/frame/contracts/common/src/lib.rs +++ b/frame/contracts/common/src/lib.rs @@ -22,7 +22,10 @@ use bitflags::bitflags; use codec::{Decode, Encode}; use sp_core::Bytes; -use sp_runtime::{traits::Zero, DispatchError, RuntimeDebug, traits::Saturating}; +use sp_runtime::{ + traits::{Saturating, Zero}, + DispatchError, RuntimeDebug, +}; use sp_std::prelude::*; #[cfg(feature = "std")] @@ -192,7 +195,7 @@ impl StorageDeposit { impl StorageDeposit where - Balance: Saturating + Ord + Copy + Balance: Saturating + Ord + Copy, { /// This is essentially a saturating signed add. pub fn saturating_add(&self, rhs: &Self) -> Self { @@ -200,16 +203,18 @@ where 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)) - }, + (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)) + }, } } @@ -219,16 +224,18 @@ where 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)) - }, + (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)) + }, } } From bc06dbd81013ad507a45f016ed9e41e3df418b55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Sun, 7 Nov 2021 14:15:39 +0100 Subject: [PATCH 05/34] Fix weight annotation --- frame/contracts/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index a4bbd162a1aa6..0e0f5bb522782 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -409,7 +409,7 @@ pub mod pallet { /// 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)] + #[pallet::weight(T::WeightInfo::upload_code(code.len() as u32 / 1024))] pub fn upload_code( origin: OriginFor, code: Vec, @@ -423,7 +423,7 @@ pub mod pallet { /// /// 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)] + #[pallet::weight(T::WeightInfo::remove_code())] pub fn remove_code( origin: OriginFor, code_hash: CodeHash, From 77015a2a01333b61deaa8a25d20a28bde5fbadb8 Mon Sep 17 00:00:00 2001 From: Parity Bot Date: Fri, 19 Nov 2021 12:34:50 +0000 Subject: [PATCH 06/34] cargo run --quiet --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_contracts --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/contracts/src/weights.rs --template=./.maintain/frame-weight-template.hbs --- frame/contracts/src/weights.rs | 1127 ++++++++++++++++---------------- 1 file changed, 559 insertions(+), 568 deletions(-) diff --git a/frame/contracts/src/weights.rs b/frame/contracts/src/weights.rs index 0e205efae4e27..504ee9ebde03a 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-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2021-11-19, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: @@ -35,7 +35,6 @@ // --output=./frame/contracts/src/weights.rs // --template=./.maintain/frame-weight-template.hbs - #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] @@ -151,77 +150,77 @@ pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { // Storage: Contracts DeletionQueue (r:1 w:0) fn on_initialize() -> Weight { - (2_969_000 as Weight) + (2_832_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_184_000 as Weight).saturating_mul(k as Weight)) + // Standard Error: 3_000 + .saturating_add((2_197_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_477_000 as Weight) + (92_378_000 as Weight) // Standard Error: 2_000 - .saturating_add((319_000 as Weight).saturating_mul(q as Weight)) + .saturating_add((328_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 { - (30_226_000 as Weight) - // Standard Error: 93_000 - .saturating_add((74_603_000 as Weight).saturating_mul(c as Weight)) + (27_381_000 as Weight) + // Standard Error: 92_000 + .saturating_add((90_220_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 { - (8_062_000 as Weight) + (7_710_000 as Weight) // Standard Error: 0 - .saturating_add((1_455_000 as Weight).saturating_mul(c as Weight)) + .saturating_add((1_457_000 as Weight).saturating_mul(c as Weight)) .saturating_add(T::DbWeight::get().reads(1 as Weight)) } // Storage: Contracts CodeStorage (r:1 w:1) - // Storage: Contracts AccountCounter (r:1 w:0) + // Storage: Contracts AccountCounter (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Timestamp Now (r:1 w:0) // Storage: System Account (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 { - (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)) + (559_294_000 as Weight) + // Standard Error: 171_000 + .saturating_add((206_170_000 as Weight).saturating_mul(c as Weight)) + // Standard Error: 11_000 + .saturating_add((2_107_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)) + .saturating_add(T::DbWeight::get().writes(6 as Weight)) } // Storage: Contracts CodeStorage (r:1 w:1) - // Storage: Contracts AccountCounter (r:1 w:0) + // Storage: Contracts AccountCounter (r:1 w:1) // 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 { - (263_013_000 as Weight) + (236_193_000 as Weight) // Standard Error: 2_000 - .saturating_add((1_991_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((2_022_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)) + .saturating_add(T::DbWeight::get().writes(5 as Weight)) } // 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:1) fn call() -> Weight { - (210_423_000 as Weight) + (189_193_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } @@ -229,9 +228,9 @@ impl WeightInfo for SubstrateWeight { // 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)) + (79_753_000 as Weight) + // Standard Error: 98_000 + .saturating_add((90_928_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)) } @@ -239,7 +238,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:0 w:1) // Storage: Contracts PristineCode (r:0 w:1) fn remove_code() -> Weight { - (41_641_000 as Weight) + (40_206_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } @@ -248,9 +247,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_caller(r: u32, ) -> Weight { - (459_561_000 as Weight) - // Standard Error: 172_000 - .saturating_add((114_881_000 as Weight).saturating_mul(r as Weight)) + (464_937_000 as Weight) + // Standard Error: 182_000 + .saturating_add((112_166_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)) } @@ -259,9 +258,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_address(r: u32, ) -> Weight { - (458_035_000 as Weight) - // Standard Error: 174_000 - .saturating_add((116_818_000 as Weight).saturating_mul(r as Weight)) + (464_387_000 as Weight) + // Standard Error: 155_000 + .saturating_add((112_307_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)) } @@ -270,9 +269,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_gas_left(r: u32, ) -> Weight { - (459_535_000 as Weight) - // Standard Error: 164_000 - .saturating_add((114_361_000 as Weight).saturating_mul(r as Weight)) + (462_005_000 as Weight) + // Standard Error: 185_000 + .saturating_add((111_402_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)) } @@ -281,9 +280,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_balance(r: u32, ) -> Weight { - (457_456_000 as Weight) - // Standard Error: 225_000 - .saturating_add((343_777_000 as Weight).saturating_mul(r as Weight)) + (468_139_000 as Weight) + // Standard Error: 192_000 + .saturating_add((317_634_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)) } @@ -292,9 +291,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_value_transferred(r: u32, ) -> Weight { - (470_778_000 as Weight) - // Standard Error: 169_000 - .saturating_add((114_043_000 as Weight).saturating_mul(r as Weight)) + (447_610_000 as Weight) + // Standard Error: 196_000 + .saturating_add((112_915_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)) } @@ -303,9 +302,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) 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)) + (450_012_000 as Weight) + // Standard Error: 167_000 + .saturating_add((112_311_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)) } @@ -314,9 +313,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_block_number(r: u32, ) -> Weight { - (460_130_000 as Weight) - // Standard Error: 377_000 - .saturating_add((116_803_000 as Weight).saturating_mul(r as Weight)) + (453_687_000 as Weight) + // Standard Error: 185_000 + .saturating_add((111_902_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)) } @@ -325,9 +324,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_now(r: u32, ) -> Weight { - (478_140_000 as Weight) - // Standard Error: 158_000 - .saturating_add((112_277_000 as Weight).saturating_mul(r as Weight)) + (453_283_000 as Weight) + // Standard Error: 162_000 + .saturating_add((112_543_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)) } @@ -337,9 +336,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Timestamp Now (r:1 w:0) // Storage: TransactionPayment NextFeeMultiplier (r:1 w:0) fn seal_weight_to_fee(r: u32, ) -> Weight { - (474_028_000 as Weight) - // Standard Error: 249_000 - .saturating_add((283_763_000 as Weight).saturating_mul(r as Weight)) + (454_171_000 as Weight) + // Standard Error: 205_000 + .saturating_add((267_487_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)) } @@ -348,9 +347,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_gas(r: u32, ) -> Weight { - (184_619_000 as Weight) - // Standard Error: 120_000 - .saturating_add((52_888_000 as Weight).saturating_mul(r as Weight)) + (190_254_000 as Weight) + // Standard Error: 116_000 + .saturating_add((51_037_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)) } @@ -359,9 +358,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_input(r: u32, ) -> Weight { - (455_537_000 as Weight) - // Standard Error: 199_000 - .saturating_add((99_547_000 as Weight).saturating_mul(r as Weight)) + (460_590_000 as Weight) + // Standard Error: 173_000 + .saturating_add((99_887_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)) } @@ -370,9 +369,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_input_per_kb(n: u32, ) -> Weight { - (592_179_000 as Weight) - // Standard Error: 6_000 - .saturating_add((38_070_000 as Weight).saturating_mul(n as Weight)) + (616_346_000 as Weight) + // Standard Error: 9_000 + .saturating_add((38_016_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)) } @@ -380,10 +379,8 @@ impl WeightInfo for SubstrateWeight { // 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 { - (440_955_000 as Weight) - // Standard Error: 177_000 - .saturating_add((15_497_000 as Weight).saturating_mul(r as Weight)) + fn seal_return(_r: u32, ) -> Weight { + (447_340_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } @@ -392,9 +389,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_return_per_kb(n: u32, ) -> Weight { - (441_001_000 as Weight) + (452_105_000 as Weight) // Standard Error: 1_000 - .saturating_add((654_000 as Weight).saturating_mul(n as Weight)) + .saturating_add((633_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)) } @@ -405,9 +402,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts DeletionQueue (r:1 w:1) // Storage: Contracts OwnerInfoOf (r:1 w:1) fn seal_terminate(r: u32, ) -> Weight { - (455_397_000 as Weight) - // Standard Error: 3_901_000 - .saturating_add((100_527_000 as Weight).saturating_mul(r as Weight)) + (456_662_000 as Weight) + // Standard Error: 2_550_000 + .saturating_add((71_297_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)) @@ -419,9 +416,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Timestamp Now (r:1 w:0) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) fn seal_random(r: u32, ) -> Weight { - (461_076_000 as Weight) - // Standard Error: 298_000 - .saturating_add((383_206_000 as Weight).saturating_mul(r as Weight)) + (460_302_000 as Weight) + // Standard Error: 223_000 + .saturating_add((351_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)) } @@ -430,9 +427,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_deposit_event(r: u32, ) -> Weight { - (446_683_000 as Weight) - // Standard Error: 350_000 - .saturating_add((640_909_000 as Weight).saturating_mul(r as Weight)) + (456_800_000 as Weight) + // Standard Error: 413_000 + .saturating_add((574_183_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)) } @@ -442,11 +439,11 @@ impl WeightInfo for SubstrateWeight { // 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_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)) + (1_099_926_000 as Weight) + // Standard Error: 2_170_000 + .saturating_add((461_522_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 427_000 + .saturating_add((164_778_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(2 as Weight)) @@ -457,39 +454,39 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_debug_message(r: u32, ) -> Weight { - (206_163_000 as Weight) - // Standard Error: 132_000 - .saturating_add((69_314_000 as Weight).saturating_mul(r as Weight)) + (208_518_000 as Weight) + // Standard Error: 131_000 + .saturating_add((70_862_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 { - (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)) + (492_961_000 as Weight) + // Standard Error: 506_000 + .saturating_add((373_157_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: System Account (r:2 w:2) // 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:1) fn seal_set_storage_per_kb(n: u32, ) -> Weight { - (823_649_000 as Weight) - // Standard Error: 307_000 - .saturating_add((74_809_000 as Weight).saturating_mul(n as Weight)) + (793_894_000 as Weight) + // Standard Error: 250_000 + .saturating_add((74_081_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 { - (226_639_000 as Weight) - // Standard Error: 1_501_000 - .saturating_add((867_367_000 as Weight).saturating_mul(r as Weight)) + (214_581_000 as Weight) + // Standard Error: 1_604_000 + .saturating_add((844_938_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)) @@ -497,9 +494,9 @@ impl WeightInfo for SubstrateWeight { } // Storage: Skipped Metadata (r:0 w:0) fn seal_get_storage(r: u32, ) -> Weight { - (343_416_000 as Weight) - // Standard Error: 848_000 - .saturating_add((511_880_000 as Weight).saturating_mul(r as Weight)) + (328_005_000 as Weight) + // Standard Error: 753_000 + .saturating_add((493_893_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)) @@ -510,9 +507,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Timestamp Now (r:1 w:0) // Storage: unknown [0x7afa01283080ef247df84e0ba38ea5a587d25ce6633a6bfbba02068c14023441] (r:1 w:0) fn seal_get_storage_per_kb(n: u32, ) -> Weight { - (760_951_000 as Weight) - // Standard Error: 255_000 - .saturating_add((112_137_000 as Weight).saturating_mul(n as Weight)) + (736_348_000 as Weight) + // Standard Error: 232_000 + .saturating_add((110_871_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)) } @@ -521,9 +518,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_transfer(r: u32, ) -> Weight { - (326_749_000 as Weight) - // Standard Error: 2_723_000 - .saturating_add((4_099_913_000 as Weight).saturating_mul(r as Weight)) + (346_206_000 as Weight) + // Standard Error: 1_888_000 + .saturating_add((3_729_835_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)) @@ -534,9 +531,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_call(r: u32, ) -> 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)) + (0 as Weight) + // Standard Error: 11_456_000 + .saturating_add((39_404_197_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)) @@ -546,12 +543,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts ContractInfoOf (r:101 w:101) // Storage: Contracts CodeStorage (r:2 w:0) // Storage: Timestamp Now (r:1 w:0) - fn seal_call_per_transfer_input_output_kb(t: u32, i: u32, o: u32, ) -> 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)) + fn seal_call_per_transfer_input_output_kb(t: u32, i: u32, _o: u32, ) -> Weight { + (136_101_024_000 as Weight) + // Standard Error: 6_000 + .saturating_add((62_954_000 as Weight).saturating_mul(i 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)) @@ -564,8 +559,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts OwnerInfoOf (r:100 w:100) fn seal_instantiate(r: u32, ) -> Weight { (0 as Weight) - // Standard Error: 42_300_000 - .saturating_add((51_184_882_000 as Weight).saturating_mul(r as Weight)) + // Standard Error: 47_948_000 + .saturating_add((48_494_963_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)) @@ -578,13 +573,13 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts AccountCounter (r:1 w:1) // Storage: Contracts OwnerInfoOf (r:1 w:1) fn seal_instantiate_per_input_output_salt_kb(i: u32, o: u32, s: u32, ) -> Weight { - (47_809_145_000 as Weight) - // Standard Error: 30_000 - .saturating_add((64_218_000 as Weight).saturating_mul(i as Weight)) - // Standard Error: 30_000 - .saturating_add((100_891_000 as Weight).saturating_mul(o as Weight)) - // Standard Error: 30_000 - .saturating_add((201_407_000 as Weight).saturating_mul(s as Weight)) + (45_566_426_000 as Weight) + // Standard Error: 50_000 + .saturating_add((63_809_000 as Weight).saturating_mul(i as Weight)) + // Standard Error: 50_000 + .saturating_add((101_448_000 as Weight).saturating_mul(o as Weight)) + // Standard Error: 50_000 + .saturating_add((201_652_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)) } @@ -593,9 +588,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_sha2_256(r: u32, ) -> Weight { - (456_042_000 as Weight) - // Standard Error: 153_000 - .saturating_add((129_938_000 as Weight).saturating_mul(r as Weight)) + (457_229_000 as Weight) + // Standard Error: 197_000 + .saturating_add((124_742_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)) } @@ -604,9 +599,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight { - (764_274_000 as Weight) - // Standard Error: 24_000 - .saturating_add((499_753_000 as Weight).saturating_mul(n as Weight)) + (586_056_000 as Weight) + // Standard Error: 30_000 + .saturating_add((505_871_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)) } @@ -615,9 +610,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_keccak_256(r: u32, ) -> Weight { - (460_114_000 as Weight) - // Standard Error: 186_000 - .saturating_add((134_203_000 as Weight).saturating_mul(r as Weight)) + (458_528_000 as Weight) + // Standard Error: 154_000 + .saturating_add((137_710_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)) } @@ -626,9 +621,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight { - (575_294_000 as Weight) - // Standard Error: 20_000 - .saturating_add((346_682_000 as Weight).saturating_mul(n as Weight)) + (573_132_000 as Weight) + // Standard Error: 19_000 + .saturating_add((363_983_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)) } @@ -637,9 +632,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_blake2_256(r: u32, ) -> Weight { - (456_380_000 as Weight) - // Standard Error: 196_000 - .saturating_add((110_306_000 as Weight).saturating_mul(r as Weight)) + (456_881_000 as Weight) + // Standard Error: 187_000 + .saturating_add((106_987_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)) } @@ -648,9 +643,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight { - (535_192_000 as Weight) - // Standard Error: 18_000 - .saturating_add((164_174_000 as Weight).saturating_mul(n as Weight)) + (587_192_000 as Weight) + // Standard Error: 17_000 + .saturating_add((164_114_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)) } @@ -659,9 +654,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_blake2_128(r: u32, ) -> Weight { - (459_977_000 as Weight) - // Standard Error: 171_000 - .saturating_add((107_852_000 as Weight).saturating_mul(r as Weight)) + (457_938_000 as Weight) + // Standard Error: 184_000 + .saturating_add((105_658_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)) } @@ -670,9 +665,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight { - (553_463_000 as Weight) + (567_129_000 as Weight) // Standard Error: 16_000 - .saturating_add((164_158_000 as Weight).saturating_mul(n as Weight)) + .saturating_add((164_146_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)) } @@ -681,266 +676,266 @@ impl WeightInfo for SubstrateWeight { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_ecdsa_recover(r: u32, ) -> Weight { - (486_280_000 as Weight) - // Standard Error: 1_265_000 - .saturating_add((15_591_161_000 as Weight).saturating_mul(r as Weight)) + (426_602_000 as Weight) + // Standard Error: 1_393_000 + .saturating_add((15_587_531_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 { - (53_752_000 as Weight) + (39_689_000 as Weight) // Standard Error: 14_000 - .saturating_add((853_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((1_361_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64load(r: u32, ) -> Weight { - (48_323_000 as Weight) - // Standard Error: 9_000 - .saturating_add((2_426_000 as Weight).saturating_mul(r as Weight)) + (45_362_000 as Weight) + // Standard Error: 10_000 + .saturating_add((2_665_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64store(r: u32, ) -> Weight { - (48_636_000 as Weight) - // Standard Error: 10_000 - .saturating_add((2_633_000 as Weight).saturating_mul(r as Weight)) + (45_514_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_696_000 as Weight).saturating_mul(r as Weight)) } fn instr_select(r: u32, ) -> Weight { - (51_403_000 as Weight) - // Standard Error: 11_000 - .saturating_add((2_369_000 as Weight).saturating_mul(r as Weight)) + (45_931_000 as Weight) + // Standard Error: 12_000 + .saturating_add((2_919_000 as Weight).saturating_mul(r as Weight)) } fn instr_if(r: u32, ) -> Weight { - (48_766_000 as Weight) - // Standard Error: 12_000 - .saturating_add((2_412_000 as Weight).saturating_mul(r as Weight)) + (46_362_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_942_000 as Weight).saturating_mul(r as Weight)) } fn instr_br(r: u32, ) -> Weight { - (51_707_000 as Weight) - // Standard Error: 16_000 - .saturating_add((1_391_000 as Weight).saturating_mul(r as Weight)) + (44_648_000 as Weight) + // Standard Error: 15_000 + .saturating_add((1_856_000 as Weight).saturating_mul(r as Weight)) } fn instr_br_if(r: u32, ) -> Weight { - (45_136_000 as Weight) - // Standard Error: 18_000 - .saturating_add((2_123_000 as Weight).saturating_mul(r as Weight)) + (45_989_000 as Weight) + // Standard Error: 10_000 + .saturating_add((2_418_000 as Weight).saturating_mul(r as Weight)) } fn instr_br_table(r: u32, ) -> Weight { - (38_198_000 as Weight) - // Standard Error: 19_000 - .saturating_add((2_786_000 as Weight).saturating_mul(r as Weight)) + (48_883_000 as Weight) + // Standard Error: 18_000 + .saturating_add((2_471_000 as Weight).saturating_mul(r 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)) + (48_685_000 as Weight) + // Standard Error: 2_000 + .saturating_add((39_000 as Weight).saturating_mul(e as Weight)) } fn instr_call(r: u32, ) -> Weight { - (40_645_000 as Weight) - // Standard Error: 25_000 - .saturating_add((20_039_000 as Weight).saturating_mul(r as Weight)) + (50_428_000 as Weight) + // Standard Error: 24_000 + .saturating_add((20_121_000 as Weight).saturating_mul(r as Weight)) } fn instr_call_indirect(r: u32, ) -> Weight { - (57_228_000 as Weight) - // Standard Error: 33_000 - .saturating_add((29_388_000 as Weight).saturating_mul(r as Weight)) + (54_899_000 as Weight) + // Standard Error: 32_000 + .saturating_add((29_588_000 as Weight).saturating_mul(r as Weight)) } fn instr_call_indirect_per_param(p: u32, ) -> Weight { - (89_382_000 as Weight) + (92_176_000 as Weight) // Standard Error: 5_000 - .saturating_add((1_143_000 as Weight).saturating_mul(p as Weight)) + .saturating_add((989_000 as Weight).saturating_mul(p as Weight)) } fn instr_local_get(r: u32, ) -> Weight { - (48_789_000 as Weight) - // Standard Error: 11_000 - .saturating_add((692_000 as Weight).saturating_mul(r as Weight)) + (48_130_000 as Weight) + // Standard Error: 10_000 + .saturating_add((1_194_000 as Weight).saturating_mul(r as Weight)) } fn instr_local_set(r: u32, ) -> Weight { - (49_067_000 as Weight) - // Standard Error: 12_000 - .saturating_add((803_000 as Weight).saturating_mul(r as Weight)) + (47_550_000 as Weight) + // Standard Error: 10_000 + .saturating_add((1_244_000 as Weight).saturating_mul(r as Weight)) } fn instr_local_tee(r: u32, ) -> Weight { - (45_765_000 as Weight) - // Standard Error: 11_000 - .saturating_add((1_395_000 as Weight).saturating_mul(r as Weight)) + (48_806_000 as Weight) + // Standard Error: 10_000 + .saturating_add((1_757_000 as Weight).saturating_mul(r as Weight)) } fn instr_global_get(r: u32, ) -> Weight { - (61_552_000 as Weight) - // Standard Error: 20_000 - .saturating_add((1_440_000 as Weight).saturating_mul(r as Weight)) + (62_369_000 as Weight) + // Standard Error: 9_000 + .saturating_add((1_779_000 as Weight).saturating_mul(r as Weight)) } fn instr_global_set(r: u32, ) -> Weight { - (56_926_000 as Weight) - // Standard Error: 22_000 - .saturating_add((1_741_000 as Weight).saturating_mul(r as Weight)) + (61_063_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_751_000 as Weight).saturating_mul(r as Weight)) } fn instr_memory_current(r: u32, ) -> Weight { - (53_287_000 as Weight) + (39_781_000 as Weight) // Standard Error: 14_000 - .saturating_add((769_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((1_370_000 as Weight).saturating_mul(r as Weight)) } fn instr_memory_grow(r: u32, ) -> Weight { - (38_009_000 as Weight) - // Standard Error: 2_445_000 - .saturating_add((637_570_000 as Weight).saturating_mul(r as Weight)) + (50_983_000 as Weight) + // Standard Error: 4_238_000 + .saturating_add((618_956_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64clz(r: u32, ) -> Weight { - (55_460_000 as Weight) - // Standard Error: 10_000 - .saturating_add((1_222_000 as Weight).saturating_mul(r as Weight)) + (42_490_000 as Weight) + // Standard Error: 13_000 + .saturating_add((1_937_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ctz(r: u32, ) -> Weight { - (55_058_000 as Weight) + (42_296_000 as Weight) // Standard Error: 13_000 - .saturating_add((1_251_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((1_943_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64popcnt(r: u32, ) -> Weight { - (55_308_000 as Weight) - // Standard Error: 11_000 - .saturating_add((1_229_000 as Weight).saturating_mul(r as Weight)) + (42_263_000 as Weight) + // Standard Error: 13_000 + .saturating_add((1_944_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64eqz(r: u32, ) -> Weight { - (55_303_000 as Weight) - // Standard Error: 11_000 - .saturating_add((1_231_000 as Weight).saturating_mul(r as Weight)) + (42_087_000 as Weight) + // Standard Error: 13_000 + .saturating_add((1_952_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64extendsi32(r: u32, ) -> Weight { - (44_768_000 as Weight) - // Standard Error: 11_000 - .saturating_add((1_428_000 as Weight).saturating_mul(r as Weight)) + (48_024_000 as Weight) + // Standard Error: 9_000 + .saturating_add((1_783_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64extendui32(r: u32, ) -> Weight { - (44_806_000 as Weight) - // Standard Error: 11_000 - .saturating_add((1_429_000 as Weight).saturating_mul(r as Weight)) + (48_056_000 as Weight) + // Standard Error: 10_000 + .saturating_add((1_780_000 as Weight).saturating_mul(r as Weight)) } fn instr_i32wrapi64(r: u32, ) -> Weight { - (55_388_000 as Weight) - // Standard Error: 11_000 - .saturating_add((1_228_000 as Weight).saturating_mul(r as Weight)) + (42_352_000 as Weight) + // Standard Error: 13_000 + .saturating_add((1_928_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64eq(r: u32, ) -> Weight { - (50_942_000 as Weight) + (45_439_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_826_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_382_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ne(r: u32, ) -> Weight { - (50_964_000 as Weight) + (45_232_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_825_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_393_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64lts(r: u32, ) -> Weight { - (50_749_000 as Weight) - // Standard Error: 10_000 - .saturating_add((1_830_000 as Weight).saturating_mul(r as Weight)) + (45_351_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_386_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ltu(r: u32, ) -> Weight { - (50_581_000 as Weight) + (45_448_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_837_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_385_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64gts(r: u32, ) -> Weight { - (50_720_000 as Weight) - // Standard Error: 10_000 - .saturating_add((1_831_000 as Weight).saturating_mul(r as Weight)) + (45_275_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_386_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64gtu(r: u32, ) -> Weight { - (50_686_000 as Weight) + (45_180_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_832_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_396_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64les(r: u32, ) -> Weight { - (50_914_000 as Weight) - // Standard Error: 12_000 - .saturating_add((1_828_000 as Weight).saturating_mul(r as Weight)) + (45_396_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_400_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64leu(r: u32, ) -> Weight { - (51_126_000 as Weight) + (45_597_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_820_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_392_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ges(r: u32, ) -> Weight { - (50_960_000 as Weight) + (45_137_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_829_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_400_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64geu(r: u32, ) -> Weight { - (50_796_000 as Weight) - // Standard Error: 11_000 - .saturating_add((1_831_000 as Weight).saturating_mul(r as Weight)) + (45_091_000 as Weight) + // Standard Error: 10_000 + .saturating_add((2_401_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64add(r: u32, ) -> Weight { - (51_011_000 as Weight) - // Standard Error: 12_000 - .saturating_add((1_826_000 as Weight).saturating_mul(r as Weight)) + (45_547_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_380_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64sub(r: u32, ) -> Weight { - (50_892_000 as Weight) + (45_435_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_828_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_383_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64mul(r: u32, ) -> Weight { - (50_980_000 as Weight) + (45_244_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_825_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_389_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64divs(r: u32, ) -> Weight { - (51_245_000 as Weight) + (45_253_000 as Weight) // Standard Error: 11_000 - .saturating_add((2_427_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((3_046_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64divu(r: u32, ) -> Weight { - (51_083_000 as Weight) + (45_339_000 as Weight) // Standard Error: 11_000 - .saturating_add((2_218_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_711_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64rems(r: u32, ) -> Weight { - (50_861_000 as Weight) + (45_312_000 as Weight) // Standard Error: 11_000 - .saturating_add((2_446_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((3_020_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64remu(r: u32, ) -> Weight { - (51_001_000 as Weight) - // Standard Error: 10_000 - .saturating_add((2_183_000 as Weight).saturating_mul(r as Weight)) + (45_397_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_739_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64and(r: u32, ) -> Weight { - (50_819_000 as Weight) + (45_282_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_829_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_387_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64or(r: u32, ) -> Weight { - (51_368_000 as Weight) - // Standard Error: 12_000 - .saturating_add((1_814_000 as Weight).saturating_mul(r as Weight)) + (45_508_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_390_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64xor(r: u32, ) -> Weight { - (50_952_000 as Weight) + (45_089_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_828_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_397_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64shl(r: u32, ) -> Weight { - (50_996_000 as Weight) - // Standard Error: 11_000 - .saturating_add((1_827_000 as Weight).saturating_mul(r as Weight)) + (45_878_000 as Weight) + // Standard Error: 13_000 + .saturating_add((2_395_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64shrs(r: u32, ) -> Weight { - (51_061_000 as Weight) + (45_459_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_826_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_401_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64shru(r: u32, ) -> Weight { - (50_931_000 as Weight) - // Standard Error: 12_000 - .saturating_add((1_832_000 as Weight).saturating_mul(r as Weight)) + (45_663_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_398_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64rotl(r: u32, ) -> Weight { - (50_652_000 as Weight) + (45_739_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_832_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_393_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64rotr(r: u32, ) -> Weight { - (50_817_000 as Weight) + (45_290_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_830_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_392_000 as Weight).saturating_mul(r as Weight)) } } @@ -948,77 +943,77 @@ impl WeightInfo for SubstrateWeight { impl WeightInfo for () { // Storage: Contracts DeletionQueue (r:1 w:0) fn on_initialize() -> Weight { - (2_969_000 as Weight) + (2_832_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_184_000 as Weight).saturating_mul(k as Weight)) + // Standard Error: 3_000 + .saturating_add((2_197_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_477_000 as Weight) + (92_378_000 as Weight) // Standard Error: 2_000 - .saturating_add((319_000 as Weight).saturating_mul(q as Weight)) + .saturating_add((328_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 { - (30_226_000 as Weight) - // Standard Error: 93_000 - .saturating_add((74_603_000 as Weight).saturating_mul(c as Weight)) + (27_381_000 as Weight) + // Standard Error: 92_000 + .saturating_add((90_220_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 { - (8_062_000 as Weight) + (7_710_000 as Weight) // Standard Error: 0 - .saturating_add((1_455_000 as Weight).saturating_mul(c as Weight)) + .saturating_add((1_457_000 as Weight).saturating_mul(c as Weight)) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) } // Storage: Contracts CodeStorage (r:1 w:1) - // Storage: Contracts AccountCounter (r:1 w:0) + // Storage: Contracts AccountCounter (r:1 w:1) // Storage: Contracts ContractInfoOf (r:1 w:1) // Storage: Timestamp Now (r:1 w:0) // Storage: System Account (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 { - (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)) + (559_294_000 as Weight) + // Standard Error: 171_000 + .saturating_add((206_170_000 as Weight).saturating_mul(c as Weight)) + // Standard Error: 11_000 + .saturating_add((2_107_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) - .saturating_add(RocksDbWeight::get().writes(5 as Weight)) + .saturating_add(RocksDbWeight::get().writes(6 as Weight)) } // Storage: Contracts CodeStorage (r:1 w:1) - // Storage: Contracts AccountCounter (r:1 w:0) + // Storage: Contracts AccountCounter (r:1 w:1) // 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 { - (263_013_000 as Weight) + (236_193_000 as Weight) // Standard Error: 2_000 - .saturating_add((1_991_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((2_022_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) - .saturating_add(RocksDbWeight::get().writes(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(5 as Weight)) } // 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:1) fn call() -> Weight { - (210_423_000 as Weight) + (189_193_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1026,9 +1021,9 @@ impl WeightInfo for () { // 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)) + (79_753_000 as Weight) + // Standard Error: 98_000 + .saturating_add((90_928_000 as Weight).saturating_mul(c as Weight)) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } @@ -1036,7 +1031,7 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:0 w:1) // Storage: Contracts PristineCode (r:0 w:1) fn remove_code() -> Weight { - (41_641_000 as Weight) + (40_206_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } @@ -1045,9 +1040,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_caller(r: u32, ) -> Weight { - (459_561_000 as Weight) - // Standard Error: 172_000 - .saturating_add((114_881_000 as Weight).saturating_mul(r as Weight)) + (464_937_000 as Weight) + // Standard Error: 182_000 + .saturating_add((112_166_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1056,9 +1051,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_address(r: u32, ) -> Weight { - (458_035_000 as Weight) - // Standard Error: 174_000 - .saturating_add((116_818_000 as Weight).saturating_mul(r as Weight)) + (464_387_000 as Weight) + // Standard Error: 155_000 + .saturating_add((112_307_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1067,9 +1062,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_gas_left(r: u32, ) -> Weight { - (459_535_000 as Weight) - // Standard Error: 164_000 - .saturating_add((114_361_000 as Weight).saturating_mul(r as Weight)) + (462_005_000 as Weight) + // Standard Error: 185_000 + .saturating_add((111_402_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1078,9 +1073,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_balance(r: u32, ) -> Weight { - (457_456_000 as Weight) - // Standard Error: 225_000 - .saturating_add((343_777_000 as Weight).saturating_mul(r as Weight)) + (468_139_000 as Weight) + // Standard Error: 192_000 + .saturating_add((317_634_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1089,9 +1084,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_value_transferred(r: u32, ) -> Weight { - (470_778_000 as Weight) - // Standard Error: 169_000 - .saturating_add((114_043_000 as Weight).saturating_mul(r as Weight)) + (447_610_000 as Weight) + // Standard Error: 196_000 + .saturating_add((112_915_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1100,9 +1095,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) 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)) + (450_012_000 as Weight) + // Standard Error: 167_000 + .saturating_add((112_311_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1111,9 +1106,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_block_number(r: u32, ) -> Weight { - (460_130_000 as Weight) - // Standard Error: 377_000 - .saturating_add((116_803_000 as Weight).saturating_mul(r as Weight)) + (453_687_000 as Weight) + // Standard Error: 185_000 + .saturating_add((111_902_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1122,9 +1117,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_now(r: u32, ) -> Weight { - (478_140_000 as Weight) - // Standard Error: 158_000 - .saturating_add((112_277_000 as Weight).saturating_mul(r as Weight)) + (453_283_000 as Weight) + // Standard Error: 162_000 + .saturating_add((112_543_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1134,9 +1129,9 @@ impl WeightInfo for () { // Storage: Timestamp Now (r:1 w:0) // Storage: TransactionPayment NextFeeMultiplier (r:1 w:0) fn seal_weight_to_fee(r: u32, ) -> Weight { - (474_028_000 as Weight) - // Standard Error: 249_000 - .saturating_add((283_763_000 as Weight).saturating_mul(r as Weight)) + (454_171_000 as Weight) + // Standard Error: 205_000 + .saturating_add((267_487_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1145,9 +1140,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_gas(r: u32, ) -> Weight { - (184_619_000 as Weight) - // Standard Error: 120_000 - .saturating_add((52_888_000 as Weight).saturating_mul(r as Weight)) + (190_254_000 as Weight) + // Standard Error: 116_000 + .saturating_add((51_037_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1156,9 +1151,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_input(r: u32, ) -> Weight { - (455_537_000 as Weight) - // Standard Error: 199_000 - .saturating_add((99_547_000 as Weight).saturating_mul(r as Weight)) + (460_590_000 as Weight) + // Standard Error: 173_000 + .saturating_add((99_887_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1167,9 +1162,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_input_per_kb(n: u32, ) -> Weight { - (592_179_000 as Weight) - // Standard Error: 6_000 - .saturating_add((38_070_000 as Weight).saturating_mul(n as Weight)) + (616_346_000 as Weight) + // Standard Error: 9_000 + .saturating_add((38_016_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1177,10 +1172,8 @@ impl WeightInfo for () { // 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 { - (440_955_000 as Weight) - // Standard Error: 177_000 - .saturating_add((15_497_000 as Weight).saturating_mul(r as Weight)) + fn seal_return(_r: u32, ) -> Weight { + (447_340_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1189,9 +1182,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_return_per_kb(n: u32, ) -> Weight { - (441_001_000 as Weight) + (452_105_000 as Weight) // Standard Error: 1_000 - .saturating_add((654_000 as Weight).saturating_mul(n as Weight)) + .saturating_add((633_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1202,9 +1195,9 @@ impl WeightInfo for () { // Storage: Contracts DeletionQueue (r:1 w:1) // Storage: Contracts OwnerInfoOf (r:1 w:1) fn seal_terminate(r: u32, ) -> Weight { - (455_397_000 as Weight) - // Standard Error: 3_901_000 - .saturating_add((100_527_000 as Weight).saturating_mul(r as Weight)) + (456_662_000 as Weight) + // Standard Error: 2_550_000 + .saturating_add((71_297_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)) @@ -1216,9 +1209,9 @@ impl WeightInfo for () { // Storage: Timestamp Now (r:1 w:0) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) fn seal_random(r: u32, ) -> Weight { - (461_076_000 as Weight) - // Standard Error: 298_000 - .saturating_add((383_206_000 as Weight).saturating_mul(r as Weight)) + (460_302_000 as Weight) + // Standard Error: 223_000 + .saturating_add((351_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)) } @@ -1227,9 +1220,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_deposit_event(r: u32, ) -> Weight { - (446_683_000 as Weight) - // Standard Error: 350_000 - .saturating_add((640_909_000 as Weight).saturating_mul(r as Weight)) + (456_800_000 as Weight) + // Standard Error: 413_000 + .saturating_add((574_183_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1239,11 +1232,11 @@ impl WeightInfo for () { // 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_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)) + (1_099_926_000 as Weight) + // Standard Error: 2_170_000 + .saturating_add((461_522_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 427_000 + .saturating_add((164_778_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(2 as Weight)) @@ -1254,39 +1247,39 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_debug_message(r: u32, ) -> Weight { - (206_163_000 as Weight) - // Standard Error: 132_000 - .saturating_add((69_314_000 as Weight).saturating_mul(r as Weight)) + (208_518_000 as Weight) + // Standard Error: 131_000 + .saturating_add((70_862_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 { - (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)) + (492_961_000 as Weight) + // Standard Error: 506_000 + .saturating_add((373_157_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: System Account (r:2 w:2) // 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:1) fn seal_set_storage_per_kb(n: u32, ) -> Weight { - (823_649_000 as Weight) - // Standard Error: 307_000 - .saturating_add((74_809_000 as Weight).saturating_mul(n as Weight)) + (793_894_000 as Weight) + // Standard Error: 250_000 + .saturating_add((74_081_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 { - (226_639_000 as Weight) - // Standard Error: 1_501_000 - .saturating_add((867_367_000 as Weight).saturating_mul(r as Weight)) + (214_581_000 as Weight) + // Standard Error: 1_604_000 + .saturating_add((844_938_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)) @@ -1294,9 +1287,9 @@ impl WeightInfo for () { } // Storage: Skipped Metadata (r:0 w:0) fn seal_get_storage(r: u32, ) -> Weight { - (343_416_000 as Weight) - // Standard Error: 848_000 - .saturating_add((511_880_000 as Weight).saturating_mul(r as Weight)) + (328_005_000 as Weight) + // Standard Error: 753_000 + .saturating_add((493_893_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)) @@ -1307,9 +1300,9 @@ impl WeightInfo for () { // Storage: Timestamp Now (r:1 w:0) // Storage: unknown [0x7afa01283080ef247df84e0ba38ea5a587d25ce6633a6bfbba02068c14023441] (r:1 w:0) fn seal_get_storage_per_kb(n: u32, ) -> Weight { - (760_951_000 as Weight) - // Standard Error: 255_000 - .saturating_add((112_137_000 as Weight).saturating_mul(n as Weight)) + (736_348_000 as Weight) + // Standard Error: 232_000 + .saturating_add((110_871_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1318,9 +1311,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_transfer(r: u32, ) -> Weight { - (326_749_000 as Weight) - // Standard Error: 2_723_000 - .saturating_add((4_099_913_000 as Weight).saturating_mul(r as Weight)) + (346_206_000 as Weight) + // Standard Error: 1_888_000 + .saturating_add((3_729_835_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)) @@ -1331,9 +1324,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_call(r: u32, ) -> 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)) + (0 as Weight) + // Standard Error: 11_456_000 + .saturating_add((39_404_197_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)) @@ -1343,12 +1336,10 @@ impl WeightInfo for () { // Storage: Contracts ContractInfoOf (r:101 w:101) // Storage: Contracts CodeStorage (r:2 w:0) // Storage: Timestamp Now (r:1 w:0) - fn seal_call_per_transfer_input_output_kb(t: u32, i: u32, o: u32, ) -> 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)) + fn seal_call_per_transfer_input_output_kb(t: u32, i: u32, _o: u32, ) -> Weight { + (136_101_024_000 as Weight) + // Standard Error: 6_000 + .saturating_add((62_954_000 as Weight).saturating_mul(i 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)) @@ -1361,8 +1352,8 @@ impl WeightInfo for () { // Storage: Contracts OwnerInfoOf (r:100 w:100) fn seal_instantiate(r: u32, ) -> Weight { (0 as Weight) - // Standard Error: 42_300_000 - .saturating_add((51_184_882_000 as Weight).saturating_mul(r as Weight)) + // Standard Error: 47_948_000 + .saturating_add((48_494_963_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)) @@ -1375,13 +1366,13 @@ impl WeightInfo for () { // Storage: Contracts AccountCounter (r:1 w:1) // Storage: Contracts OwnerInfoOf (r:1 w:1) fn seal_instantiate_per_input_output_salt_kb(i: u32, o: u32, s: u32, ) -> Weight { - (47_809_145_000 as Weight) - // Standard Error: 30_000 - .saturating_add((64_218_000 as Weight).saturating_mul(i as Weight)) - // Standard Error: 30_000 - .saturating_add((100_891_000 as Weight).saturating_mul(o as Weight)) - // Standard Error: 30_000 - .saturating_add((201_407_000 as Weight).saturating_mul(s as Weight)) + (45_566_426_000 as Weight) + // Standard Error: 50_000 + .saturating_add((63_809_000 as Weight).saturating_mul(i as Weight)) + // Standard Error: 50_000 + .saturating_add((101_448_000 as Weight).saturating_mul(o as Weight)) + // Standard Error: 50_000 + .saturating_add((201_652_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(208 as Weight)) .saturating_add(RocksDbWeight::get().writes(206 as Weight)) } @@ -1390,9 +1381,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_sha2_256(r: u32, ) -> Weight { - (456_042_000 as Weight) - // Standard Error: 153_000 - .saturating_add((129_938_000 as Weight).saturating_mul(r as Weight)) + (457_229_000 as Weight) + // Standard Error: 197_000 + .saturating_add((124_742_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1401,9 +1392,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight { - (764_274_000 as Weight) - // Standard Error: 24_000 - .saturating_add((499_753_000 as Weight).saturating_mul(n as Weight)) + (586_056_000 as Weight) + // Standard Error: 30_000 + .saturating_add((505_871_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1412,9 +1403,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_keccak_256(r: u32, ) -> Weight { - (460_114_000 as Weight) - // Standard Error: 186_000 - .saturating_add((134_203_000 as Weight).saturating_mul(r as Weight)) + (458_528_000 as Weight) + // Standard Error: 154_000 + .saturating_add((137_710_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1423,9 +1414,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight { - (575_294_000 as Weight) - // Standard Error: 20_000 - .saturating_add((346_682_000 as Weight).saturating_mul(n as Weight)) + (573_132_000 as Weight) + // Standard Error: 19_000 + .saturating_add((363_983_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1434,9 +1425,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_blake2_256(r: u32, ) -> Weight { - (456_380_000 as Weight) - // Standard Error: 196_000 - .saturating_add((110_306_000 as Weight).saturating_mul(r as Weight)) + (456_881_000 as Weight) + // Standard Error: 187_000 + .saturating_add((106_987_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1445,9 +1436,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight { - (535_192_000 as Weight) - // Standard Error: 18_000 - .saturating_add((164_174_000 as Weight).saturating_mul(n as Weight)) + (587_192_000 as Weight) + // Standard Error: 17_000 + .saturating_add((164_114_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1456,9 +1447,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_blake2_128(r: u32, ) -> Weight { - (459_977_000 as Weight) - // Standard Error: 171_000 - .saturating_add((107_852_000 as Weight).saturating_mul(r as Weight)) + (457_938_000 as Weight) + // Standard Error: 184_000 + .saturating_add((105_658_000 as Weight).saturating_mul(r as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1467,9 +1458,9 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight { - (553_463_000 as Weight) + (567_129_000 as Weight) // Standard Error: 16_000 - .saturating_add((164_158_000 as Weight).saturating_mul(n as Weight)) + .saturating_add((164_146_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -1478,265 +1469,265 @@ impl WeightInfo for () { // Storage: Contracts CodeStorage (r:1 w:0) // Storage: Timestamp Now (r:1 w:0) fn seal_ecdsa_recover(r: u32, ) -> Weight { - (486_280_000 as Weight) - // Standard Error: 1_265_000 - .saturating_add((15_591_161_000 as Weight).saturating_mul(r as Weight)) + (426_602_000 as Weight) + // Standard Error: 1_393_000 + .saturating_add((15_587_531_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 { - (53_752_000 as Weight) + (39_689_000 as Weight) // Standard Error: 14_000 - .saturating_add((853_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((1_361_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64load(r: u32, ) -> Weight { - (48_323_000 as Weight) - // Standard Error: 9_000 - .saturating_add((2_426_000 as Weight).saturating_mul(r as Weight)) + (45_362_000 as Weight) + // Standard Error: 10_000 + .saturating_add((2_665_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64store(r: u32, ) -> Weight { - (48_636_000 as Weight) - // Standard Error: 10_000 - .saturating_add((2_633_000 as Weight).saturating_mul(r as Weight)) + (45_514_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_696_000 as Weight).saturating_mul(r as Weight)) } fn instr_select(r: u32, ) -> Weight { - (51_403_000 as Weight) - // Standard Error: 11_000 - .saturating_add((2_369_000 as Weight).saturating_mul(r as Weight)) + (45_931_000 as Weight) + // Standard Error: 12_000 + .saturating_add((2_919_000 as Weight).saturating_mul(r as Weight)) } fn instr_if(r: u32, ) -> Weight { - (48_766_000 as Weight) - // Standard Error: 12_000 - .saturating_add((2_412_000 as Weight).saturating_mul(r as Weight)) + (46_362_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_942_000 as Weight).saturating_mul(r as Weight)) } fn instr_br(r: u32, ) -> Weight { - (51_707_000 as Weight) - // Standard Error: 16_000 - .saturating_add((1_391_000 as Weight).saturating_mul(r as Weight)) + (44_648_000 as Weight) + // Standard Error: 15_000 + .saturating_add((1_856_000 as Weight).saturating_mul(r as Weight)) } fn instr_br_if(r: u32, ) -> Weight { - (45_136_000 as Weight) - // Standard Error: 18_000 - .saturating_add((2_123_000 as Weight).saturating_mul(r as Weight)) + (45_989_000 as Weight) + // Standard Error: 10_000 + .saturating_add((2_418_000 as Weight).saturating_mul(r as Weight)) } fn instr_br_table(r: u32, ) -> Weight { - (38_198_000 as Weight) - // Standard Error: 19_000 - .saturating_add((2_786_000 as Weight).saturating_mul(r as Weight)) + (48_883_000 as Weight) + // Standard Error: 18_000 + .saturating_add((2_471_000 as Weight).saturating_mul(r 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)) + (48_685_000 as Weight) + // Standard Error: 2_000 + .saturating_add((39_000 as Weight).saturating_mul(e as Weight)) } fn instr_call(r: u32, ) -> Weight { - (40_645_000 as Weight) - // Standard Error: 25_000 - .saturating_add((20_039_000 as Weight).saturating_mul(r as Weight)) + (50_428_000 as Weight) + // Standard Error: 24_000 + .saturating_add((20_121_000 as Weight).saturating_mul(r as Weight)) } fn instr_call_indirect(r: u32, ) -> Weight { - (57_228_000 as Weight) - // Standard Error: 33_000 - .saturating_add((29_388_000 as Weight).saturating_mul(r as Weight)) + (54_899_000 as Weight) + // Standard Error: 32_000 + .saturating_add((29_588_000 as Weight).saturating_mul(r as Weight)) } fn instr_call_indirect_per_param(p: u32, ) -> Weight { - (89_382_000 as Weight) + (92_176_000 as Weight) // Standard Error: 5_000 - .saturating_add((1_143_000 as Weight).saturating_mul(p as Weight)) + .saturating_add((989_000 as Weight).saturating_mul(p as Weight)) } fn instr_local_get(r: u32, ) -> Weight { - (48_789_000 as Weight) - // Standard Error: 11_000 - .saturating_add((692_000 as Weight).saturating_mul(r as Weight)) + (48_130_000 as Weight) + // Standard Error: 10_000 + .saturating_add((1_194_000 as Weight).saturating_mul(r as Weight)) } fn instr_local_set(r: u32, ) -> Weight { - (49_067_000 as Weight) - // Standard Error: 12_000 - .saturating_add((803_000 as Weight).saturating_mul(r as Weight)) + (47_550_000 as Weight) + // Standard Error: 10_000 + .saturating_add((1_244_000 as Weight).saturating_mul(r as Weight)) } fn instr_local_tee(r: u32, ) -> Weight { - (45_765_000 as Weight) - // Standard Error: 11_000 - .saturating_add((1_395_000 as Weight).saturating_mul(r as Weight)) + (48_806_000 as Weight) + // Standard Error: 10_000 + .saturating_add((1_757_000 as Weight).saturating_mul(r as Weight)) } fn instr_global_get(r: u32, ) -> Weight { - (61_552_000 as Weight) - // Standard Error: 20_000 - .saturating_add((1_440_000 as Weight).saturating_mul(r as Weight)) + (62_369_000 as Weight) + // Standard Error: 9_000 + .saturating_add((1_779_000 as Weight).saturating_mul(r as Weight)) } fn instr_global_set(r: u32, ) -> Weight { - (56_926_000 as Weight) - // Standard Error: 22_000 - .saturating_add((1_741_000 as Weight).saturating_mul(r as Weight)) + (61_063_000 as Weight) + // Standard Error: 11_000 + .saturating_add((1_751_000 as Weight).saturating_mul(r as Weight)) } fn instr_memory_current(r: u32, ) -> Weight { - (53_287_000 as Weight) + (39_781_000 as Weight) // Standard Error: 14_000 - .saturating_add((769_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((1_370_000 as Weight).saturating_mul(r as Weight)) } fn instr_memory_grow(r: u32, ) -> Weight { - (38_009_000 as Weight) - // Standard Error: 2_445_000 - .saturating_add((637_570_000 as Weight).saturating_mul(r as Weight)) + (50_983_000 as Weight) + // Standard Error: 4_238_000 + .saturating_add((618_956_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64clz(r: u32, ) -> Weight { - (55_460_000 as Weight) - // Standard Error: 10_000 - .saturating_add((1_222_000 as Weight).saturating_mul(r as Weight)) + (42_490_000 as Weight) + // Standard Error: 13_000 + .saturating_add((1_937_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ctz(r: u32, ) -> Weight { - (55_058_000 as Weight) + (42_296_000 as Weight) // Standard Error: 13_000 - .saturating_add((1_251_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((1_943_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64popcnt(r: u32, ) -> Weight { - (55_308_000 as Weight) - // Standard Error: 11_000 - .saturating_add((1_229_000 as Weight).saturating_mul(r as Weight)) + (42_263_000 as Weight) + // Standard Error: 13_000 + .saturating_add((1_944_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64eqz(r: u32, ) -> Weight { - (55_303_000 as Weight) - // Standard Error: 11_000 - .saturating_add((1_231_000 as Weight).saturating_mul(r as Weight)) + (42_087_000 as Weight) + // Standard Error: 13_000 + .saturating_add((1_952_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64extendsi32(r: u32, ) -> Weight { - (44_768_000 as Weight) - // Standard Error: 11_000 - .saturating_add((1_428_000 as Weight).saturating_mul(r as Weight)) + (48_024_000 as Weight) + // Standard Error: 9_000 + .saturating_add((1_783_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64extendui32(r: u32, ) -> Weight { - (44_806_000 as Weight) - // Standard Error: 11_000 - .saturating_add((1_429_000 as Weight).saturating_mul(r as Weight)) + (48_056_000 as Weight) + // Standard Error: 10_000 + .saturating_add((1_780_000 as Weight).saturating_mul(r as Weight)) } fn instr_i32wrapi64(r: u32, ) -> Weight { - (55_388_000 as Weight) - // Standard Error: 11_000 - .saturating_add((1_228_000 as Weight).saturating_mul(r as Weight)) + (42_352_000 as Weight) + // Standard Error: 13_000 + .saturating_add((1_928_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64eq(r: u32, ) -> Weight { - (50_942_000 as Weight) + (45_439_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_826_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_382_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ne(r: u32, ) -> Weight { - (50_964_000 as Weight) + (45_232_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_825_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_393_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64lts(r: u32, ) -> Weight { - (50_749_000 as Weight) - // Standard Error: 10_000 - .saturating_add((1_830_000 as Weight).saturating_mul(r as Weight)) + (45_351_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_386_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ltu(r: u32, ) -> Weight { - (50_581_000 as Weight) + (45_448_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_837_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_385_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64gts(r: u32, ) -> Weight { - (50_720_000 as Weight) - // Standard Error: 10_000 - .saturating_add((1_831_000 as Weight).saturating_mul(r as Weight)) + (45_275_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_386_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64gtu(r: u32, ) -> Weight { - (50_686_000 as Weight) + (45_180_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_832_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_396_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64les(r: u32, ) -> Weight { - (50_914_000 as Weight) - // Standard Error: 12_000 - .saturating_add((1_828_000 as Weight).saturating_mul(r as Weight)) + (45_396_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_400_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64leu(r: u32, ) -> Weight { - (51_126_000 as Weight) + (45_597_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_820_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_392_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64ges(r: u32, ) -> Weight { - (50_960_000 as Weight) + (45_137_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_829_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_400_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64geu(r: u32, ) -> Weight { - (50_796_000 as Weight) - // Standard Error: 11_000 - .saturating_add((1_831_000 as Weight).saturating_mul(r as Weight)) + (45_091_000 as Weight) + // Standard Error: 10_000 + .saturating_add((2_401_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64add(r: u32, ) -> Weight { - (51_011_000 as Weight) - // Standard Error: 12_000 - .saturating_add((1_826_000 as Weight).saturating_mul(r as Weight)) + (45_547_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_380_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64sub(r: u32, ) -> Weight { - (50_892_000 as Weight) + (45_435_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_828_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_383_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64mul(r: u32, ) -> Weight { - (50_980_000 as Weight) + (45_244_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_825_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_389_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64divs(r: u32, ) -> Weight { - (51_245_000 as Weight) + (45_253_000 as Weight) // Standard Error: 11_000 - .saturating_add((2_427_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((3_046_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64divu(r: u32, ) -> Weight { - (51_083_000 as Weight) + (45_339_000 as Weight) // Standard Error: 11_000 - .saturating_add((2_218_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_711_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64rems(r: u32, ) -> Weight { - (50_861_000 as Weight) + (45_312_000 as Weight) // Standard Error: 11_000 - .saturating_add((2_446_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((3_020_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64remu(r: u32, ) -> Weight { - (51_001_000 as Weight) - // Standard Error: 10_000 - .saturating_add((2_183_000 as Weight).saturating_mul(r as Weight)) + (45_397_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_739_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64and(r: u32, ) -> Weight { - (50_819_000 as Weight) + (45_282_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_829_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_387_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64or(r: u32, ) -> Weight { - (51_368_000 as Weight) - // Standard Error: 12_000 - .saturating_add((1_814_000 as Weight).saturating_mul(r as Weight)) + (45_508_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_390_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64xor(r: u32, ) -> Weight { - (50_952_000 as Weight) + (45_089_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_828_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_397_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64shl(r: u32, ) -> Weight { - (50_996_000 as Weight) - // Standard Error: 11_000 - .saturating_add((1_827_000 as Weight).saturating_mul(r as Weight)) + (45_878_000 as Weight) + // Standard Error: 13_000 + .saturating_add((2_395_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64shrs(r: u32, ) -> Weight { - (51_061_000 as Weight) + (45_459_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_826_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_401_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64shru(r: u32, ) -> Weight { - (50_931_000 as Weight) - // Standard Error: 12_000 - .saturating_add((1_832_000 as Weight).saturating_mul(r as Weight)) + (45_663_000 as Weight) + // Standard Error: 11_000 + .saturating_add((2_398_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64rotl(r: u32, ) -> Weight { - (50_652_000 as Weight) + (45_739_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_832_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_393_000 as Weight).saturating_mul(r as Weight)) } fn instr_i64rotr(r: u32, ) -> Weight { - (50_817_000 as Weight) + (45_290_000 as Weight) // Standard Error: 11_000 - .saturating_add((1_830_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_392_000 as Weight).saturating_mul(r as Weight)) } } From 69a17931949da0fc8766dcd14022babfbcf93693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Sun, 21 Nov 2021 12:40:21 +0100 Subject: [PATCH 07/34] Simplify keep check for contract accounts - Make sure that the "base deposit" for each contract >= ed - Remove now obsolete checks when sneding away free balance --- frame/contracts/common/src/lib.rs | 12 +++---- frame/contracts/fixtures/drain.wat | 4 ++- frame/contracts/src/exec.rs | 52 ++++++---------------------- frame/contracts/src/lib.rs | 16 ++------- frame/contracts/src/storage/meter.rs | 9 ++++- frame/contracts/src/tests.rs | 8 ++++- frame/contracts/src/wasm/runtime.rs | 6 ++-- 7 files changed, 39 insertions(+), 68 deletions(-) diff --git a/frame/contracts/common/src/lib.rs b/frame/contracts/common/src/lib.rs index cef739f46b4ef..28b5d30f46392 100644 --- a/frame/contracts/common/src/lib.rs +++ b/frame/contracts/common/src/lib.rs @@ -161,20 +161,20 @@ pub enum Code { } /// The amount of balance that was either charged or refunded in order to pay for storage. -#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, Clone)] +#[derive(Eq, PartialEq, Ord, PartialOrd, 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), + /// 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), } impl Default for StorageDeposit { diff --git a/frame/contracts/fixtures/drain.wat b/frame/contracts/fixtures/drain.wat index 3e2a68805a8af..b28253d9aa00c 100644 --- a/frame/contracts/fixtures/drain.wat +++ b/frame/contracts/fixtures/drain.wat @@ -34,6 +34,8 @@ ) ;; Try to self-destruct by sending full balance to the 0 address. + ;; All the free balance will be send away but the contract's which is a valid + ;; thing to do because the storage deposits will keep the account alive. (call $assert (i32.eq (call $seal_transfer @@ -42,7 +44,7 @@ (i32.const 0) ;; Pointer to the buffer with value to transfer (i32.const 8) ;; Length of the buffer with value to transfer ) - (i32.const 5) ;; ReturnCode::TransferFailed + (i32.const 0) ;; ReturnCode::Success ) ) ) diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 53ffbd12c74af..7dbcccde3e2d1 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -615,6 +615,11 @@ where nested_storage.charge(&storage::meter::Diff { bytes_added: contract.encoded_size() as u32, items_added: 1, + // We make sure that this deposit that is charged for every new contract + // is at least the existential deposit. This makes sure that the contract's + // account is never dusted even when all the free balance is removed. This + // saves us all kind of checks when dealing with balance transfers. + require_ed: true, ..Default::default() })?; (account_id, contract, executable, ExportedFunction::Constructor, Some(trie_seed)) @@ -812,42 +817,14 @@ where } /// Transfer some funds from `from` to `to`. - /// - /// We only allow allow for draining all funds of the sender if `allow_death` is - /// is specified as `true`. Otherwise, any transfer that would bring the sender below the - /// subsistence threshold (for contracts) or the existential deposit (for plain accounts) - /// results in an error. fn transfer( - sender_is_origin: bool, - allow_death: bool, + existence_requirement: ExistenceRequirement, from: &T::AccountId, to: &T::AccountId, value: BalanceOf, ) -> DispatchResult { - if value == 0u32.into() { - return Ok(()) - } - - 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(), - Error::::TransferFailed, - ); - ExistenceRequirement::KeepAlive - }, - (false, true) => ExistenceRequirement::KeepAlive, - }; - T::Currency::transfer(from, to, value, existence_requirement) .map_err(|_| Error::::TransferFailed)?; - Ok(()) } @@ -855,14 +832,8 @@ where fn initial_transfer(&self) -> DispatchResult { let frame = self.top_frame(); let value = frame.value_transferred; - let min_balance = T::Currency::minimum_balance(); - - // New contracts must receive at least the minimum balance as endowment. - if frame.entry_point == ExportedFunction::Constructor && value < min_balance { - return Err(>::EndowmentTooLow.into()) - } - Self::transfer(self.caller_is_origin(), false, self.caller(), &frame.account_id, value) + Self::transfer(ExistenceRequirement::KeepAlive, self.caller(), &frame.account_id, value) } /// Wether the caller is the initiator of the call stack. @@ -1002,8 +973,7 @@ where frame.nested_storage.terminate(&info); Storage::::queue_trie_for_deletion(&info)?; >::transfer( - false, - true, + ExistenceRequirement::AllowDeath, &frame.account_id, beneficiary, T::Currency::free_balance(&frame.account_id), @@ -1018,7 +988,7 @@ where } fn transfer(&mut self, to: &T::AccountId, value: BalanceOf) -> DispatchResult { - Self::transfer(false, false, &self.top_frame().account_id, to, value) + Self::transfer(ExistenceRequirement::KeepAlive, &self.top_frame().account_id, to, value) } fn get_storage(&mut self, key: &StorageKey) -> Option> { @@ -1359,7 +1329,7 @@ mod tests { set_balance(&origin, 100); set_balance(&dest, 0); - MockStack::transfer(true, false, &origin, &dest, 55).unwrap(); + MockStack::transfer(ExistenceRequirement::KeepAlive, &origin, &dest, 55).unwrap(); assert_eq!(get_balance(&origin), 45); assert_eq!(get_balance(&dest), 55); @@ -1412,7 +1382,7 @@ mod tests { ExtBuilder::default().build().execute_with(|| { set_balance(&origin, 0); - let result = MockStack::transfer(true, false, &origin, &dest, 100); + let result = MockStack::transfer(ExistenceRequirement::KeepAlive, &origin, &dest, 100); assert_eq!(result, Err(Error::::TransferFailed.into())); assert_eq!(get_balance(&origin), 0); diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 0e0f5bb522782..d55101ac7dbc1 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -478,20 +478,8 @@ pub mod pallet { OutOfGas, /// The output buffer supplied to a contract API call was too small. OutputBufferTooSmall, - /// When creating a new contract at least the minimum balance must be provided - /// as endowment. If that is not the case this error is returned. - EndowmentTooLow, - /// Performing the requested transfer failed. Most probably the transfer would have - /// brought the contract's account below the minimum balance. Other possible reasons - /// are balance locks or reservations on the contract's account. - /// - /// # Note - /// - /// Any transfer must leave the minimum balance as **free** balance in the contract. - /// This is different from a usual keep alive transfer where the reserved balance - /// also counts into the minimum balance. This is enforced because otherwise a refund - /// of a storage deposit could remove a contract's account as it was only kept alive - /// by this deposit. + /// Performing the requested transfer failed. Probably because there isn't enough + /// free balance in the sender's account. TransferFailed, /// Performing a call was denied because the calling depth reached the limit /// of what is specified in the schedule. diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs index 03eefacf729f3..f1d9474e02d28 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -20,7 +20,7 @@ use crate::{storage::ContractInfo, BalanceOf, Config, Error}; use frame_support::{ dispatch::{DispatchError, DispatchResult}, - traits::{tokens::BalanceStatus, Get, ReservableCurrency}, + traits::{tokens::BalanceStatus, Currency, Get, ReservableCurrency}, DefaultNoBound, }; use pallet_contracts_primitives::StorageDeposit as Deposit; @@ -123,6 +123,9 @@ pub struct Diff { pub items_added: u32, /// How many storage items were removed from storage. pub items_removed: u32, + /// If set to true the derived deposit will always a `Charge` larger than the + /// the existential deposit. + pub require_ed: bool, } impl Diff { @@ -152,6 +155,10 @@ impl Diff { )); } + if self.require_ed { + deposit = deposit.max(Deposit::Charge(T::Currency::minimum_balance())) + } + deposit } } diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index ec3b689322e99..ca8396bda8660 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -678,8 +678,14 @@ fn cannot_self_destruct_through_draning() { assert_matches!(ContractInfoOf::::get(&addr), Some(_)); // Call BOB which makes it send all funds to the zero address - // The contract code asserts that the correct error value is returned. + // The contract code asserts that the transfer was successful assert_ok!(Contracts::call(Origin::signed(ALICE), addr, 0, GAS_LIMIT, None, vec![])); + + // Make sure the account wasn't remove by sending all free balance away. + assert!( + ::Currency::total_balance(&ALICE) >= + ::Currency::minimum_balance() + ); }); } diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index c5540a1790d12..3e61b98b7dfc0 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -59,8 +59,8 @@ pub enum ReturnCode { _BelowSubsistenceThreshold = 4, /// See [`Error::TransferFailed`]. TransferFailed = 5, - /// See [`Error::EndowmentTooLow`]. - EndowmentTooLow = 6, + /// Deprecated and no longer returned: Endowment is no longer required. + _EndowmentTooLow = 6, /// No code could be found at the supplied code hash. CodeNotFound = 7, /// The contract that was called is no contract (a plain account). @@ -606,13 +606,11 @@ where use ReturnCode::*; let transfer_failed = Error::::TransferFailed.into(); - let endowment_too_low = Error::::EndowmentTooLow.into(); let no_code = Error::::CodeNotFound.into(); let not_found = Error::::ContractNotFound.into(); match from { x if x == transfer_failed => Ok(TransferFailed), - x if x == endowment_too_low => Ok(EndowmentTooLow), x if x == no_code => Ok(CodeNotFound), x if x == not_found => Ok(NotCallable), err => Err(err), From 8f5d92b2ebe69109fc4a40f542370717381ee7a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Sun, 21 Nov 2021 13:26:59 +0100 Subject: [PATCH 08/34] Remove unused imports and functions --- frame/contracts/src/exec.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 7dbcccde3e2d1..bebd8995a0049 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -23,7 +23,6 @@ use crate::{ }; use frame_support::{ dispatch::{DispatchError, DispatchResult, DispatchResultWithPostInfo, Dispatchable}, - ensure, pallet_prelude::Encode, storage::{with_transaction, TransactionOutcome}, traits::{Contains, Currency, ExistenceRequirement, Get, OriginTrait, Randomness, Time}, @@ -34,7 +33,7 @@ use pallet_contracts_primitives::ExecReturnValue; use smallvec::{Array, SmallVec}; use sp_core::crypto::UncheckedFrom; use sp_io::crypto::secp256k1_ecdsa_recover_compressed; -use sp_runtime::traits::{Convert, Saturating}; +use sp_runtime::traits::Convert; use sp_std::{marker::PhantomData, mem, prelude::*}; pub type AccountIdOf = ::AccountId; @@ -836,11 +835,6 @@ where Self::transfer(ExistenceRequirement::KeepAlive, self.caller(), &frame.account_id, value) } - /// Wether the caller is the initiator of the call stack. - fn caller_is_origin(&self) -> bool { - self.frames.is_empty() - } - /// Reference to the current (top) frame. fn top_frame(&self) -> &Frame { top_frame!(self) From 0929c0bf21c5161d22630472a8baef73c58b029c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Thu, 25 Nov 2021 15:13:28 +0100 Subject: [PATCH 09/34] Rename storage_limit to storage_deposit_limit --- frame/contracts/rpc/runtime-api/src/lib.rs | 6 +-- frame/contracts/rpc/src/lib.rs | 36 ++++++------- frame/contracts/src/exec.rs | 2 +- frame/contracts/src/lib.rs | 62 +++++++++++----------- frame/contracts/src/storage/meter.rs | 2 +- frame/contracts/src/tests.rs | 16 +++--- 6 files changed, 62 insertions(+), 62 deletions(-) diff --git a/frame/contracts/rpc/runtime-api/src/lib.rs b/frame/contracts/rpc/runtime-api/src/lib.rs index ac6f6475cc9a0..a154c50321353 100644 --- a/frame/contracts/rpc/runtime-api/src/lib.rs +++ b/frame/contracts/rpc/runtime-api/src/lib.rs @@ -45,7 +45,7 @@ sp_api::decl_runtime_apis! { dest: AccountId, value: Balance, gas_limit: u64, - storage_limit: Option, + storage_deposit_limit: Option, input_data: Vec, ) -> ContractExecResult; @@ -56,7 +56,7 @@ sp_api::decl_runtime_apis! { origin: AccountId, endowment: Balance, gas_limit: u64, - storage_limit: Option, + storage_deposit_limit: Option, code: Code, data: Vec, salt: Vec, @@ -69,7 +69,7 @@ sp_api::decl_runtime_apis! { fn upload_code( origin: AccountId, code: Vec, - storage_limit: Option, + storage_deposit_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 aba8f684463cf..b18a664521586 100644 --- a/frame/contracts/rpc/src/lib.rs +++ b/frame/contracts/rpc/src/lib.rs @@ -80,7 +80,7 @@ pub struct CallRequest { dest: AccountId, value: NumberOrHex, gas_limit: NumberOrHex, - storage_limit: Option, + storage_deposit_limit: Option, input_data: Bytes, } @@ -92,7 +92,7 @@ pub struct InstantiateRequest { origin: AccountId, endowment: NumberOrHex, gas_limit: NumberOrHex, - storage_limit: Option, + storage_deposit_limit: Option, code: Code, data: Bytes, salt: Bytes, @@ -105,7 +105,7 @@ pub struct InstantiateRequest { pub struct CodeUploadRequest { origin: AccountId, code: Bytes, - storage_limit: Option, + storage_deposit_limit: Option, } /// Contracts RPC methods. @@ -206,16 +206,16 @@ where // If the block hash is not supplied assume the best block. self.client.info().best_hash)); - let CallRequest { origin, dest, value, gas_limit, storage_limit, input_data } = + let CallRequest { origin, dest, value, gas_limit, storage_deposit_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()?; + let storage_deposit_limit: Option = + storage_deposit_limit.map(|l| decode_hex(l, "balance")).transpose()?; limit_gas(gas_limit)?; - api.call(&at, origin, dest, value, gas_limit, storage_limit, input_data.to_vec()) + api.call(&at, origin, dest, value, gas_limit, storage_deposit_limit, input_data.to_vec()) .map_err(runtime_error_into_rpc_err) } @@ -229,13 +229,13 @@ where // If the block hash is not supplied assume the best block. self.client.info().best_hash)); - let InstantiateRequest { origin, endowment, gas_limit, storage_limit, code, data, salt } = + let InstantiateRequest { origin, endowment, gas_limit, storage_deposit_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()?; + let storage_deposit_limit: Option = + storage_deposit_limit.map(|l| decode_hex(l, "balance")).transpose()?; limit_gas(gas_limit)?; api.instantiate( @@ -243,7 +243,7 @@ where origin, endowment, gas_limit, - storage_limit, + storage_deposit_limit, code, data.to_vec(), salt.to_vec(), @@ -261,12 +261,12 @@ where // 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 CodeUploadRequest { origin, code, storage_deposit_limit } = upload_request; - let storage_limit: Option = - storage_limit.map(|l| decode_hex(l, "balance")).transpose()?; + let storage_deposit_limit: Option = + storage_deposit_limit.map(|l| decode_hex(l, "balance")).transpose()?; - api.upload_code(&at, origin, code.to_vec(), storage_limit) + api.upload_code(&at, origin, code.to_vec(), storage_deposit_limit) .map_err(runtime_error_into_rpc_err) } @@ -349,7 +349,7 @@ mod tests { ) .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.storage_deposit_limit.map(|l| l.into_u256()), Some(5000.into())); assert_eq!(req.value.into_u256(), U256::from(1234567890987654321u128)); } @@ -373,7 +373,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.storage_deposit_limit, None); assert_eq!(&*req.data, [0x42, 0x99].as_ref()); assert_eq!(&*req.salt, [0x99, 0x88].as_ref()); let code = match req.code { @@ -398,7 +398,7 @@ mod tests { .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())); + assert_eq!(req.storage_deposit_limit.map(|l| l.into_u256()), Some(5000.into())); } #[test] diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index bebd8995a0049..f63a6fee253db 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -284,7 +284,7 @@ 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. + /// The storage meter makes sure that the storage deposit limit is obeyed. storage_meter: &'a mut storage::meter::Meter, /// The timestamp at the point of call stack instantiation. timestamp: MomentOf, diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index d55101ac7dbc1..22b414ae4a143 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -269,7 +269,7 @@ pub mod pallet { /// * `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 + /// * `storage_deposit_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. /// @@ -284,7 +284,7 @@ pub mod pallet { dest: ::Source, #[pallet::compact] value: BalanceOf, #[pallet::compact] gas_limit: Weight, - storage_limit: Option< as codec::HasCompact>::Type>, + storage_deposit_limit: Option< as codec::HasCompact>::Type>, data: Vec, ) -> DispatchResultWithPostInfo { let origin = ensure_signed(origin)?; @@ -294,7 +294,7 @@ pub mod pallet { dest, value, gas_limit, - storage_limit.map(Into::into), + storage_deposit_limit.map(Into::into), data, None, ); @@ -312,7 +312,7 @@ pub mod pallet { /// /// * `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 + /// * `storage_deposit_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. @@ -338,7 +338,7 @@ pub mod pallet { origin: OriginFor, #[pallet::compact] endowment: BalanceOf, #[pallet::compact] gas_limit: Weight, - storage_limit: Option< as codec::HasCompact>::Type>, + storage_deposit_limit: Option< as codec::HasCompact>::Type>, code: Vec, data: Vec, salt: Vec, @@ -350,7 +350,7 @@ pub mod pallet { origin, endowment, gas_limit, - storage_limit.map(Into::into), + storage_deposit_limit.map(Into::into), Code::Upload(Bytes(code)), data, salt, @@ -374,7 +374,7 @@ pub mod pallet { origin: OriginFor, #[pallet::compact] endowment: BalanceOf, #[pallet::compact] gas_limit: Weight, - storage_limit: Option< as codec::HasCompact>::Type>, + storage_deposit_limit: Option< as codec::HasCompact>::Type>, code_hash: CodeHash, data: Vec, salt: Vec, @@ -385,7 +385,7 @@ pub mod pallet { origin, endowment, gas_limit, - storage_limit.map(Into::into), + storage_deposit_limit.map(Into::into), Code::Existing(code_hash), data, salt, @@ -413,10 +413,10 @@ pub mod pallet { pub fn upload_code( origin: OriginFor, code: Vec, - storage_limit: Option< as codec::HasCompact>::Type>, + storage_deposit_limit: Option< as codec::HasCompact>::Type>, ) -> DispatchResult { let origin = ensure_signed(origin)?; - Self::bare_upload_code(origin, code, storage_limit.map(Into::into)).map(|_| ()) + Self::bare_upload_code(origin, code, storage_deposit_limit.map(Into::into)).map(|_| ()) } /// Remove the code stored under `code_hash` and refund the deposit to its owner. @@ -530,9 +530,9 @@ 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. + /// Origin doesn't have enough balance to pay for the storage deposit limit. StorageLimitTooHigh, - /// More storage was created than allowed by the storage limit. + /// More storage was created than allowed by the storage deposit limit. StorageExhausted, /// Code removal was denied because the code is still in use by at least one contract. CodeInUse, @@ -607,7 +607,7 @@ where dest: T::AccountId, value: BalanceOf, gas_limit: Weight, - storage_limit: Option>, + storage_deposit_limit: Option>, data: Vec, debug: bool, ) -> ContractExecResult> { @@ -617,7 +617,7 @@ where dest, value, gas_limit, - storage_limit, + storage_deposit_limit, data, debug_message.as_mut(), ); @@ -646,7 +646,7 @@ where origin: T::AccountId, endowment: BalanceOf, gas_limit: Weight, - storage_limit: Option>, + storage_deposit_limit: Option>, code: Code>, data: Vec, salt: Vec, @@ -657,7 +657,7 @@ where origin, endowment, gas_limit, - storage_limit, + storage_deposit_limit, code, data, salt, @@ -682,13 +682,13 @@ where pub fn bare_upload_code( origin: T::AccountId, code: Vec, - storage_limit: Option>, + storage_deposit_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); + if let Some(storage_deposit_limit) = storage_deposit_limit { + ensure!(storage_deposit_limit <= deposit, >::StorageExhausted); } let result = CodeUploadReturnValue { code_hash: *module.code_hash(), deposit }; module.store()?; @@ -755,14 +755,14 @@ where dest: T::AccountId, value: BalanceOf, gas_limit: Weight, - storage_limit: Option>, + storage_deposit_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 storage_deposit_limit = + storage_deposit_limit.unwrap_or_else(|| Self::max_storage_deposit_limit(&origin, value)); let mut gas_meter = GasMeter::new(gas_limit); - let mut storage_meter = match StorageMeter::new(origin.clone(), storage_limit) { + let mut storage_meter = match StorageMeter::new(origin.clone(), storage_deposit_limit) { Ok(meter) => meter, Err(err) => return InternalCallOutput { @@ -792,14 +792,14 @@ where origin: T::AccountId, endowment: BalanceOf, gas_limit: Weight, - storage_limit: Option>, + storage_deposit_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_limit = + storage_deposit_limit.unwrap_or_else(|| Self::max_storage_deposit_limit(&origin, endowment)); let mut storage_deposit = Default::default(); let mut gas_meter = GasMeter::new(gas_limit); let try_exec = || { @@ -821,8 +821,8 @@ where // 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)?; + storage_deposit_limit = + storage_deposit_limit.checked_sub(&deposit).ok_or(>::StorageExhausted)?; (executable, StorageDeposit::Charge(deposit)) }, Code::Existing(hash) => ( @@ -830,7 +830,7 @@ where Default::default(), ), }; - let mut storage_meter = StorageMeter::new(origin.clone(), storage_limit)?; + let mut storage_meter = StorageMeter::new(origin.clone(), storage_deposit_limit)?; let result = ExecStack::>::run_instantiate( origin, executable, @@ -848,8 +848,8 @@ where 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 { + /// If no storage deposit limit is specified we use this function. + fn max_storage_deposit_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/storage/meter.rs b/frame/contracts/src/storage/meter.rs index f1d9474e02d28..e0ae700e89dbf 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -46,7 +46,7 @@ pub type GenericMeter = RawMeter; /// /// 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. + /// This will be called to inform the implementer about the `storage_deposit_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. diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index ca8396bda8660..16c7acf5910af 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -392,7 +392,7 @@ fn instantiate_and_call_and_deposit_event() { let min_balance = ::Currency::minimum_balance(); let endowment = min_balance * 100; - // We determine the storage limit after uploading because it depends on ALICEs free + // We determine the storage deposit 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)); @@ -400,7 +400,7 @@ fn instantiate_and_call_and_deposit_event() { initialize_block(2); // Check at the end to get hash on error easily - let storage_limit = >::max_storage_limit(&ALICE, endowment); + let storage_deposit_limit = >::max_storage_deposit_limit(&ALICE, endowment); assert_ok!(Contracts::instantiate( Origin::signed(ALICE), endowment, @@ -417,7 +417,7 @@ fn instantiate_and_call_and_deposit_event() { // that the contract itself occupies. let events = System::events(); let storage_cost = deposits(&events).next().unwrap(); - let unused = storage_limit - storage_cost; + let unused = storage_deposit_limit - storage_cost; assert_eq!( events, @@ -426,7 +426,7 @@ fn instantiate_and_call_and_deposit_event() { phase: Phase::Initialization, event: Event::Balances(pallet_balances::Event::Reserved { who: ALICE, - amount: storage_limit, + amount: storage_deposit_limit, }), topics: vec![], }, @@ -748,7 +748,7 @@ fn self_destruct_works() { initialize_block(2); // We need to gather this before the call. Otherwise it was already reserved. - let storage_limit = >::max_storage_limit(&ALICE, 0); + let storage_deposit_limit = >::max_storage_deposit_limit(&ALICE, 0); // There is only one user of this contract. assert_refcount!(&code_hash, 1); @@ -772,7 +772,7 @@ fn self_destruct_works() { // all the storage deposits of the contract. let events = System::events(); let storage_refund = deposits(&events).next().unwrap(); - let unreserved = storage_limit + storage_refund; + let unreserved = storage_deposit_limit + storage_refund; pretty_assertions::assert_eq!( events, @@ -781,7 +781,7 @@ fn self_destruct_works() { phase: Phase::Initialization, event: Event::Balances(pallet_balances::Event::Reserved { who: ALICE, - amount: storage_limit, + amount: storage_deposit_limit, }), topics: vec![], }, @@ -1934,7 +1934,7 @@ fn gas_estimation_call_runtime() { dest: addr_callee, value: 0, gas_limit: GAS_LIMIT / 3, - storage_limit: None, + storage_deposit_limit: None, data: vec![], }); let result = Contracts::bare_call( From 6489f7f958ce7bda2fb548c1fee839e538eb80d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Thu, 25 Nov 2021 15:17:32 +0100 Subject: [PATCH 10/34] cargo fmt --- frame/contracts/rpc/src/lib.rs | 11 +++++++++-- frame/contracts/src/lib.rs | 21 +++++++++++---------- frame/contracts/src/storage/meter.rs | 3 ++- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/frame/contracts/rpc/src/lib.rs b/frame/contracts/rpc/src/lib.rs index b18a664521586..0a2dc5385a02e 100644 --- a/frame/contracts/rpc/src/lib.rs +++ b/frame/contracts/rpc/src/lib.rs @@ -229,8 +229,15 @@ where // If the block hash is not supplied assume the best block. self.client.info().best_hash)); - let InstantiateRequest { origin, endowment, gas_limit, storage_deposit_limit, code, data, salt } = - instantiate_request; + let InstantiateRequest { + origin, + endowment, + gas_limit, + storage_deposit_limit, + code, + data, + salt, + } = instantiate_request; let endowment: Balance = decode_hex(endowment, "balance")?; let gas_limit: Weight = decode_hex(gas_limit, "weight")?; diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 22b414ae4a143..d51df8d2fe68e 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -269,8 +269,8 @@ pub mod pallet { /// * `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_deposit_limit`: The maximum amount of balance that can be charged from the caller to - /// pay for the storage consumed. + /// * `storage_deposit_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 @@ -312,8 +312,8 @@ pub mod pallet { /// /// * `endowment`: The balance to transfer from the `origin` to the newly created contract. /// * `gas_limit`: The gas limit enforced when executing the constructor. - /// * `storage_deposit_limit`: The maximum amount of balance that can be charged/reserved from the - /// caller to pay for the storage consumed. + /// * `storage_deposit_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`]. @@ -759,8 +759,8 @@ where data: Vec, debug_message: Option<&mut Vec>, ) -> InternalCallOutput { - let storage_deposit_limit = - storage_deposit_limit.unwrap_or_else(|| Self::max_storage_deposit_limit(&origin, value)); + let storage_deposit_limit = storage_deposit_limit + .unwrap_or_else(|| Self::max_storage_deposit_limit(&origin, value)); let mut gas_meter = GasMeter::new(gas_limit); let mut storage_meter = match StorageMeter::new(origin.clone(), storage_deposit_limit) { Ok(meter) => meter, @@ -798,8 +798,8 @@ where salt: Vec, debug_message: Option<&mut Vec>, ) -> InternalInstantiateOutput { - let mut storage_deposit_limit = - storage_deposit_limit.unwrap_or_else(|| Self::max_storage_deposit_limit(&origin, endowment)); + let mut storage_deposit_limit = storage_deposit_limit + .unwrap_or_else(|| Self::max_storage_deposit_limit(&origin, endowment)); let mut storage_deposit = Default::default(); let mut gas_meter = GasMeter::new(gas_limit); let try_exec = || { @@ -821,8 +821,9 @@ where // storage meter because it is not transfered to the contract but // reserved on the uploading account. let deposit = executable.open_deposit(); - storage_deposit_limit = - storage_deposit_limit.checked_sub(&deposit).ok_or(>::StorageExhausted)?; + storage_deposit_limit = storage_deposit_limit + .checked_sub(&deposit) + .ok_or(>::StorageExhausted)?; (executable, StorageDeposit::Charge(deposit)) }, Code::Existing(hash) => ( diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs index e0ae700e89dbf..a8bcbe1771af9 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -46,7 +46,8 @@ pub type GenericMeter = RawMeter; /// /// 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_deposit_limit` of the meter. + /// This will be called to inform the implementer about the `storage_deposit_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. From 4d0bb09dc719c640fe2abf3771b880aa8deaa2ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Fri, 26 Nov 2021 10:27:23 +0100 Subject: [PATCH 11/34] Fix typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michael Müller --- frame/contracts/fixtures/drain.wat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/contracts/fixtures/drain.wat b/frame/contracts/fixtures/drain.wat index b28253d9aa00c..94c6518422667 100644 --- a/frame/contracts/fixtures/drain.wat +++ b/frame/contracts/fixtures/drain.wat @@ -34,8 +34,8 @@ ) ;; Try to self-destruct by sending full balance to the 0 address. - ;; All the free balance will be send away but the contract's which is a valid - ;; thing to do because the storage deposits will keep the account alive. + ;; All the *free* balance will be send away, which is a valid thing to do + ;; because the storage deposits will keep the account alive. (call $assert (i32.eq (call $seal_transfer From 2b1723bafdd5a911bb6016b9682daa0ca314dd4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Fri, 26 Nov 2021 10:32:44 +0100 Subject: [PATCH 12/34] Finish up rename of storage_limit --- bin/node/executor/tests/basic.rs | 4 ++-- bin/node/runtime/src/lib.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs index 46efe3f60a799..a5aaaa0842d1c 100644 --- a/bin/node/executor/tests/basic.rs +++ b/bin/node/executor/tests/basic.rs @@ -702,7 +702,7 @@ fn deploying_wasm_contract_should_work() { pallet_contracts::Call::instantiate_with_code:: { endowment: 1000 * DOLLARS + min_balance, gas_limit: 500_000_000, - storage_limit: None, + storage_deposit_limit: None, code: transfer_code, data: Vec::new(), salt: Vec::new(), @@ -715,7 +715,7 @@ fn deploying_wasm_contract_should_work() { dest: sp_runtime::MultiAddress::Id(addr.clone()), value: 10, gas_limit: 500_000_000, - storage_limit: None, + storage_deposit_limit: None, data: vec![0x00, 0x01, 0x02, 0x03], }), }, diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 660e727f91634..f166a443aecd1 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1517,32 +1517,32 @@ impl_runtime_apis! { dest: AccountId, value: Balance, gas_limit: u64, - storage_limit: Option, + storage_deposit_limit: Option, input_data: Vec, ) -> pallet_contracts_primitives::ContractExecResult { - Contracts::bare_call(origin, dest, value, gas_limit, storage_limit, input_data, true) + Contracts::bare_call(origin, dest, value, gas_limit, storage_deposit_limit, input_data, true) } fn instantiate( origin: AccountId, endowment: Balance, gas_limit: u64, - storage_limit: Option, + storage_deposit_limit: Option, code: pallet_contracts_primitives::Code, data: Vec, salt: Vec, ) -> pallet_contracts_primitives::ContractInstantiateResult { - Contracts::bare_instantiate(origin, endowment, gas_limit, storage_limit, code, data, salt, true) + Contracts::bare_instantiate(origin, endowment, gas_limit, storage_deposit_limit, code, data, salt, true) } fn upload_code( origin: AccountId, code: Vec, - storage_limit: Option, + storage_deposit_limit: Option, ) -> pallet_contracts_primitives::CodeUploadResult { - Contracts::bare_upload_code(origin, code, storage_limit) + Contracts::bare_upload_code(origin, code, storage_deposit_limit) } fn get_storage( From a8f24e879160587fc659c5cd483e6bf70cf60a26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Fri, 26 Nov 2021 11:02:16 +0100 Subject: [PATCH 13/34] Fix rpc tests --- frame/contracts/rpc/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/contracts/rpc/src/lib.rs b/frame/contracts/rpc/src/lib.rs index 0a2dc5385a02e..7ebf458404644 100644 --- a/frame/contracts/rpc/src/lib.rs +++ b/frame/contracts/rpc/src/lib.rs @@ -349,7 +349,7 @@ mod tests { "dest": "5DRakbLVnjVrW6niwLfHGW24EeCEvDAFGEXrtaYS5M4ynoom", "value": "0x112210f4B16c1cb1", "gasLimit": 1000000000000, - "storageLimit": 5000, + "storageDepositLimit": 5000, "inputData": "0x8c97db39" } "#, @@ -398,7 +398,7 @@ mod tests { { "origin": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", "code": "0x8c97db39", - "storageLimit": 5000 + "storageDepositLimit": 5000 } "#, ) From 23474cfd6c900b036820a7c3227a17ff895f0f4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Fri, 26 Nov 2021 11:17:57 +0100 Subject: [PATCH 14/34] Make use of `StorageDepositLimitTooHigh` --- frame/contracts/src/lib.rs | 2 +- frame/contracts/src/storage/meter.rs | 1 + frame/contracts/src/wasm/code_cache.rs | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index d51df8d2fe68e..7070f342e022b 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -531,7 +531,7 @@ pub mod pallet { /// 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 deposit limit. - StorageLimitTooHigh, + StorageDepositLimitTooHigh, /// More storage was created than allowed by the storage deposit limit. StorageExhausted, /// Code removal was denied because the code is still in use by at least one contract. diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs index a8bcbe1771af9..8e6aa4b4fc129 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -323,6 +323,7 @@ where impl Ext for ReservingExt { fn reserve_limit(origin: &T::AccountId, limit: &BalanceOf) -> DispatchResult { T::Currency::reserve(origin, *limit) + .map_err(|_| >::StorageDepositLimitTooHigh.into()) } fn unreserve_limit(origin: &T::AccountId, limit: &BalanceOf, deposit: &DepositOf) { diff --git a/frame/contracts/src/wasm/code_cache.rs b/frame/contracts/src/wasm/code_cache.rs index 9b60f3344c733..f00b9e515f22f 100644 --- a/frame/contracts/src/wasm/code_cache.rs +++ b/frame/contracts/src/wasm/code_cache.rs @@ -91,7 +91,8 @@ where ); // 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)?; + T::Currency::reserve(&owner_info.owner, owner_info.deposit) + .map_err(|_| >::StorageDepositLimitTooHigh)?; owner_info.refcount = if instantiated { 1 } else { 0 }; >::insert(&code_hash, orig_code); >::insert(&code_hash, owner_info); From 38bb5c284bfcdb5bf7ef8f4a0039fd278ea0e386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Fri, 26 Nov 2021 12:11:52 +0100 Subject: [PATCH 15/34] Add tests and fix bugs discovered by tests --- frame/contracts/common/src/lib.rs | 7 + frame/contracts/src/lib.rs | 10 +- frame/contracts/src/storage/meter.rs | 348 ++++++++++++++++++++++++- frame/contracts/src/tests.rs | 237 ++++++++++++++++- frame/contracts/src/wasm/code_cache.rs | 2 +- 5 files changed, 587 insertions(+), 17 deletions(-) diff --git a/frame/contracts/common/src/lib.rs b/frame/contracts/common/src/lib.rs index 28b5d30f46392..272aaff082e41 100644 --- a/frame/contracts/common/src/lib.rs +++ b/frame/contracts/common/src/lib.rs @@ -191,6 +191,13 @@ impl StorageDeposit { Self::Refund(_) => Zero::zero(), } } + + pub fn is_zero(&self) -> bool { + match self { + Self::Charge(amount) => amount.is_zero(), + Self::Refund(amount) => amount.is_zero(), + } + } } impl StorageDeposit diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 7070f342e022b..2a023a81d29be 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -530,10 +530,10 @@ 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 deposit limit. - StorageDepositLimitTooHigh, + /// Origin doesn't have enough balance to pay the required storage deposits. + StorageDepositNotEnoughFunds, /// More storage was created than allowed by the storage deposit limit. - StorageExhausted, + StorageDepositLimitExhausted, /// Code removal was denied because the code is still in use by at least one contract. CodeInUse, } @@ -688,7 +688,7 @@ where let module = PrefabWasmModule::from_code(code, &schedule, origin)?; let deposit = module.open_deposit(); if let Some(storage_deposit_limit) = storage_deposit_limit { - ensure!(storage_deposit_limit <= deposit, >::StorageExhausted); + ensure!(storage_deposit_limit >= deposit, >::StorageDepositLimitExhausted); } let result = CodeUploadReturnValue { code_hash: *module.code_hash(), deposit }; module.store()?; @@ -823,7 +823,7 @@ where let deposit = executable.open_deposit(); storage_deposit_limit = storage_deposit_limit .checked_sub(&deposit) - .ok_or(>::StorageExhausted)?; + .ok_or(>::StorageDepositLimitExhausted)?; (executable, StorageDeposit::Charge(deposit)) }, Code::Existing(hash) => ( diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs index 8e6aa4b4fc129..4081c0e5e8147 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -150,7 +150,7 @@ impl Diff { 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 { + } else if self.items_removed > self.items_added { deposit = deposit.saturating_add(&Deposit::Refund( per_item.saturating_mul((self.items_removed - self.items_added).into()), )); @@ -195,7 +195,7 @@ where /// 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 { + pub fn nested(&self) -> RawMeter { RawMeter { origin: None, limit: self.available(), @@ -234,15 +234,23 @@ where 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); + // This case can happen when costs change due to a runtime upgrade. + let amount = { + let corrected_amount = (*amount).min(info.storage_deposit); + let correction = (*amount).saturating_sub(corrected_amount); + self.total_deposit.saturating_sub(&Deposit::Refund(correction)); + *amount = corrected_amount; + corrected_amount + }; + 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); + if !absorbed.own_deposit.is_zero() { + E::charge(origin, &contract, &absorbed.own_deposit); + } } /// The amount of balance that is still available from the original `limit`. @@ -296,7 +304,7 @@ where let total_deposit = self.total_deposit.saturating_add(&deposit); if let Deposit::Charge(amount) = total_deposit { if amount > self.limit { - return Err(>::StorageExhausted.into()) + return Err(>::StorageDepositLimitExhausted.into()) } } self.total_deposit = total_deposit; @@ -323,7 +331,7 @@ where impl Ext for ReservingExt { fn reserve_limit(origin: &T::AccountId, limit: &BalanceOf) -> DispatchResult { T::Currency::reserve(origin, *limit) - .map_err(|_| >::StorageDepositLimitTooHigh.into()) + .map_err(|_| >::StorageDepositNotEnoughFunds.into()) } fn unreserve_limit(origin: &T::AccountId, limit: &BalanceOf, deposit: &DepositOf) { @@ -366,3 +374,327 @@ mod private { impl Sealed for super::Root {} impl Sealed for super::Nested {} } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + exec::AccountIdOf, + tests::{Test, ALICE, BOB, CHARLIE}, + }; + use pretty_assertions::assert_eq; + use std::cell::RefCell; + + type TestMeter = RawMeter; + + thread_local! { + static TEST_EXT: RefCell = RefCell::new(Default::default()); + } + + #[derive(Debug, PartialEq, Eq)] + struct Reserve { + origin: AccountIdOf, + limit: BalanceOf, + } + + #[derive(Debug, PartialEq, Eq)] + struct Unreserve { + origin: AccountIdOf, + limit: BalanceOf, + deposit: DepositOf, + } + + #[derive(Debug, PartialEq, Eq)] + struct Charge { + origin: AccountIdOf, + contract: AccountIdOf, + amount: DepositOf, + } + + #[derive(Default, Debug, PartialEq, Eq)] + struct TestExt { + reserves: Vec, + unreserves: Vec, + charges: Vec, + } + + impl TestExt { + fn clear(&mut self) { + self.reserves.clear(); + self.unreserves.clear(); + self.charges.clear(); + } + } + + impl Ext for TestExt { + fn reserve_limit(origin: &AccountIdOf, limit: &BalanceOf) -> DispatchResult { + TEST_EXT.with(|ext| { + ext.borrow_mut() + .reserves + .push(Reserve { origin: origin.clone(), limit: limit.clone() }) + }); + Ok(()) + } + + fn unreserve_limit( + origin: &AccountIdOf, + limit: &BalanceOf, + deposit: &DepositOf, + ) { + TEST_EXT.with(|ext| { + ext.borrow_mut().unreserves.push(Unreserve { + origin: origin.clone(), + limit: limit.clone(), + deposit: deposit.clone(), + }) + }); + } + + fn charge( + origin: &AccountIdOf, + contract: &AccountIdOf, + amount: &DepositOf, + ) { + TEST_EXT.with(|ext| { + ext.borrow_mut().charges.push(Charge { + origin: origin.clone(), + contract: contract.clone(), + amount: amount.clone(), + }) + }); + } + } + + fn clear_ext() { + TEST_EXT.with(|ext| ext.borrow_mut().clear()) + } + + fn new_info(deposit: BalanceOf) -> ContractInfo { + use crate::storage::Storage; + use sp_runtime::traits::Hash; + + ContractInfo:: { + trie_id: >::generate_trie_id(&ALICE, 42), + code_hash: ::Hashing::hash(b"42"), + storage_deposit: deposit, + _reserved: Default::default(), + } + } + + #[test] + fn new_reserves_balance_works() { + clear_ext(); + + let _meter = TestMeter::new(ALICE, 1_000).unwrap(); + + TEST_EXT.with(|ext| { + assert_eq!( + *ext.borrow(), + TestExt { + reserves: vec![Reserve { origin: ALICE, limit: 1_000 }], + ..Default::default() + } + ) + }); + } + + #[test] + fn unreserve_on_drop_works() { + clear_ext(); + + let meter = TestMeter::new(ALICE, 1_000).unwrap(); + drop(meter); + + TEST_EXT.with(|ext| { + assert_eq!( + *ext.borrow(), + TestExt { + reserves: vec![Reserve { origin: ALICE, limit: 1_000 }], + unreserves: vec![Unreserve { + origin: ALICE, + limit: 1_000, + deposit: Deposit::Charge(0) + }], + ..Default::default() + } + ) + }); + } + + #[test] + fn empty_charge_works() { + clear_ext(); + + let mut meter = TestMeter::new(ALICE, 1_000).unwrap(); + assert_eq!(meter.available(), 1_000); + + // an empty charge foes not create a `Charge` entry + let mut nested0 = meter.nested(); + nested0.charge(&Default::default()).unwrap(); + meter.absorb(nested0, &ALICE, &BOB, None); + + drop(meter); + + TEST_EXT.with(|ext| { + assert_eq!( + *ext.borrow(), + TestExt { + reserves: vec![Reserve { origin: ALICE, limit: 1_000 }], + unreserves: vec![Unreserve { + origin: ALICE, + limit: 1_000, + deposit: Deposit::Charge(0) + }], + ..Default::default() + } + ) + }); + } + + #[test] + fn existential_deposit_works() { + clear_ext(); + + let mut meter = TestMeter::new(ALICE, 1_000).unwrap(); + assert_eq!(meter.available(), 1_000); + + // a `Refund` will be turned into a `Charge(ed)` which is intended behaviour + let mut nested0 = meter.nested(); + nested0.charge(&Diff { require_ed: true, ..Default::default() }).unwrap(); + nested0 + .charge(&Diff { bytes_removed: 1, require_ed: true, ..Default::default() }) + .unwrap(); + meter.absorb(nested0, &ALICE, &BOB, None); + + drop(meter); + + TEST_EXT.with(|ext| { + assert_eq!( + *ext.borrow(), + TestExt { + reserves: vec![Reserve { origin: ALICE, limit: 1_000 }], + unreserves: vec![Unreserve { + origin: ALICE, + limit: 1_000, + deposit: Deposit::Charge(2) + }], + charges: vec![Charge { + origin: ALICE, + contract: BOB, + amount: Deposit::Charge(::Currency::minimum_balance() * 2), + }], + ..Default::default() + } + ) + }); + } + + #[test] + fn charging_works() { + clear_ext(); + + let mut meter = TestMeter::new(ALICE, 1_000).unwrap(); + assert_eq!(meter.available(), 1_000); + + let mut nested0_info = new_info(100); + let mut nested0 = meter.nested(); + nested0 + .charge(&Diff { + bytes_added: 10, + bytes_removed: 5, + items_added: 1, + items_removed: 2, + ..Default::default() + }) + .unwrap(); + nested0.charge(&Diff { bytes_removed: 1, ..Default::default() }).unwrap(); + + let mut nested1_info = new_info(50); + let mut nested1 = nested0.nested(); + nested1.charge(&Diff { items_removed: 5, ..Default::default() }).unwrap(); + nested0.absorb(nested1, &ALICE, &CHARLIE, Some(&mut nested1_info)); + + // Trying to refund more than is available in the contract will cap the charge + // to that value. This value is `1` in this case. + let mut nested2_info = new_info(1); + let mut nested2 = nested0.nested(); + nested2.charge(&Diff { bytes_removed: 7, ..Default::default() }).unwrap(); + nested0.absorb(nested2, &ALICE, &CHARLIE, Some(&mut nested2_info)); + + meter.absorb(nested0, &ALICE, &BOB, Some(&mut nested0_info)); + drop(meter); + + assert_eq!(nested0_info.storage_deposit, 102); + assert_eq!(nested1_info.storage_deposit, 40); + assert_eq!(nested2_info.storage_deposit, 0); + + TEST_EXT.with(|ext| { + assert_eq!( + *ext.borrow(), + TestExt { + reserves: vec![Reserve { origin: ALICE, limit: 1_000 }], + unreserves: vec![Unreserve { + origin: ALICE, + limit: 1_000, + deposit: Deposit::Refund(15) + }], + charges: vec![ + Charge { origin: ALICE, contract: CHARLIE, amount: Deposit::Refund(10) }, + Charge { origin: ALICE, contract: CHARLIE, amount: Deposit::Refund(1) }, + Charge { origin: ALICE, contract: BOB, amount: Deposit::Charge(2) } + ], + ..Default::default() + } + ) + }); + } + + #[test] + fn termination_works() { + clear_ext(); + + let mut meter = TestMeter::new(ALICE, 1_000).unwrap(); + assert_eq!(meter.available(), 1_000); + + let mut nested0 = meter.nested(); + nested0 + .charge(&Diff { + bytes_added: 5, + bytes_removed: 1, + items_added: 3, + items_removed: 1, + ..Default::default() + }) + .unwrap(); + nested0.charge(&Diff { items_added: 2, ..Default::default() }).unwrap(); + + let nested1_info = new_info(400); + let mut nested1 = nested0.nested(); + nested1.charge(&Diff { items_removed: 5, ..Default::default() }).unwrap(); + nested1.charge(&Diff { bytes_added: 20, ..Default::default() }).unwrap(); + nested1.terminate(&nested1_info); + nested0.absorb(nested1, &ALICE, &CHARLIE, None); + + meter.absorb(nested0, &ALICE, &BOB, None); + drop(meter); + + TEST_EXT.with(|ext| { + assert_eq!( + *ext.borrow(), + TestExt { + reserves: vec![Reserve { origin: ALICE, limit: 1_000 }], + unreserves: vec![Unreserve { + origin: ALICE, + limit: 1_000, + deposit: Deposit::Refund(388) + }], + charges: vec![ + Charge { origin: ALICE, contract: CHARLIE, amount: Deposit::Refund(400) }, + Charge { origin: ALICE, contract: BOB, amount: Deposit::Charge(12) } + ], + ..Default::default() + } + ) + }); + } +} diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 16c7acf5910af..5e5446a0cc4ea 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -24,12 +24,12 @@ use crate::{ storage::Storage, wasm::{PrefabWasmModule, ReturnCode as RuntimeReturnCode}, weights::WeightInfo, - BalanceOf, Config, ContractInfoOf, Error, Pallet, Schedule, + BalanceOf, CodeStorage, Config, ContractInfoOf, Error, Pallet, Schedule, }; use assert_matches::assert_matches; use codec::Encode; use frame_support::{ - assert_err, assert_err_ignore_postinfo, assert_ok, + assert_err, assert_err_ignore_postinfo, assert_noop, assert_ok, dispatch::DispatchErrorWithPostInfo, parameter_types, storage::child, @@ -242,7 +242,7 @@ parameter_types! { pub MySchedule: Schedule = >::default(); pub const TransactionByteFee: u64 = 0; pub const DepositPerByte: BalanceOf = 1; - pub const DepositPerItem: BalanceOf = 1; + pub const DepositPerItem: BalanceOf = 2; } impl Convert> for Test { @@ -2017,3 +2017,234 @@ fn ecdsa_recover() { assert_eq!(result.data.as_ref(), &EXPECTED_COMPRESSED_PUBLIC_KEY); }) } + +#[test] +fn upload_code_works() { + let (wasm, code_hash) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + + // Drop previous events + initialize_block(2); + + assert!(!>::contains_key(code_hash)); + assert_ok!(Contracts::upload_code( + Origin::signed(ALICE), + wasm, + Some(codec::Compact(1_000)) + )); + assert!(>::contains_key(code_hash)); + + let events = System::events(); + + assert_eq!( + events, + vec![ + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Reserved { + who: ALICE, + amount: 181, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Contracts(crate::Event::CodeStored { code_hash }), + topics: vec![], + }, + ] + ); + }); +} + +#[test] +fn upload_code_limit_too_low() { + let (wasm, _code_hash) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + + // Drop previous events + initialize_block(2); + + assert_noop!( + Contracts::upload_code(Origin::signed(ALICE), wasm, Some(codec::Compact(100))), + >::StorageDepositLimitExhausted, + ); + + assert_eq!(System::events(), vec![]); + }); +} + +#[test] +fn upload_code_not_enough_balance() { + let (wasm, _code_hash) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 150); + + // Drop previous events + initialize_block(2); + + assert_noop!( + Contracts::upload_code(Origin::signed(ALICE), wasm, Some(codec::Compact(1_000))), + >::StorageDepositNotEnoughFunds, + ); + + assert_eq!(System::events(), vec![]); + }); +} + +#[test] +fn remove_code_works() { + let (wasm, code_hash) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + + // Drop previous events + initialize_block(2); + + assert_ok!(Contracts::upload_code( + Origin::signed(ALICE), + wasm, + Some(codec::Compact(1_000)) + )); + + assert!(>::contains_key(code_hash)); + assert_ok!(Contracts::remove_code(Origin::signed(ALICE), code_hash)); + assert!(!>::contains_key(code_hash)); + + let events = System::events(); + + assert_eq!( + events, + vec![ + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Reserved { + who: ALICE, + amount: 181, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Contracts(crate::Event::CodeStored { code_hash }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Unreserved { + who: ALICE, + amount: 181, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Contracts(crate::Event::CodeRemoved { code_hash }), + topics: vec![], + }, + ] + ); + }); +} + +#[test] +fn remove_code_wrong_origin() { + let (wasm, code_hash) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + + // Drop previous events + initialize_block(2); + + assert_ok!(Contracts::upload_code( + Origin::signed(ALICE), + wasm, + Some(codec::Compact(1_000)) + )); + + assert_noop!( + Contracts::remove_code(Origin::signed(BOB), code_hash), + sp_runtime::traits::BadOrigin, + ); + + let events = System::events(); + + assert_eq!( + events, + vec![ + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Reserved { + who: ALICE, + amount: 181, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Contracts(crate::Event::CodeStored { code_hash }), + topics: vec![], + }, + ] + ); + }); +} + +#[test] +fn remove_code_in_use() { + let (wasm, code_hash) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + + assert_ok!(Contracts::instantiate_with_code( + Origin::signed(ALICE), + 0, + GAS_LIMIT, + None, + wasm, + vec![], + vec![], + )); + + // Drop previous events + initialize_block(2); + + assert_noop!( + Contracts::remove_code(Origin::signed(BOB), code_hash), + >::CodeInUse, + ); + + let events = System::events(); + + assert_eq!(events, vec![]); + }); +} + +#[test] +fn remove_code_not_found() { + let (_wasm, code_hash) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + + // Drop previous events + initialize_block(2); + + assert_noop!( + Contracts::remove_code(Origin::signed(ALICE), code_hash), + >::CodeNotFound, + ); + + let events = System::events(); + + assert_eq!(events, vec![]); + }); +} diff --git a/frame/contracts/src/wasm/code_cache.rs b/frame/contracts/src/wasm/code_cache.rs index f00b9e515f22f..823d6ed34a671 100644 --- a/frame/contracts/src/wasm/code_cache.rs +++ b/frame/contracts/src/wasm/code_cache.rs @@ -92,7 +92,7 @@ where // 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) - .map_err(|_| >::StorageDepositLimitTooHigh)?; + .map_err(|_| >::StorageDepositNotEnoughFunds)?; owner_info.refcount = if instantiated { 1 } else { 0 }; >::insert(&code_hash, orig_code); >::insert(&code_hash, owner_info); From 1ea021b8125f1e627aa4dc346baafbea1f5bad1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Sun, 28 Nov 2021 17:14:02 +0100 Subject: [PATCH 16/34] Add storage migration --- frame/contracts/src/exec.rs | 6 +- frame/contracts/src/lib.rs | 2 +- frame/contracts/src/migration.rs | 125 ++++++++++++++++++++++++--- frame/contracts/src/storage.rs | 10 +-- frame/contracts/src/storage/meter.rs | 1 - frame/contracts/src/tests.rs | 8 +- frame/contracts/src/wasm/mod.rs | 8 -- frame/contracts/src/wasm/prepare.rs | 2 - 8 files changed, 124 insertions(+), 38 deletions(-) diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index f63a6fee253db..02c080949b553 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -1910,10 +1910,10 @@ mod tests { let code_bob = MockLoader::insert(Call, |ctx, _| { if ctx.input_data[0] == 0 { let info = ctx.ext.contract_info(); - assert_eq!(info._reserved, None); - info._reserved = Some(()); + assert_eq!(info.storage_deposit, 0); + info.storage_deposit = 42; assert_eq!(ctx.ext.call(0, CHARLIE, 0, vec![], true), exec_trapped()); - assert_eq!(ctx.ext.contract_info()._reserved, Some(())); + assert_eq!(ctx.ext.contract_info().storage_deposit, 42); } exec_success() }); diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 2a023a81d29be..f5665e428c647 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -134,7 +134,7 @@ type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; /// The current storage version. -const STORAGE_VERSION: StorageVersion = StorageVersion::new(5); +const STORAGE_VERSION: StorageVersion = StorageVersion::new(6); #[frame_support::pallet] pub mod pallet { diff --git a/frame/contracts/src/migration.rs b/frame/contracts/src/migration.rs index d1f0164967683..464e1edc4a98e 100644 --- a/frame/contracts/src/migration.rs +++ b/frame/contracts/src/migration.rs @@ -15,12 +15,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{Config, Pallet, Weight}; +use crate::{BalanceOf, CodeHash, Config, Pallet, TrieId, Weight}; +use codec::{Decode, Encode}; use frame_support::{ + codec, generate_storage_alias, storage::migration, traits::{Get, PalletInfoAccess}, + Identity, Twox64Concat, }; -use sp_std::prelude::*; +use sp_std::{marker::PhantomData, prelude::*}; pub fn migrate() -> Weight { use frame_support::traits::StorageVersion; @@ -38,6 +41,11 @@ pub fn migrate() -> Weight { StorageVersion::new(5).put::>(); } + if version < 6 { + weight = weight.saturating_add(v6::migrate::()); + StorageVersion::new(6).put::>(); + } + weight } @@ -54,10 +62,6 @@ mod v4 { /// V5: State rent is removed which obsoletes some fields in `ContractInfo`. mod v5 { use super::*; - use crate::{BalanceOf, CodeHash, TrieId}; - use codec::{Decode, Encode}; - use frame_support::{codec, generate_storage_alias, Twox64Concat}; - use sp_std::marker::PhantomData; type AliveContractInfo = RawAliveContractInfo, BalanceOf, ::BlockNumber>; @@ -94,13 +98,13 @@ mod v5 { trie_id: TrieId, } - type ContractInfo = RawContractInfo>; + pub type ContractInfo = RawContractInfo>; #[derive(Encode, Decode)] - struct RawContractInfo { - trie_id: TrieId, - code_hash: CodeHash, - _reserved: Option<()>, + pub struct RawContractInfo { + pub trie_id: TrieId, + pub code_hash: CodeHash, + pub _reserved: Option<()>, } #[derive(Encode, Decode)] @@ -142,3 +146,102 @@ mod v5 { weight } } + +/// V6: Added storage deposits +mod v6 { + use super::*; + + #[derive(Encode, Decode)] + struct OldPrefabWasmModule { + #[codec(compact)] + instruction_weights_version: u32, + #[codec(compact)] + initial: u32, + #[codec(compact)] + maximum: u32, + #[codec(compact)] + refcount: u64, + _reserved: Option<()>, + code: Vec, + original_code_len: u32, + } + + #[derive(Encode, Decode)] + struct PrefabWasmModule { + #[codec(compact)] + instruction_weights_version: u32, + #[codec(compact)] + initial: u32, + #[codec(compact)] + maximum: u32, + code: Vec, + } + + use v5::ContractInfo as OldContractInfo; + + #[derive(Encode, Decode)] + pub struct RawContractInfo { + trie_id: TrieId, + code_hash: CodeHash, + storage_deposit: Balance, + } + + #[derive(Encode, Decode)] + pub struct OwnerInfo { + owner: T::AccountId, + #[codec(compact)] + deposit: BalanceOf, + #[codec(compact)] + refcount: u64, + } + + type ContractInfo = RawContractInfo, BalanceOf>; + + generate_storage_alias!( + Contracts, + ContractInfoOf => Map<(Twox64Concat, T::AccountId), ContractInfo> + ); + + generate_storage_alias!( + Contracts, + CodeStorage => Map<(Identity, CodeHash), PrefabWasmModule> + ); + + generate_storage_alias!( + Contracts, + OwnerInfoOf => Map<(Identity, CodeHash), OwnerInfo> + ); + + pub fn migrate() -> Weight { + let mut weight: Weight = 0; + + >::translate(|_key, old: OldContractInfo| { + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); + Some(ContractInfo:: { + trie_id: old.trie_id, + code_hash: old.code_hash, + storage_deposit: Default::default(), + }) + }); + + >::translate(|key, old: OldPrefabWasmModule| { + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); + >::insert( + key, + OwnerInfo { + refcount: old.refcount, + owner: Default::default(), + deposit: Default::default(), + }, + ); + Some(PrefabWasmModule { + instruction_weights_version: old.instruction_weights_version, + initial: old.initial, + maximum: old.maximum, + code: old.code, + }) + }); + + weight + } +} diff --git a/frame/contracts/src/storage.rs b/frame/contracts/src/storage.rs index 549f2a9c368c1..f76517aed1a2c 100644 --- a/frame/contracts/src/storage.rs +++ b/frame/contracts/src/storage.rs @@ -52,8 +52,6 @@ pub struct RawContractInfo { 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 { @@ -149,12 +147,8 @@ where return Err(Error::::DuplicateContract.into()) } - let contract = ContractInfo:: { - code_hash: ch, - trie_id, - storage_deposit: >::zero(), - _reserved: None, - }; + let contract = + ContractInfo:: { code_hash: ch, trie_id, storage_deposit: >::zero() }; Ok(contract) } diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs index 4081c0e5e8147..32ce5a92eaf0c 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -477,7 +477,6 @@ mod tests { trie_id: >::generate_trie_id(&ALICE, 42), code_hash: ::Hashing::hash(b"42"), storage_deposit: deposit, - _reserved: Default::default(), } } diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 5e5446a0cc4ea..48f8fd1810e18 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -2045,7 +2045,7 @@ fn upload_code_works() { phase: Phase::Initialization, event: Event::Balances(pallet_balances::Event::Reserved { who: ALICE, - amount: 181, + amount: 180, }), topics: vec![], }, @@ -2126,7 +2126,7 @@ fn remove_code_works() { phase: Phase::Initialization, event: Event::Balances(pallet_balances::Event::Reserved { who: ALICE, - amount: 181, + amount: 180, }), topics: vec![], }, @@ -2139,7 +2139,7 @@ fn remove_code_works() { phase: Phase::Initialization, event: Event::Balances(pallet_balances::Event::Unreserved { who: ALICE, - amount: 181, + amount: 180, }), topics: vec![], }, @@ -2183,7 +2183,7 @@ fn remove_code_wrong_origin() { phase: Phase::Initialization, event: Event::Balances(pallet_balances::Event::Reserved { who: ALICE, - amount: 181, + amount: 180, }), topics: vec![], }, diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index 1fe105d33197a..8347e982a7cc0 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -65,14 +65,6 @@ pub struct PrefabWasmModule { /// The maximum memory size of a contract's sandbox. #[codec(compact)] maximum: u32, - /// 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 - /// type parameter to a new struct that contains the fields that we want to add. - /// That new struct would also contain a reserved field for its future extensions. - /// This works because in SCALE `None` is encoded independently from the type parameter - /// of the option. - _reserved: Option<()>, /// Code instrumented with the latest schedule. code: Vec, /// The uninstrumented, pristine version of the code. diff --git a/frame/contracts/src/wasm/prepare.rs b/frame/contracts/src/wasm/prepare.rs index 0347e12dd6785..9afa10c33bbbd 100644 --- a/frame/contracts/src/wasm/prepare.rs +++ b/frame/contracts/src/wasm/prepare.rs @@ -408,7 +408,6 @@ fn do_preparation( instruction_weights_version: schedule.instruction_weights.version, initial, maximum, - _reserved: None, code, code_hash: T::Hashing::hash(&original_code), original_code: Some(original_code), @@ -489,7 +488,6 @@ pub mod benchmarking { instruction_weights_version: schedule.instruction_weights.version, initial: memory_limits.0, maximum: memory_limits.1, - _reserved: None, code: contract_module.into_wasm_code()?, code_hash: T::Hashing::hash(&original_code), original_code: Some(original_code), From 0420871d0b0f844f97b6f96af8e345564bfd9793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Mon, 29 Nov 2021 11:56:05 +0100 Subject: [PATCH 17/34] Don't use u128 in RPC --- Cargo.lock | 1 + frame/contracts/common/Cargo.toml | 2 ++ frame/contracts/common/src/lib.rs | 47 +++++++++++++++++++++++++++++-- frame/contracts/rpc/src/lib.rs | 9 ++++-- 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 08186dca36c5c..690d24b7ccf13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5440,6 +5440,7 @@ dependencies = [ "scale-info", "serde", "sp-core", + "sp-rpc", "sp-runtime", "sp-std", ] diff --git a/frame/contracts/common/Cargo.toml b/frame/contracts/common/Cargo.toml index c30efc6869403..5f52ce7fcfe55 100644 --- a/frame/contracts/common/Cargo.toml +++ b/frame/contracts/common/Cargo.toml @@ -21,6 +21,7 @@ serde = { version = "1", features = ["derive"], optional = true } # Substrate Dependencies (This crate should not rely on frame) sp-core = { version = "4.0.0-dev", path = "../../../primitives/core", default-features = false } sp-std = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/std" } +sp-rpc = { version = "4.0.0-dev", path = "../../../primitives/rpc", optional = true } sp-runtime = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/runtime" } [features] @@ -31,5 +32,6 @@ std = [ "sp-core/std", "sp-runtime/std", "sp-std/std", + "sp-rpc", "serde", ] diff --git a/frame/contracts/common/src/lib.rs b/frame/contracts/common/src/lib.rs index 272aaff082e41..dcc77365ed288 100644 --- a/frame/contracts/common/src/lib.rs +++ b/frame/contracts/common/src/lib.rs @@ -31,12 +31,22 @@ use sp_std::prelude::*; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; +#[cfg(feature = "std")] +use sp_rpc::number::NumberOrHex; + /// Result type of a `bare_call` or `bare_instantiate` call. /// /// It contains the execution result together with some auxiliary information. #[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] +#[cfg_attr( + feature = "std", + serde( + rename_all = "camelCase", + bound(serialize = "R: Serialize, NumberOrHex: From, Balance: Copy"), + bound(deserialize = "R: Deserialize<'de>, Balance: TryFrom") + ) +)] pub struct ContractResult { /// How much gas was consumed during execution. pub gas_consumed: u64, @@ -163,17 +173,26 @@ pub enum Code { /// The amount of balance that was either charged or refunded in order to pay for storage. #[derive(Eq, PartialEq, Ord, PartialOrd, Encode, Decode, RuntimeDebug, Clone)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] +#[cfg_attr( + feature = "std", + serde( + rename_all = "camelCase", + bound(serialize = "NumberOrHex: From, Balance: Copy"), + bound(deserialize = "Balance: TryFrom") + ) +)] pub enum StorageDeposit { /// The transaction reduced storage consumption. /// /// This means that the specified amount of balance was transferred from the involved /// contracts to the call origin. + #[cfg_attr(feature = "std", serde(with = "as_hex"))] Refund(Balance), /// The transaction increased overall storage usage. /// /// This means that the specified amount of balance was transferred from the call origin /// to the contracts involved. + #[cfg_attr(feature = "std", serde(with = "as_hex"))] Charge(Balance), } @@ -276,3 +295,27 @@ mod as_string { Ok(String::deserialize(deserializer)?.into_bytes()) } } + +#[cfg(feature = "std")] +mod as_hex { + use super::*; + use serde::{de::Error as _, Deserializer, Serializer}; + + pub fn serialize(balance: &Balance, serializer: S) -> Result + where + S: Serializer, + NumberOrHex: From, + Balance: Copy, + { + NumberOrHex::from(*balance).serialize(serializer) + } + + pub fn deserialize<'de, D, Balance>(deserializer: D) -> Result + where + D: Deserializer<'de>, + Balance: TryFrom, + { + Balance::try_from(NumberOrHex::deserialize(deserializer)?) + .map_err(|_| D::Error::custom("Cannot decode NumberOrHex to Balance")) + } +} diff --git a/frame/contracts/rpc/src/lib.rs b/frame/contracts/rpc/src/lib.rs index 7ebf458404644..603b00c428392 100644 --- a/frame/contracts/rpc/src/lib.rs +++ b/frame/contracts/rpc/src/lib.rs @@ -110,7 +110,11 @@ pub struct CodeUploadRequest { /// Contracts RPC methods. #[rpc] -pub trait ContractsApi { +pub trait ContractsApi +where + NumberOrHex: From, + Balance: Copy + TryFrom, +{ /// Executes a call to a contract. /// /// This call is performed locally without submitting any transactions. Thus executing this @@ -193,7 +197,8 @@ where Hash, >, AccountId: Codec, - Balance: Codec + TryFrom, + Balance: Codec + Copy + TryFrom, + NumberOrHex: From, Hash: Codec, { fn call( From 788716162e68b1a56da62481943d4d633fd93047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Mon, 29 Nov 2021 15:08:51 +0100 Subject: [PATCH 18/34] Fix weight of migration --- frame/contracts/src/migration.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/contracts/src/migration.rs b/frame/contracts/src/migration.rs index 464e1edc4a98e..93eb2e64a1d84 100644 --- a/frame/contracts/src/migration.rs +++ b/frame/contracts/src/migration.rs @@ -225,7 +225,7 @@ mod v6 { }); >::translate(|key, old: OldPrefabWasmModule| { - weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 2)); >::insert( key, OwnerInfo { From 8a10478e737f9d512b79e2a83f322931b66c46f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Mon, 29 Nov 2021 15:20:51 +0100 Subject: [PATCH 19/34] Rename `endowment` to `value` --- bin/node/executor/tests/basic.rs | 4 +- bin/node/runtime/src/lib.rs | 4 +- .../fixtures/destroy_and_transfer.wat | 2 +- frame/contracts/rpc/runtime-api/src/lib.rs | 2 +- frame/contracts/rpc/src/lib.rs | 12 +++--- frame/contracts/src/benchmarking/mod.rs | 40 +++++++++---------- frame/contracts/src/exec.rs | 10 ++--- frame/contracts/src/lib.rs | 22 +++++----- frame/contracts/src/tests.rs | 6 +-- frame/contracts/src/wasm/mod.rs | 8 ++-- frame/contracts/src/wasm/runtime.rs | 3 +- 11 files changed, 53 insertions(+), 60 deletions(-) diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs index a5aaaa0842d1c..f83ffe017cd1c 100644 --- a/bin/node/executor/tests/basic.rs +++ b/bin/node/executor/tests/basic.rs @@ -684,8 +684,6 @@ fn deploying_wasm_contract_should_work() { let addr = pallet_contracts::Pallet::::contract_address(&charlie(), &transfer_ch, &[]); - let min_balance = ::Currency::minimum_balance(); - let time = 42 * 1000; let b = construct_block( &mut new_test_ext(compact_code_unwrap()), @@ -700,7 +698,7 @@ 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 + min_balance, + value: 0, gas_limit: 500_000_000, storage_deposit_limit: None, code: transfer_code, diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 3875793a081ac..c1a99668de10d 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1532,7 +1532,7 @@ impl_runtime_apis! { fn instantiate( origin: AccountId, - endowment: Balance, + value: Balance, gas_limit: u64, storage_deposit_limit: Option, code: pallet_contracts_primitives::Code, @@ -1540,7 +1540,7 @@ impl_runtime_apis! { salt: Vec, ) -> pallet_contracts_primitives::ContractInstantiateResult { - Contracts::bare_instantiate(origin, endowment, gas_limit, storage_deposit_limit, code, data, salt, true) + Contracts::bare_instantiate(origin, value, gas_limit, storage_deposit_limit, code, data, salt, true) } fn upload_code( diff --git a/frame/contracts/fixtures/destroy_and_transfer.wat b/frame/contracts/fixtures/destroy_and_transfer.wat index aa13cd8b81072..2555479555272 100644 --- a/frame/contracts/fixtures/destroy_and_transfer.wat +++ b/frame/contracts/fixtures/destroy_and_transfer.wat @@ -9,7 +9,7 @@ )) (import "env" "memory" (memory 1 1)) - ;; [0, 8) Endowment to send when creating contract. + ;; [0, 8) value to send when creating contract. (data (i32.const 0) "\00\00\01") ;; [8, 16) Value to send when calling contract. diff --git a/frame/contracts/rpc/runtime-api/src/lib.rs b/frame/contracts/rpc/runtime-api/src/lib.rs index a154c50321353..485040fd235cd 100644 --- a/frame/contracts/rpc/runtime-api/src/lib.rs +++ b/frame/contracts/rpc/runtime-api/src/lib.rs @@ -54,7 +54,7 @@ sp_api::decl_runtime_apis! { /// See `pallet_contracts::Pallet::instantiate`. fn instantiate( origin: AccountId, - endowment: Balance, + value: Balance, gas_limit: u64, storage_deposit_limit: Option, code: Code, diff --git a/frame/contracts/rpc/src/lib.rs b/frame/contracts/rpc/src/lib.rs index 603b00c428392..029405d3088e5 100644 --- a/frame/contracts/rpc/src/lib.rs +++ b/frame/contracts/rpc/src/lib.rs @@ -90,7 +90,7 @@ pub struct CallRequest { #[serde(deny_unknown_fields)] pub struct InstantiateRequest { origin: AccountId, - endowment: NumberOrHex, + value: NumberOrHex, gas_limit: NumberOrHex, storage_deposit_limit: Option, code: Code, @@ -236,7 +236,7 @@ where let InstantiateRequest { origin, - endowment, + value, gas_limit, storage_deposit_limit, code, @@ -244,7 +244,7 @@ where salt, } = instantiate_request; - let endowment: Balance = decode_hex(endowment, "balance")?; + let value: Balance = decode_hex(value, "balance")?; let gas_limit: Weight = decode_hex(gas_limit, "weight")?; let storage_deposit_limit: Option = storage_deposit_limit.map(|l| decode_hex(l, "balance")).transpose()?; @@ -253,7 +253,7 @@ where api.instantiate( &at, origin, - endowment, + value, gas_limit, storage_deposit_limit, code, @@ -372,7 +372,7 @@ mod tests { r#" { "origin": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", - "endowment": "0x88", + "value": "0x88", "gasLimit": 42, "code": { "existing": "0x1122" }, "data": "0x4299", @@ -383,7 +383,7 @@ mod tests { .unwrap(); assert_eq!(req.origin, "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL"); - assert_eq!(req.endowment.into_u256(), 0x88.into()); + assert_eq!(req.value.into_u256(), 0x88.into()); assert_eq!(req.gas_limit.into_u256(), 42.into()); assert_eq!(req.storage_deposit_limit, None); assert_eq!(&*req.data, [0x42, 0x99].as_ref()); diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index 65b7198967e63..884fddb6f907f 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -57,7 +57,7 @@ struct Contract { caller: T::AccountId, account_id: T::AccountId, addr: ::Source, - endowment: BalanceOf, + value: BalanceOf, } impl Contract @@ -86,7 +86,7 @@ where module: WasmModule, data: Vec, ) -> Result, &'static str> { - let endowment = T::Currency::minimum_balance(); + let value = 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); @@ -94,7 +94,7 @@ where Contracts::::store_code_raw(module.code, caller.clone())?; Contracts::::instantiate( RawOrigin::Signed(caller.clone()).into(), - endowment, + value, Weight::MAX, None, module.hash, @@ -102,12 +102,8 @@ where salt, )?; - let result = Contract { - caller, - account_id: addr.clone(), - addr: T::Lookup::unlookup(addr), - endowment, - }; + let result = + Contract { caller, account_id: addr.clone(), addr: T::Lookup::unlookup(addr), value }; ContractInfoOf::::insert(&result.account_id, result.info()?); @@ -270,13 +266,13 @@ 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 = T::Currency::minimum_balance(); + let value = 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, None, code, vec![], salt) + }: _(origin, value, Weight::MAX, None, code, vec![], salt) verify { // the contract itself does not trigger any reserves let deposit = T::Currency::reserved_balance(&addr); @@ -284,10 +280,10 @@ benchmarks! { let code_deposit = T::Currency::reserved_balance(&caller); assert_eq!( T::Currency::free_balance(&caller), - caller_funding::() - endowment - deposit - code_deposit, + caller_funding::() - value - deposit - code_deposit, ); - // contract has the full endowment - assert_eq!(T::Currency::free_balance(&addr), endowment); + // contract has the full value + assert_eq!(T::Currency::free_balance(&addr), value); // instantiate should leave a contract Contract::::address_info(&addr)?; } @@ -297,21 +293,21 @@ benchmarks! { instantiate { let s in 0 .. code::max_pages::() * 64; let salt = vec![42u8; (s * 1024) as usize]; - let endowment = T::Currency::minimum_balance(); + let value = 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, caller.clone())?; - }: _(origin, endowment, Weight::MAX, None, hash, vec![], salt) + }: _(origin, value, 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 - deposit); - // contract has the full endowment - assert_eq!(T::Currency::free_balance(&addr), endowment); + // value was removed from the caller + assert_eq!(T::Currency::free_balance(&caller), caller_funding::() - value - deposit); + // contract has the full value + assert_eq!(T::Currency::free_balance(&addr), value); // instantiate should leave a contract Contract::::address_info(&addr)?; } @@ -334,10 +330,10 @@ benchmarks! { 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 + // value and value transfered via call should be removed from the caller assert_eq!( T::Currency::free_balance(&instance.caller), - caller_funding::() - instance.endowment - value - deposit, + caller_funding::() - instance.value - value - deposit, ); // contract should have received the value assert_eq!(T::Currency::free_balance(&instance.account_id), before + value); diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 02c080949b553..386b0f4c74558 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -109,7 +109,7 @@ pub trait Ext: sealing::Sealed { /// /// Returns the original code size of the called contract. /// The newly created account will be associated with `code`. `value` specifies the amount of - /// value transferred from this to the newly created account (also known as endowment). + /// value transferred from this to the newly created account (also known as value). /// /// # Return Value /// @@ -156,7 +156,7 @@ pub trait Ext: sealing::Sealed { /// The `value_transferred` is already added. fn balance(&self) -> BalanceOf; - /// Returns the value transferred along with this call or as endowment. + /// Returns the value transferred along with this call or as value. fn value_transferred(&self) -> BalanceOf; /// Returns a reference to the timestamp of the current block @@ -938,7 +938,7 @@ where &mut self, gas_limit: Weight, code_hash: CodeHash, - endowment: BalanceOf, + value: BalanceOf, input_data: Vec, salt: &[u8], ) -> Result<(AccountIdOf, ExecReturnValue), ExecError> { @@ -951,7 +951,7 @@ where executable, salt, }, - endowment, + value, gas_limit, )?; let account_id = self.top_frame().account_id.clone(); @@ -1660,7 +1660,7 @@ mod tests { &mut gas_meter, &mut storage_meter, &schedule, - 0, // <- zero endowment + 0, // <- zero value vec![], &[], None, diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index f5665e428c647..6fcd8cabf0f4e 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -310,7 +310,7 @@ pub mod pallet { /// /// # Parameters /// - /// * `endowment`: The balance to transfer from the `origin` to the newly created contract. + /// * `value`: The balance to transfer from the `origin` to the newly created contract. /// * `gas_limit`: The gas limit enforced when executing the constructor. /// * `storage_deposit_limit`: The maximum amount of balance that can be charged/reserved /// from the caller to pay for the storage consumed. @@ -325,7 +325,7 @@ pub mod pallet { /// - If the `code_hash` already exists on the chain the underlying `code` will be shared. /// - The destination address is computed based on the sender, code_hash and the salt. /// - The smart-contract account is created at the computed address. - /// - The `endowment` is transferred to the new account. + /// - The `value` is transferred to the new account. /// - The `deploy` function is executed in the context of the newly-created account. #[pallet::weight( T::WeightInfo::instantiate_with_code( @@ -336,7 +336,7 @@ pub mod pallet { )] pub fn instantiate_with_code( origin: OriginFor, - #[pallet::compact] endowment: BalanceOf, + #[pallet::compact] value: BalanceOf, #[pallet::compact] gas_limit: Weight, storage_deposit_limit: Option< as codec::HasCompact>::Type>, code: Vec, @@ -348,7 +348,7 @@ pub mod pallet { let salt_len = salt.len() as u32; let output = Self::internal_instantiate( origin, - endowment, + value, gas_limit, storage_deposit_limit.map(Into::into), Code::Upload(Bytes(code)), @@ -372,7 +372,7 @@ pub mod pallet { )] pub fn instantiate( origin: OriginFor, - #[pallet::compact] endowment: BalanceOf, + #[pallet::compact] value: BalanceOf, #[pallet::compact] gas_limit: Weight, storage_deposit_limit: Option< as codec::HasCompact>::Type>, code_hash: CodeHash, @@ -383,7 +383,7 @@ pub mod pallet { let salt_len = salt.len() as u32; let output = Self::internal_instantiate( origin, - endowment, + value, gas_limit, storage_deposit_limit.map(Into::into), Code::Existing(code_hash), @@ -644,7 +644,7 @@ where /// If set to `true` it returns additional human readable debugging information. pub fn bare_instantiate( origin: T::AccountId, - endowment: BalanceOf, + value: BalanceOf, gas_limit: Weight, storage_deposit_limit: Option>, code: Code>, @@ -655,7 +655,7 @@ where let mut debug_message = if debug { Some(Vec::new()) } else { None }; let output = Self::internal_instantiate( origin, - endowment, + value, gas_limit, storage_deposit_limit, code, @@ -790,7 +790,7 @@ where /// Called by dispatchables and public functions. fn internal_instantiate( origin: T::AccountId, - endowment: BalanceOf, + value: BalanceOf, gas_limit: Weight, storage_deposit_limit: Option>, code: Code>, @@ -799,7 +799,7 @@ where debug_message: Option<&mut Vec>, ) -> InternalInstantiateOutput { let mut storage_deposit_limit = storage_deposit_limit - .unwrap_or_else(|| Self::max_storage_deposit_limit(&origin, endowment)); + .unwrap_or_else(|| Self::max_storage_deposit_limit(&origin, value)); let mut storage_deposit = Default::default(); let mut gas_meter = GasMeter::new(gas_limit); let try_exec = || { @@ -838,7 +838,7 @@ where &mut gas_meter, &mut storage_meter, &schedule, - endowment, + value, data, &salt, debug_message, diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 48f8fd1810e18..ce1c3985265ce 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -390,7 +390,7 @@ 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; + let value = min_balance * 100; // We determine the storage deposit limit after uploading because it depends on ALICEs free // balance which is changed by uploading a module. @@ -400,10 +400,10 @@ fn instantiate_and_call_and_deposit_event() { initialize_block(2); // Check at the end to get hash on error easily - let storage_deposit_limit = >::max_storage_deposit_limit(&ALICE, endowment); + let storage_deposit_limit = >::max_storage_deposit_limit(&ALICE, value); assert_ok!(Contracts::instantiate( Origin::signed(ALICE), - endowment, + value, GAS_LIMIT, None, code_hash, diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index 8347e982a7cc0..1030cfe982e34 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -280,7 +280,7 @@ mod tests { #[derive(Debug, PartialEq, Eq)] struct InstantiateEntry { code_hash: H256, - endowment: u64, + value: u64, data: Vec, gas_left: u64, salt: Vec, @@ -361,13 +361,13 @@ mod tests { &mut self, gas_limit: Weight, code_hash: CodeHash, - endowment: u64, + value: u64, data: Vec, salt: &[u8], ) -> Result<(AccountIdOf, ExecReturnValue), ExecError> { self.instantiates.push(InstantiateEntry { code_hash: code_hash.clone(), - endowment, + value, data: data.to_vec(), gas_left: gas_limit, salt: salt.to_vec(), @@ -783,7 +783,7 @@ mod tests { &mock_ext.instantiates[..], [InstantiateEntry { code_hash, - endowment: 3, + value: 3, data, gas_left: _, salt, diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 0b94ea254175f..aeb72ce5d2170 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -1021,7 +1021,6 @@ define_env!(Env, , // `ReturnCode::CalleeReverted`: Output buffer is returned. // `ReturnCode::CalleeTrapped` // `ReturnCode::TransferFailed` - // `ReturnCode::EndowmentTooLow` // `ReturnCode::CodeNotFound` [seal1] seal_instantiate( ctx, @@ -1216,7 +1215,7 @@ define_env!(Env, , )?) }, - // Stores the value transferred along with this call or as endowment into the supplied buffer. + // Stores the value transferred along with this call or as value into the supplied buffer. // // The value is stored to linear memory at the address pointed to by `out_ptr`. // `out_len_ptr` must point to a u32 value that describes the available space at From 434f487e052bcd9fd95d1c9f7b2e15e5015c95fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Tue, 30 Nov 2021 13:56:53 +0100 Subject: [PATCH 20/34] Fix bug where contract couldn't get funded by a storage deposit - Make sure that contract gets funded from deposits before value is transferred - Don't reserve value at origin because otherwise funding isn't possible - Just transfer free balance and reserve it after the transfer - When refunding make sure that this refund can't dust the contract - Can only happen after a runtime upgrade where costs where upped - Add more tests --- Cargo.lock | 1 + frame/contracts/Cargo.toml | 1 + frame/contracts/fixtures/multi_store.wat | 54 ++ frame/contracts/fixtures/store.wat | 45 ++ frame/contracts/src/exec.rs | 81 +-- frame/contracts/src/lib.rs | 37 +- frame/contracts/src/storage/meter.rs | 335 ++++++------- frame/contracts/src/tests.rs | 597 ++++++++++++++++++----- 8 files changed, 802 insertions(+), 349 deletions(-) create mode 100644 frame/contracts/fixtures/multi_store.wat create mode 100644 frame/contracts/fixtures/store.wat diff --git a/Cargo.lock b/Cargo.lock index 690d24b7ccf13..5ec9380096342 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5402,6 +5402,7 @@ version = "4.0.0-dev" dependencies = [ "assert_matches", "bitflags", + "env_logger 0.9.0", "frame-benchmarking", "frame-support", "frame-system", diff --git a/frame/contracts/Cargo.toml b/frame/contracts/Cargo.toml index 6639d939e1796..431c250b75980 100644 --- a/frame/contracts/Cargo.toml +++ b/frame/contracts/Cargo.toml @@ -46,6 +46,7 @@ sp-std = { version = "4.0.0-dev", default-features = false, path = "../../primit [dev-dependencies] assert_matches = "1" +env_logger = "0.9" hex-literal = "0.3" pretty_assertions = "1" wat = "1" diff --git a/frame/contracts/fixtures/multi_store.wat b/frame/contracts/fixtures/multi_store.wat new file mode 100644 index 0000000000000..2592baf618355 --- /dev/null +++ b/frame/contracts/fixtures/multi_store.wat @@ -0,0 +1,54 @@ +;; Does two stores to two seperate storage items +;; Expects (len0, len1) as input. +(module + (import "seal0" "seal_set_storage" (func $seal_set_storage (param i32 i32 i32))) + (import "seal0" "seal_input" (func $seal_input (param i32 i32))) + (import "env" "memory" (memory 16 16)) + + ;; [0, 32) storage key 0 + (data (i32.const 0) "\01") + + ;; [32, 64) storage key 1 + (data (i32.const 32) "\02") + + ;; [64, 72) buffer where input is copied (expected sizes of storage items) + + ;; [72, 76) size of the input buffer + (data (i32.const 72) "\08") + + (func $assert (param i32) + (block $ok + (br_if $ok + (get_local 0) + ) + (unreachable) + ) + ) + + (func (export "call") + (call $seal_input (i32.const 64) (i32.const 72)) + + ;; assert input size == 8 + (call $assert + (i32.eq + (i32.load (i32.const 72)) + (i32.const 8) + ) + ) + + ;; place a values in storage sizes are specified in the input buffer + ;; we don't care about the contents of the storage item + (call $seal_set_storage + (i32.const 0) ;; Pointer to storage key + (i32.const 0) ;; Pointer to value + (i32.load (i32.const 64)) ;; Size of value + ) + (call $seal_set_storage + (i32.const 32) ;; Pointer to storage key + (i32.const 0) ;; Pointer to value + (i32.load (i32.const 68)) ;; Size of value + ) + ) + + (func (export "deploy")) +) diff --git a/frame/contracts/fixtures/store.wat b/frame/contracts/fixtures/store.wat new file mode 100644 index 0000000000000..9e090d31801f8 --- /dev/null +++ b/frame/contracts/fixtures/store.wat @@ -0,0 +1,45 @@ +;; Stores a value of the passed size. +(module + (import "seal0" "seal_set_storage" (func $seal_set_storage (param i32 i32 i32))) + (import "seal0" "seal_input" (func $seal_input (param i32 i32))) + (import "env" "memory" (memory 16 16)) + + ;; [0, 32) storage key + (data (i32.const 0) "\01") + + ;; [32, 36) buffer where input is copied (expected size of storage item) + + ;; [36, 40) size of the input buffer + (data (i32.const 36) "\04") + + (func $assert (param i32) + (block $ok + (br_if $ok + (get_local 0) + ) + (unreachable) + ) + ) + + (func (export "call") + (call $seal_input (i32.const 32) (i32.const 36)) + + ;; assert input size == 4 + (call $assert + (i32.eq + (i32.load (i32.const 36)) + (i32.const 4) + ) + ) + + ;; place a value in storage, the size of which is specified by the call input. + ;; we don't care about the contents of the storage item + (call $seal_set_storage + (i32.const 0) ;; Pointer to storage key + (i32.const 0) ;; Pointer to value + (i32.load (i32.const 32)) ;; Size of value + ) + ) + + (func (export "deploy")) +) diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 386b0f4c74558..178423220f795 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -23,7 +23,6 @@ use crate::{ }; use frame_support::{ dispatch::{DispatchError, DispatchResult, DispatchResultWithPostInfo, Dispatchable}, - pallet_prelude::Encode, storage::{with_transaction, TransactionOutcome}, traits::{Contains, Currency, ExistenceRequirement, Get, OriginTrait, Randomness, Time}, weights::Weight, @@ -588,7 +587,6 @@ where 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 } => { @@ -611,16 +609,6 @@ where trie_id, executable.code_hash().clone(), )?; - nested_storage.charge(&storage::meter::Diff { - bytes_added: contract.encoded_size() as u32, - items_added: 1, - // We make sure that this deposit that is charged for every new contract - // is at least the existential deposit. This makes sure that the contract's - // account is never dusted even when all the free balance is removed. This - // saves us all kind of checks when dealing with balance transfers. - require_ed: true, - ..Default::default() - })?; (account_id, contract, executable, ExportedFunction::Constructor, Some(trie_seed)) }, }; @@ -631,7 +619,7 @@ where account_id, entry_point, nested_gas: gas_meter.nested(gas_limit)?, - nested_storage, + nested_storage: storage_meter.nested(), allows_reentry: true, }; @@ -681,6 +669,17 @@ where fn run(&mut self, executable: E, input_data: Vec) -> Result { let entry_point = self.top_frame().entry_point; let do_transaction = || { + // We need to charge the storage deposit before the initial transfer so that + // it can create the account incase the initial transfer is < ed. + if entry_point == ExportedFunction::Constructor { + let top_frame = top_frame_mut!(self); + top_frame.nested_storage.charge_instantiate( + &self.origin, + &top_frame.account_id, + &mut top_frame.contract_info.get(&top_frame.account_id), + )?; + } + // Every call or instantiate also optionally transferres balance. self.initial_transfer()?; @@ -1292,7 +1291,7 @@ 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(); + let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), value).unwrap(); assert_matches!( MockStack::run_call( @@ -1346,7 +1345,7 @@ 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 mut storage_meter = storage::meter::Meter::new(&origin, Some(0), 55).unwrap(); let output = MockStack::run_call( origin.clone(), @@ -1396,7 +1395,7 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let schedule = ::Schedule::get(); - let mut storage_meter = storage::meter::Meter::new(origin.clone(), 0).unwrap(); + let mut storage_meter = storage::meter::Meter::new(&origin, Some(0), 0).unwrap(); place_contract(&BOB, return_ch); let result = MockStack::run_call( @@ -1429,7 +1428,7 @@ 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 mut storage_meter = storage::meter::Meter::new(&origin, Some(0), 0).unwrap(); let result = MockStack::run_call( origin, @@ -1459,7 +1458,7 @@ 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 mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); let result = MockStack::run_call( ALICE, @@ -1490,7 +1489,8 @@ mod tests { let executable = MockExecutable::from_storage(input_data_ch, &schedule, &mut gas_meter).unwrap(); set_balance(&ALICE, min_balance * 1000); - let mut storage_meter = storage::meter::Meter::new(ALICE, min_balance * 100).unwrap(); + let mut storage_meter = + storage::meter::Meter::new(&ALICE, Some(min_balance * 100), min_balance).unwrap(); let result = MockStack::run_instantiate( ALICE, @@ -1539,7 +1539,7 @@ 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 mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), value).unwrap(); let result = MockStack::run_call( ALICE, @@ -1586,7 +1586,7 @@ 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 mut storage_meter = storage::meter::Meter::new(&origin, Some(0), 0).unwrap(); let result = MockStack::run_call( origin.clone(), @@ -1625,7 +1625,7 @@ 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 mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); let result = MockStack::run_call( ALICE, @@ -1651,7 +1651,7 @@ 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(); + let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); assert_matches!( MockStack::run_instantiate( @@ -1683,7 +1683,8 @@ mod tests { let executable = MockExecutable::from_storage(dummy_ch, &schedule, &mut gas_meter).unwrap(); set_balance(&ALICE, min_balance * 1000); - let mut storage_meter = storage::meter::Meter::new(ALICE, min_balance * 100).unwrap(); + let mut storage_meter = + storage::meter::Meter::new(&ALICE, Some(min_balance * 100), min_balance).unwrap(); let instantiated_contract_address = assert_matches!( MockStack::run_instantiate( @@ -1726,7 +1727,8 @@ mod tests { let executable = MockExecutable::from_storage(dummy_ch, &schedule, &mut gas_meter).unwrap(); set_balance(&ALICE, min_balance * 1000); - let mut storage_meter = storage::meter::Meter::new(ALICE, min_balance * 100).unwrap(); + let mut storage_meter = + storage::meter::Meter::new(&ALICE, Some(min_balance * 100), min_balance).unwrap(); let instantiated_contract_address = assert_matches!( MockStack::run_instantiate( @@ -1779,7 +1781,9 @@ mod tests { 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(); + let mut storage_meter = + storage::meter::Meter::new(&ALICE, Some(min_balance * 10), min_balance * 10) + .unwrap(); assert_matches!( MockStack::run_call( @@ -1841,7 +1845,7 @@ 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(); + let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(100), 0).unwrap(); assert_matches!( MockStack::run_call( @@ -1876,7 +1880,7 @@ 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(); + let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(100), 100).unwrap(); assert_eq!( MockStack::run_instantiate( @@ -1927,7 +1931,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(); + let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); let result = MockStack::run_call( ALICE, @@ -1960,7 +1964,8 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); let executable = MockExecutable::from_storage(code, &schedule, &mut gas_meter).unwrap(); set_balance(&ALICE, min_balance * 1000); - let mut storage_meter = storage::meter::Meter::new(ALICE, min_balance * 100).unwrap(); + let mut storage_meter = + storage::meter::Meter::new(&ALICE, Some(min_balance * 100), min_balance).unwrap(); let result = MockStack::run_instantiate( ALICE, @@ -1993,7 +1998,7 @@ 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 mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); MockStack::run_call( ALICE, BOB, @@ -2026,7 +2031,7 @@ 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 mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); let result = MockStack::run_call( ALICE, BOB, @@ -2057,7 +2062,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(); + let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); // Calling another contract should succeed assert_ok!(MockStack::run_call( @@ -2107,7 +2112,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(); + let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); // BOB -> CHARLIE -> BOB fails as BOB denies reentry. assert_err!( @@ -2143,7 +2148,7 @@ 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 mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); System::reset_events(); MockStack::run_call( ALICE, @@ -2211,7 +2216,7 @@ 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 mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); System::reset_events(); MockStack::run_call( ALICE, @@ -2287,7 +2292,9 @@ 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(); + let mut storage_meter = + storage::meter::Meter::new(&ALICE, Some(min_balance * 500), min_balance * 100) + .unwrap(); MockStack::run_instantiate( ALICE, diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 6fcd8cabf0f4e..9fe411f243f74 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -125,7 +125,7 @@ use pallet_contracts_primitives::{ }; use scale_info::TypeInfo; use sp_core::{crypto::UncheckedFrom, Bytes}; -use sp_runtime::traits::{CheckedSub, Convert, Hash, Saturating, StaticLookup}; +use sp_runtime::traits::{Convert, Hash, Saturating, StaticLookup}; use sp_std::{fmt::Debug, prelude::*}; type CodeHash = ::Hash; @@ -759,10 +759,8 @@ where data: Vec, debug_message: Option<&mut Vec>, ) -> InternalCallOutput { - let storage_deposit_limit = storage_deposit_limit - .unwrap_or_else(|| Self::max_storage_deposit_limit(&origin, value)); let mut gas_meter = GasMeter::new(gas_limit); - let mut storage_meter = match StorageMeter::new(origin.clone(), storage_deposit_limit) { + let mut storage_meter = match StorageMeter::new(&origin, storage_deposit_limit, value) { Ok(meter) => meter, Err(err) => return InternalCallOutput { @@ -798,13 +796,11 @@ where salt: Vec, debug_message: Option<&mut Vec>, ) -> InternalInstantiateOutput { - let mut storage_deposit_limit = storage_deposit_limit - .unwrap_or_else(|| Self::max_storage_deposit_limit(&origin, value)); let mut storage_deposit = Default::default(); let mut gas_meter = GasMeter::new(gas_limit); let try_exec = || { let schedule = T::Schedule::get(); - let (executable, extra_deposit) = match code { + let (extra_deposit, executable) = match code { Code::Upload(Bytes(binary)) => { ensure!( binary.len() as u32 <= schedule.limits.code_len, @@ -816,22 +812,22 @@ where executable.code_len() <= schedule.limits.code_len, >::CodeTooLarge ); - // There might be an deposit that will be charged during execution when the + // The open deposit 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_deposit_limit = storage_deposit_limit - .checked_sub(&deposit) - .ok_or(>::StorageDepositLimitExhausted)?; - (executable, StorageDeposit::Charge(deposit)) + (executable.open_deposit(), executable) }, Code::Existing(hash) => ( - PrefabWasmModule::from_storage(hash, &schedule, &mut gas_meter)?, Default::default(), + PrefabWasmModule::from_storage(hash, &schedule, &mut gas_meter)?, ), }; - let mut storage_meter = StorageMeter::new(origin.clone(), storage_deposit_limit)?; + let mut storage_meter = StorageMeter::new( + &origin, + storage_deposit_limit, + value.saturating_add(extra_deposit), + )?; let result = ExecStack::>::run_instantiate( origin, executable, @@ -843,16 +839,11 @@ where &salt, debug_message, ); - storage_deposit = storage_meter.into_deposit().saturating_add(&extra_deposit); + storage_deposit = storage_meter + .into_deposit() + .saturating_add(&StorageDeposit::Charge(extra_deposit)); result }; InternalInstantiateOutput { result: try_exec(), gas_meter, storage_deposit } } - - /// If no storage deposit limit is specified we use this function. - fn max_storage_deposit_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/storage/meter.rs b/frame/contracts/src/storage/meter.rs index 32ce5a92eaf0c..fe9cedc16f949 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -18,14 +18,15 @@ //! This module contains functions to meter the storage deposit. use crate::{storage::ContractInfo, BalanceOf, Config, Error}; +use codec::Encode; use frame_support::{ - dispatch::{DispatchError, DispatchResult}, - traits::{tokens::BalanceStatus, Currency, Get, ReservableCurrency}, + dispatch::DispatchError, + traits::{tokens::BalanceStatus, Currency, ExistenceRequirement, Get, ReservableCurrency}, DefaultNoBound, }; use pallet_contracts_primitives::StorageDeposit as Deposit; use sp_core::crypto::UncheckedFrom; -use sp_runtime::traits::Saturating; +use sp_runtime::traits::{Saturating, Zero}; use sp_std::marker::PhantomData; /// Deposit that uses the native currency's balance type. @@ -46,31 +47,29 @@ pub type GenericMeter = RawMeter; /// /// 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_deposit_limit` of the - /// meter. + /// This checks whether `origin` is able to afford the storage deposit limit. /// - /// 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. + /// It is necessary to do this check beforehand so that the charge won't fail later on. /// /// `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. + /// `min_leftover`: How much `free_balance` in addition to the ed should be left inside the + /// `origin` account. /// - /// 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); + /// Returns the limit that should be used by the meter. If origin can't afford the `limit` + /// it returns `Err`. + fn check_limit( + origin: &T::AccountId, + limit: Option>, + min_leftover: BalanceOf, + ) -> Result, DispatchError>; /// 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. + /// It is garantueed that that this succeeds because no more balance then returned by + /// `check_limit` is ever charged. This is why this function is infallible. fn charge(origin: &T::AccountId, contract: &T::AccountId, amount: &DepositOf); } @@ -96,13 +95,6 @@ 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. @@ -164,24 +156,6 @@ impl Diff { } } -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 @@ -197,7 +171,6 @@ where /// with the current contract we are interacting with. pub fn nested(&self) -> RawMeter { RawMeter { - origin: None, limit: self.available(), total_deposit: Default::default(), own_deposit: Default::default(), @@ -233,12 +206,17 @@ where 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 case can happen when costs change due to a runtime upgrade. + // We need to make sure to never refund more than what was deposited and + // still leave the existential deposit inside the contract's account. + // This case can happen when costs change due to a runtime upgrade where + // increased costs could remove an account due to refunds. let amount = { - let corrected_amount = (*amount).min(info.storage_deposit); + let corrected_amount = (*amount).min( + info.storage_deposit.saturating_sub(T::Currency::minimum_balance()), + ); let correction = (*amount).saturating_sub(corrected_amount); - self.total_deposit.saturating_sub(&Deposit::Refund(correction)); + absorbed.total_deposit = + absorbed.total_deposit.saturating_sub(&Deposit::Refund(correction)); *amount = corrected_amount; corrected_amount }; @@ -268,11 +246,14 @@ where { /// 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)?; + /// This tries to [`Ext::check_limit`] on `origin` and fails if this is not possible. + pub fn new( + origin: &T::AccountId, + limit: Option>, + min_leftover: BalanceOf, + ) -> Result { + let limit = E::check_limit(&origin, limit, min_leftover)?; Ok(Self { - origin: Some(origin), limit, total_deposit: Default::default(), own_deposit: Default::default(), @@ -312,6 +293,42 @@ where Ok(deposit) } + /// Charge from `origin` a storage deposit for contract instantiation. + /// + /// This immediatly transfers the balance in order to create the account. + pub fn charge_instantiate( + &mut self, + origin: &T::AccountId, + contract: &T::AccountId, + info: &mut ContractInfo, + ) -> Result, DispatchError> { + let deposit = Diff { + bytes_added: info.encoded_size() as u32, + items_added: 1, + require_ed: true, + ..Default::default() + } + .to_deposit::(); + // We do not increase `own_deposit` because this will be charged later when the contract + // execution does conclude. + let total_deposit = self.total_deposit.saturating_add(&deposit); + if let Deposit::Charge(amount) = &total_deposit { + if amount > &self.limit { + return Err(>::StorageDepositLimitExhausted.into()) + } + } + if let Deposit::Charge(amount) = &deposit { + info.storage_deposit = info.storage_deposit.saturating_add(*amount); + } + self.total_deposit = total_deposit; + if !deposit.is_zero() { + // We need to charge immediatly so that the account is created before the `value` + // is transferred from the caller to the contract. + E::charge(origin, contract, &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 @@ -329,43 +346,72 @@ where } impl Ext for ReservingExt { - fn reserve_limit(origin: &T::AccountId, limit: &BalanceOf) -> DispatchResult { - T::Currency::reserve(origin, *limit) - .map_err(|_| >::StorageDepositNotEnoughFunds.into()) - } - - fn unreserve_limit(origin: &T::AccountId, limit: &BalanceOf, deposit: &DepositOf) { - T::Currency::unreserve(origin, deposit.available(&limit)); + fn check_limit( + origin: &T::AccountId, + limit: Option>, + min_leftover: BalanceOf, + ) -> Result, DispatchError> { + let max = T::Currency::free_balance(origin) + .saturating_sub(T::Currency::minimum_balance()) + .saturating_sub(min_leftover); + match limit { + Some(limit) if limit <= max => Ok(limit), + None => Ok(max), + _ => Err(>::StorageDepositNotEnoughFunds.into()), + } } 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), + // There is nothing we can do when this fails 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. We need to settle for emitting an error log + // in this case. + match amount { + Deposit::Charge(amount) => { + // This will never fail because a contract's account is required to exist + // at all times. The pallet enforces this invariant by depositing at least the + // existential deposit when instantiating and never refunds it unless the contract + // is removed. This means the receiver always exists except when instantiating a + // contract. In this case we made sure that at least the existential deposit is + // sent. The sender always has enough balance because we checked that it had enough + // balance when instantiating the storage meter. + let result = T::Currency::transfer( + origin, + contract, + *amount, + ExistenceRequirement::KeepAlive, + ) + .and_then(|_| T::Currency::reserve(contract, *amount)); + if let Err(err) = result { + log::warn!( + target: "runtime::contracts", + "Failed to transfer storage deposit {:?} from origin {:?} to contract {:?}: {:?}", + amount, origin, contract, err, + ); + } + }, + // For `Refund(_)` no error happen because the initial value transfer from the + // origin to the contract has a keep alive existence requirement and when reserving we + // make sure to leave at least the ed in the free balance. Therefore the receiver always + // exists because there is no way for it to be removed in between. The sender always has + // enough reserved balance because we track it in the `ContractInfo` and never send more + // back than we have. + Deposit::Refund(amount) => { + let result = T::Currency::repatriate_reserved( + contract, + origin, + *amount, + BalanceStatus::Free, + ); + if matches!(result, Ok(val) if !val.is_zero()) || matches!(result, Err(_)) { + log::warn!( + target: "runtime::contracts", + "Failed to repatriate storage deposit {:?} from contract {:?} to origin {:?}: {:?}", + amount, contract, origin, result, + ); + } + }, }; - - // 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, - ); } } @@ -392,16 +438,10 @@ mod tests { } #[derive(Debug, PartialEq, Eq)] - struct Reserve { + struct LimitCheck { origin: AccountIdOf, limit: BalanceOf, - } - - #[derive(Debug, PartialEq, Eq)] - struct Unreserve { - origin: AccountIdOf, - limit: BalanceOf, - deposit: DepositOf, + min_leftover: BalanceOf, } #[derive(Debug, PartialEq, Eq)] @@ -413,41 +453,32 @@ mod tests { #[derive(Default, Debug, PartialEq, Eq)] struct TestExt { - reserves: Vec, - unreserves: Vec, + limit_checks: Vec, charges: Vec, } impl TestExt { fn clear(&mut self) { - self.reserves.clear(); - self.unreserves.clear(); + self.limit_checks.clear(); self.charges.clear(); } } impl Ext for TestExt { - fn reserve_limit(origin: &AccountIdOf, limit: &BalanceOf) -> DispatchResult { - TEST_EXT.with(|ext| { - ext.borrow_mut() - .reserves - .push(Reserve { origin: origin.clone(), limit: limit.clone() }) - }); - Ok(()) - } - - fn unreserve_limit( + fn check_limit( origin: &AccountIdOf, - limit: &BalanceOf, - deposit: &DepositOf, - ) { + limit: Option>, + min_leftover: BalanceOf, + ) -> Result, DispatchError> { + let limit = limit.unwrap_or(42); TEST_EXT.with(|ext| { - ext.borrow_mut().unreserves.push(Unreserve { + ext.borrow_mut().limit_checks.push(LimitCheck { origin: origin.clone(), - limit: limit.clone(), - deposit: deposit.clone(), + limit, + min_leftover, }) }); + Ok(limit) } fn charge( @@ -484,36 +515,13 @@ mod tests { fn new_reserves_balance_works() { clear_ext(); - let _meter = TestMeter::new(ALICE, 1_000).unwrap(); + TestMeter::new(&ALICE, Some(1_000), 0).unwrap(); TEST_EXT.with(|ext| { assert_eq!( *ext.borrow(), TestExt { - reserves: vec![Reserve { origin: ALICE, limit: 1_000 }], - ..Default::default() - } - ) - }); - } - - #[test] - fn unreserve_on_drop_works() { - clear_ext(); - - let meter = TestMeter::new(ALICE, 1_000).unwrap(); - drop(meter); - - TEST_EXT.with(|ext| { - assert_eq!( - *ext.borrow(), - TestExt { - reserves: vec![Reserve { origin: ALICE, limit: 1_000 }], - unreserves: vec![Unreserve { - origin: ALICE, - limit: 1_000, - deposit: Deposit::Charge(0) - }], + limit_checks: vec![LimitCheck { origin: ALICE, limit: 1_000, min_leftover: 0 }], ..Default::default() } ) @@ -524,7 +532,7 @@ mod tests { fn empty_charge_works() { clear_ext(); - let mut meter = TestMeter::new(ALICE, 1_000).unwrap(); + let mut meter = TestMeter::new(&ALICE, Some(1_000), 0).unwrap(); assert_eq!(meter.available(), 1_000); // an empty charge foes not create a `Charge` entry @@ -532,18 +540,11 @@ mod tests { nested0.charge(&Default::default()).unwrap(); meter.absorb(nested0, &ALICE, &BOB, None); - drop(meter); - TEST_EXT.with(|ext| { assert_eq!( *ext.borrow(), TestExt { - reserves: vec![Reserve { origin: ALICE, limit: 1_000 }], - unreserves: vec![Unreserve { - origin: ALICE, - limit: 1_000, - deposit: Deposit::Charge(0) - }], + limit_checks: vec![LimitCheck { origin: ALICE, limit: 1_000, min_leftover: 0 }], ..Default::default() } ) @@ -554,7 +555,7 @@ mod tests { fn existential_deposit_works() { clear_ext(); - let mut meter = TestMeter::new(ALICE, 1_000).unwrap(); + let mut meter = TestMeter::new(&ALICE, Some(1_000), 0).unwrap(); assert_eq!(meter.available(), 1_000); // a `Refund` will be turned into a `Charge(ed)` which is intended behaviour @@ -565,18 +566,11 @@ mod tests { .unwrap(); meter.absorb(nested0, &ALICE, &BOB, None); - drop(meter); - TEST_EXT.with(|ext| { assert_eq!( *ext.borrow(), TestExt { - reserves: vec![Reserve { origin: ALICE, limit: 1_000 }], - unreserves: vec![Unreserve { - origin: ALICE, - limit: 1_000, - deposit: Deposit::Charge(2) - }], + limit_checks: vec![LimitCheck { origin: ALICE, limit: 1_000, min_leftover: 0 }], charges: vec![Charge { origin: ALICE, contract: BOB, @@ -592,7 +586,9 @@ mod tests { fn charging_works() { clear_ext(); - let mut meter = TestMeter::new(ALICE, 1_000).unwrap(); + let min_balance = ::Currency::minimum_balance(); + + let mut meter = TestMeter::new(&ALICE, Some(1_000), 0).unwrap(); assert_eq!(meter.available(), 1_000); let mut nested0_info = new_info(100); @@ -614,32 +610,26 @@ mod tests { nested0.absorb(nested1, &ALICE, &CHARLIE, Some(&mut nested1_info)); // Trying to refund more than is available in the contract will cap the charge - // to that value. This value is `1` in this case. - let mut nested2_info = new_info(1); + // to (deposit_in_contract - ed). + let mut nested2_info = new_info(5); let mut nested2 = nested0.nested(); nested2.charge(&Diff { bytes_removed: 7, ..Default::default() }).unwrap(); nested0.absorb(nested2, &ALICE, &CHARLIE, Some(&mut nested2_info)); meter.absorb(nested0, &ALICE, &BOB, Some(&mut nested0_info)); - drop(meter); assert_eq!(nested0_info.storage_deposit, 102); assert_eq!(nested1_info.storage_deposit, 40); - assert_eq!(nested2_info.storage_deposit, 0); + assert_eq!(nested2_info.storage_deposit, min_balance); TEST_EXT.with(|ext| { assert_eq!( *ext.borrow(), TestExt { - reserves: vec![Reserve { origin: ALICE, limit: 1_000 }], - unreserves: vec![Unreserve { - origin: ALICE, - limit: 1_000, - deposit: Deposit::Refund(15) - }], + limit_checks: vec![LimitCheck { origin: ALICE, limit: 1_000, min_leftover: 0 }], charges: vec![ Charge { origin: ALICE, contract: CHARLIE, amount: Deposit::Refund(10) }, - Charge { origin: ALICE, contract: CHARLIE, amount: Deposit::Refund(1) }, + Charge { origin: ALICE, contract: CHARLIE, amount: Deposit::Refund(4) }, Charge { origin: ALICE, contract: BOB, amount: Deposit::Charge(2) } ], ..Default::default() @@ -652,7 +642,7 @@ mod tests { fn termination_works() { clear_ext(); - let mut meter = TestMeter::new(ALICE, 1_000).unwrap(); + let mut meter = TestMeter::new(&ALICE, Some(1_000), 0).unwrap(); assert_eq!(meter.available(), 1_000); let mut nested0 = meter.nested(); @@ -681,12 +671,7 @@ mod tests { assert_eq!( *ext.borrow(), TestExt { - reserves: vec![Reserve { origin: ALICE, limit: 1_000 }], - unreserves: vec![Unreserve { - origin: ALICE, - limit: 1_000, - deposit: Deposit::Refund(388) - }], + limit_checks: vec![LimitCheck { origin: ALICE, limit: 1_000, min_leftover: 0 }], charges: vec![ Charge { origin: ALICE, contract: CHARLIE, amount: Deposit::Refund(400) }, Charge { origin: ALICE, contract: BOB, amount: Deposit::Charge(12) } diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index ce1c3985265ce..e098a1a343548 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -241,7 +241,7 @@ parameter_types! { pub const MaxCodeSize: u32 = 2 * 1024; pub MySchedule: Schedule = >::default(); pub const TransactionByteFee: u64 = 0; - pub const DepositPerByte: BalanceOf = 1; + pub static DepositPerByte: BalanceOf = 1; pub const DepositPerItem: BalanceOf = 2; } @@ -312,6 +312,9 @@ impl ExtBuilder { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); } pub fn build(self) -> sp_io::TestExternalities { + use env_logger::{Builder, Env}; + let env = Env::new().default_filter_or("runtime=debug"); + let _ = Builder::from_env(env).is_test(true).try_init(); self.set_associated_consts(); let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); pallet_balances::GenesisConfig:: { balances: vec![] } @@ -337,30 +340,6 @@ 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 { - from: _, - to: _, - amount, - destination_status: 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.) @@ -387,10 +366,10 @@ fn calling_plain_account_fails() { fn instantiate_and_call_and_deposit_event() { let (wasm, code_hash) = compile_module::("return_from_start_fn").unwrap(); - ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + ExtBuilder::default().existential_deposit(500).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); let min_balance = ::Currency::minimum_balance(); - let value = min_balance * 100; + let value = 100; // We determine the storage deposit limit after uploading because it depends on ALICEs free // balance which is changed by uploading a module. @@ -400,7 +379,6 @@ fn instantiate_and_call_and_deposit_event() { initialize_block(2); // Check at the end to get hash on error easily - let storage_deposit_limit = >::max_storage_deposit_limit(&ALICE, value); assert_ok!(Contracts::instantiate( Origin::signed(ALICE), value, @@ -413,23 +391,9 @@ fn instantiate_and_call_and_deposit_event() { 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_deposit_limit - storage_cost; - assert_eq!( - events, + System::events(), vec![ - EventRecord { - phase: Phase::Initialization, - event: Event::Balances(pallet_balances::Event::Reserved { - who: ALICE, - amount: storage_deposit_limit, - }), - topics: vec![], - }, EventRecord { phase: Phase::Initialization, event: Event::System(frame_system::Event::NewAccount(addr.clone())), @@ -439,7 +403,7 @@ fn instantiate_and_call_and_deposit_event() { phase: Phase::Initialization, event: Event::Balances(pallet_balances::Event::Endowed { account: addr.clone(), - free_balance: min_balance * 100 + free_balance: min_balance, }), topics: vec![], }, @@ -448,41 +412,40 @@ fn instantiate_and_call_and_deposit_event() { event: Event::Balances(pallet_balances::Event::Transfer { from: ALICE, to: addr.clone(), - amount: min_balance * 100 + amount: min_balance, }), topics: vec![], }, EventRecord { phase: Phase::Initialization, - event: Event::Contracts(crate::Event::ContractEmitted { - contract: addr.clone(), - data: vec![1, 2, 3, 4] + event: Event::Balances(pallet_balances::Event::Reserved { + who: addr.clone(), + amount: min_balance, }), topics: vec![], }, EventRecord { phase: Phase::Initialization, - event: Event::Contracts(crate::Event::Instantiated { - deployer: ALICE, - contract: addr.clone() + event: Event::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: addr.clone(), + amount: value, }), topics: vec![], }, EventRecord { phase: Phase::Initialization, - event: Event::Balances(pallet_balances::Event::ReserveRepatriated { - from: ALICE, - to: addr.clone(), - amount: storage_cost, - destination_status: BalanceStatus::Reserved, + event: Event::Contracts(crate::Event::ContractEmitted { + contract: addr.clone(), + data: vec![1, 2, 3, 4] }), topics: vec![], }, EventRecord { phase: Phase::Initialization, - event: Event::Balances(pallet_balances::Event::Unreserved { - who: ALICE, - amount: unused, + event: Event::Contracts(crate::Event::Instantiated { + deployer: ALICE, + contract: addr.clone() }), topics: vec![], }, @@ -618,10 +581,14 @@ fn storage_max_value_limit() { #[test] fn deploy_and_call_other_contract() { - let (callee_wasm, callee_code_hash) = compile_module::("return_with_data").unwrap(); let (caller_wasm, caller_code_hash) = compile_module::("caller_contract").unwrap(); + let (callee_wasm, callee_code_hash) = compile_module::("return_with_data").unwrap(); + let caller_addr = Contracts::contract_address(&ALICE, &caller_code_hash, &[]); + let callee_addr = Contracts::contract_address(&caller_addr, &callee_code_hash, &[]); + + ExtBuilder::default().existential_deposit(500).build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create let _ = Balances::deposit_creating(&ALICE, 1_000_000); assert_ok!(Contracts::instantiate_with_code( @@ -643,29 +610,94 @@ fn deploy_and_call_other_contract() { vec![42], )); + // Drop previous events + initialize_block(2); + // Call BOB contract, which attempts to instantiate and call the callee contract and // makes various assertions on the results from those calls. assert_ok!(Contracts::call( Origin::signed(ALICE), - Contracts::contract_address(&ALICE, &caller_code_hash, &[]), + caller_addr.clone(), 0, GAS_LIMIT, None, callee_code_hash.as_ref().to_vec(), )); + + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: Event::System(frame_system::Event::NewAccount(callee_addr.clone())), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Endowed { + account: callee_addr.clone(), + free_balance: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: callee_addr.clone(), + amount: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Reserved { + who: callee_addr.clone(), + amount: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Transfer { + from: caller_addr.clone(), + to: callee_addr.clone(), + amount: 32768, // hard coded in wasm + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Contracts(crate::Event::Instantiated { + deployer: caller_addr.clone(), + contract: callee_addr.clone(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Transfer { + from: caller_addr.clone(), + to: callee_addr.clone(), + amount: 32768, + }), + topics: vec![], + }, + ] + ); }); } #[test] fn cannot_self_destruct_through_draning() { let (wasm, code_hash) = compile_module::("drain").unwrap(); - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); // Instantiate the BOB contract. assert_ok!(Contracts::instantiate_with_code( Origin::signed(ALICE), - 100_000, + 1_000, GAS_LIMIT, None, wasm, @@ -679,12 +711,73 @@ fn cannot_self_destruct_through_draning() { // Call BOB which makes it send all funds to the zero address // The contract code asserts that the transfer was successful - assert_ok!(Contracts::call(Origin::signed(ALICE), addr, 0, GAS_LIMIT, None, vec![])); + assert_ok!(Contracts::call( + Origin::signed(ALICE), + addr.clone(), + 0, + GAS_LIMIT, + None, + vec![] + )); // Make sure the account wasn't remove by sending all free balance away. - assert!( - ::Currency::total_balance(&ALICE) >= - ::Currency::minimum_balance() + assert_eq!( + ::Currency::total_balance(&addr), + ::Currency::minimum_balance(), + ); + }); +} + +#[test] +fn cannot_self_destruct_through_storage_refund() { + let (wasm, code_hash) = compile_module::("store").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let min_balance = ::Currency::minimum_balance(); + + // Instantiate the BOB contract. + assert_ok!(Contracts::instantiate_with_code( + Origin::signed(ALICE), + 0, + GAS_LIMIT, + None, + wasm, + vec![], + vec![], + )); + let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); + + // Check that the BOB contract has been instantiated and has the minimum balance + let info = ContractInfoOf::::get(&addr).unwrap(); + assert_eq!(info.storage_deposit, min_balance); + assert_eq!(::Currency::total_balance(&addr), min_balance); + + // Create 100 bytes of storage with a price of per byte + assert_ok!(Contracts::call( + Origin::signed(ALICE), + addr.clone(), + 0, + GAS_LIMIT, + None, + 100u32.to_le_bytes().to_vec() + )); + + // Increase the byte price and trigger a refund. This could potentially destroy the account + // because the refund removes the reserved existential deposit. This should not happen. + DEPOSIT_PER_BYTE.with(|c| *c.borrow_mut() = 500); + assert_ok!(Contracts::call( + Origin::signed(ALICE), + addr.clone(), + 0, + GAS_LIMIT, + None, + 0u32.to_le_bytes().to_vec() + )); + + // Make sure the account wasn't removed by the refund + assert_eq!( + ::Currency::total_balance(&addr), + ::Currency::minimum_balance(), ); }); } @@ -725,7 +818,7 @@ fn cannot_self_destruct_while_live() { #[test] fn self_destruct_works() { let (wasm, code_hash) = compile_module::("self_destruct").unwrap(); - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + ExtBuilder::default().existential_deposit(1_000).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); let _ = Balances::deposit_creating(&DJANGO, 1_000_000); @@ -747,12 +840,6 @@ 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_deposit_limit = >::max_storage_deposit_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, None, vec![],), @@ -764,27 +851,14 @@ fn self_destruct_works() { // Check that account is gone assert!(ContractInfoOf::::get(&addr).is_none()); + assert_eq!(Balances::total_balance(&addr), 0); // 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_deposit_limit + storage_refund; - pretty_assertions::assert_eq!( - events, + System::events(), vec![ - EventRecord { - phase: Phase::Initialization, - event: Event::Balances(pallet_balances::Event::Reserved { - who: ALICE, - amount: storage_deposit_limit, - }), - topics: vec![], - }, EventRecord { phase: Phase::Initialization, event: Event::Balances(pallet_balances::Event::Transfer { @@ -812,16 +886,8 @@ fn self_destruct_works() { event: Event::Balances(pallet_balances::Event::ReserveRepatriated { from: addr.clone(), to: ALICE, - amount: storage_refund, - destination_status: BalanceStatus::Reserved, - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: Event::Balances(pallet_balances::Event::Unreserved { - who: ALICE, - amount: unreserved, + amount: 1_000, + destination_status: BalanceStatus::Free, }), topics: vec![], }, @@ -2036,10 +2102,8 @@ fn upload_code_works() { )); assert!(>::contains_key(code_hash)); - let events = System::events(); - assert_eq!( - events, + System::events(), vec![ EventRecord { phase: Phase::Initialization, @@ -2117,10 +2181,8 @@ fn remove_code_works() { assert_ok!(Contracts::remove_code(Origin::signed(ALICE), code_hash)); assert!(!>::contains_key(code_hash)); - let events = System::events(); - assert_eq!( - events, + System::events(), vec![ EventRecord { phase: Phase::Initialization, @@ -2174,10 +2236,8 @@ fn remove_code_wrong_origin() { sp_runtime::traits::BadOrigin, ); - let events = System::events(); - assert_eq!( - events, + System::events(), vec![ EventRecord { phase: Phase::Initialization, @@ -2222,9 +2282,7 @@ fn remove_code_in_use() { >::CodeInUse, ); - let events = System::events(); - - assert_eq!(events, vec![]); + assert_eq!(System::events(), vec![]); }); } @@ -2243,8 +2301,319 @@ fn remove_code_not_found() { >::CodeNotFound, ); - let events = System::events(); + assert_eq!(System::events(), vec![]); + }); +} + +#[test] +fn instantiate_with_zero_balance_works() { + let (wasm, code_hash) = compile_module::("dummy").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let min_balance = ::Currency::minimum_balance(); + + // Drop previous events + initialize_block(2); + + // Instantiate the BOB contract. + assert_ok!(Contracts::instantiate_with_code( + Origin::signed(ALICE), + 0, + GAS_LIMIT, + None, + wasm, + vec![], + vec![], + )); + let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); + + // Check that the BOB contract has been instantiated. + assert_matches!(ContractInfoOf::::get(&addr), Some(_)); - assert_eq!(events, vec![]); + // Make sure the account exists even though no free balance was send + assert_eq!(::Currency::free_balance(&addr), 0,); + assert_eq!( + ::Currency::total_balance(&addr), + ::Currency::minimum_balance(), + ); + + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: Event::System(frame_system::Event::NewAccount(addr.clone())), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Endowed { + account: addr.clone(), + free_balance: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: addr.clone(), + amount: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Reserved { + who: addr.clone(), + amount: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Reserved { + who: ALICE, + amount: 180, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Contracts(crate::Event::CodeStored { code_hash }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Contracts(crate::Event::Instantiated { + deployer: ALICE, + contract: addr.clone(), + }), + topics: vec![], + }, + ] + ); + }); +} + +#[test] +fn instantiate_with_below_existential_deposit_works() { + let (wasm, code_hash) = compile_module::("dummy").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let min_balance = ::Currency::minimum_balance(); + + // Drop previous events + initialize_block(2); + + // Instantiate the BOB contract. + assert_ok!(Contracts::instantiate_with_code( + Origin::signed(ALICE), + 50, + GAS_LIMIT, + None, + wasm, + vec![], + vec![], + )); + let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); + + // Check that the BOB contract has been instantiated. + assert_matches!(ContractInfoOf::::get(&addr), Some(_)); + + // Make sure the account exists even though no free balance was send + assert_eq!(::Currency::free_balance(&addr), 50,); + assert_eq!( + ::Currency::total_balance(&addr), + ::Currency::minimum_balance() + 50, + ); + + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: Event::System(frame_system::Event::NewAccount(addr.clone())), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Endowed { + account: addr.clone(), + free_balance: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: addr.clone(), + amount: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Reserved { + who: addr.clone(), + amount: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: addr.clone(), + amount: 50, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Reserved { + who: ALICE, + amount: 180, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Contracts(crate::Event::CodeStored { code_hash }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Contracts(crate::Event::Instantiated { + deployer: ALICE, + contract: addr.clone(), + }), + topics: vec![], + }, + ] + ); + }); +} + +#[test] +fn storage_deposit_works() { + let (wasm, code_hash) = compile_module::("multi_store").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let mut deposit = ::Currency::minimum_balance(); + + assert_ok!(Contracts::instantiate_with_code( + Origin::signed(ALICE), + 0, + GAS_LIMIT, + None, + wasm, + vec![], + vec![], + )); + let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); + + // Drop previous events + initialize_block(2); + + // Create storage + assert_ok!(Contracts::call( + Origin::signed(ALICE), + addr.clone(), + 42, + GAS_LIMIT, + None, + (1_000u32, 5_000u32).encode(), + )); + // 4 is for creating 2 storage items + let charged0 = 4 + 1_000 + 5_000; + deposit += charged0; + assert_eq!(>::get(&addr).unwrap().storage_deposit, deposit); + + // Add more storage (but also remove some) + assert_ok!(Contracts::call( + Origin::signed(ALICE), + addr.clone(), + 0, + GAS_LIMIT, + None, + (2_000u32, 4_900u32).encode(), + )); + let charged1 = 1_000 - 100; + deposit += charged1; + assert_eq!(>::get(&addr).unwrap().storage_deposit, deposit); + + // Remove more storage (but also add some) + assert_ok!(Contracts::call( + Origin::signed(ALICE), + addr.clone(), + 0, + GAS_LIMIT, + None, + (2_100u32, 900u32).encode(), + )); + let refunded0 = 4_000 - 100; + deposit -= refunded0; + assert_eq!(>::get(&addr).unwrap().storage_deposit, deposit); + + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: addr.clone(), + amount: 42, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: addr.clone(), + amount: charged0, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Reserved { + who: addr.clone(), + amount: charged0, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: addr.clone(), + amount: charged1, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Reserved { + who: addr.clone(), + amount: charged1, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::ReserveRepatriated { + from: addr.clone(), + to: ALICE, + amount: refunded0, + destination_status: BalanceStatus::Free, + }), + topics: vec![], + }, + ] + ); }); } From bddf1659d1f002f5a34e999cb0c2cb6707ba8b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Thu, 2 Dec 2021 12:07:31 +0100 Subject: [PATCH 21/34] Apply suggestions from code review Co-authored-by: Andrew Jones --- frame/contracts/src/wasm/mod.rs | 10 +++++----- frame/contracts/src/wasm/prepare.rs | 5 ++--- frame/contracts/src/wasm/runtime.rs | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index 1030cfe982e34..a620c93acc23f 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -50,7 +50,7 @@ pub use tests::MockExt; /// /// This data structure is mostly immutable once created and stored. The exceptions that /// 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 +/// `instruction_weights_version` and `code` change when a contract with an outdated instrumentation /// 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)] @@ -80,7 +80,7 @@ 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. + // This isn't needed for contract execution and does not get loaded from storage by default. // It is `Some` if and only if this struct was generated from code. #[codec(skip)] owner_info: Option>, @@ -88,7 +88,7 @@ pub struct PrefabWasmModule { /// 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. +/// It is stored in a separate 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))] @@ -143,7 +143,7 @@ where code_cache::try_remove::(origin, code_hash) } - /// Returns whether there is an deposit to be payed for this module. + /// Returns whether there is a 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. @@ -152,7 +152,7 @@ where 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. + // Therefore it is correct to return `0` in this case. self.owner_info.as_ref().map(|i| i.deposit).unwrap_or_default() } } diff --git a/frame/contracts/src/wasm/prepare.rs b/frame/contracts/src/wasm/prepare.rs index 9afa10c33bbbd..6d13c097bd434 100644 --- a/frame/contracts/src/wasm/prepare.rs +++ b/frame/contracts/src/wasm/prepare.rs @@ -25,8 +25,7 @@ use crate::{ wasm::{env_def::ImportSatisfyCheck, OwnerInfo, PrefabWasmModule}, AccountIdOf, Config, Schedule, }; -use codec::Encode; -use frame_support::pallet_prelude::MaxEncodedLen; +use codec::{Encode, MaxEncodedLen}; use pwasm_utils::parity_wasm::elements::{self, External, Internal, MemoryType, Type, ValueType}; use sp_runtime::traits::Hash; use sp_std::prelude::*; @@ -415,7 +414,7 @@ fn do_preparation( }; // 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. + // storage items. This is also why we have `3` items added and not only one. let bytes_added = module .encoded_size() .saturating_add(original_code_len) diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index aeb72ce5d2170..a7341e9e02e56 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -1215,7 +1215,7 @@ define_env!(Env, , )?) }, - // Stores the value transferred along with this call or as value into the supplied buffer. + // Stores the value transferred along with this call/instantiate into the supplied buffer. // // The value is stored to linear memory at the address pointed to by `out_ptr`. // `out_len_ptr` must point to a u32 value that describes the available space at From e23c49433c648c771e576d2f68b1b65ffe611d3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Thu, 2 Dec 2021 12:19:14 +0100 Subject: [PATCH 22/34] Remove unused `fn storage_meter` --- frame/contracts/src/exec.rs | 7 ------- frame/contracts/src/wasm/mod.rs | 4 ---- 2 files changed, 11 deletions(-) diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index aeed2c1f11213..b7754ad525f0e 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -187,9 +187,6 @@ 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. @@ -1053,10 +1050,6 @@ where &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 { if let Some(buffer) = &mut self.debug_message { if !msg.is_empty() { diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index a620c93acc23f..329d63eedb1c0 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -260,7 +260,6 @@ mod tests { AccountIdOf, BlockNumberOf, ErrorOrigin, ExecError, Executable, Ext, SeedOf, StorageKey, }, gas::GasMeter, - storage, tests::{Call, Test, ALICE, BOB}, BalanceOf, CodeHash, Error, Pallet as Contracts, }; @@ -431,9 +430,6 @@ 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 From 5ccd4918ebbbc36feab3e38c4806d33638fc510d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Thu, 2 Dec 2021 12:20:37 +0100 Subject: [PATCH 23/34] Fix copy pasta doc error --- frame/contracts/rpc/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/contracts/rpc/src/lib.rs b/frame/contracts/rpc/src/lib.rs index 029405d3088e5..7944dc84b58da 100644 --- a/frame/contracts/rpc/src/lib.rs +++ b/frame/contracts/rpc/src/lib.rs @@ -147,7 +147,7 @@ where /// 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. + /// This method is useful for UIs to dry-run code upload. #[rpc(name = "contracts_upload_code")] fn upload_code( &self, From f8786443c743dc96bfbced49fd043c0135a8c4f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Thu, 2 Dec 2021 12:27:03 +0100 Subject: [PATCH 24/34] Import `MaxEncodeLen` from codec --- frame/contracts/src/wasm/mod.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index 329d63eedb1c0..01e220d410fa4 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -33,11 +33,8 @@ use crate::{ wasm::env_def::FunctionImplProvider, AccountIdOf, BalanceOf, CodeHash, CodeStorage, Config, Schedule, }; -use codec::{Decode, Encode}; -use frame_support::{ - dispatch::{DispatchError, DispatchResult}, - pallet_prelude::MaxEncodedLen, -}; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::dispatch::{DispatchError, DispatchResult}; use sp_core::crypto::UncheckedFrom; use sp_sandbox::{SandboxEnvironmentBuilder, SandboxInstance, SandboxMemory}; use sp_std::prelude::*; From c428ea9169b4d660258e26b06dadd4d6f9f119ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Thu, 2 Dec 2021 13:07:49 +0100 Subject: [PATCH 25/34] Beautify RPC trait bounds --- frame/contracts/common/src/lib.rs | 9 ++++----- frame/contracts/rpc/src/lib.rs | 6 ++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/frame/contracts/common/src/lib.rs b/frame/contracts/common/src/lib.rs index dcc77365ed288..1dca78aeaa75a 100644 --- a/frame/contracts/common/src/lib.rs +++ b/frame/contracts/common/src/lib.rs @@ -43,7 +43,7 @@ use sp_rpc::number::NumberOrHex; feature = "std", serde( rename_all = "camelCase", - bound(serialize = "R: Serialize, NumberOrHex: From, Balance: Copy"), + bound(serialize = "R: Serialize, Balance: Copy + Into"), bound(deserialize = "R: Deserialize<'de>, Balance: TryFrom") ) )] @@ -177,7 +177,7 @@ pub enum Code { feature = "std", serde( rename_all = "camelCase", - bound(serialize = "NumberOrHex: From, Balance: Copy"), + bound(serialize = "Balance: Copy + Into"), bound(deserialize = "Balance: TryFrom") ) )] @@ -304,10 +304,9 @@ mod as_hex { pub fn serialize(balance: &Balance, serializer: S) -> Result where S: Serializer, - NumberOrHex: From, - Balance: Copy, + Balance: Copy + Into, { - NumberOrHex::from(*balance).serialize(serializer) + Into::::into(*balance).serialize(serializer) } pub fn deserialize<'de, D, Balance>(deserializer: D) -> Result diff --git a/frame/contracts/rpc/src/lib.rs b/frame/contracts/rpc/src/lib.rs index 7944dc84b58da..c4a577196c999 100644 --- a/frame/contracts/rpc/src/lib.rs +++ b/frame/contracts/rpc/src/lib.rs @@ -112,8 +112,7 @@ pub struct CodeUploadRequest { #[rpc] pub trait ContractsApi where - NumberOrHex: From, - Balance: Copy + TryFrom, + Balance: Copy + TryFrom + Into, { /// Executes a call to a contract. /// @@ -197,8 +196,7 @@ where Hash, >, AccountId: Codec, - Balance: Codec + Copy + TryFrom, - NumberOrHex: From, + Balance: Codec + Copy + TryFrom + Into, Hash: Codec, { fn call( From 05d275b1462a1b4742c6155f2e220adda4cdac8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Thu, 2 Dec 2021 13:30:06 +0100 Subject: [PATCH 26/34] Add re-instrument behaviour to dispatchable doc --- frame/contracts/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 9fe411f243f74..0b8786fa704a3 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -403,6 +403,10 @@ pub mod pallet { /// and unreserved only when [`Self::remove_code`] is called. The size of the reserve /// depends on the instrumented size of the the supplied `code`. /// + /// If the code already exists in storage it will still return `Ok` and upgrades + /// the in storage version to the current + /// [`InstructionWeights::version`](InstructionWeights). + /// /// # Note /// /// Anyone can instantiate a contract from any uploaded code and thus prevent its removal. From 3a1391ab17765ce4ca16518deec3f6d749052664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Fri, 3 Dec 2021 11:41:51 +0100 Subject: [PATCH 27/34] Make sure a account won't be destroyed a refund after a slash --- frame/contracts/src/storage/meter.rs | 94 ++++++++++---- frame/contracts/src/tests.rs | 188 ++++++++++++++++++++++++++- 2 files changed, 254 insertions(+), 28 deletions(-) diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs index fe9cedc16f949..bebf0b3084066 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -70,7 +70,13 @@ pub trait Ext { /// around depending on whether `amount` constitues a `Charge` or a `Refund`. /// It is garantueed that that this succeeds because no more balance then returned by /// `check_limit` is ever charged. This is why this function is infallible. - fn charge(origin: &T::AccountId, contract: &T::AccountId, amount: &DepositOf); + // `terminated` designates whether the `contract` was terminated. + fn charge( + origin: &T::AccountId, + contract: &T::AccountId, + amount: &DepositOf, + terminated: bool, + ); } /// This [`Ext`] is used for actual on-chain execution when balance needs to be charged. @@ -101,6 +107,8 @@ pub struct RawMeter, S: State> { total_deposit: DepositOf, /// The amount of balance that was used in this meter alone. own_deposit: DepositOf, + /// Only when a contract was terminated we allow it to drop below the minimum balance. + terminated: bool, /// Type parameters are only used in impls. _phantom: PhantomData<(E, S)>, } @@ -170,12 +178,7 @@ where /// 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(&self) -> RawMeter { - RawMeter { - limit: self.available(), - total_deposit: Default::default(), - own_deposit: Default::default(), - _phantom: PhantomData, - } + RawMeter { limit: self.available(), ..Default::default() } } /// Absorb a child that was spawned to handle a sub call. @@ -227,7 +230,7 @@ where self.total_deposit = self.total_deposit.saturating_add(&absorbed.total_deposit); if !absorbed.own_deposit.is_zero() { - E::charge(origin, &contract, &absorbed.own_deposit); + E::charge(origin, &contract, &absorbed.own_deposit, absorbed.terminated); } } @@ -253,12 +256,7 @@ where min_leftover: BalanceOf, ) -> Result { let limit = E::check_limit(&origin, limit, min_leftover)?; - Ok(Self { - limit, - total_deposit: Default::default(), - own_deposit: Default::default(), - _phantom: PhantomData, - }) + Ok(Self { limit, ..Default::default() }) } /// The total amount of deposit that should change hands as result of the execution @@ -281,6 +279,7 @@ where { /// 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> { + debug_assert!(!self.terminated); let deposit = diff.to_deposit::(); let total_deposit = self.total_deposit.saturating_add(&deposit); if let Deposit::Charge(amount) = total_deposit { @@ -302,6 +301,7 @@ where contract: &T::AccountId, info: &mut ContractInfo, ) -> Result, DispatchError> { + debug_assert!(!self.terminated); let deposit = Diff { bytes_added: info.encoded_size() as u32, items_added: 1, @@ -324,7 +324,7 @@ where if !deposit.is_zero() { // We need to charge immediatly so that the account is created before the `value` // is transferred from the caller to the contract. - E::charge(origin, contract, &deposit); + E::charge(origin, contract, &deposit, false); } Ok(deposit) } @@ -334,6 +334,7 @@ where /// 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) { + debug_assert!(!self.terminated); let refund = Deposit::Refund(contract_info.storage_deposit); // The deposit for `own_deposit` isn't persisted into the contract info until the current @@ -342,6 +343,7 @@ where self.total_deposit = self.total_deposit.saturating_add(&refund).saturating_sub(&self.own_deposit); self.own_deposit = refund; + self.terminated = true; } } @@ -361,7 +363,12 @@ impl Ext for ReservingExt { } } - fn charge(origin: &T::AccountId, contract: &T::AccountId, amount: &DepositOf) { + fn charge( + origin: &T::AccountId, + contract: &T::AccountId, + amount: &DepositOf, + terminated: bool, + ) { // There is nothing we can do when this fails 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. We need to settle for emitting an error log @@ -397,12 +404,16 @@ impl Ext for ReservingExt { // enough reserved balance because we track it in the `ContractInfo` and never send more // back than we have. Deposit::Refund(amount) => { - let result = T::Currency::repatriate_reserved( - contract, - origin, - *amount, - BalanceStatus::Free, - ); + let amount = if terminated { + *amount + } else { + *amount.min( + &T::Currency::reserved_balance(contract) + .saturating_sub(T::Currency::minimum_balance()), + ) + }; + let result = + T::Currency::repatriate_reserved(contract, origin, amount, BalanceStatus::Free); if matches!(result, Ok(val) if !val.is_zero()) || matches!(result, Err(_)) { log::warn!( target: "runtime::contracts", @@ -449,6 +460,7 @@ mod tests { origin: AccountIdOf, contract: AccountIdOf, amount: DepositOf, + terminated: bool, } #[derive(Default, Debug, PartialEq, Eq)] @@ -485,12 +497,14 @@ mod tests { origin: &AccountIdOf, contract: &AccountIdOf, amount: &DepositOf, + terminated: bool, ) { TEST_EXT.with(|ext| { ext.borrow_mut().charges.push(Charge { origin: origin.clone(), contract: contract.clone(), amount: amount.clone(), + terminated, }) }); } @@ -575,6 +589,7 @@ mod tests { origin: ALICE, contract: BOB, amount: Deposit::Charge(::Currency::minimum_balance() * 2), + terminated: false, }], ..Default::default() } @@ -628,9 +643,24 @@ mod tests { TestExt { limit_checks: vec![LimitCheck { origin: ALICE, limit: 1_000, min_leftover: 0 }], charges: vec![ - Charge { origin: ALICE, contract: CHARLIE, amount: Deposit::Refund(10) }, - Charge { origin: ALICE, contract: CHARLIE, amount: Deposit::Refund(4) }, - Charge { origin: ALICE, contract: BOB, amount: Deposit::Charge(2) } + Charge { + origin: ALICE, + contract: CHARLIE, + amount: Deposit::Refund(10), + terminated: false + }, + Charge { + origin: ALICE, + contract: CHARLIE, + amount: Deposit::Refund(4), + terminated: false + }, + Charge { + origin: ALICE, + contract: BOB, + amount: Deposit::Charge(2), + terminated: false + } ], ..Default::default() } @@ -673,8 +703,18 @@ mod tests { TestExt { limit_checks: vec![LimitCheck { origin: ALICE, limit: 1_000, min_leftover: 0 }], charges: vec![ - Charge { origin: ALICE, contract: CHARLIE, amount: Deposit::Refund(400) }, - Charge { origin: ALICE, contract: BOB, amount: Deposit::Charge(12) } + Charge { + origin: ALICE, + contract: CHARLIE, + amount: Deposit::Refund(400), + terminated: true + }, + Charge { + origin: ALICE, + contract: BOB, + amount: Deposit::Charge(12), + terminated: false + } ], ..Default::default() } diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index b3b403fef62ab..be1993aa95886 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -731,7 +731,7 @@ fn cannot_self_destruct_through_draning() { } #[test] -fn cannot_self_destruct_through_storage_refund() { +fn cannot_self_destruct_through_storage_refund_after_price_change() { let (wasm, code_hash) = compile_module::("store").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); @@ -784,6 +784,84 @@ fn cannot_self_destruct_through_storage_refund() { }); } +#[test] +fn cannot_self_destruct_by_refund_after_slash() { + let (wasm, code_hash) = compile_module::("store").unwrap(); + ExtBuilder::default().existential_deposit(500).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let min_balance = ::Currency::minimum_balance(); + + assert_ok!(Contracts::instantiate_with_code( + Origin::signed(ALICE), + 0, + GAS_LIMIT, + None, + wasm, + vec![], + vec![], + )); + let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); + + // create 100 more reserved balance + assert_ok!(Contracts::call( + Origin::signed(ALICE), + addr.clone(), + 0, + GAS_LIMIT, + None, + 98u32.encode(), + )); + + // Drop previous events + initialize_block(2); + + // slash parts of the 100 so that the next refund ould remove the account + // because it the value it stored for `storage_deposit` becomes out of sync + let _ = ::Currency::slash(&addr, 90); + assert_eq!(::Currency::total_balance(&addr), min_balance + 10); + + // trigger a refund of 50 which would bring the contract below min when actually refunded + assert_ok!(Contracts::call( + Origin::signed(ALICE), + addr.clone(), + 0, + GAS_LIMIT, + None, + 48u32.encode(), + )); + + // Make sure the account kept the minimum balance and was not destroyed + assert_eq!(::Currency::total_balance(&addr), min_balance); + + // even though it was not charged it is still substracted from the storage deposit tracker + assert_eq!(ContractInfoOf::::get(&addr).unwrap().storage_deposit, 550); + + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Slashed { + who: addr.clone(), + amount: 90, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::ReserveRepatriated { + from: addr.clone(), + to: ALICE, + amount: 10, + destination_status: BalanceStatus::Free, + }), + topics: vec![], + }, + ] + ); + }); +} + #[test] fn cannot_self_destruct_while_live() { let (wasm, code_hash) = compile_module::("self_destruct").unwrap(); @@ -2621,3 +2699,111 @@ fn storage_deposit_works() { ); }); } + +#[test] +fn call_after_killed_accout_needs_funding() { + let (wasm, code_hash) = compile_module::("dummy").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let min_balance = ::Currency::minimum_balance(); + + assert_ok!(Contracts::instantiate_with_code( + Origin::signed(ALICE), + 700, + GAS_LIMIT, + None, + wasm, + vec![], + vec![], + )); + let addr = Contracts::contract_address(&ALICE, &code_hash, &[]); + + // Drop previous events + initialize_block(2); + + // Destroy the account of the contract by slashing. + // Slashing can actually happen if the contract takes part in staking. + // It is a corner case and we except the destruction of the account. + let _ = ::Currency::slash( + &addr, + ::Currency::total_balance(&addr), + ); + + // Sending below the minimum balance will fail the call because it needs to create the + // account in order to send balance there. + assert_err_ignore_postinfo!( + Contracts::call( + Origin::signed(ALICE), + addr.clone(), + min_balance - 1, + GAS_LIMIT, + None, + vec![], + ), + >::TransferFailed + ); + + // Sending zero should work as it does not do a transfer + assert_ok!(Contracts::call( + Origin::signed(ALICE), + addr.clone(), + 0, + GAS_LIMIT, + None, + vec![], + )); + + // Sending minimum balance should work + assert_ok!(Contracts::call( + Origin::signed(ALICE), + addr.clone(), + min_balance, + GAS_LIMIT, + None, + vec![], + )); + + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: Event::System(frame_system::Event::KilledAccount { + account: addr.clone() + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Slashed { + who: addr.clone(), + amount: min_balance + 700 + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::System(frame_system::Event::NewAccount { account: addr.clone() }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Endowed { + account: addr.clone(), + free_balance: min_balance + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: Event::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: addr.clone(), + amount: min_balance + }), + topics: vec![], + }, + ] + ); + }); +} From d6e32957fc8cfde636c7be4f5fb7bed233f3392d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Tue, 7 Dec 2021 10:14:14 +0100 Subject: [PATCH 28/34] Apply suggestions from code review Co-authored-by: Andrew Jones --- frame/contracts/src/exec.rs | 6 +++--- frame/contracts/src/storage/meter.rs | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index b7754ad525f0e..916c2d3df84f5 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -108,7 +108,7 @@ pub trait Ext: sealing::Sealed { /// /// Returns the original code size of the called contract. /// The newly created account will be associated with `code`. `value` specifies the amount of - /// value transferred from this to the newly created account (also known as value). + /// value transferred from this to the newly created account. /// /// # Return Value /// @@ -155,7 +155,7 @@ pub trait Ext: sealing::Sealed { /// The `value_transferred` is already added. fn balance(&self) -> BalanceOf; - /// Returns the value transferred along with this call or as value. + /// Returns the value transferred along with this call. fn value_transferred(&self) -> BalanceOf; /// Returns a reference to the timestamp of the current block @@ -667,7 +667,7 @@ where let entry_point = self.top_frame().entry_point; let do_transaction = || { // We need to charge the storage deposit before the initial transfer so that - // it can create the account incase the initial transfer is < ed. + // it can create the account in case the initial transfer is < ed. if entry_point == ExportedFunction::Constructor { let top_frame = top_frame_mut!(self); top_frame.nested_storage.charge_instantiate( diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs index bebf0b3084066..cb2c7005fce43 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -51,7 +51,7 @@ pub trait Ext { /// /// It is necessary to do this check beforehand so that the charge won't fail later on. /// - /// `origin`: The origin of the call stack from which is reponsible for putting down a deposit. + /// `origin`: The origin of the call stack from which is responsible for putting down a deposit. /// `limit`: The limit with which the meter was constructed. /// `min_leftover`: How much `free_balance` in addition to the ed should be left inside the /// `origin` account. @@ -70,7 +70,7 @@ pub trait Ext { /// around depending on whether `amount` constitues a `Charge` or a `Refund`. /// It is garantueed that that this succeeds because no more balance then returned by /// `check_limit` is ever charged. This is why this function is infallible. - // `terminated` designates whether the `contract` was terminated. + /// `terminated` designates whether the `contract` was terminated. fn charge( origin: &T::AccountId, contract: &T::AccountId, @@ -194,7 +194,7 @@ where /// /// `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. + /// `contract`: The contract that this sub call belongs to. /// `info`: The info of the contract in question. `None` if the contract was terminated. pub fn absorb( &mut self, @@ -203,7 +203,7 @@ where contract: &T::AccountId, info: Option<&mut ContractInfo>, ) { - // Absorbing from an exisiting (non terminated) contract. + // Absorbing from an existing (non terminated) contract. if let Some(info) = info { match &mut absorbed.own_deposit { Deposit::Charge(amount) => @@ -294,7 +294,7 @@ where /// Charge from `origin` a storage deposit for contract instantiation. /// - /// This immediatly transfers the balance in order to create the account. + /// This immediately transfers the balance in order to create the account. pub fn charge_instantiate( &mut self, origin: &T::AccountId, @@ -322,7 +322,7 @@ where } self.total_deposit = total_deposit; if !deposit.is_zero() { - // We need to charge immediatly so that the account is created before the `value` + // We need to charge immediately so that the account is created before the `value` // is transferred from the caller to the contract. E::charge(origin, contract, &deposit, false); } From 9435231ca82dced0e90750ea4b2524fecc59512d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Tue, 7 Dec 2021 10:19:08 +0100 Subject: [PATCH 29/34] Update `Storage::write` docs --- frame/contracts/src/storage.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/frame/contracts/src/storage.rs b/frame/contracts/src/storage.rs index f76517aed1a2c..6d67729a9a682 100644 --- a/frame/contracts/src/storage.rs +++ b/frame/contracts/src/storage.rs @@ -89,11 +89,10 @@ where /// Update a storage entry into a contract's kv storage. /// - /// If the `opt_new_value` is `None` then the kv pair is removed. + /// If the `new_value` is `None` then the kv pair is removed. /// - /// This function also updates the bookkeeping info such as: number of total non-empty pairs a - /// 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. + /// This function also records how much storage was created or removed if a `storage_meter` + /// is supplied. It should only be absent for testing or benchmarking code. pub fn write( trie_id: &TrieId, key: &StorageKey, From 18de7478fa5ab11d53ebd534d0b5279d24787efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Tue, 7 Dec 2021 10:40:36 +0100 Subject: [PATCH 30/34] Improve doc --- frame/contracts/src/storage/meter.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs index cb2c7005fce43..fc42e0b426c57 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -68,7 +68,7 @@ pub trait Ext { /// /// 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 this succeeds because no more balance then returned by + /// It is garantueed that that this succeeds because no more balance than returned by /// `check_limit` is ever charged. This is why this function is infallible. /// `terminated` designates whether the `contract` was terminated. fn charge( @@ -390,7 +390,7 @@ impl Ext for ReservingExt { ) .and_then(|_| T::Currency::reserve(contract, *amount)); if let Err(err) = result { - log::warn!( + log::error!( target: "runtime::contracts", "Failed to transfer storage deposit {:?} from origin {:?} to contract {:?}: {:?}", amount, origin, contract, err, @@ -407,6 +407,10 @@ impl Ext for ReservingExt { let amount = if terminated { *amount } else { + // This is necessary when the `storage_deposit` tracked inside the account + // info is out of sync with the actual balance. That can only happen due to + // slashing. We make sure to never dust the contract's account through a + // refund because we consider this unexpected behaviour. *amount.min( &T::Currency::reserved_balance(contract) .saturating_sub(T::Currency::minimum_balance()), @@ -415,7 +419,7 @@ impl Ext for ReservingExt { let result = T::Currency::repatriate_reserved(contract, origin, amount, BalanceStatus::Free); if matches!(result, Ok(val) if !val.is_zero()) || matches!(result, Err(_)) { - log::warn!( + log::error!( target: "runtime::contracts", "Failed to repatriate storage deposit {:?} from contract {:?} to origin {:?}: {:?}", amount, contract, origin, result, From 9d92ccbebb58eb457d2b614d4401715980a75e4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Tue, 7 Dec 2021 10:49:11 +0100 Subject: [PATCH 31/34] Remove superflous conditional --- frame/contracts/common/src/lib.rs | 6 +++--- frame/contracts/src/storage/meter.rs | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/frame/contracts/common/src/lib.rs b/frame/contracts/common/src/lib.rs index 1dca78aeaa75a..d8408f77e06e2 100644 --- a/frame/contracts/common/src/lib.rs +++ b/frame/contracts/common/src/lib.rs @@ -202,11 +202,11 @@ impl Default for StorageDeposit { } } -impl StorageDeposit { +impl StorageDeposit { /// Returns how much balance is charged or `0` in case of a refund. - pub fn charge_or_zero(self) -> Balance { + pub fn charge_or_zero(&self) -> Balance { match self { - Self::Charge(amount) => amount, + Self::Charge(amount) => *amount, Self::Refund(_) => Zero::zero(), } } diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs index fc42e0b426c57..24fa55efbc918 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -309,6 +309,7 @@ where ..Default::default() } .to_deposit::(); + debug_assert!(matches!(deposit, Deposit::Charge(_))); // We do not increase `own_deposit` because this will be charged later when the contract // execution does conclude. let total_deposit = self.total_deposit.saturating_add(&deposit); @@ -317,9 +318,7 @@ where return Err(>::StorageDepositLimitExhausted.into()) } } - if let Deposit::Charge(amount) = &deposit { - info.storage_deposit = info.storage_deposit.saturating_add(*amount); - } + info.storage_deposit = info.storage_deposit.saturating_add(deposit.charge_or_zero()); self.total_deposit = total_deposit; if !deposit.is_zero() { // We need to charge immediately so that the account is created before the `value` From 0fdf0cd505fc7f2910f1f224d824a7a12fc052d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Tue, 7 Dec 2021 12:27:38 +0100 Subject: [PATCH 32/34] Typos --- frame/contracts/src/storage/meter.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs index 24fa55efbc918..2b29593a7706a 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -67,8 +67,8 @@ pub trait Ext { /// 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 this succeeds because no more balance than returned by + /// around depending on whether `amount` constitutes a `Charge` or a `Refund`. + /// It is guaranteed that that this succeeds because no more balance than returned by /// `check_limit` is ever charged. This is why this function is infallible. /// `terminated` designates whether the `contract` was terminated. fn charge( From e09c89eadf71e5ba748d2fcc85fe42f3eb5b9855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Tue, 7 Dec 2021 12:29:53 +0100 Subject: [PATCH 33/34] Remove superflous clone (refactoring artifact) --- frame/contracts/src/storage/meter.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs index 2b29593a7706a..96ce02fd83ae7 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -265,8 +265,7 @@ where /// 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() + self.total_deposit } } From a92d39d814cf85e92fc54d4cefdb1666b3ac0fbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Tue, 7 Dec 2021 12:34:07 +0100 Subject: [PATCH 34/34] Apply suggestions from code review Co-authored-by: Andrew Jones --- frame/contracts/src/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index be1993aa95886..f19f5a3159557 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -2360,7 +2360,7 @@ fn remove_code_in_use() { initialize_block(2); assert_noop!( - Contracts::remove_code(Origin::signed(BOB), code_hash), + Contracts::remove_code(Origin::signed(ALICE), code_hash), >::CodeInUse, ); @@ -2701,7 +2701,7 @@ fn storage_deposit_works() { } #[test] -fn call_after_killed_accout_needs_funding() { +fn call_after_killed_account_needs_funding() { let (wasm, code_hash) = compile_module::("dummy").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000);