From 25de2fbbbdad179e472972408843a5cb3c841e22 Mon Sep 17 00:00:00 2001 From: pgherveou <pgherveou@gmail.com> Date: Fri, 12 Apr 2024 15:11:14 +0200 Subject: [PATCH] Revert "Contracts: Remove ED from base deposit (#3536)" This reverts commit 643aa2be2a2c0611eeb648cfc21eb4cb3c1c9cd8. --- .../frame/contracts/src/benchmarking/mod.rs | 22 +--- substrate/frame/contracts/src/lib.rs | 2 +- substrate/frame/contracts/src/migration.rs | 1 - .../frame/contracts/src/migration/v16.rs | 107 ------------------ substrate/frame/contracts/src/storage.rs | 13 ++- .../frame/contracts/src/storage/meter.rs | 27 +++-- substrate/frame/contracts/src/tests.rs | 21 ++-- substrate/frame/contracts/src/weights.rs | 23 ---- 8 files changed, 42 insertions(+), 174 deletions(-) delete mode 100644 substrate/frame/contracts/src/migration/v16.rs diff --git a/substrate/frame/contracts/src/benchmarking/mod.rs b/substrate/frame/contracts/src/benchmarking/mod.rs index 676fd320a17..9e245c319b1 100644 --- a/substrate/frame/contracts/src/benchmarking/mod.rs +++ b/substrate/frame/contracts/src/benchmarking/mod.rs @@ -32,7 +32,7 @@ use self::{ use crate::{ exec::Key, migration::{ - codegen::LATEST_MIGRATION_VERSION, v09, v10, v11, v12, v13, v14, v15, v16, MigrationStep, + codegen::LATEST_MIGRATION_VERSION, v09, v10, v11, v12, v13, v14, v15, MigrationStep, }, Pallet as Contracts, *, }; @@ -331,26 +331,6 @@ mod benchmarks { Ok(()) } - // This benchmarks the v16 migration step (Remove ED from base_deposit). - #[benchmark(pov_mode = Measured)] - fn v16_migration_step() -> Result<(), BenchmarkError> { - let contract = - <Contract<T>>::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?; - - let info = contract.info()?; - let base_deposit = v16::store_old_contract_info::<T>(contract.account_id.clone(), &info); - let mut m = v16::Migration::<T>::default(); - - #[block] - { - m.step(&mut WeightMeter::new()); - } - let ed = Pallet::<T>::min_balance(); - let info = v16::ContractInfoOf::<T>::get(&contract.account_id).unwrap(); - assert_eq!(info.storage_base_deposit, base_deposit - ed); - Ok(()) - } - // This benchmarks the weight of executing Migration::migrate to execute a noop migration. #[benchmark(pov_mode = Measured)] fn migration_noop() { diff --git a/substrate/frame/contracts/src/lib.rs b/substrate/frame/contracts/src/lib.rs index 73c70a7704e..9a1a0aada89 100644 --- a/substrate/frame/contracts/src/lib.rs +++ b/substrate/frame/contracts/src/lib.rs @@ -244,7 +244,7 @@ pub mod pallet { use sp_runtime::Perbill; /// The in-code storage version. - pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(16); + pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(15); #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] diff --git a/substrate/frame/contracts/src/migration.rs b/substrate/frame/contracts/src/migration.rs index c633ba9c2d5..b0df4508285 100644 --- a/substrate/frame/contracts/src/migration.rs +++ b/substrate/frame/contracts/src/migration.rs @@ -64,7 +64,6 @@ pub mod v12; pub mod v13; pub mod v14; pub mod v15; -pub mod v16; include!(concat!(env!("OUT_DIR"), "/migration_codegen.rs")); use crate::{weights::WeightInfo, Config, Error, MigrationInProgress, Pallet, Weight, LOG_TARGET}; diff --git a/substrate/frame/contracts/src/migration/v16.rs b/substrate/frame/contracts/src/migration/v16.rs deleted file mode 100644 index 74fbc997718..00000000000 --- a/substrate/frame/contracts/src/migration/v16.rs +++ /dev/null @@ -1,107 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 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. - -//! Remove ED from storage base deposit. -//! See <https://github.com/paritytech/polkadot-sdk/pull/3536>. - -use crate::{ - migration::{IsFinished, MigrationStep}, - weights::WeightInfo, - BalanceOf, CodeHash, Config, Pallet, TrieId, Weight, WeightMeter, LOG_TARGET, -}; -use codec::{Decode, Encode}; -use frame_support::{pallet_prelude::*, storage_alias, DefaultNoBound}; -use sp_runtime::{BoundedBTreeMap, Saturating}; -use sp_std::prelude::*; - -#[cfg(feature = "runtime-benchmarks")] -pub fn store_old_contract_info<T: Config>( - account: T::AccountId, - info: &crate::ContractInfo<T>, -) -> BalanceOf<T> { - let storage_base_deposit = Pallet::<T>::min_balance() + 1u32.into(); - ContractInfoOf::<T>::insert( - account, - ContractInfo { - trie_id: info.trie_id.clone(), - code_hash: info.code_hash, - storage_bytes: Default::default(), - storage_items: Default::default(), - storage_byte_deposit: Default::default(), - storage_item_deposit: Default::default(), - storage_base_deposit, - delegate_dependencies: Default::default(), - }, - ); - - storage_base_deposit -} - -#[storage_alias] -pub type ContractInfoOf<T: Config> = - StorageMap<Pallet<T>, Twox64Concat, <T as frame_system::Config>::AccountId, ContractInfo<T>>; - -#[derive(Encode, Decode, CloneNoBound, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] -#[scale_info(skip_type_params(T))] -pub struct ContractInfo<T: Config> { - trie_id: TrieId, - code_hash: CodeHash<T>, - storage_bytes: u32, - storage_items: u32, - storage_byte_deposit: BalanceOf<T>, - storage_item_deposit: BalanceOf<T>, - pub storage_base_deposit: BalanceOf<T>, - delegate_dependencies: BoundedBTreeMap<CodeHash<T>, BalanceOf<T>, T::MaxDelegateDependencies>, -} - -#[derive(Encode, Decode, MaxEncodedLen, DefaultNoBound)] -pub struct Migration<T: Config> { - last_account: Option<T::AccountId>, -} - -impl<T: Config> MigrationStep for Migration<T> { - const VERSION: u16 = 16; - - fn max_step_weight() -> Weight { - T::WeightInfo::v16_migration_step() - } - - fn step(&mut self, meter: &mut WeightMeter) -> IsFinished { - let mut iter = if let Some(last_account) = self.last_account.take() { - ContractInfoOf::<T>::iter_keys_from(ContractInfoOf::<T>::hashed_key_for(last_account)) - } else { - ContractInfoOf::<T>::iter_keys() - }; - - if let Some(key) = iter.next() { - log::debug!(target: LOG_TARGET, "Migrating contract {:?}", key); - ContractInfoOf::<T>::mutate(key.clone(), |info| { - let ed = Pallet::<T>::min_balance(); - let mut updated_info = info.take().expect("Item exists; qed"); - updated_info.storage_base_deposit.saturating_reduce(ed); - *info = Some(updated_info); - }); - self.last_account = Some(key); - meter.consume(T::WeightInfo::v16_migration_step()); - IsFinished::No - } else { - log::debug!(target: LOG_TARGET, "No more contracts to migrate"); - meter.consume(T::WeightInfo::v16_migration_step()); - IsFinished::Yes - } - } -} diff --git a/substrate/frame/contracts/src/storage.rs b/substrate/frame/contracts/src/storage.rs index 1e9739a1599..fc6d514ebf4 100644 --- a/substrate/frame/contracts/src/storage.rs +++ b/substrate/frame/contracts/src/storage.rs @@ -23,7 +23,7 @@ use crate::{ exec::{AccountIdOf, Key}, weights::WeightInfo, BalanceOf, CodeHash, CodeInfo, Config, ContractInfoOf, DeletionQueue, DeletionQueueCounter, - Error, TrieId, SENTINEL, + Error, Pallet, TrieId, SENTINEL, }; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ @@ -125,7 +125,9 @@ impl<T: Config> ContractInfo<T> { /// Same as [`Self::extra_deposit`] but including the base deposit. pub fn total_deposit(&self) -> BalanceOf<T> { - self.extra_deposit().saturating_add(self.storage_base_deposit) + self.extra_deposit() + .saturating_add(self.storage_base_deposit) + .saturating_sub(Pallet::<T>::min_balance()) } /// Returns the storage base deposit of the contract. @@ -211,6 +213,7 @@ impl<T: Config> ContractInfo<T> { /// The base deposit is updated when the `code_hash` of the contract changes, as it depends on /// the deposit paid to upload the contract's code. pub fn update_base_deposit(&mut self, code_info: &CodeInfo<T>) -> BalanceOf<T> { + let ed = Pallet::<T>::min_balance(); let info_deposit = Diff { bytes_added: self.encoded_size() as u32, items_added: 1, ..Default::default() } .update_contract::<T>(None) @@ -221,7 +224,11 @@ impl<T: Config> ContractInfo<T> { // to prevent abuse. let upload_deposit = T::CodeHashLockupDepositPercent::get().mul_ceil(code_info.deposit()); - let deposit = info_deposit.saturating_add(upload_deposit); + // Instantiate needs to transfer at least the minimum balance in order to pull the + // contract's own account into existence, as the deposit itself does not contribute to the + // `ed`. + let deposit = info_deposit.saturating_add(upload_deposit).saturating_add(ed); + self.storage_base_deposit = deposit; deposit } diff --git a/substrate/frame/contracts/src/storage/meter.rs b/substrate/frame/contracts/src/storage/meter.rs index 5db9a772ad8..495cbd90db5 100644 --- a/substrate/frame/contracts/src/storage/meter.rs +++ b/substrate/frame/contracts/src/storage/meter.rs @@ -435,12 +435,22 @@ where contract: &T::AccountId, contract_info: &mut ContractInfo<T>, code_info: &CodeInfo<T>, - ) -> Result<(), DispatchError> { + ) -> Result<DepositOf<T>, DispatchError> { debug_assert!(matches!(self.contract_state(), ContractState::Alive)); - - // We need to make sure that the contract's account exists. let ed = Pallet::<T>::min_balance(); + + let deposit = contract_info.update_base_deposit(&code_info); + if deposit > self.limit { + return Err(<Error<T>>::StorageDepositLimitExhausted.into()) + } + + let deposit = Deposit::Charge(deposit); + + // We do not increase `own_contribution` because this will be charged later when the + // contract execution does conclude and hence would lead to a double charge. self.total_deposit = Deposit::Charge(ed); + + // We need to make sure that the contract's account exists. T::Currency::transfer(origin, contract, ed, Preservation::Preserve)?; // A consumer is added at account creation and removed it on termination, otherwise the @@ -448,11 +458,9 @@ where // With the consumer, a correct runtime cannot remove the account. System::<T>::inc_consumers(contract)?; - let deposit = contract_info.update_base_deposit(&code_info); - let deposit = Deposit::Charge(deposit); + self.charge_deposit(contract.clone(), deposit.saturating_sub(&Deposit::Charge(ed))); - self.charge_deposit(contract.clone(), deposit); - Ok(()) + Ok(deposit) } /// Call to tell the meter that the currently executing contract was terminated. @@ -851,14 +859,14 @@ mod tests { let test_cases = vec![ ChargingTestCase { origin: Origin::<Test>::from_account_id(ALICE), - deposit: Deposit::Refund(108), + deposit: Deposit::Refund(107), expected: TestExt { limit_checks: vec![LimitCheck { origin: ALICE, limit: 1_000, min_leftover: 0 }], charges: vec![ Charge { origin: ALICE, contract: CHARLIE, - amount: Deposit::Refund(120), + amount: Deposit::Refund(119), state: ContractState::Terminated { beneficiary: CHARLIE }, }, Charge { @@ -907,6 +915,7 @@ mod tests { meter.absorb(nested0, &BOB, None); assert_eq!(meter.try_into_deposit(&test_case.origin).unwrap(), test_case.deposit); + assert_eq!(TestExtTestValue::get(), test_case.expected) } } diff --git a/substrate/frame/contracts/src/tests.rs b/substrate/frame/contracts/src/tests.rs index 0b83358a7f5..7a910a6a8f9 100644 --- a/substrate/frame/contracts/src/tests.rs +++ b/substrate/frame/contracts/src/tests.rs @@ -3817,7 +3817,7 @@ fn locking_delegate_dependency_works() { &HoldReason::StorageDepositReserve.into(), &addr_caller ), - dependency_deposit + contract.storage_base_deposit() + dependency_deposit + contract.storage_base_deposit() - ED ); // Removing the code should fail, since we have added a dependency. @@ -3856,7 +3856,7 @@ fn locking_delegate_dependency_works() { &HoldReason::StorageDepositReserve.into(), &addr_caller ), - contract.storage_base_deposit() + contract.storage_base_deposit() - ED ); // Removing a nonexistent dependency should fail. @@ -3888,7 +3888,7 @@ fn locking_delegate_dependency_works() { assert_ok!(call(&addr_caller, &terminate_input).result); assert_eq!( test_utils::get_balance(&ALICE), - ED + balance_before + contract.storage_base_deposit() + dependency_deposit + balance_before + contract.storage_base_deposit() + dependency_deposit ); // Terminate should also remove the dependency, so we can remove the code. @@ -3904,9 +3904,13 @@ fn native_dependency_deposit_works() { // Set hash lock up deposit to 30%, to test deposit calculation. CODE_HASH_LOCKUP_DEPOSIT_PERCENT.with(|c| *c.borrow_mut() = Perbill::from_percent(30)); + // Set a low existential deposit so that the base storage deposit is based on the contract + // storage deposit rather than the existential deposit. + const ED: u64 = 10; + // Test with both existing and uploaded code for code in [Code::Upload(wasm.clone()), Code::Existing(code_hash)] { - ExtBuilder::default().build().execute_with(|| { + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { let _ = Balances::set_balance(&ALICE, 1_000_000); let lockup_deposit_percent = CodeHashLockupDepositPercent::get(); @@ -3938,16 +3942,16 @@ fn native_dependency_deposit_works() { let res = builder::bare_instantiate(code).build(); let addr = res.result.unwrap().account_id; - let base_deposit = test_utils::contract_info_storage_deposit(&addr); + let base_deposit = ED + test_utils::contract_info_storage_deposit(&addr); let upload_deposit = test_utils::get_code_deposit(&code_hash); let extra_deposit = add_upload_deposit.then(|| upload_deposit).unwrap_or_default(); // Check initial storage_deposit - // The base deposit should be: contract_info_storage_deposit + 30% * deposit + // The base deposit should be: ED + contract_info_storage_deposit + 30% * deposit let deposit = extra_deposit + base_deposit + lockup_deposit_percent.mul_ceil(upload_deposit); - assert_eq!(res.storage_deposit.charge_or_zero(), deposit + Contracts::min_balance()); + assert_eq!(res.storage_deposit.charge_or_zero(), deposit); // call set_code_hash builder::bare_call(addr.clone()) @@ -3958,10 +3962,9 @@ fn native_dependency_deposit_works() { let code_deposit = test_utils::get_code_deposit(&dummy_code_hash); let deposit = base_deposit + lockup_deposit_percent.mul_ceil(code_deposit); assert_eq!(test_utils::get_contract(&addr).storage_base_deposit(), deposit); - assert_eq!( test_utils::get_balance_on_hold(&HoldReason::StorageDepositReserve.into(), &addr), - deposit + deposit - ED ); }); } diff --git a/substrate/frame/contracts/src/weights.rs b/substrate/frame/contracts/src/weights.rs index ca7f58cf5b0..5c69d9218fa 100644 --- a/substrate/frame/contracts/src/weights.rs +++ b/substrate/frame/contracts/src/weights.rs @@ -58,7 +58,6 @@ pub trait WeightInfo { fn v13_migration_step() -> Weight; fn v14_migration_step() -> Weight; fn v15_migration_step() -> Weight; - fn v16_migration_step() -> Weight; fn migration_noop() -> Weight; fn migrate() -> Weight; fn on_runtime_upgrade_noop() -> Weight; @@ -272,17 +271,6 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - fn v16_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `409` - // Estimated: `6349` - // Minimum execution time: 12_595_000 picoseconds. - Weight::from_parts(13_059_000, 6349) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) fn migration_noop() -> Weight { @@ -1552,17 +1540,6 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - fn v16_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `409` - // Estimated: `6349` - // Minimum execution time: 12_595_000 picoseconds. - Weight::from_parts(13_059_000, 6349) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) fn migration_noop() -> Weight { -- GitLab